音影片編解碼流程與如何使用 FFMPEG 命令進行音影片處理

語言: CN / TW / HK

一、前言

FFMPEG 是特別強大的專門用於處理音影片的開源庫。你既可以使用它的 API 對音影片進行處理,也可以使用它提供的工具,如 ffmpeg , ffplay , ffprobe ,來編輯你的音影片檔案。

本文將簡要介紹一下 FFMPEG 庫的基本目錄結構及其功能,然後詳細介紹一下我們在日常工作中,如何使用 ffmpeg 提供的工具來處理音影片檔案。

二、FFMPEG 目錄及作用

libavcodec:提供了一系列編碼器的實現。

libavformat:實現在流協議,容器格式及其本 IO 訪問。

libavutil:包括了 hash 器,解碼器和各利工具函式。

libavfilter:提供了各種音影片過濾器。

libavdevice:提供了訪問捕獲裝置和回放裝置的介面。

libswresample:實現了混音和重取樣。

libswscale:實現了色彩轉換和縮放工能。

三、FFMPEG 基本概念

在講解 FFMPEG 命令之前,我們先要介紹一些音影片格式的基要概念。

(1) 音/影片流

在音影片領域,我們把一路音/影片稱為一路 。如我們小時候經常使用 VCD 看港片,在裡邊可以選擇粵語或國語聲音,其實就是 CD 影片檔案中存放了兩路音訊流,使用者可以選擇其中一路進行播放。

(2) 容器

我們一般把 MP4、 FLV、MOV 等檔案格式稱之為 容器 。也就是在這些常用格式檔案中,可以存放多路音影片檔案。以 MP4 為例,就可以存放一路影片流,多路音訊流,多路字幕流。

(3) channel

channel 是音訊中的概念,稱之為聲道。在一路音訊流中,可以有單聲道,雙聲道或立體聲。

四、FFMPEG 功能分類

我們按使用目的可以將 FFMPEG 命令分成以下幾類:

  • 基本資訊查詢命令
  • 錄製
  • 分解/複用
  • 處理原始資料
  • 濾鏡
  • 切割與合併
  • 圖/視互轉
  • 直播相關

除了 FFMPEG 的基本資訊查詢命令外,其它命令都按下圖所示的流程處理音影片。

然後將編碼的資料包傳送給解碼器(除非為資料流選擇了流拷貝,請參閱進一步描述)。 解碼器產生未壓縮的幀(原始影片/ PCM 音訊/ …),可以通過濾波進一步處理(見下一節)。 在過濾之後,幀被傳遞到編碼器,編碼器並輸出編碼的資料包。 最後,這些傳遞給複用器,將編碼的資料包寫入輸出檔案。

預設情況下,ffmpeg 只包含輸入檔案中每種型別(影片,音訊,字幕)的一個流,並將其新增到每個輸出檔案中。 它根據以下標準挑選每一個的“最佳”:對於影片,它是具有最高解析度的流,對於音訊,它是具有最多 channel 的流,對於字幕,是第一個字幕流。 在相同型別的幾個流相等的情況下,選擇具有最低索引的流。

您可以通過使用-vn / -an / -sn / -dn 選項來禁用某些預設設定。 要進行全面的手動控制,請使用-map 選項,該選項禁用剛描述的預設設定。

五、基本資訊查詢命令

MPEG 可以使用下面的引數進行基本資訊查詢。例如,想查詢一下現在使用的 FFMPEG 都支援哪些 filter,就可以用 ffmpeg -filters 來查詢。詳細引數說明如下:

引數 說明

-version 顯示版本。

-formats 顯示可用的格式(包括裝置)。

-demuxers 顯示可用的 demuxers。

-muxers 顯示可用的 muxers。

-devices 顯示可用的裝置。

-codecs 顯示 libavcodec 已知的所有編解碼器。

-decoders 顯示可用的解碼器。

-encoders 顯示所有可用的編碼器。

-bsfs 顯示可用的位元流 filter。

-protocols 顯示可用的協議。

-filters 顯示可用的 libavfilter 過濾器。

-pix_fmts 顯示可用的畫素格式。

-sample_fmts 顯示可用的取樣格式。

-layouts 顯示 channel 名稱和標準 channel 佈局。

-colors 顯示識別的顏色名稱。

接下來介紹的是 FFMPEG 處理音影片時使用的命令格式與引數

六、命令基本格式及引數

下面是 FFMPEG 的基本命令格式:

ffmpeg [global_options] {[input_file_options] -i input_url} ...

                         {[output_file_options] output_url} ...

