这两天一直在处理flv视频环境的搭建工作,包括服务器的安装和java中的应用。安装ffmpeg加mencoder倒没有什么大问题,不过还是有一个小问题弄得我郁闷了下,就是在安装amrwb和amrnb的时候出错,错误如下:
/usr/bin/wget -N http://www.3gpp.org/ftp/Specs/archive/26_series/26.104/26104-610.zip
--20:41:59-- http://www.3gpp.org/ftp/Specs/archive/26_series/26.104/26104-610.zip
正在解析主机 www.3gpp.org... 212.234.161.21
Connecting to www.3gpp.org|212.234.161.21|:80... 已连接。
已发出 HTTP 请求,正在等待回应... 403 Forbidden
20:42:00 错误 403:Forbidden。
make: *** [26104-610.zip] 错误 1
后来仔细看了和问了下公司的网络管理员,才知道我这台测试机器没有开外网,刚开始也想到了是这个问题,可ping的时候却可以ping通,所以就放弃了那个想法,谁知道还真是这问题,结果就好说咯,开通网络后安装就非常顺利了~~~这是题外话了,呵
ffmpeg的环境搭建起来后,在本地进行手动转换没什么问题,通过程序调用(不获取程序反馈结果)也没什么问题。可后来进一步深入的时候,我在提交视频的转换请求的时候,必然要进行数据库的相关操作,比如在转换前更新该视频的状态为正在更新状态,在转换完毕后填如该视频的flv文件路径和图片路径信息,还有flv文件的大小和时间长度等等数据。这就需要在线程中控制ffmpeg进程的状态了,这里我们就需要用到Process这个类了,典型的我们写代码如下:
……
Process process = Runtime.getRuntime.exec(cmd); // 执行调用ffmpeg命令
InputStream is = process.getInputStream(); // 获取ffmpeg进程的输出流
BufferedReader br = new Buffered(new InputStreamReader(is)); // 缓冲读入
StringBuilder buf = new StringBuilder(); // 保存ffmpeg的输出结果流
String line = null;
while((line = br.readLine()) != null) buf.append(line); // 循环等待ffmpeg进程结束
System.out.println("ffmpeg输出内容为:" + buf);
……
本来一般都是这样来调用程序并获取进程的输出流的,但是我在windows上执行这样的调用的时候却总是在while那里被堵塞了,结果造成ffmpeg程序在执行了一会后不再执行,这里从官方的参考文档中我们可以看到这是由于缓冲区的问题,由于java进程没有清空ffmpeg程序写到缓冲区的内容,结果导致ffmpeg程序一直在等待。在网上也查找了很多这样的问题,不过说的都是使用单独的线程来进行控制,我也尝试过很多网是所说的方法,可一直没起什么作用。下面就是我的解决方法了,注意到上述代码中的红色部分了么?这里就是关键,我把它改成如下结果就可以正常运行了。
InputStream is = process.getErrorStream(); // 获取ffmpeg进程的输出流
注意到没?我把它改成获取错误流这样进程就不会被堵塞了,而我之前一直想的是同样的命令我手动调用的时候可以完成,而java调用却总是完成不了,一直认为是getInputStream的缓冲区没有被清空,不过问题确实是缓冲区的内容没有被清空,但不是getInputStream的,而是getErrorStream的缓冲区,这样问题就得到解决了。所以我们在遇到java调用外部程序而导致线程阻塞的时候,可以考虑使用两个线程来同时清空process获取的两个输入流,如下这段程序:
……
Process process = Runtime.getRuntime.exec(command); // 调用外部程序
final InputStream is1 = process.getInputStream();
new Thread(new Runnable() {
public void run() {
BufferedReader br = new Buffered(new InputStreamReader(is));
while(br.readLine() != null) ;
}
}.start(); // 启动单独的线程来清空process.getInputStream()的缓冲区
InputStream is2 = process.getErrorStream();
BufferedReader br2 = new Buffered(new InputStreamReader(is2));
StringBuilder buf = new StringBuilder(); // 保存输出结果流
String line = null;
while((line = br.readLine()) != null) buf.append(line); // 循环等待ffmpeg进程结束
System.out.println("输出结果为:" + buf);
……
通过这样我们使用一个线程来读取process.getInputStream()的输出流,使用另外一个线程来获取process.getErrorStream()的输出流,这样我们就可以保证缓冲区得到及时的清空而不担心线程被阻塞了。当然根据需要你也可以保留process.getInputStream()流中的内容,这个就看调用的程序的处理了。我在windows下调用FFmpeg程序进行视频转换的时候就是通过这样来解决线程被堵塞的问题的,呵呵~
所以我觉得像Process.getInputStream和process.getErrorStream两个方法并不是真的就像名称表示的那样,一个是获取被调用程序的正常输出,一个是获取被调用程序的错误流。有时候错误流(即ErrorStream)也是程序的正常输出,因为毕竟java在调用windows和linux下的都是一套这样的程序,所以我们在写程序的时候应该打开思路,我们自己通常认为理所当然的并不一定是正确的呢!
相关推荐
在进行系统集成或自动化脚本编写时,经常需要从Java程序中调用外部程序或命令。这种需求常见于多种场景,例如远程服务管理、批处理操作等。本文将详细介绍如何使用Java来调用外部程序,并解决在这一过程中可能遇到的...
在提供的`Test`文件中,可能包含一个演示了上述概念的Java程序。实际使用时,需要根据具体需求调整`ProcessBuilder`构造的命令和参数。 总结来说,Java中的`Process`和`ProcessBuilder`是实现与操作系统交互的关键...
在Java中,可以通过`java.lang.Runtime`类或者`java.lang.ProcessBuilder`类来启动外部程序(exe文件)。这两种方式都可以实现同样的功能,但在实际开发过程中,选择哪一种方式取决于具体需求和个人偏好。 - **...
当执行的程序会产生大量的标准输出时,可能会导致DOS窗口不会自动关闭,进而使得Java程序在`waitFor()`处阻塞。为了解决这个问题,可以通过Java的`BufferedReader`和`InputStreamReader`来读取并处理这些输出。 ...
Java提供了`java.lang.Runtime`类和`java.lang.Process`类来帮助开发者在Java程序中执行外部命令。最常用的方法是`Runtime.getRuntime().exec(command)`,该方法用于创建一个新的子进程来执行指定的命令,并返回一个...
通过以上方式,我们可以在Java程序中调用`doc`命令,即启动Word并打开指定的文档。同时,`CMDExec`可能指的是使用命令行执行操作的场景,但作为一个具体的库或工具,它并不常见于Java生态中。如果你指的是某种特定的...
总的来说,Java调用DOS命令的能力使得开发者能够在Java程序中集成系统级别的操作,极大地扩展了其功能。熟练掌握这一技术,可以帮助我们更好地将Java与操作系统环境结合,实现更复杂的应用场景。
在实际开发过程中,我们经常需要在Java程序中调用Shell脚本或Python脚本来执行一些特定的任务,比如系统管理任务、数据分析等。Java通过`java.lang.Runtime`类提供了非常方便的方式来启动新的进程,并与之通信。然而...
本文将深入探讨如何在Java程序中调用Shell脚本,理解其背后的原理,并提供一系列实用代码示例与应用场景。 ### Java调用Shell的基本原理 Java调用Shell主要依赖于`java.lang.Runtime`类中的`exec()`方法。这个方法...
本文将详细介绍如何在Java程序中执行这些任务,并提供具体的示例来帮助理解。 #### Runtime类简介 `java.lang.Runtime`类提供了运行时环境的信息和操作,包括执行外部进程的能力。`Runtime.getRuntime()`方法用于...
在Java编程语言中,进程通信(Process Communication)是多进程应用程序之间交换信息的方式。Java提供了多种机制来实现进程间的通信,这些机制可以帮助开发者构建复杂的分布式系统。本资料包"基于Java的进程通信.zip...
在Java中,`java.lang.ProcessBuilder`类和`java.lang.Process`接口是进行进程交互的核心工具。`ProcessBuilder`用于创建新的操作系统进程,并允许设置命令行参数、工作目录和环境变量。一旦启动,它将返回一个`...
- 当需要在Java应用程序中执行一些操作系统级别的操作,而这些操作在Java标准库中没有直接支持时,调用批处理文件是一个可行的解决方案。 8. **安全性考虑** - 调用外部程序(包括批处理文件)可能带来安全风险,...
首先,文档中提到了`Runtime`类,它位于`java.lang`包下,是Java程序中进行系统级别的操作时最常用的类之一。`Runtime`类允许Java程序与运行环境交互,比如执行系统命令、获取系统资源使用情况、内存管理等。特别地...
如果在实际操作中遇到类似问题,如Java通过`Runtime`调用外部程序出现阻塞,可以参考相关的解决方法,例如使用`StreamGobbler`类进行异步读取。 总的来说,通过Java Source在Oracle中调用外部程序,可以实现数据库...
Java管道(Pipes)和EXEC在Java...总结来说,Java管道和EXEC提供了强大的能力,允许开发者在Java应用程序中调用外部系统命令并进行数据交换。通过熟练掌握这些工具,开发者可以构建更加灵活和高效的跨平台应用程序。
例如,它不处理标准输入/输出和错误流,这可能导致程序阻塞或数据丢失。为了正确处理这些流,你应该使用`ProcessBuilder`类,它提供了更灵活的控制: ```java List<String> commands = Arrays.asList("/bin/bash", ...
这些功能允许开发者在Java程序中调用操作系统提供的功能,从而实现自动化任务或者与系统进行更深度的交互。 首先,我们来看如何在Java中运行DOS命令。Java提供了`Runtime`类和`ProcessBuilder`类来执行外部命令。`...
Java线程可以通过继承`java.lang.Thread`类或实现`Runnable`接口来创建。 ##### 创建线程 1. **继承Thread类** - 创建`Thread`类的子类并重写`run()`方法。 - 实例化子类对象,并调用`start()`方法启动线程。 ...