鏈接:https://trac.ffmpeg.org/
docs:FFmpeg
FFmpeg
是一個強大的多媒體框架,旨在處理媒體處理的各個階段。
它就像一個數字媒體工廠,包含以下部門:打包/解包(容器處理)、
轉譯/壓縮(編解碼器處理)、管理原始視頻幀和壓縮數據包、
連接輸入/輸出源(輸入輸出訪問)、執行編輯和特效(過濾與處理)、
保持所有內容的同步性(時間與速率管理),并提供靈活定制(配置選項)。
概覽
章節列表
- 輸入輸出訪問
- 容器處理
- 壓縮媒體數據包
- 原始媒體幀
- 編解碼器處理
- 過濾與處理
- 時間與速率管理
- 配置選項
第一章:輸入輸出訪問
歡迎來到FFmpeg的第一章~
在FFmpeg能夠執行諸如轉換視頻或提取音頻等酷炫操作之前,它需要從某處獲取多媒體數據并將其發送到另一處
。這個首要關鍵步驟由FFmpeg的輸入輸出訪問層處理。
可將輸入輸出訪問視為連接FFmpeg與外部世界的"管道系統"。
就像房屋需要管道來進出水一樣,FFmpeg需要一種方式來輸入輸出視頻、音頻和其他數據。該層提供了處理數據流的標準化方式,無論數據是來自計算機本地文件、網絡視頻流,甚至是網絡攝像頭等實時捕獲設備。
如果沒有這種抽象層,FFmpeg將需要完全不同的代碼來讀取本地.mp4
文件(與從網站下載視頻或從麥克風捕獲相比)。輸入輸出訪問層隱藏了這些差異,提供了一致的接口。
讓我們從一個常見用例開始:將視頻文件從一種格式轉換為另一種格式。假設您有一個名為input.mp4
的視頻文件,希望另存為output.avi
。
在命令行中使用ffmpeg
工具(底層使用FFmpeg庫)時,通常執行如下操作:
ffmpeg -i input.mp4 output.avi
從輸入輸出角度分析此命令:
-i input.mp4
:告知FFmpeg使用input.mp4
作為輸入源,-i
選項是標準輸入文件或URL指定參數output.avi
:指定輸出目標,FFmpeg將其識別為文件名并準備寫入數據
在此簡單命令中,FFmpeg的輸入輸出訪問層負責:
- 打開
input.mp4
進行讀取 - 分塊讀取
input.mp4
數據 - 打開
output.avi
進行寫入 - 將處理后的數據寫入
output.avi
-f
選項(-format
縮寫)可顯式指定輸入輸出格式,這對于沒有明確頭部的原始視頻數據等輸入尤為重要:
ffmpeg -f rawvideo -video_size 640x480 -pixel_format yuv420p -i input.yuv output.mp4
此處-f rawvideo
告知FFmpeg將input.yuv
視為原始視頻數據,因為.yuv
文件本身不包含尺寸或像素格式等格式信息。輸入輸出層仍負責打開和讀取文件,但需要這些額外參數(-video_size
, -pixel_format
)來理解讀取數據的結構。
因此,輸入輸出訪問層的核心職責是獲取字節輸入和發送字節輸出。
內部機制:管道系統
當運行
ffmpeg -i input.mp4 output.avi
時,FFmpeg如何處理文件讀取?
FFmpeg使用稱為協議(或URLProtocol)的組件來了解如何與不同類型數據源和目的地交互。
當提供input.mp4
時,FFmpeg識別其為本地文件,內部通常視為file://input.mp4
URL。
隨后FFmpeg查找能處理file://
URL的"協議處理器"——即file
協議。同理,output.avi
也由file
協議處理寫入。
這些協議處理器抽象了底層細節。
FFmpeg主引擎只需要求輸入輸出層"打開此URL"、“讀取數據”、“寫入數據"或"關閉URL”,相應協議處理器執行實際工作,無論是調用文件系統函數、發起網絡請求還是與設備驅動交互。
FFmpeg I/O處理流程:
- FFmpeg告知輸入輸出層打開目標,輸入輸出層使用正確協議(如本例的
file
協議)與外部資源交互。 - 隨后FFmpeg循環請求輸入數據并提供輸出數據直至完成,最終關閉所有資源。
超越本地文件:多樣化數據源與目的地
輸入輸出訪問層的強大之處在于其通過統一方式處理多種不同類型數據源和目的地的能力。
除本地文件外,FFmpeg還能讀寫:
-
網絡流媒體:HTTP、RTMP、RTSP、TCP、UDP等,只需提供不同URL:
ffmpeg -i http://example.com/video.mp4 output.avi ffmpeg -i input.mp4 rtmp://publish.example.com/app/streamkey
此類情況使用
http
或rtmp
等專用協議,詳見FFmpeg文檔協議選項。 -
捕獲/播放設備:攝像頭、麥克風、屏幕捕獲、視頻/音頻卡,通過操作系統特定的設備名或URL訪問:
ffmpeg -f video4linux2 -i /dev/video0 output.mp4 # Linux攝像頭 ffmpeg -f dshow -i video="集成攝像頭":audio="麥克風" output.mp4 # Windows攝像頭+麥克風
使用設備特定的輸入輸出處理器(參見輸入設備和輸出設備)。
-
標準輸入/輸出(管道):通過stdin讀取、stdout寫入,實現命令行工具鏈式調用:
cat input.ts | ffmpeg -i pipe:0 output.mp4 # 從stdin讀取 ffmpeg -i input.mp4 -f avi pipe:1 | ffplay pipe:0 # 寫入stdout
通常使用
pipe
或fd
協議。
核心思想保持不變:FFmpeg通過輸入輸出層獲取原始數據字節,無需關心字節獲取方式(通過各種上層協議的制定,實現了很好的抽象
),只需確保可讀寫。
🎢協議解析結構體:AVIOContext與自定義數據源處理
AVIOContext 與 URLProtocol
-
AVIOContext
是 FFmpeg 中用于抽象 I/O 操作的核心結構體,負責數據的讀寫緩沖和協議處理。 -
它通過
URLProtocol
實現與具體協議(如文件、HTTP、RTMP等)的交互。
URLProtocol 是協議實現的接口,定義了一組標準函數(如打開、讀取、寫入、關閉等)。每種協議(如 file、http)需要實現自己的 URLProtocol 實例。
協作流程
AVIOContext 初始化時綁定一個 URLProtocol
當用戶通過 AVIOContext 讀寫數據時,AVIOContext 會調用對應 URLProtocol 的函數。
例如,讀取 HTTP 數據時:
- AVIOContext 處理
緩沖
和狀態管理
。- 實際網絡
請求由
HTTP 協議的URLProtocol 實現
完成。
聯系
- AVIOContext 是上層抽象,提供統一的 I/O 接口。
- URLProtocol 是底層實現,處理具體協議的細節。
- 開發者通常只需操作 AVIOContext,無需直接調用 URLProtocol。
FFmpeg支持多種協議(HTTP、RTMP、RTSP、文件等)的多媒體數據獲取。其協議解析模塊通過三個核心結構體實現靈活的數據處理:
核心結構體解析
1. AVIOContext
頭文件:libavformat/avio.h
https://github.com/FFmpeg/FFmpeg/blob/master/libavformat/avio.h
關鍵字段:
buffer
:數據緩沖區buffer_size
:緩沖區容量pos
:當前讀取偏移量
opaque
:指向用戶數據或URLContextread_packet
/write_packet
:讀寫回調函數
緩沖區關系:
2. URLContext
典型實現(以RTP協議
為例):
typedef struct RTPContext
{URLContext *rtp_hd, *rtcp_hd;int ttl;int buffer_size;// ...其他協議特定字段
} RTPContext;const URLProtocol ff_rtp_protocol =
{.name = "rtp",.url_open = rtp_open,.url_read = rtp_read,.priv_data_size = sizeof(RTPContext),// ...其他回調函數
};
3. URLProtocol
作為協議實現的基類,定義標準操作接口:
url_open
:建立協議連接url_read
/url_write
:數據讀寫priv_data_size
:協議私有數據大小
URLProtocol是FFMPEG操作文件的結構(包括文件,網絡數據流等等),包括open、close、read、write、seek等操作。
定義了協議相關的回調函數,類似于url協議的虛函數定義
。
也類似門面模式
,具體的回調函數的方法實現在ff_rtp_protocol
等具體的對象中。
- 門面模式通過一個統一的高層接口簡化復雜子系統調用,像前臺接待一樣隱藏內部細節。
應用場景
標準協議處理
使用avformat_open_input
即可完成:
AVFormatContext *fmt_ctx = NULL;
avformat_open_input(&fmt_ctx, "rtsp://example.com/stream", NULL, NULL);
自定義數據源處理
當需要處理以下特殊場景時需直接操作AVIOContext:
- 內存數據源
struct buffer_data {uint8_t *ptr; size_t size;
};static int read_packet(void *opaque, uint8_t *buf, int buf_size) {struct buffer_data *bd = opaque;// 實現自定義讀取邏輯
}
- 實時流處理(代碼示例)
// 創建自定義AVIOContext
avio_ctx = avio_alloc_context(avio_ctx_buffer, avio_ctx_buffer_size,0, &bd, &read_packet, NULL, NULL);// 關聯到FormatContext
fmt_ctx->pb = avio_ctx;
avformat_open_input(&fmt_ctx, NULL, NULL, NULL);
- 加密傳輸
static int decrypt_packet(void *opaque, uint8_t *buf, int buf_size) {// 解密邏輯實現return decrypt_data(buf, buf_size);
}
示例代碼
#include <libavformat/avformat.h>struct buffer_data {uint8_t *ptr;size_t size;
};static int read_packet(void *opaque, uint8_t *buf, int buf_size) {struct buffer_data *bd = opaque;// 實現內存到緩沖區的拷貝邏輯
}int main() {// 初始化AVFormatContextAVFormatContext *fmt_ctx = avformat_alloc_context();// 創建自定義IO上下文AVIOContext *avio_ctx = avio_alloc_context(av_malloc(4096), 4096, 0, &bd, &read_packet, NULL, NULL);fmt_ctx->pb = avio_ctx;avformat_open_input(&fmt_ctx, NULL, NULL, NULL);// 后續處理流程...
}
運行測試前置條件:
sudo apt-get update
sudo apt-get install libavformat-dev libavutil-dev
運行:
gcc -o ffmpeg_buffer_test ffmpeg_buffer_test.c -lavformat -lavutil
架構設計思想
FFmpeg通過分層設計實現協議處理的靈活性:
- 高層接口:
avformat_open_input
封裝常規操作 - 擴展層:
AVIOContext
提供自定義I/O接入點 - 協議實現層:
URLProtocol
定義標準協議接口
這種設計使得開發者既能快速處理標準協議,又能通過自定義AVIOContext實現特殊需求,在易用性和靈活性之間取得平衡。
C語言API:AVIOContext與URLProtocol
對于使用FFmpeg庫(libavformat、libavcodec等)的開發人員,輸入輸出訪問層主要通過AVIOContext
結構體和URLProtocol
相關函數管理。
AVIOContext
:表示I/O操作上下文,包含讀寫緩沖區及指向底層I/O函數的指針URLProtocol
:定義特定協議處理器,包含協議名("file"
、"http"
、"rtmp"
)及實現打開、讀寫、尋址、關閉等操作的函數指針
多數應用不直接操作
URLProtocol
,而是通過avio_open2
等函數為給定URL創建AVIOContext
。
// API函數使用示例(非完整代碼)
AVFormatContext* fmt_ctx = avformat_alloc_context(); // 媒體文件格式上下文
AVIOContext* avio_ctx = NULL; // I/O操作上下文
char* input_url = "input.mp4";
int ret;// 使用avio_open2打開輸入URL并創建AVIOContext
ret = avio_open2(&avio_ctx, input_url, AVIO_FLAG_READ, NULL, NULL);
if (ret < 0) {// 錯誤處理
}fmt_ctx->pb = avio_ctx; // 將AVIOContext關聯到格式上下文// ...后續讀取數據...
uint8_t buffer[4096];
int bytes_read = avio_read(avio_ctx, buffer, sizeof(buffer));// ...關閉資源...
avio_close(avio_ctx);
// 注意:avio_context_free在關閉后釋放AVIOContext本身
該代碼段展示了核心函數:
avio_open2
初始化avio_read
/avio_write
數據傳輸avio_close
終止操作。
doc/APIchanges
文件記錄了庫接口變更,包含諸多AVIOContext
相關更新,例如avio_open2
的引入、支持更多參數等。
示例代碼如
avio_list_dir.c
展示了目錄協議交互avio_read_callback.c
演示了自定義I/O上下文。
這些API細節主要面向使用FFmpeg庫的開發人員,證實輸入輸出訪問層(AVIOContext
、URLProtocol
)是FFmpeg架構的基礎組成部分,專門負責原始數據字節的輸入輸出。
平時調api,最多用到avio,具體的數據結構和urlprotocol的實現我們并不關心,我們只需要調用,并且能輸入和輸出數據
總結
本章我們了解到,在FFmpeg解碼、編碼或處理多媒體數據前,必須首先讀取數據,處理完成后寫出數據。
這是輸入輸出訪問層的職責,它通過特定協議處理器為本地文件、網絡流和硬件設備等多樣化數據源/目的地提供統一處理方式。
現在我們理解FFmpeg如何獲取原始字節
,下一步是解析這些字節
。
多媒體數據通常封裝在容器格式(如MP4
、MKV
、AVI
)中,這些容器提供結構和元數據。
下一章我們將探討FFmpeg如何處理這些容器。
容器處理
(抽象和協議的重要性)
統一接口設計 --前文傳送:
[xiaozhi-esp32] 應用層(9種state) | 音頻編解碼層 | 雙循環架構
[xiaozhi-esp32] 音頻處理 | Display | 純虛鎖的抽象思想
[Subtitle Edit] 字幕數據模型Paragraph
| 核心邏輯庫(libse)
[shadPS4] 內存管理 | 權限管理 |文件系統 | 掛載和句柄