前端多媒體小知識之:FFmpeg介紹

語言: CN / TW / HK

本文作者:申嬙嬙

🚩是什麼?(Fast forward moving picture expert groups)

A complete, cross-platform solution to record, convert and stream audio and video.

FFmpeg 提供了多種媒體格式的封裝和解封裝,比如多種音視訊編碼,多種協議的流媒體,

多種色彩格式轉換,多種取樣率轉換,多種位元速率切換,可以在多種作業系統安裝使用。

🔫 ffplay的使用

作為FFmpeg的基礎元件之一,它具有強大的音視訊解碼播放能力,也可以作為音視訊資料的圖形化分析工具,是Linux和macOS系統中最常用的多媒體播放器之一,目前它廣泛被各種流行播放器(QQ影音、暴風影音等)整合應用;可隨時快速地呼叫ffplay對音視訊檔案播放和測試;

// ffplay按某個日誌輸出級別進行播放 ffplay -loglevel [debug|quiet|panic|fatal|error|warning...] -i demo.mp4 // 全屏播放 可選擇禁用音視訊字幕流 ffplay [-an|-vn|-sn] -i demo.mkv -fs // 指定播放起始時間 時長 ffplay -ss 300 -t 20 -i input.avi -autoexit // 檢視音訊波形/頻譜 ffplay -showmode [1|2] test.mp3 .....

🔫 ffprobe的使用

是FFmpeg提供的媒體資訊檢測工具,使用ffprobe不僅可以檢測音視訊檔案的整體封裝格式,還可以分析其中每一路音訊流或視訊流資訊,甚至可以進一步分析音視訊流的每一個碼流包或影象幀的資訊

ffprobe -i demo.mp4

  • 預設情況下會檢測輸入音視訊檔案的封裝格式、metadata資訊,以及媒體檔案的總時長、起始時間及位元速率、每一路媒體流的資訊:
    • 對視訊流,輸出編碼格式、影象顏色格式、影象的寬與高、縱橫比和視訊位元速率;
    • 對音訊流,輸出編碼格式、取樣率、音訊型別、音訊位元速率

//顯示詳細的封裝格式資訊 ffprobe -show_format -i demo.mp4 //顯示每一路媒體流資訊(一個音視訊檔案通常包括兩路及以上媒體流) ffprobe -show_streams -i demo.mp4 //音訊取樣和視訊的影象幀都被壓縮為多個碼流包,再儲存在容器檔案中 //顯示每一個碼流包資訊 ffprobe -show_packets -i demo.mp4 //顯示每一幀影象的資訊(對視訊流的每一幀影象進行解碼) ffprobe -show_frames -i demo.mp4

🔫 ffmpeg的命令

通常一路音訊訊號或一路視訊訊號編碼後會生成各自的編碼資料流,媒體資訊在播放時會同時播放這樣的多路媒體流,為了解決多路音視訊流的同步問題,基本資料流在經過處理後需複用到一個檔案中,這就是檔案容器格式/封裝格式,包含了音視訊碼流資訊、音視訊同步資訊、以及可能有的字幕流資料和元資料(發行商、語言、演員資訊等)。

視訊播放器播放一個視訊檔案,會經歷下面的過程,隨後根據解封裝時獲取到的引數資訊,同步解碼出來的視訊和音訊資料,將其送至系統的顯示卡和音效卡播放出來

image.png

💁🏻 封裝格式轉變

ffmpeg -i demo.mp4 output.avi

可以看出,輸出檔案的視訊流和音訊流的編碼格式都發生了改變,說明在轉封裝同時發生了轉碼操作,它會將音視訊流轉碼為目標封裝格式的預設編碼格式。

🚰 轉碼輸出的過程

image.png

將一個音視訊檔案作為輸入源,設定編碼引數和封裝格式,指定輸出檔案後轉碼

ffmpeg -i input.flv -c:v libx264 -b:v 3000k -r 25 new_output.mp4

因為編解碼耗cpu資源,所以如果直接把輸入檔案中的音視訊包封裝入輸出檔案,速度會快很多

🚰 流複製

只執行demuxing和muxing。它對於更改容器格式或修改容器級元資料非常有用。

應用過濾器顯然也是不可能的,因為過濾器處理未壓縮的資料。

image.png

ffmpeg -i demo.mp4 -c copy output.avi ffmpeg -i demo.mp4 -codec copy output.avi

