FFmpeg視頻處理

FFmpeg是一個用於音視頻處理的自由軟件,被普遍用於音視頻開發。FFmpeg功能強大,本文主要介紹如何使用FFmpeg命令行工具進行簡單的視頻處理。html

安裝FFmpeg能夠在官網下載各平臺軟件包或者靜態編譯版本,也可使用包管理工具安裝。算法

基本概念

容器shell

咱們熟悉的mp4,rmvb,mkv,avi是多媒體容器文件格式(或稱多媒體封裝格式),所謂容器是指將不一樣的數據流(視頻流,音頻流,字幕流等)封裝在一個文件(載體)中。json

播放時各類流分別進行解碼等處理後,而後輸出到顯示器和音響等設備進行播放。多媒體容器格式不一樣於編碼格式,一個容器中能夠封裝多種編碼格式的媒體流。bash

流封裝了實際的媒體數據,如視頻流,音頻流和字幕流等。通常狀況下,流中的數據只能使用一種編碼格式。ide

幀率工具

幀率(frames per second, fps)是每秒畫面刷新的次數,幀率越高視頻越流暢。通常來講30fps就是能夠接受的,60fps則能夠明顯提高交互感和逼真感,可是通常超過75fps通常就不容易察覺到有明顯的流暢度提高了。oop

分辨率佈局

分辨率表示畫面的精細程度,一般用像素密度來表示,經常使用的單位爲ppi(像素每英寸)。一般像素密度越高畫面越精細,模糊程度越低。性能

對於視頻文件而言,像素密度是沒法控制的(由播放器和顯示設備決定)。咱們一般用視頻的像素數來表示它的分辨率如1080x640, 640x320等。

比特率

比特率(bit rate)又稱碼率,表示多媒體流每秒輸出的字節數,單位爲KB/s, Kbps等。一樣的壓縮算法下,比特率越高音視頻的質量越好。

可變碼率(Variable Bitrate, VBR)指的是編碼器的輸出碼率能夠根據輸入源信號的複雜度進行自適應調整,以在輸出質量保持不變的條件下儘量減小數據量。VBR適用於存儲,不太適用流式傳輸。

固定碼率(Constant Bitrate, CBR)指的是編碼器輸出碼率固定,CBR不適合存儲,對於複雜內容可能沒有足夠碼率進行編碼,從而致使質量降低,同時會在簡單內容部分浪費一些碼率。

採樣率

每秒鐘對音頻信號的採樣次數,採樣頻率越高聲音還原度越高,聲音更加天然,單位是赫茲 Hz。

音頻文件通常使用的採樣率是 44.1 kHz,也就是一秒鐘採樣44100次,實驗發現低於這個值就會有較明顯的損失,而高於這個值人的耳朵已經很難分辨,並且增大了數字音頻所佔用的空間。

視頻編碼

視頻流能夠看作圖片的序列,咱們把這個序列中的一張圖片稱爲一幀。若存儲視頻中全部幀則會數據量過大,不便於存儲和傳輸。

所幸統計代表大多數視頻相鄰幀之間的區別並不大,因此對於一段變化不大的視頻,咱們能夠先完整編碼幀A,其後的B幀只須要編碼與A幀不一樣的部分,B幀後的C幀則只編碼與B幀的差別。如此遞推,將一段視頻編碼爲一個序列。

當某個圖像與以前的圖像變化很大沒法參考前面的幀來生成,咱們就結束上一個序列將該幀完整編碼開始一個新的序列。

H264是目前流行的一種視頻編碼算法,它定義了三種幀:完整編碼的I幀,參考I幀生成只包含差別的P幀,以及以及參考先後幀編碼的B幀。

H264採用的核心算法是幀內壓縮和幀間壓縮,幀內壓縮是生成I幀的算法,幀間壓縮是生成B幀和P幀的算法。

一般,咱們也把完整編碼的I幀稱爲關鍵幀。由於解碼非關鍵幀須要解碼其參考的幀,所以在截圖等不須要所有解碼的操做中,常常截取關鍵幀以提高性能。

得到音視頻信息

ffprobe是FFmpeg項目提供的用於分析視頻信息的命令行工具。

隨意下載一個測試視頻testmp4, 而後終端中輸入指令:

ffprobe -v quiet -print_format json -show_format -show_streams test.mp4

