视频转码(同一资源同时进行不同格式的转码)
缘由:这个是工作的时候遇到的问题,就是我们在开发网页的时候,发现有一些MP4格式的视频无法在谷歌上播放,原因的是h5的视频解码器不支持这个视频,因此,我们用ffmpeg进行转码。后来发现IE5不支持video标签,只支持flash播放,而flash只能播放.swf格式的视频。所以就有了这么一个业务需求,一个源视频就要进行两次转码。一次转成h5支持解码的.mp4文件,一次转成.swf文件。最初的实现方案是对源视频进行串行转码,先进行.mp4的转码,后进行.swf转码。在这两次转码过程中都需要开两个子线程,阻塞主线程进行转码。过程图:
后来感觉串行转码效率太慢,所以决定改成并行转码。过程图:
即.mp4和.swf文件同时进行转码。同时进行转码就要额外再开两个子线程。再开线程就要考虑两个问题,一,线程同时访问共享资源是否要加锁的问题,二,主线程的阻塞和这两个线程的阻塞以及主线程的唤醒和这两个子线程的唤醒顺序问题。
第一个问题,虽然两个子线程访问的同一资源(共享),但两个子线程都没有进行写操作,所以不需要加锁。
第二个问题,开启两个子线程1和子线程2,这时候需要阻塞主线程,当开启子线程11和子线程12时,阻塞子线程1,当子线程11和子线程12完成时,唤醒子线程1.子线程2同理,最后唤醒主线程。考虑到以后可能还需要转其他格式的视频,比如说.flv,因此开始子线程需要写成通用的代码。因此代码如下:
public static boolean process() {
int threadNumber = 2;
final CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
for (int i = 0; i < threadNumber; i++) {
final int threadID = i;
new Thread() {
public void run() {
System.out.println(threadID + "转换开始");
Process process = getProcessByIndex(threadID,inputPath);
try {
int status = process.waitFor();
if(status == 0){
System.out.println(threadID + "转换成功");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
try {
process.getErrorStream().close();
process.getInputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
}
countDownLatch.countDown();
}
}.start();
}
try {
countDownLatch.await();
System.out.println("转换结束");
return true;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
最后附加完成的视频转码代码如下:
public class ConvertVideo {
private static String inputPath = "";
private static String outputPath = "";
private static String ffmpegPath = "";
public static void main(String args[]) throws IOException {
//getPath();
if (!checkfile(inputPath)) {
System.out.println(inputPath + " is not file");
return;
}
if (process()) {
System.out.println("ok");
}
}
public static void getPath(String srcPath,String aimPath){
try {
inputPath = srcPath;
outputPath = aimPath;
ffmpegPath = ServiceConfig.FFMPEG;
}
catch (Exception e) {
System.out.println("getPath出错");
}
}
public static boolean process() {
int threadNumber = 2;
final CountDownLatch countDownLatch = new CountDownLatch(threadNumber);
for (int i = 0; i < threadNumber; i++) {
final int threadID = i;
new Thread() {
public void run() {
System.out.println(threadID + "转换开始");
Process process = getProcessByIndex(threadID,inputPath);
try {
int status = process.waitFor();
if(status == 0){
System.out.println(threadID + "转换成功");
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally{
try {
process.getErrorStream().close();
process.getInputStream().close();
} catch (IOException e) {
e.printStackTrace();
}
}
countDownLatch.countDown();
}
}.start();
}
try {
countDownLatch.await();
System.out.println("转换结束");
return true;
} catch (InterruptedException e) {
e.printStackTrace();
return false;
}
}
public static boolean checkfile(String path) {
File file = new File(path);
if (!file.isFile()) {
return false;
}
return true;
}
// 对ffmpeg无法解析的文件格式(wmv9,rm,rmvb等), 可以先用别的工具(mencoder)转换为avi(ffmpeg能解析的)格式.
public static Process processAVI(String path) {
if (!checkfile(inputPath)) {
System.out.println(path + " is not file");
return null;
}
String aimPath = inputPath.substring(inputPath.lastIndexOf("\\"),inputPath.lastIndexOf("."));
List<String> commend = new ArrayList<String>();
commend.add(ffmpegPath + "mencoder");
commend.add(path);
commend.add("-y");
commend.add("-oac");
commend.add("lavc");
commend.add("-lavcopts");
commend.add("acodec=mp3:abitrate=64");
commend.add("-ovc");
commend.add("xvid");
commend.add("-xvidencopts");
commend.add("bitrate=600");
commend.add("-of");
commend.add("mp4");
commend.add("-o");
commend.add(outputPath + aimPath+".AVI");
Process process = null;
try {
ProcessBuilder builder = new ProcessBuilder();
process = builder.command(commend).redirectErrorStream(true).start();
new PrintStream(process.getInputStream());
new PrintStream(process.getErrorStream());
return process;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
// ffmpeg能解析的格式:(asx,asf,mpg,wmv,3gp,mp4,mov,avi,flv等)
private static Process processFlv(String oldfilepath) {
if (!checkfile(inputPath)) {
System.out.println(oldfilepath + " is not file");
return null;
}
String aimPath = inputPath.substring(inputPath.lastIndexOf("\\"),inputPath.lastIndexOf("."));
List<String> command = new ArrayList<String>();
command.add(ffmpegPath + "ffmpeg");
command.add("-y");
command.add("-i");
command.add(oldfilepath);
command.add("-ab");
command.add("56");
command.add("-ar");
command.add("22050");
command.add("-qscale");
command.add("8");
command.add("-r");
command.add("15");
command.add("-s");
command.add("600x500");
command.add(outputPath + aimPath+".flv");
try {
// 方案2
Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();
new PrintStream(videoProcess.getErrorStream()).start();
new PrintStream(videoProcess.getInputStream()).start();
return videoProcess;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static Process processMp4(String oldfilepath) {
if (!checkfile(inputPath)) {
System.out.println(oldfilepath + " is not file");
return null;
}
String aimPath = inputPath.substring(inputPath.lastIndexOf("\\"),inputPath.lastIndexOf("."));
List<String> command = new ArrayList<String>();
command.add(ffmpegPath + "ffmpeg");
command.add("-y");
command.add("-i");
command.add(oldfilepath);
command.add("-c:v");
command.add("libx264");
command.add("-mbd");
command.add("0");
command.add("-c:a");
command.add("aac");
command.add("-strict");
command.add("-2");
command.add("-pix_fmt");
command.add("yuv420p");
command.add("-movflags");
command.add("faststart");
command.add(outputPath + aimPath+"m.mp4");
try {
// 方案2
Process videoProcess = new ProcessBuilder(command).redirectErrorStream(true).start();
new PrintStream(videoProcess.getErrorStream()).start();
new PrintStream(videoProcess.getInputStream()).start();
return videoProcess;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static Process processFfmpegSwf(String srcVideoPath) {
if (!checkfile(srcVideoPath)) {
System.out.println(srcVideoPath+"不存在");
return null;
}
String aimPath = inputPath.substring(inputPath.lastIndexOf("\\"),inputPath.lastIndexOf("."));
List<String> commend = new java.util.ArrayList<String>();
commend.add(ffmpegPath + "ffmpeg");
commend.add("-y");
commend.add("-i");
commend.add(srcVideoPath);
commend.add("-b");
commend.add("360");
commend.add("-r");
commend.add("25");
commend.add("-s");
commend.add("640x480");
commend.add("-ab");
commend.add("56");
commend.add("-ar");
commend.add("22050");
commend.add("-ac");
commend.add("1");
commend.add("-qscale");
commend.add("1");
commend.add(outputPath +aimPath+".swf");
try {
Process videoProcess = new ProcessBuilder(commend).redirectErrorStream(true).start();
new PrintStream(videoProcess.getErrorStream()).start();
new PrintStream(videoProcess.getInputStream()).start();
return videoProcess;
} catch (Exception e) {
System.out.println("【" + srcVideoPath + "】 processFfmpegSwf 转换不成功 !");
return null;
}
}
private static Process getProcessByIndex(int index,String path){
switch(index){
case 0:
return processMp4(path);
case 1:
return processFfmpegSwf(path);
case 2:
return processFlv(path);
case 3:
return processAVI(path);
default :
return processMp4(path);
}
}
}
class PrintStream extends Thread{
java.io.InputStream __is = null;
public PrintStream(java.io.InputStream is){
__is = is;
}
public void run(){
try
{
while(this != null)
{
int _ch = __is.read();
if(_ch != -1)
// System.out.print((char)_ch);
;
else break;
}
}
catch (Exception e)
{
e.printStackTrace();
}
}
}