Java使用javacv處理影片檔案過程記錄

everyong發表於2024-04-15

最近接到一個需求是將.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();
        }
    }
}

相關文章