能夠得到json格式輸出的視頻信息:

{
    "streams": [ // 文件中包含的流
        {
            "index": 0,  // 流的序號
            "codec_name": "h264", // 流的編碼格式
            "codec_long_name": "H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10", // 編碼格式的全名
            "profile": "High", 
            "codec_type": "video", // video表示這是一個視頻流
            "codec_time_base": "1/60",
            "codec_tag_string": "avc1",
            "codec_tag": "0x31637661",
            "width": 1080, // 視頻寬爲1080像素
            "height": 614, // 視頻高爲614像素
            "coded_width": 1080,
            "coded_height": 614,
            "has_b_frames": 2,
            "sample_aspect_ratio": "0:1",
            "display_aspect_ratio": "0:1",
            "pix_fmt": "yuv420p",
            "level": 31,
            "chroma_location": "left",
            "refs": 1,
            "is_avc": "true",
            "nal_length_size": "4",
            "r_frame_rate": "30/1", // 實際幀率
            "avg_frame_rate": "30/1",
            "time_base": "1/15360",
            "start_pts": 0,
            "start_time": "0.000000",
            "duration_ts": 153093,  
            "duration": "9.966992",  // 以秒爲單位的視頻時間
            "bit_rate": "2077265",  // 視頻的比特率
            "bits_per_raw_sample": "8",
            "nb_frames": "299",
            "tags": {  // 流中的附加信息,其中的字段可能爲空
                "rotate": 90, // 視頻旋轉的角度
                "language": "und",
                "handler_name": "VideoHandler"
            }
        },
        {
            "index": 1, // 流編號
            "codec_name": "aac", // 流的編碼格式
            "codec_long_name": "AAC (Advanced Audio Coding)", // 編碼格式的全名
            "profile": "LC",
            "codec_type": "audio", // 這是一個音頻流
            "codec_time_base": "1/44100", 
            "codec_tag_string": "mp4a",
            "codec_tag": "0x6134706d",
            "sample_fmt": "fltp",
            "sample_rate": "44100", // 採樣率
            "channels": 2, // 聲道數
            "channel_layout": "stereo", // 聲道佈局,stereo爲立體雙聲道
            "bits_per_sample": 0,
            "r_frame_rate": "0/0",
            "avg_frame_rate": "0/0",
            "time_base": "1/44100",  // 每幀時長
            "start_pts": 0,
            "start_time": "0.000000", // 流開始播放時間
            "duration_ts": 442367,
            "duration": "10.030998",  // 流時長
            "bit_rate": "129341",  // 比特率
            "max_bit_rate": "129341",
            "nb_frames": "433",
            "tags": {
                "language": "und",
                "handler_name": "SoundHandler"
            }
        }
    ],
    "format": {  // 容器信息
        "filename": "test.mp4",  // 文件名
        "nb_streams": 2,
        "nb_programs": 0,
        "format_name": "mov,mp4,m4a,3gp,3g2,mj2", // 封裝格式名
        "format_long_name": "QuickTime / MOV",
        "start_time": "0.000000",
        "duration": "10.055000",
        "size": "2762615",  // 文件字節數
        "bit_rate": "2198002", // 比特率
        "probe_score": 100,
        "tags": {
            "major_brand": "isom",
            "minor_version": "512",
            "compatible_brands": "isomiso2avc1mp41",
            "encoder": "Lavf57.71.100"
        }
    }
}

示例中使用-v quiet選項將日誌級別設爲quiet避免日誌信息污染json,-show_format顯示文件的容器信息,-show_stream顯示容器中流的信息,-show_frames則能夠顯示視頻中每一幀的信息。

更多關於ffprobe的內容能夠參考官方文檔

使用ffmpeg進行視頻處理

ffmpeg的命令格式:

ffmpeg \
    [global_options] \
    [input_file_options] -i input_url \
    [actions] \
    [output_file_options] output_url

咱們能夠將ffmpeg的選項分爲全局選項和局部選項,局部選項用於設置輸入輸出或者濾鏡等,一般位於被修飾的指令前面。

ffmpeg的基本流程爲將容器中的各流進行解碼,而後從新編碼爲指定的格式。在編碼以前,可使用filter對視頻進行處理。

選項

選項的詳細內容請參考官方文檔

