本文對在使用ffmpeg進行音視訊編解碼時使用到的一些函數做一個簡單介紹,我目前使用的ffmpeg版本為:0.8.5,因為本人發現在不同的版本中,有些函數名稱會有點小改動,是以在此有必要說明下ffmpeg的版本号。
ffmpeg本人也是剛接觸,本文将采用累加的方法逐個介紹我使用到的函數,如有不妥之處,還望諒解!
頭檔案引入方法:
extern "C" {
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/avutil.h"
#include "libavutil/mem.h"
#include "libavutil/fifo.h"
#include "libswscale/swscale.h"
};
1 avcodec_init()
void avcodec_init(void);
// 初始化libavcodec,一般最先調用該函數
// 引入頭檔案: #include "libavcodec/avcodec.h"
// 實作在: \ffmpeg\libavcodec\utils.c
// 該函數必須在調用libavcodec裡的其它函數前調用,一般在程式啟動或子產品初始化時調用,如果你調用了多次也無所謂,因為後面的調用不會做任何事情.從函數的實作裡你可以發現,代碼中對多次調用進行了控制.
// 該函數是非線程安全的
2 av_register_all()
void av_register_all(void);
// 初始化 libavformat和注冊所有的muxers、demuxers和protocols,
// 一般在調用avcodec_init後調用該方法
// 引入頭檔案:#include "libavformat/avformat.h"
// 實作在:\ffmpeg\libavformat\allformats.c
// 其中會調用avcodec_register_all()注冊多種音視訊格式的編解碼器,并注冊各種檔案的編解複用器
// 當然,你也可以不調用該函數,而通過選擇調用特定的方法來提供支援
3 avformat_alloc_context()
AVFormatContext *avformat_alloc_context(void);
// 配置設定一個AVFormatContext結構
// 引入頭檔案:#include "libavformat/avformat.h"
// 實作在:\ffmpeg\libavformat\options.c
// 其中負責申請一個AVFormatContext結構的記憶體,并進行簡單初始化
// avformat_free_context()可以用來釋放該結構裡的所有東西以及該結構本身
// 也是就說使用 avformat_alloc_context()配置設定的結構,需要使用avformat_free_context()來釋放
// 有些版本中函數名可能為: av_alloc_format_context();
4 avformat_free_context()
void avformat_free_context(AVFormatContext *s);
// 釋放一個AVFormatContext結構
// 引入頭檔案:#include "libavformat/avformat.h"
// 實作在:\ffmpeg\libavformat\utils.c
// 使用 avformat_alloc_context()配置設定的結構,采用該函數進行釋放,除釋放AVFormatContext結構本身記憶體之外,AVFormatContext中指針所指向的記憶體也會一并釋放
// 有些版本中函數名猜測可能為: av_free_format_context();
5 AVFormatContext 結構
typedef struct AVFormatContext {
struct AVInputFormat *iformat;
struct AVOutputFormat *oformat;
AVIOContext *pb;
unsigned int nb_streams;
AVStream **streams;
char filename[1024];
....
} AVFormatContext;
// AVFormatContext在FFMpeg裡是一個非常重要的的結構,是其它輸入、輸出相關資訊的一個容器
// 引入頭檔案:#include "libavformat/avformat.h"
// 以上隻列出了其中的部分成員
// 作為輸入容器時 struct AVInputFormat *iformat; 不能為空, 其中包含了輸入檔案的音視訊流資訊,程式從輸入容器從讀出音視訊包進行解碼處理
// 作為輸出容器時 struct AVOutputFormat *oformat; 不能為空, 程式把編碼好的音視訊包寫入到輸出容器中
// AVIOContext *pb: I/O上下文,通過對該變量指派可以改變輸入源或輸出目的
// unsigned int nb_streams; 音視訊流數量
// AVStream **streams; 音視訊流
6 AVIOContext 結構
typedef struct {
unsigned char *buffer;
int buffer_size;
unsigned char *buf_ptr;
unsigned char *buf_end;
void *opaque;
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size);
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size);
int64_t (*seek)(void *opaque, int64_t offset, int whence);
int64_t pos;
int must_flush;
int eof_reached;
int write_flag;
#if FF_API_OLD_AVIO
attribute_deprecated int is_streamed;
#endif
int max_packet_size;
unsigned long checksum;
unsigned char *checksum_ptr;
unsigned long (*update_checksum)(unsigned long checksum, const uint8_t *buf, unsigned int size);
int error;
int (*read_pause)(void *opaque, int pause);
int64_t (*read_seek)(void *opaque, int stream_index,
int64_t timestamp, int flags);
int seekable;
} AVIOContext;
// 位元組流 I/O 上下文
// 在結構的尾部增加變量可以減少版本沖突
// 移除、排序和修改已經存在的變量将會導緻較大的版本沖突
// sizeof(AVIOContext)在libav*.外部不可使用
// AVIOContext裡的函數指針不能直接調用,通常使用avio_alloc_context()函數來設定其中的函數指針
// unsigned char *buffer: 緩存的起始指針
// int buffer_size: 緩存的最大值
// void *opaque: 在回調函數中使用的指針
// int (*read_packet)(void *opaque, uint8_t *buf, int buf_size): 讀檔案回調方法
// int (*write_packet)(void *opaque, uint8_t *buf, int buf_size): 寫檔案回調方法
// int64_t (*seek)(void *opaque, int64_t offset, int whence): seek檔案回調方法
7 avio_alloc_context()
AVIOContext *avio_alloc_context(
unsigned char *buffer,
int buffer_size,
int write_flag,
void *opaque,
int (*read_packet)(void *opaque, uint8_t *buf, int buf_size),
int (*write_packet)(void *opaque, uint8_t *buf, int buf_size),
int64_t (*seek)(void *opaque, int64_t offset, int whence));
// 為I/0緩存申請并初始化一個AVIOContext結構,結束使用時必須使用av_free()進行釋放
// unsigned char *buffer: 輸入/輸出緩存記憶體塊,必須是使用av_malloc()配置設定的
// int buffer_size: 緩存大小是非常重要的
// int write_flag: 如果緩存為可寫則設定為1,否則設定為0
// void *opaque: 指針,用于回調時使用
// int (*read_packet): 讀包函數指針
// int (*write_packet): 寫包函數指針
// int64_t (*seek): seek檔案函數指針
8 av_open_input_file()
attribute_deprecated int av_open_input_file(AVFormatContext **ic_ptr, const char *filename,
AVInputFormat *fmt,
int buf_size,
AVFormatParameters *ap);
// 以輸入方式打開一個媒體檔案,也即源檔案,codecs并沒有打開,隻讀取了檔案的頭資訊.
// 引入頭檔案:#include "libavformat/avformat.h"
// AVFormatContext **ic_ptr 輸入檔案容器
// const char *filename 輸入檔案名,全路徑,并且保證檔案存在
// AVInputFormat *fmt 輸入檔案格式,填NULL即可
// int buf_size,緩沖區大小,直接填0即可
// AVFormatParameters *ap, 格式參數,添NULL即可
// 成功傳回0,其它失敗
// 不贊成使用 avformat_open_input 代替
9 av_close_input_file()
void av_close_input_file(AVFormatContext *s);
// 關閉使用avformat_close_input()打開的輸入檔案容器,但并不關系它的codecs
// 引入頭檔案:#include "libavformat/avformat.h"
// 使用av_open_input_file 打開的檔案容器,可以使用該函數關閉
// 使用 av_close_input_file 關閉後,就不再需要使用avformat_free_context 進行釋放了
10 av_find_stream_info()
int av_find_stream_info(AVFormatContext *ic);
// 通過讀取媒體檔案的中的包來擷取媒體檔案中的流資訊,對于沒有頭資訊的檔案如(mpeg)是非常有用的,
// 該函數通常重算類似mpeg-2幀模式的真實幀率,該函數并未改變邏輯檔案的position.
// 引入頭檔案:#include "libavformat/avformat.h"
// 也就是把媒體檔案中的音視訊流等資訊讀出來,儲存在容器中,以便解碼時使用
// 傳回>=0時成功,否則失敗
1 avcodec_find_decoder()
AVCodec *avcodec_find_decoder(enum CodecID id);
// 通過code ID查找一個已經注冊的音視訊解碼器
// 引入 #include "libavcodec/avcodec.h"
// 實作在: \ffmpeg\libavcodec\utils.c
// 查找解碼器之前,必須先調用av_register_all注冊所有支援的解碼器
// 查找成功傳回解碼器指針,否則傳回NULL
// 音視訊解碼器儲存在一個連結清單中,查找過程中,函數從頭到尾周遊連結清單,通過比較解碼器的ID來查找
2 avcodec_find_decoder_by_name()
AVCodec *avcodec_find_decoder_by_name(const char *name);
// 通過一個指定的名稱查找一個已經注冊的音視訊解碼器
// 引入 #include "libavcodec/avcodec.h"
// 實作在: \ffmpeg\libavcodec\utils.c
// 查找解碼器之前,必須先調用av_register_all注冊所有支援的解碼器
// 查找成功傳回解碼器指針,否則傳回NULL
// 音視訊解碼器儲存在一個連結清單中,查找過程中,函數從頭到尾周遊連結清單,通過比較解碼器的name來查找
3 avcodec_find_encoder()
AVCodec *avcodec_find_encoder(enum CodecID id);
// 通過code ID查找一個已經注冊的音視訊編碼器
// 引入 #include "libavcodec/avcodec.h"
// 實作在: \ffmpeg\libavcodec\utils.c
// 查找編碼器之前,必須先調用av_register_all注冊所有支援的編碼器
// 查找成功傳回編碼器指針,否則傳回NULL
// 音視訊編碼器儲存在一個連結清單中,查找過程中,函數從頭到尾周遊連結清單,通過比較編碼器的ID來查找
4 avcodec_find_encoder_by_name()
AVCodec *avcodec_find_encoder_by_name(const char *name);
// 通過一個指定的名稱查找一個已經注冊的音視訊編碼器
// 引入 #include "libavcodec/avcodec.h"
// 實作在: \ffmpeg\libavcodec\utils.c
// 查找編碼器之前,必須先調用av_register_all注冊所有支援的編碼器
// 查找成功傳回編碼器指針,否則傳回NULL
// 音視訊編碼器儲存在一個連結清單中,查找過程中,函數從頭到尾周遊連結清單,通過比較編碼器的名稱來查找
5 avcodec_open()
int avcodec_open(AVCodecContext *avctx, AVCodec *codec);
// 使用給定的AVCodec初始化AVCodecContext
// 引入#include "libavcodec/avcodec.h"
// 方法: avcodec_find_decoder_by_name(), avcodec_find_encoder_by_name(), avcodec_find_decoder() and avcodec_find_encoder() 提供了快速擷取一個codec的途徑
// 該方法在編碼和解碼時都會用到
// 傳回0時成功,打開作為輸出時,參數設定不對的話,調用會失敗
6 av_guess_format()
AVOutputFormat *av_guess_format(const char *short_name,
const char *filename,
const char *mime_type);
// 傳回一個已經注冊的最合适的輸出格式
// 引入#include "libavformat/avformat.h"
// 可以通過 const char *short_name 擷取,如"mpeg"
// 也可以通過 const char *filename 擷取,如"E:\a.mp4"
7 av_new_stream()
AVStream *av_new_stream(AVFormatContext *s, int id);
// 為媒體檔案添加一個流,一般為作為輸出的媒體檔案容器添加音視訊流
// 引入 #include "libavformat/avformat.h"
// 再打開源檔案時使用者一般不需要直接調用該方法
8 dump_format()
#if FF_API_DUMP_FORMAT
attribute_deprecated void dump_format(AVFormatContext *ic,
int index,
const char *url,
int is_output);
#endif
// 該函數的作用就是檢查下初始化過程中設定的參數是否符合規範 // 有些版本中為 av_ dump_format 9 av_set_parameters()
#if FF_API_FORMAT_PARAMETERS
attribute_deprecated int av_set_parameters(AVFormatContext *s, AVFormatParameters *ap);
#endif // 設定初始化參數 // 不贊成跳過該方法,直接調用 avformat_write_header/av_write_header 10 av_write_header()
#if FF_API_FORMAT_PARAMETERS
attribute_deprecated int av_write_header(AVFormatContext *s);
#endif
// 把流頭資訊寫入到媒體檔案中 // 傳回0成功
1 AVPacket
typedef struct AVPacket {
int64_t pts;
int64_t dts;
uint8_t *data;
int size;
int stream_index;
int flags;
int duration;
.
.
.
} AVPacket
// AVPacket是個很重要的結構,該結構在讀媒體源檔案和寫輸出檔案時都需要用到 // int64_t pts; 顯示時間戳 // int64_t dts; 解碼時間戳 // uint8_t *data; 包資料 // int size; 包資料長度 // int stream_index; 包所屬流序号 // int duration; 時長 // 以上資訊,如果是在讀媒體源檔案那麼avcodec會初始化,如果是輸出檔案,使用者需要對以上資訊指派 2 av_init_packet()
void av_init_packet(AVPacket *pkt);
// 使用預設值初始化AVPacket // 定義AVPacket對象後,請使用av_init_packet進行初始化 3 av_free_packet()
void av_free_packet(AVPacket *pkt);
// 釋放AVPacket對象 4 av_read_frame()
int av_read_frame(AVFormatContext *s, AVPacket *pkt);
// 從輸入源檔案容器中讀取一個AVPacket資料包
// 該函數讀出的包并不每次都是有效的,對于讀出的包我們都應該進行相應的解碼(視訊解碼/音頻解碼),
// 在傳回值>=0時,循環調用該函數進行讀取,循環調用之前請調用av_free_packet函數清理AVPacket 5 avcodec_decode_video2()
int avcodec_decode_video2(AVCodecContext *avctx, AVFrame *picture,
int *got_picture_ptr,
AVPacket *avpkt);
// 解碼視訊流AVPacket // 使用av_read_frame讀取媒體流後需要進行判斷,如果為視訊流則調用該函數解碼 // 傳回結果<0時失敗,此時程式應該退出檢查原因 // 傳回>=0時正常,假設 讀取包為:AVPacket vPacket 傳回值為 int vLen; 每次解碼正常時,對vPacket做 // 如下處理: // vPacket.size -= vLen;
// vPacket.data += vLen; // 如果 vPacket.size==0,則繼續讀下一流包,否則繼續排程該方法進行解碼,直到vPacket.size==0 // 傳回 got_picture_ptr > 0 時,表示解碼到了AVFrame *picture,其後可以對picture程序處理 6 avcodec_decode_audio3()
int avcodec_decode_audio3(AVCodecContext *avctx, int16_t *samples,
int *frame_size_ptr,
AVPacket *avpkt);
// 解碼音頻流AVPacket // 使用av_read_frame讀取媒體流後需要進行判斷,如果為音頻流則調用該函數解碼 // 傳回結果<0時失敗,此時程式應該退出檢查原因 // 傳回>=0時正常,假設 讀取包為:AVPacket vPacket 傳回值為 int vLen; 每次解碼正常時,對vPacket做 // 如下處理: // vPacket.size -= vLen;
// vPacket.data += vLen; // 如果 vPacket.size==0,則繼續讀下一流包,否則繼續排程該方法進行解碼,直到vPacket.size==0 轉自:http://blog.chinaunix.net/uid/20718335/frmd/153034.html