本文介紹一個簡單的基於FFmpeg的轉碼器。它可以將一種視頻格式(包括封轉格式和編碼格式)轉換為另一種視頻格式。轉碼器在視音頻編解碼處理的程式中,屬於一個比較複雜的東西。因為它結合了視頻的解碼和編碼。一個視頻播放器,一般只包含解碼功能;一個視頻編碼工具,一般只包含編碼功能;而一個視頻轉碼器,則需要先對視頻進行解碼,然後再對視頻進行編碼,因而相當於解碼器和編碼器的結合。下圖例舉了一個視頻的轉碼流程。輸入視頻的封裝格式是FLV,視頻編碼通訊協定是H.264,音頻編碼通訊協定是AAC;輸出視頻的封裝格式是AVI,視頻編碼通訊協定是MPEG2,音頻編碼通訊協定是MP3。從流程中可以看出,首先從輸入視頻中分離出視頻碼流和音頻壓縮碼流,然後分別將視頻碼流和音頻碼流進行解碼,擷取到非壓縮的像素資料/音頻採樣資料,接著將非壓縮的像素資料/音頻採樣資料重新進行編碼,獲得重新編碼後的視頻碼流和音頻碼流,最後將視頻碼流和音頻碼流重新封裝成一個檔案。
本文介紹的視頻轉碼器正是使用FFMPEG類庫從編程的角度實現了上述流程。該例子是從FFmpeg的例子改編的,平台是VC2010,類庫版本是2014.5.6。 流程圖(2014.9.29更新)
下面附兩張使用FFmpeg轉碼視頻的流程圖。圖中使用淺綠色標出了視頻的編碼、解碼函數。從代碼中可以看出,使用了AVFilter的不少東西,因此建議先學習AVFilter的內容後再看這個轉碼器的原始碼。
PS:實際上,轉碼器不是一定依賴AVFilter的。因此打算有時間對這個轉碼器進行進一步的簡化,使學習的人無需AVFilter的基礎也可以理解轉碼器。
簡單介紹一下流程中各個函數的意義:
open_input_file():開啟輸入檔案,並初始化相關的結構體。
open_output_file():開啟輸出檔案,並初始化相關的結構體。
init_filters():初始化AVFilter相關的結構體。
av_read_frame():從輸入檔案中讀取一個AVPacket。
avcodec_decode_video2():解碼一個視頻AVPacket(儲存H.264等壓縮碼流資料)為AVFrame(儲存YUV等非壓縮的像素資料)。
avcodec_decode_video4():解碼一個音頻AVPacket(儲存MP3等壓縮碼流資料)為AVFrame(儲存PCM採樣資料)。
filter_encode_write_frame():編碼一個AVFrame。
flush_encoder():輸入檔案讀取完畢後,輸出編碼器中剩餘的AVPacket。
以上函數中open_input_file(),open_output_file(),init_filters()中的函數在其他文章中都有所敘述,在這裡不再重複:
open_input_file()可參考:100行代碼實現最簡單的基於FFMPEG+SDL的視頻播放器(SDL1.x)
open_output_file()可參考:最簡單的基於FFMPEG的視頻編碼器(YUV編碼為H.264)
init_filters()可參考:最簡單的基於FFmpeg的AVfilter例子(浮水印疊加)
在這裡介紹一下其中編碼的函數filter_encode_write_frame()。filter_encode_write_frame()函數的流程如下圖所示,它完成了視頻/音訊編碼功能。
PS:視頻和音訊編碼流程中除了編碼函數avcodec_encode_video2()和avcodec_encode_audio2()不一樣之外,其他部分幾乎完全一樣。
簡單介紹一下filter_encode_write_frame()中各個函數的意義:
av_buffersrc_add_frame():將解碼後的AVFrame加入Filtergraph。
av_buffersink_get_buffer_ref():從Filtergraph中取一個AVFrame。
avcodec_encode_video2():編碼一個視頻AVFrame為AVPacket。
avcodec_encode_audio2():編碼一個音頻AVFrame為AVPacket。
av_interleaved_write_frame():將編碼後的AVPacket寫入檔案。
代碼
貼上代碼
[cpp] view plain copy /* *最簡單的基於FFmpeg的轉碼器 *Simplest FFmpeg Transcoder * *雷霄驊 Lei Xiaohua *leixiaohua1020@126.com *中國傳媒大學/數字電視技術 *Communication University of China / DigitalTV Technology *http://blog.csdn.net/leixiaohua1020 * *本程式實現了視頻格式之間的轉換。是一個最簡單的視頻轉碼程式。 * */ #include "stdafx.h" extern "C" { #include "libavcodec/avcodec.h" #include "libavformat/avformat.h" #include "libavfilter/avfiltergraph.h" #include "libavfilter/avcodec.h" #include "libavfilter/buffersink.h" #include "libavfilter/buffersrc.h" #include "libavutil/avutil.h" #include "libavutil/opt.h" #include "libavutil/pixdesc.h" }; static AVFormatContext *ifmt_ctx; static AVFormatContext *ofmt_ctx; typedef struct FilteringContext{ AVFilterContext*buffersink_ctx; AVFilterContext*buffersrc_ctx; AVFilterGraph*filter_graph; } FilteringContext; static FilteringContext *filter_ctx; static int open_input_file(const char *filename) {