-y / -n

-y/-n 爲全局選項, -y表示直接覆蓋已經存在的輸出文件, -n表示若某個輸出文件已經存在則退出。

若沒有設置-y-n選項,且某個輸出文件已經存在ffmpeg會詢問是否要覆蓋輸出文件。

ffmpeg -y -i test.mp4 test.mkv

-codec(-c)

指定輸入輸出的解碼編碼器, 可用的編解碼器能夠參考官方文檔:

fmpeg -y -i test.mp4 -c:v libx264 -c:a copy test.mov

codec指定爲copy則將輸入流直接複製到輸出流不進行編解碼操做。

使用-c:STREAM_INDEX方式能夠指定某一個流的解碼器,STREAM_INDEX爲stream對象的index屬性。

-c:v-vcodec能夠爲全部視頻流指定編碼器,-c:v:1爲第2個視頻流指定編解碼器。

-c:a-acodec能夠爲全部音頻流指定編碼器,-c:a:12爲第13個視頻流指定編解碼器。

-ss

-ss選項用於設置流的開始時間,能夠設置輸入輸出或者濾鏡。在開始時間以前的幀將被跳過不被處理(輸入不被解碼,輸出不被編碼,濾鏡不被處理)。

ffmpeg -ss 2 -t 10 -i test.mp4 test.mov

時長有兩種方式來表示:

  • 秒數: 如-t 10, -t 23.167
  • 時分秒: 如-t 10:23, -t 21:31:00.233

-t

-t選項用於用於設置輸入輸出,-t-i前能夠限制輸入時長,-t在輸出文件前能夠限制輸出時長。

讀入test.mp4文件2s開始10s內的數據,轉碼後輸出到test.mov:

ffmpeg -ss 2 -t 10 -i test.mp4 test.mov

讀入test.mp4所有數據,所有轉碼後輸出從第2s開始1min10s內的數據到test.mov:

ffmpeg -i test.mp4 -ss 2 -t 01:10 test.mov

-to

-to選項相似於-t選項,不一樣的是-to指定結束時刻,-t指定持續時間。

讀入test.mp4文件2s到12s內的數據,轉碼後輸出到test.mov:

ffmpeg -ss 2 -to 12 -i test.mp4 test.mov

讀入test.mp4所有數據,所有轉碼後輸出從01:00到01:30內的數據到test.mov:

ffmpeg -i test.mp4 -ss 01:00 -to 01:30 test.mov

-f

強制設置輸入輸出的文件格式,默認狀況下ffmpeg會根據文件後綴名判斷文件格式。

ffmpeg -formats命令會顯示全部支持的編碼格式。

-filter / -filter_complex

使用過濾器對流進行處理,下文將簡要介紹filter的相關內容。

可使用-vf代替-filter:v處理視頻流, -af代替-filter:a處理音頻流。

-vframes

設置輸出文件中包含的總幀數:

ffmpeg -i test.mp4 -vframes 1 test.mov

-vn

不將視頻流寫到輸出文件中

ffmpeg -i test.mp4 -vn -a:c copy out.mp3

-r

設置某個流的幀率:

ffmpeg -i test.mp4 -r:v 30 test.mov

-s

設置幀的大小:

ffmpeg -i test.mp4 -s 1080x680 out.mp4

-an

不將音頻流寫到輸出文件中:

ffmpeg -i test.mp4 -v:c copy -an out.mp4

-threads

設置處理線程數:

ffmpeg -threads 8 -i test.mp4 out.mp4

能夠設置處理

-shortest

當最短的輸入流結束後即中止編碼和輸出。

ffmpeg -i bgm.mp3 -i test.mp4 -shortest output.mp4

filter

過濾器會對已解碼的幀進行處理,處理後的幀會被從新編碼輸出,整個流程能夠歸納爲:

Input -> DecodedFrames -> FilteredFrames -> EncodedData

簡單過濾器是單輸入單輸出的(只能處理一個流),而複雜過濾器(filter_complex)是多輸入多輸出的能夠進行更復雜的操做。

ffmpeg支持的各類濾鏡能夠參考官方文檔-濾鏡

scale

ffmpeg -y -i test.mp4 -vf "scale=2*in_w:2*in_h" test.mov

scale濾鏡用於縮放視頻, in_win_h表明輸入的寬和高。

