最近接到一個需求是將.mp4/.m4v檔案體積進行壓縮,我使用javacv中的FFmpegFrameGrabber、FFmpegFrameFilter、FFmpegFrameRecorder簡單的實現影片幀的抓取、過濾、錄製與輸出。
效能暫未驗證。文章對這次的過程進行記錄。
1.jdk的選擇
mcr.microsoft.com/java/jdk:8u222-zulu-centos
2.maven依賴
`<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv</artifactId>
<version>1.4.4</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.4.4</version>
</dependency>`
3.實現過程
import lombok.extern.slf4j.Slf4j;
import org.bytedeco.javacpp.avcodec;
import org.bytedeco.javacpp.avutil;
import org.bytedeco.javacv.FFmpegFrameFilter;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.junit.Test;
@Slf4j
public class CoverVideoImageResolution {
@Test
public void changeResolution() {
String inputFilePath = "原檔案地址";
String outputFilePath = "目標檔案地址";
// 建立一個影片幀抓取器
FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFilePath);
// 建立一個影片幀過濾器,用於修改影片解析度
FFmpegFrameFilter frameFilter = new FFmpegFrameFilter("scale=720:480", grabber.getImageWidth(), grabber.getImageHeight());
// 建立一個影片幀錄製器,用於將處理後的影片幀寫入輸出檔案
FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFilePath, 720, 480);
try {
long now = System.currentTimeMillis();
// 開始從輸入檔案中抓取影片幀
grabber.start();
log.info("Video Frame Rate:{};",grabber.getFrameRate());
log.info("Video Width:{};",grabber.getImageWidth());
log.info("Video Height:{};",grabber.getImageHeight());
log.info("Video Bitrate:{};",grabber.getVideoBitrate());
log.info("Video Sample Rate:{};",grabber.getSampleRate());
log.info("Video Codec:{};",grabber.getVideoCodec());
// 啟動影片幀過濾器
frameFilter.start();
// 設定輸出影片的格式,與輸入影片相同
recorder.setFormat(grabber.getFormat());
// 設定影片的取樣率和幀速率與輸入影片相同
recorder.setSampleRate(grabber.getSampleRate());
recorder.setFrameRate(grabber.getFrameRate());
// 設定影片位元率,值越大影片質量越好,檔案體積也越大,可根據需要調整
log.info("原影片位元率:{}", grabber.getVideoBitrate());
//CRF 的值範圍是0到51,其中 0 表示無失真壓縮,而 51 表示質量非常差的壓縮
recorder.setOption("crf","28");
// 設定影片位元率
recorder.setVideoBitrate(grabber.getVideoBitrate()/2);
// 設定影片編解碼器為h.264
recorder.setVideoCodec(avcodec.AV_CODEC_ID_H264); // 設定影片編解碼器為h.264
// 設定畫素格式為YUV420P
recorder.setPixelFormat(avutil.AV_PIX_FMT_YUV420P);
// 設定音訊引數
recorder.setAudioChannels(2); // 設定音訊通道數
recorder.setAudioCodec(avcodec.AV_CODEC_ID_AAC); // 設定音訊編解碼器
recorder.setSampleRate(grabber.getSampleRate()); // 設定音訊取樣率
recorder.setAudioBitrate(grabber.getAudioBitrate()); // 音訊位元率
// 啟動影片幀錄製器
recorder.start();
Frame frame;
// 處理影片幀並將處理後的幀寫入輸出檔案
while ((frame = grabber.grab()) != null) {
frameFilter.push(frame);// 將抓取的影片幀傳遞給過濾器
Frame filteredFrame = frameFilter.pull();// 獲取過濾後的影片幀
recorder.record(filteredFrame);// 將過濾後的影片幀寫入輸出檔案
}
// 停止影片幀抓取器、過濾器和錄製器
grabber.stop();
frameFilter.stop();
recorder.stop();
System.out.println("Video resolution modified successfully.");
log.info("壓縮耗時:{}秒", (System.currentTimeMillis()-now)/1000);
} catch (Exception e) {
e.printStackTrace();
}
}
}