🚰 資料流選擇
  • 每個輸入或輸出原則上可以包含任意數量的不同型別的流(視訊/音訊/字幕/附件/資料)。允許的流的數量和/或型別可能受到容器格式的限制。選擇哪個流進入哪個流輸出,要麼自動完成,要麼使用-map選項來指定媒體流。
    • ffmpeg -i input1.mp4 -i input2.mp4 -map 0:a -map 1:v output.mp4

檔案中的流通過其索引來引用

ffmpeg不驗證指定的編碼器是否可以轉換所選流,或者轉換後的流在輸出格式中是否可接受。所以這將導致:當用戶手動設定編碼器時,流選擇過程不能檢查編碼的流是否可以混合到輸出檔案中。如果不能,ffmpeg將中止,所有輸出檔案將無法處理。

💁🏻 濾鏡使用

avfilter元件用於多媒體的處理和編輯

🌰:

  • 為視訊加水印:
    • 文字水印:
    • ffmpeg -i demo.mp4 -vf "drawtext=fontsize=60:fontfile=lazy.ttf:text='hello sqq %{localtime:%Y-%m-%d}':x=100:y=100:fontcolor=green:box=1:boxcolor=yellow:enable=lt(mod(t,3),1)" out.mp4
    • 暫時無法在飛書文件外展示此內容
    • 圖片水印
    • 使用movie濾鏡
    • ffmpeg -i demo.mp4 -vf "movie=icon.jpeg[wm];[in][wm]overlay=30:10[out]" output.mp4

畫中畫:

ffmpeg -y -re -i demo.mp4 -t 10 -vf "movie=333.mp4,scale=480x320[test];[in][test] overlay =x='if(gte(t,2),-w+(t-2)*200,NAN)':y=0 [out]" -vcodec libx264 output_danmu.flv

image.png

多宮格

ffmpeg -re -i demo.mp4 -re -i ./video/1.avi -re -i ./video/2.wmv -re -i ./video/8.flv -t 5 -filter_complex "nullsrc=size=640x480 [base]; [0:v] setpts=PTS-STARTPTS,scale=320x240 [upperleft]; [1:v] setpts=PTS-STARTPTS, scale=320x240 [upperright];[2:v] setpts=PTS-STARTPTS, scale=320x240 [lowerleft]; [3:v] setpts=PTS-STARTPTS, scale=320x240 [lowerright]; [base][upperleft] overlay=shortest=1[tmp1]; [tmp1][upperright] overlay=shortest=1:x=320 [tmp2]; [tmp2][lowerleft]overlay=shortest=1:y=240 [tmp3]; [tmp3][lowerright] overlay=shortest=1:x=320:y=240" -c:v libx264 output_4.mp4

image.png

字幕濾鏡:

  • 將字幕編碼進入視訊流,和視訊增加水印相似
  • 在封裝容器中加入字幕流(需要容器支援加入字幕流)

ffmpeg -i ./video/2.wmv -vf ass=test.ass -f mp4 -t 10 output_ass.mp4 ffmpeg -i output_4.mp4 -i test.ass -acodec copy -vcodec copy -scodec copy -t 10 output.mkv

image.png

    • 音訊流濾鏡 包含了重取樣、混音器 、調整音訊時間戳、淡入淡出、靜音檢測等幾十種濾鏡
    • 視訊濾鏡 包含影象剪下、水印處理、色彩空間變換、3d視訊處理、定時視訊截圖、音視訊倍速等許多渲染效果...

🔫 FFmpeg在瀏覽器中的使用

FFmpeg底層用c語言實現,基於WebAssembly,使得我們在瀏覽器上也可以呼叫ffmpeg,ffmpeg.wasm是FFmpeg的純Webassembly埠。它可以讓我們在瀏覽器內部進行視訊和音訊錄製,轉碼和傳輸。通過npm 安裝 npm install @ffmpeg/ffmpeg @ffmpeg/core

``` //載入可供使用的 WebAssembly 檔案,建立 FFmpeg 例項 const { createFFmpeg } = FFmpeg; this.ffmpeg = createFFmpeg({log: true }); ...

async clipFile(){ ... ffmpeg.FS('writeFile', name, await fetchFile(file)); await ffmpeg.run('-y','-i', name,'-ss', startTime,'-to', endTime,'-c:a', 'copy', 'outputs.mp4'); const data = ffmpeg.FS('readFile', 'outputs.mp4'); const tempURL = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' })); ... } ```

也可以根據自己的業務需求,來調整包大小,或者如果想用新版本的 FFmpeg 來打包,可以編譯自己的wasm包.(可以參考編譯ffmpeg.wasm)