crop

ffmpeg -y -i test.mp4 -vf "crop=w=100:h=100:x=in_w/2:y=in_h,scale=400:400" test.mov

crop濾鏡用於截取視頻中的一個區域。

overlay

ffmpeg -y -i test.mp4 -i logo.png -filter_complex 'overlay=10:main_h-overlay_h-10' out.mp4

overlay濾鏡將一個視頻疊放在另外一個視頻上,可用於在視頻中添加水印和動畫等操做。

overlay的第一個輸入爲底層視頻流,第二個輸入爲疊加視頻流。main_wmain_h爲底層視頻的寬和高,overlay_woverlay_h爲疊加視頻的寬和高。

drawtext

ffmpeg -y -i test.mp4 -vf "drawtext=fontfile=CourierNew.ttf:text='hello world':x=100:y=50:fontsize=24" out.mp4

drawtext濾鏡用於在視頻上添加文字。

fade

ffmpeg -y -i test.mp4 -vf "fade=in:st=0:d=5" out.mp4

fade濾鏡能夠製做淡入淡出效果

fps

ffmpeg -y -i test.mp4 -vf "fps=60" out.mp4

fps濾鏡經過刪除幀或者複製幀的方法強制設置幀率。

ffmpeg -y -i test.mp4 -vf "fps=1" img%3d.png
ffmpeg -y -i test.mp4 -r 1 img%3d.png

上面兩條指令均可以對視頻每秒截取一幀圖像,-r選項會截取關鍵幀並不必定截取0s、1s...處的幀,fps濾鏡處理的是已經解碼的幀所以能夠精確的按照時間截取。

由於fps濾鏡會解碼要截圖的視頻片斷,所以這種方式截圖會慢不少。

應用示例

視頻轉碼

ffmpeg -y \
    -i test.mp4 \
    -vcodec copy \
    -acodec copy \
    out.mkv

這條指令將容器格式由MP4轉換到MKV,使用ffprobe檢查輸出文件能夠發現,視頻流沒有發生變化,可是封裝格式改變爲mkv格式。

-vcodec是一個簡單過濾器用於處理視頻編碼,copy表示將視頻流複製到輸出文件中。-acodec是處理音頻編碼的過濾器。

提取視頻流

ffmpeg -y \
    -i test.mp4 \
    -vcodec copy \
    -an \
    out.mp4

-an表示不保留音頻流。

提取音頻

ffmpeg -y \
    -i test.mp4 \
    -ar 44100 -ac 2 -ab 192 \
    -f mp3 \
    output.mp3

分析:

  • -ar: 指定輸出音頻採樣率
  • -ac: 指定輸出音頻通道(channel)數, 這裏設置爲雙聲道
  • -ab: 指定輸出音頻比特率,單位kb/s

按幀截取圖像

截取第2s開始的10幀圖像, 伸縮爲352x240:

ffmpeg -y \
    -ss 2 -i test.mp4 \
    -vframes 10 \
    -f image2 \
    -s 352x240 \
    img%03d.png

分析:

  • -ss 2 -i test.mp4: ss爲開始時間,用秒數或者hh:mm:ss[.xxx]格式表示。-i test.mp4表示輸入源
  • -vframes: 指定截取的幀數, 這裏是截取前10幀(從-ss指定開始時間算起)
  • -f: 指定輸出文件的格式,如: image2, mjpeg, gif
  • -s: 對輸出畫面進行縮放
  • img%03d.png: 格式化輸出文件名,本示例中輸出img001.png, img002.png等。

-ss參數也能夠放在vframes前:

ffmpeg -y \
    -i test.mp4 \
    -ss 2 -vframes 1 \
    -f image2 \
    -s 352x240 \
    img.png

-ss參數是局部選項用於設置其後的一個命令,-ss 2 -i test.mp4表示從輸入視頻的第2s開始處理,忽略前兩秒的內容。

-ss 2 -vframes 1表示從第2s開始截取,此時前2s的內容已經進行了解碼。

對不須要處理的部分進行解碼會浪費大量時間,所以建議使用-ss 2 -i test.mp4來表示截圖開始時間。

按時間截取圖像

從第2s到第12s內,每秒截取1幀圖像:

