瀏覽器的video標(biāo)簽,只支持部分編碼格式的視頻播放,由于客戶的視頻編碼各不相同,所以需要通過后端幫忙轉(zhuǎn)碼成統(tǒng)一的編碼。記錄一下通過 ffmpeg 工具解決視頻編碼問題。
首先下載ffmpeg工具,下載windows版本的,地址:https://ffmpeg.zeranoe.com/builds/
這個(gè)builds版本自動(dòng)很多編碼器,通過ffmpeg命令可以看到

ffmpeg.png
使用windows版是因?yàn)樵贚inux上折騰很久,編碼器沒有安裝成功,所以就放棄了,在windows上通過Kafka弄了個(gè)視頻轉(zhuǎn)碼服務(wù)。
命令參數(shù):http://www.mikewootc.com/wiki/sw_develop/multimedia/ffmpeg_app_param.html (找了很多,這是比較全的參數(shù)說明)
以下是視頻解碼的工具類,功能是將視頻轉(zhuǎn)碼成視頻編碼為 H.264,音頻編碼為 aac,是為了支持video標(biāo)簽,直接在瀏覽器播放,其他參數(shù)是為了壓縮視頻大小:
package com.spider.culture.process;
import java.util.ArrayList;
import java.util.List;
import com.spider.culture.util.Logger;
/**
* 關(guān)于moov在前的說明
* 一、關(guān)于chrome的快速播放和拖拽
* 1、chrome使用H5的video標(biāo)簽來播放
* 2、chrome很智能,如果讀取MP4文件,發(fā)現(xiàn)moov box不在文件前部,會(huì)直接讀取MP4的文件尾部,加載moov box
* 3、拖拽時(shí),chrome根據(jù)moov box得到的關(guān)鍵幀的字節(jié)偏移量,采用range請(qǐng)求來請(qǐng)求
* 二、關(guān)于Flash
* 1、Flash如果讀取MP4文件,發(fā)現(xiàn)moov box不在文件前部,不會(huì)直接讀取MP4的文件尾部去尋找moov box,所以Flash要等文件全部下載完,取到文件尾部的moov頭,才可以正常播放。
* @author spider
*
*/
public class TransCodec {
/**
* 視頻轉(zhuǎn)碼 (PC端MP4)
* @param ffmpegPath 轉(zhuǎn)碼工具的存放路徑
* @param upFilePath 用于指定要轉(zhuǎn)換格式的文件,要截圖的視頻源文件
* @param codcFilePath 格式轉(zhuǎn)換后的的文件保存路徑
* @return
* @throws Exception
*/
public static boolean exchangeToMp4(String ffmpegPath,String upFilePath, String codcFilePath) {
// 創(chuàng)建List集合來保存轉(zhuǎn)換視頻文件為flv格式的命令
List<String> convert = new ArrayList<String>();
convert.add(ffmpegPath); // 添加轉(zhuǎn)換工具路徑
convert.add("-y"); // 該參數(shù)指定將覆蓋已存在的文件
convert.add("-i"); //輸入文件
convert.add(upFilePath);
convert.add("-c:v"); //視頻
convert.add("libx264"); //視頻編碼格式
convert.add("-c:a"); //音頻編碼格式
convert.add("aac");
convert.add("-strict"); //FFmpeg自帶的aac音頻編碼要帶上-strict -2 參數(shù)
convert.add("-2");
convert.add("-pix_fmt"); //像素格式
convert.add("yuv420p");
convert.add("-movflags"); //moov頭在前
convert.add("faststart");
convert.add("-s"); //屏幕大小
convert.add("320x240");
convert.add("-b:v"); //視頻比特率 值越小文件越?。▎挝籦/s)
convert.add("262144"); //262144 = 256*1024 bit/s
convert.add("-b:a"); //音頻比特率(單位b/s)
convert.add("131072"); //131072 = 1024*128 bit/s
convert.add("-ac"); //音頻通道
convert.add("2");
convert.add("-ar"); //音頻采樣率
convert.add("44100");
//convert.add("-vf"); // 添加水印
//convert.add("movie=watermark.gif[wm];[in][wm]overlay=20:20[out]");
convert.add(codcFilePath); //輸出文件
boolean mark = true;
try {
// redirectErrorStream(true) 標(biāo)準(zhǔn)錯(cuò)誤將與標(biāo)準(zhǔn)輸出合并,使得關(guān)聯(lián)錯(cuò)誤消息和相應(yīng)的輸出變得更容易
Process videoProcess = new ProcessBuilder(convert).
redirectErrorStream(true).start();
// 合并的數(shù)據(jù)可從 Process.getInputStream() 返回的流讀取
new PrintStream(videoProcess.getInputStream()).start();
// 加上這句,系統(tǒng)會(huì)等待轉(zhuǎn)換完成。不加,就會(huì)在服務(wù)器后臺(tái)自行轉(zhuǎn)換。
// 但是后期測(cè)試一直報(bào)錯(cuò),ffmpeg卡住,無法轉(zhuǎn)換,線程也在一直等待
// videoProcess.waitFor();
} catch (Exception e) {
mark = false;
Logger.error(String.format("視頻%s編碼轉(zhuǎn)換出錯(cuò)", upFilePath),e);
}
return mark;
}
}
/**
* 視頻轉(zhuǎn)換過程
* @author spider
*
*/
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();
}
}
}