ffmpeg 通過 -i 選項讀取輸任意數量的輸入“檔案”(可以是常規檔案,管道,網路流,抓取裝置等,並寫入任意數量的輸出“檔案”。

原則上,每個輸入/輸出“檔案”都可以包含任意數量的不同型別的影片流(影片/音訊/字幕/附件/資料)。 流的數量和/或型別是由容器格式來限制。 選擇從哪個輸入進入到哪個輸出將自動完成或使用 -map 選項。

要引用選項中的輸入檔案,您必須使用它們的索引(從 0 開始)。 例如。 第一個輸入檔案是 0,第二個輸入檔案是 1,等等。類似地,檔案內的流被它們的索引引用。 例如。 2:3 是指第三個輸入檔案中的第四個流。

上面就是 FFMPEG 處理音影片的常用命令,下面是一些常用引數:

(1) 主要引數

-f fmt(輸入/輸出)

強制輸入或輸出檔案格式。 格式通常是自動檢測輸入檔案,並從輸出檔案的副檔名中猜測出來,所以在大多數情況下這個選項是不需要的。

-i url(輸入)

輸入檔案的網址

-y(全域性引數)

覆蓋輸出檔案而不詢問。

-n(全域性引數)

不要覆蓋輸出檔案,如果指定的輸出檔案已經存在,請立即退出。

-c [:stream_specifier] codec(輸入/輸出,每個流)

選擇一個編碼器(當在輸出檔案之前使用)或解碼器(當在輸入檔案之前使用時)用於一個或多個流。codec 是解碼器/編碼器的名稱或 copy(僅輸出)以指示該流不被重新編碼。如:ffmpeg -i INPUT -map 0 -c:v libx264 -c:a copy OUTPUT

-codec [:stream_specifier]編解碼器(輸入/輸出,每個流)

同 -c

-t duration(輸入/輸出)

當用作輸入選項(在-i 之前)時,限制從輸入檔案讀取的資料的持續時間。當用作輸出選項時(在輸出 url 之前),在持續時間到達持續時間之後停止輸出。

-ss 位置(輸入/輸出)

當用作輸入選項時(在-i 之前),在這個輸入檔案中尋找位置。 請注意,在大多數格式中,不可能精確搜尋,因此 ffmpeg 將在位置之前尋找最近的搜尋點。 當轉碼和-accurate_seek 被啟用時(預設),搜尋點和位置之間的這個額外的分段將被解碼和丟棄。 當進行流式複製或使用-noaccurate_seek 時,它將被保留。當用作輸出選項(在輸出 url 之前)時,解碼但丟棄輸入,直到時間戳到達位置。

-frames [:stream_specifier] framecount(output,per-stream)

停止在幀計數幀之後寫入流。

-filter [:stream_specifier] filtergraph(output,per-stream)

建立由 filtergraph 指定的過濾器圖,並使用它來過濾流。filtergraph 是應用於流的 filtergraph 的描述,並且必須具有相同型別的流的單個輸入和單個輸出。在過濾器圖形中,輸入與標籤中的標籤相關聯,標籤中的輸出與標籤相關聯。有關 filtergraph 語法的更多資訊,請參閱 ffmpeg-filters 手冊。

(2) 影片引數

-vframes num(輸出)

設定要輸出的影片幀的數量。對於-frames:v,這是一個過時的別名,您應該使用它。

-r [:stream_specifier] fps(輸入/輸出,每個流)

設定幀率(Hz 值,分數或縮寫)。作為輸入選項,忽略儲存在檔案中的任何時間戳,根據速率生成新的時間戳。這與用於-framerate 選項不同(它在 FFmpeg 的舊版本中使用的是相同的)。如果有疑問,請使用-framerate 而不是輸入選項-r。作為輸出選項,複製或丟棄輸入幀以實現恆定輸出幀頻 fps。

-s [:stream_specifier]大小(輸入/輸出,每個流)

設定視窗大小。作為輸入選項,這是 video_size 專用選項的快捷方式,由某些分幀器識別,其幀尺寸未被儲存在檔案中。作為輸出選項,這會將縮放影片過濾器插入到相應過濾器圖形的末尾。請直接使用比例過濾器將其插入到開頭或其他地方。格式是’wxh’(預設 - 與源相同)。

-aspect [:stream_specifier] 寬高比(輸出,每個流)

設定方面指定的影片顯示寬高比。aspect 可以是浮點數字符串,也可以是 num:den 形式的字串,其中 num 和 den 是寬高比的分子和分母。例如“4:3”,“16:9”,“1.3333”和“1.7777”是有效的引數值。如果與-vcodec 副本一起使用,則會影響儲存在容器級別的寬高比,但不會影響儲存在編碼幀中的寬高比(如果存在)。

-vn(輸出)

禁用影片錄製。

-vcodec 編解碼器(輸出)

設定影片編解碼器。這是-codec:v 的別名。

-vf filtergraph(輸出)

建立由 filtergraph 指定的過濾器圖,並使用它來過濾流。

(3) 音訊引數

-aframes(輸出)

設定要輸出的音訊幀的數量。這是-frames:a 的一個過時的別名。

-ar [:stream_specifier] freq(輸入/輸出,每個流)

設定音訊取樣頻率。對於輸出流,它預設設定為相應輸入流的頻率。對於輸入流,此選項僅適用於音訊捕獲裝置和原始分路器,並對映到相應的分路器選件。

-ac [:stream_specifier]通道(輸入/輸出,每個流)

設定音訊通道的數量。對於輸出流,它預設設定為輸入音訊通道的數量。對於輸入流,此選項僅適用於音訊捕獲裝置和原始分路器,並對映到相應的分路器選件。

-an(輸出)

禁用錄音。

-acodec 編解碼器(輸入/輸出)

設定音訊編解碼器。這是-codec 的別名:a。

-sample_fmt [:stream_specifier] sample_fmt(輸出,每個流)

設定音訊取樣格式。使用-sample_fmts 獲取支援的樣本格式列表。

-af filtergraph(輸出)

建立由 filtergraph 指定的過濾器圖,並使用它來過濾流。

七、功能說明

(1) 錄製

ffmpeg -f avfoundation -list_devices true -i ""

(2) 錄屏

ffmpeg -f avfoundation -i 1 -r 30 out.yuv
  • -f 指定使用 avfoundation 採集資料。
  • -i 指定從哪兒採集資料,它是一個檔案索引號。在我的電腦上,1 代表桌面(可以通過上面的命令查詢裝置索引號)。
  • -r 指定幀率。按 ffmpeg 官方文件說-r 與-framerate 作用相同,但實際測試時發現不同。-framerate 用於限制輸入,而-r 用於限制輸出。

注意,桌面的輸入對幀率沒有要求,所以不用限制桌面的幀率。其實限制了也沒用。

(3) 錄屏+聲音

ffmpeg  -f avfoundation -i 1:0  -r 29.97 -c:v libx264 -crf 0 -c:a libfdk_aac -profile:a aac_he_v2 -b:a 32k  out.flv
  • -i 1:0 冒號前面的 “1” 代表的螢幕索引號。冒號後面的"0"代表的聲音索相號。
  • -c:v 與引數 -vcodec 一樣,表示影片編碼器。c 是 codec 的縮寫,v 是 video 的縮寫。
  • -crf 是 x264 的引數。 0 表式無失真壓縮。
  • -c:a 與引數 -acodec 一樣,表示音訊編碼器。
  • -profile 是 fdk_aac 的引數。 aac_he_v2 表式使用 AAC_HE v2 壓縮資料。
  • -b:a 指定音訊位元速率。 b 是 bitrate 的縮寫, a 是 audio 的縮與。

(4) 錄影片

ffmpeg -framerate 30 -f avfoundation -i 0 out.mp4
  • -framerate 限制影片的採集幀率。這個必須要根據提示要求進行設定,如果不設定就會報錯。
  • -f 指定使用 avfoundation 採集資料。
  • -i 指定影片裝置的索引號。

(5) 影片+音訊

ffmpeg -framerate 30 -f avfoundation -i 0:0 out.mp4

(6) 錄音

ffmpeg -f avfoundation -i :0 out.wav

(7) 錄製音訊裸資料

ffmpeg  -f avfoundation -i :0 -ar 44100 -f s16le out.pcm

(8) 分解與複用

流拷貝是通過將 copy 引數提供給-codec 選項來選擇流的模式。它使得 ffmpeg 省略了指定流的解碼和編碼步驟,所以它只能進行多路分解和多路複用。 這對於更改容器格式或修改容器級元資料很有用。 在這種情況下,上圖將簡化為:

由於沒有解碼或編碼,速度非常快,沒有質量損失。 但是,由於許多因素,在某些情況下可能無法正常工作。 應用過濾器顯然也是不可能的,因為過濾器處理未壓縮的資料。

(9) 抽取音訊流

ffmpeg -i input.mp4 -acodec copy -vn out.aac
  • acodec: 指定音訊編碼器,copy 指明只拷貝,不做編解碼。
  • vn: v 代表影片,n 代表 no 也就是無影片的意思。

(10) 轉換為 MP3 格式

ffmpeg -i input.mp4 -acodec libmp3lame  out.mp3

(11) 抽取影片流

ffmpeg -i input.mp4 -vcodec copy -an out.h264
  • vcodec: 指定影片編碼器,copy 指明只拷貝,不做編解碼。
  • an: a 代表影片,n 代表 no 也就是無音訊的意思。

(12) 轉格式

ffmpeg -i out.mp4 -vcodec copy -acodec copy out.flv

上面的命令表式的音訊、影片都直接 copy,只是將 mp4 的封裝格式轉成了 flv。

(13) 音影片合併

ffmpeg -i out.h264 -i out.aac -vcodec copy -acodec copy out.mp4

(14) 提取 YUV 資料

ffmpeg -i input.mp4 -an -c:v rawvideo -pixel_format yuv420p out.yuv
# 播放
ffplay -s wxh out.yuv
  • -c:v rawvideo 指定將影片轉成原始資料
  • -pixel_format yuv420p 指定轉換格式為 yuv420p

(15) YUV 轉 H264

ffmpeg -f rawvideo -pix_fmt yuv420p -s 320x240 -r 30 -i out.yuv -c:v libx264 -f rawvideo out.h264

(16) 提取 PCM 資料

ffmpeg -i out.mp4 -vn -ar 44100 -ac 2 -f s16le out.pcm
# 播放
ffplay -ar 44100 -ac 2 -f s16le -i out.pcm

(17) PCM 轉 WAV

ffmpeg -f s16be -ar 8000 -ac 2 -acodec pcm_s16be -i input.raw output.wav

(18) 簡單濾鏡

在編碼之前,ffmpeg 可以使用 libavfilter 庫中的過濾器處理原始音訊和影片幀。 幾個鏈式過濾器形成一個過濾器圖形。 ffmpeg 區分兩種型別的過濾器圖形:簡單和複雜。

簡單的過濾器圖是那些只有一個輸入和輸出,都是相同的型別。 在上面的圖中,它們可以通過在解碼和編碼之間插入一個額外的步驟來表示:

簡單的 filtergraphs 配置了 per-stream-filter 選項(分別為影片和音訊使用-vf 和-af 別名)。 一個簡單的影片 filtergraph 可以看起來像這樣的例子:

請注意,某些濾鏡會更改幀屬性,但不會改變幀內容。 例如。 上例中的 fps 過濾器會改變幀數,但不會觸及幀內容。 另一個例子是 setpts 過濾器,它只設置時間戳,否則不改變幀。

(19) 複雜濾鏡

複雜的過濾器圖是那些不能簡單描述為應用於一個流的線性處理鏈的過濾器圖。 例如,當圖形有多個輸入和/或輸出,或者當輸出流型別與輸入不同時,就是這種情況。 他們可以用下圖來表示:

複雜的過濾器圖使用-filter_complex 選項進行配置。 請注意,此選項是全域性性的,因為複雜的過濾器圖形本質上不能與單個流或檔案明確關聯。

-lavfi 選項等同於-filter_complex。

一個複雜的過濾器圖的一個簡單的例子是覆蓋過濾器,它有兩個影片輸入和一個影片輸出,包含一個影片疊加在另一個上面。 它的音訊對應是 amix 濾波器。

(20) 新增水印

ffmpeg -i out.mp4  -vf "movie=logo.png,scale=64:48[watermask];[in][watermask] overlay=30:10 [out]" water.mp4
  • -vf 中的 movie 指定 logo 位置。scale 指定 logo 大小。overlay 指定 logo 擺放的位置。

(21) 刪除水印

先通過 ffplay 找到要刪除 LOGO 的位置

ffplay -i test.flv -vf delogo=x=806:y=20:w=70:h=80:show=1

使用 delogo 濾鏡刪除 LOGO

ffmpeg -i test.flv -vf delogo=x=806:y=20:w=70:h=80 output.flv

(22) 影片縮小一倍

fmpeg -i out.mp4 -vf scale=iw/2:-1 scale.mp4
  • -vf scale 指定使用簡單過濾器 scale,iw/2:-1 中的 iw 指定按整型取影片的寬度。 -1 表示高度隨寬度一起變化。

(23) 影片裁剪

ffmpeg -i VR.mov  -vf crop=in_w-200:in_h-200 -c:v libx264 -c:a copy -video_size 1280x720 vr_new.mp4

crop 格式:crop=out_w:out_h:x:y

  • out_w: 輸出的寬度。可以使用 in_w 表式輸入影片的寬度。
  • out_h: 輸出的高度。可以使用 in_h 表式輸入影片的高度。
  • x : X 座標
  • y : Y 座標

如果 x 和 y 設定為 0,說明從左上角開始裁剪。如果不寫是從中心點裁剪。

(24) 倍速播放

ffmpeg -i out.mp4 -filter_complex "[0:v]setpts=0.5*PTS[v];[0:a]atempo=2.0[a]" -map "[v]" -map "[a]" speed2.0.mp4
-filter_complex 複雜濾鏡,[0:v]表示第一個(檔案索引號是 0)檔案的影片作為輸入。setpts=0.5*PTS 表示每幀影片的 pts 時間戳都乘 0.5 ,也就是差少一半。[v]表示輸出的別名。音訊同理就不詳述了。
map 可用於處理複雜輸出,如可以將指定的多路流輸出到一個輸出檔案,也可以指定輸出到多個檔案。"[v]" 複雜濾鏡輸出的別名作為輸出檔案的一路流。上面 map 的用法是將複雜濾鏡輸出的影片和音訊輸出到指定檔案中。

(25) 對稱影片

ffmpeg  -i out.mp4 -filter_complex "[0:v]pad=w=2*iw[a];[0:v]hflip[b];[a][b]overlay=x=w" duicheng.mp4
  • hflip 水平翻轉

如果要修改為垂直翻轉可以用 vflip。

(26) 畫中畫

ffmpeg -i out.mp4 -i out1.mp4 -filter_complex "[1:v]scale=w=176:h=144:force_original_aspect_ratio=decrease[ckout];[0:v][ckout]overlay=x=W-w-10:y=0[out]" -map "[out]" -movflags faststart new.mp4

(27) 錄製畫中畫

ffmpeg  -f avfoundation -i "1" -framerate 30 -f avfoundation -i "0:0"

-r 30 -c:v libx264 -preset ultrafast

-c:a libfdk_aac -profile:a aac_he_v2 -ar 44100 -ac 2

-filter_complex "[1:v]scale=w=176:h=144:force_original_aspect_ratio=decrease[a];[0:v][a]overlay=x=W-w-10:y=0[out]"

-map "[out]" -movflags faststart -map 1:a b.mp4

(28) 多路影片拼接

ffmpeg  -f avfoundation -i "1" -framerate 30 -f avfoundation   -i "0:0" -r 30 -c:v libx264 -preset ultrafast -c:a libfdk_aac -profile:a aac_he_v2 -ar 44100 -ac 2 -filter_complex "[0:v]scale=320:240[a];[a]pad=640:240[b];[b][1:v]overlay=320:0[out]" -map "[out]" -movflags faststart  -map 1:a  c.mp4

(29) 裁剪

ffmpeg -i out.mp4 -ss 00:00:00 -t 10 out1.mp4
  • -ss 指定裁剪的開始時間,精確到秒
  • -t 被裁剪後的時長。

(30) 合併

首先建立一個 inputs.txt 檔案,檔案內容如下:

file ‘1.flv’

file ‘2.flv’

file ‘3.flv’

然後執行下面的命令:

ffmpeg -f concat -i inputs.txt -c copy output.flv

(31) hls 切片

ffmpeg -i out.mp4 -c:v libx264 -c:a libfdk_aac -strict -2 -f hls  out.m3u8
  • -strict -2 指明音訊使有 AAC。
  • -f hls 轉成 m3u8 格式。

(32) 影片轉 JPEG

ffmpeg -i test.flv -r 1 -f image2 image-%3d.jpeg

(33) 影片轉 gif

ffmpeg -i test.flv -r 1 -f image2 image-%3d.jpeg

(34) 圖片轉影片

ffmpeg  -f image2 -i image-%3d.jpeg images.mp4

(35) 推流

ffmpeg -re -i out.mp4 -c copy -f flv rtmp://server/live/streamName

(36) 拉流儲存

ffmpeg -i rtmp://server/live/streamName -c copy dump.flv

(37) 轉流

ffmpeg -i rtmp://server/live/originalStream -c:a copy -c:v copy -f flv rtmp://server/live/h264Stream

(38) 實時推流

ffmpeg -framerate 15 -f avfoundation -i "1" -s 1280x720 -c:v libx264  -f  flv rtmp://localhost:1935/live/room

(39) 播放 YUV 資料

ffplay -pix_fmt nv12 -s 192x144 1.yuv

(40) 播放 YUV 中的 Y 平面

ffplay -pix_fmt nv21 -s 640x480 -vf extractplanes='y' 1.yuv