ffmpeg -y \
    -ss 2 -i test.mp4 \
    -r 1 -t 10 \
    -f image2 \
    -s 352x240 \
    img%03d.png

分析:

  • -t: 指定截取時長,這裏截取10s
  • -r 1: -t的局部選項設置每秒截取的幀數(截取幀率),若不設置則截取所有幀

-vframe同樣-t的開始時間也有兩種設置方式,基於一樣的理由一樣建議將-ss放在輸入前。

截取視頻片斷

截取視頻片斷的方法與截圖方法相似,只是將輸出格式變爲視頻:

按時間截取:

ffmpeg -y \
    -ss 2 -i test.mp4 \
    -r 20 -t 10 \
    -s 352x240 \
    clip.mp4

由於輸出爲視頻,-r指定的截取幀率即爲輸出視頻幀率。

按幀數截取:

ffmpeg -y \
    -ss 2 -i test.mp4 \
    -vframes 120 \
    -s 352x240 \
    clip.mp4

截取視頻區域

截取視頻區域:

ffmpeg -y \
    -ss 2 -i test.mp4 \
    -r 1   \
    -t 10 \
    -filter_complex "[0:v]crop=w=100:h=100:x=12:y=34,scale='400:400'[v]" \
    -map "[v]" \
    img%03d.png

crop濾鏡能夠截取視頻部分區域,[0:v]crop=w=100:h=100:x=12:y=34,scale='400:400'[v]截取了左上角在(12,34)處,寬爲100,高爲100的矩形框中的內容,並將截圖放大到400x400。

拼接視頻

ffmpeg -i "concat:1.mp4|2.mp4|3.mp4" -c copy output.mp4

將圖片合併爲視頻

ffmpeg -i img%3d.png output.gif
ffmpeg -i img%3d.png output.mp4

添加音頻

ffmpeg -i bgm.mp3 -i test.mp4 output.mp4

添加水印

ffmpeg -y \
    -i test.mp4 \
    -i 1.png \
    -filter_complex "[1]scale=w=480:h=280[s];[0][s]overlay=x=main_w-overlay_w-10:y=main_h-overlay_h-10[ov]" \
    -map "[ov]" \
    output.mp4

使用filter_complex先將水印圖片(輸入1)放大到480x280, 而後使用overlay濾鏡將放大後的流[s]覆蓋到視頻(輸入0)上。

若不須要使用scale進行縮放,則能夠簡化filter_complex表達式:

ffmpeg -y \
    -i test.mp4 \
    -i 1.png \
    -filter_complex "overlay=x=main_w-overlay_w-10:y=main_h-overlay_h-10" 
    output.mp4

添加動畫

ffmpeg -y -i test.mp4 -t 10 -loop 1 -framerate 6 -i ani%3d.png -filter_complex 'overlay=10:main_h-overlay_h-10' out.mp4

將多張圖片(ani001.png, ani002.png...)組成動畫, 而後將這個動畫疊加在視頻的左下角。-t 10 -loop 1會循環播放動畫,持續10s。

該方式也支持gif格式的動畫。

添加文字

ffmpeg -y -i test.mp4 -vf "drawtext=fontfile=CourierNew.ttf:text='hello world':x=100:y=50:fontsize=24" out.mp4

添加字幕

添加字幕有兩種方式:

  • 將字幕添加爲獨立的流,mkv,avi等封裝格式支持此種方式,mp4格式不支持
  • 將字幕疊加到視頻中

添加字幕流:

ffmpeg -i video.mp4 -vf subtitles=subtitle.srt out.mp4

疊加字幕:

ffmpeg -i test.mp4  -i sub.srt -filter_complex "[0][1]overlay[v]" -map "[v]" out.mp4

HowToBurnSubtitlesIntoVideo詳細介紹了燒錄字幕的方法,做者建議儘可能使用字幕流的方法添加字幕。

旋轉視頻

旋轉視頻有兩種方式:

  • 在視頻元信息中添加旋轉角度信息,由播放器執行旋轉
  • 將每幀圖像旋轉

添加元信息:

ffmpeg -i test.mp4 -metadata:s:v rotate="90" -codec copy out.mp4

逐幀旋轉:

ffmpeg -i test.mp4 -vf "transpose=1" out.mp4

transpose濾鏡的文檔

相關文章
相關標籤/搜索