##初始化
```
av_register_all? ? 編解碼初始化函數(shù)塌碌,此函數(shù)無參數(shù)返回
avcodec_register_all? ? ? 編碼几睛、解析器初始化
avformat_network_init? ? 網(wǎng)絡(luò)編碼初始化,如rtp,udp等協(xié)議讀取視頻信息
avformat_network_deinit? 與上面相對立,撤銷所有網(wǎng)絡(luò)初始化
avfilter_register_all? ? 所有濾鏡重置<對應(yīng) avfilter_uninit>
av_register_input_format? 針對AVInputFormat輸入的視頻信息存儲的結(jié)構(gòu)體對象
av_register_output_format 針對AVInputFormat輸出的視頻信息存儲的結(jié)構(gòu)體對象
#注意:ffmpeg所有返回類型小于0是錯(cuò)誤,大于等于0表示正確
```
##打開網(wǎng)絡(luò)(本地)文件
```
AVFormatContext
官網(wǎng)定義為(Format I/O context)民逼,一個(gè)可視為讀取文件的內(nèi)容存儲器。初始化調(diào)用avformat_alloc_context()涮帘。此結(jié)構(gòu)類型中存儲有AVInputFormat和AVOutputFormat兩個(gè)結(jié)構(gòu)體拼苍,分別是解碼和編碼對應(yīng)的格式信息。視頻存儲內(nèi)容(文件的buffer)存儲在priv_data對象中焚辅,解碼調(diào)用avformat_write_header()函數(shù)會往里面寫入數(shù)據(jù)映屋,而編碼則是avformat_open_input();
#從文件名讀取相關(guān)信息,返回0是成功讀韧摺棚点;文件可以是網(wǎng)絡(luò)存儲也可以是本地存儲文件的絕對路徑。
// open format context
AVFormatContext* ifmt_ctx = NULL;
if(avformat_open_input(&ifmt_ctx, fileName, NULL, NULL) != 0){
return -1;
}
#如果打開文件成功湾蔓,從formatContext中讀取視頻流數(shù)據(jù)瘫析。如mpegts的視頻包數(shù)據(jù)是沒有視頻頭信息的,所有無法讀到相關(guān)信息默责,需要注意贬循。如果需要查找制定的流數(shù)據(jù),使用av_find_program_from_stream桃序。
// find stream info
if(avformat_find_stream_info(ifmt_ctx, NULL) < 0){
return -1;
}
#獲取到流信息之后杖虾,我們就可以開始解碼了;遍歷ifmt_ctx中的所有可用流數(shù)據(jù),從中找到編碼格式是視頻之后媒熊,記錄當(dāng)前對應(yīng)的視頻流編碼的index值奇适。如果有音頻坟比,同樣的寫法,將編碼類型改為AVMEDIA_TYPE_AUDIO嚷往。
int videoindex = -1;
for(i = 0; i < ifmt_ctx->nb_streams; i ++)
if(ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO){
videoindex = i;
break;
}
到此videoindex不等于-1葛账,無論是音頻還是視頻都是說明文件是可用的。
```
##解碼
```
AVCodecContext
這是個(gè)結(jié)構(gòu)體皮仁,記錄視頻的編碼信息籍琳,如視頻對應(yīng)的格式,像素大小贷祈,尺寸等等趋急。
// 讀取視頻的編碼信息
AVCodecContext *codecContext = ifmt_ctx->streams[i]->codec;
// 例子
* avcodec_register_all();
* av_dict_set(&opts, "b", "2.5M", 0);
* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
* if (!codec)
*? ? exit(1);
// 找到解碼器,從對應(yīng)的碼流類型中
AVCodec *codec = avcodec_find_decoder(codecContext->codec_id);
// 打開解碼器付燥,才能進(jìn)行解碼宣谈;如果已知解碼的碼流是h264數(shù)據(jù)愈犹,可用自動創(chuàng)建键科,調(diào)用avcodec_find_decoder_by_name()或avcodec_find_decoder(AV_CODEC_ID_H264);
avcodec_open2(codecContext, codec,NULL)漩怎;
// pframe是我們最后解出來的數(shù)據(jù)勋颖,packet是對應(yīng)的解碼時(shí)間戳和編碼時(shí)間戳和對應(yīng)frame時(shí)長;
AVFrame *pFrame = av_frame_alloc();
AVPacket *packet = (AVPacket *)av_malloc(sizeof(AVPacket));
while(av_read_frame(ifmt_ctx, packet) >= 0){
// 對應(yīng)的視頻流記錄的index值
if(packet->stream_index == videoindex){
// decode 解碼勋锤, pFrame即為所需要的數(shù)據(jù)
avcodec_decode_video2(codecContext, pFrame, &got_picture, packet);
}
}
```
##截圖
```
struct SwsContext *img_convert_ctx;
// 設(shè)定內(nèi)容及相關(guān)格式饭玲,對應(yīng)的像素
img_convert_ctx = sws_getContext(pCodecCtx->width,
pCodecCtx->height,
pCodecCtx->pix_fmt,
pCodecCtx->width,
pCodecCtx->height,
AV_PIX_FMT_YUV420P,
SWS_BICUBIC,
NULL,
NULL,
NULL);
// 內(nèi)容填充
sws_scale(img_convert_ctx,
(const uint8_t* const*)pFrame->data,
pFrame->linesize,
0,
pCodecCtx->height,
pFrameYUV->data,
pFrameYUV->linesize);
```
##濾鏡
濾鏡可以實(shí)現(xiàn)鏈表連接,進(jìn)行多個(gè)設(shè)置叁执,下面是我做的完整的濾鏡初始化到使用的場景
```
int gp_image_create(AVFilterGraph *filterGraph, AVCodecContext *codecContext, AVFilterContext *src_filter_context, AVFilterContext *sink_filter_context, gp_filter_type type){
avfilter_register_all();
int ret = 0;
char args[512];
// 創(chuàng)建水印的濾鏡茄厘,根據(jù)名稱查找對應(yīng)的水印
AVFilter *src_filter = avfilter_get_by_name("buffer");
AVFilter *sink_filter = avfilter_get_by_name("buffersink");
/* 輸入或輸出的過濾鏈 */
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs? = avfilter_inout_alloc();
// 像素格式,yuv420
enum AVPixelFormat pix_formats[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
// 初始化過濾器圖形
filterGraph = avfilter_graph_alloc();
// 是否初始化成功
if (!outputs || !inputs || !filterGraph) {
perror(">>>>>>>>>>>>>? 初始化失敗 \n");
goto end;
}
/* buffer video source: the decoded frames from the decoder will be inserted here. */
snprintf(args, sizeof(args), "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
codecContext->width, codecContext->height, codecContext->pix_fmt,
codecContext->time_base.num, codecContext->time_base.den,
codecContext->sample_aspect_ratio.num, codecContext->sample_aspect_ratio.den);
// 創(chuàng)建和添加過濾器實(shí)例到現(xiàn)有的圖形,輸入
ret = avfilter_graph_create_filter(&src_filter_context, src_filter, "in", args, NULL, filterGraph);
if (ret < 0) {
perror(">>>>>>>>>>>>>? 無法創(chuàng)建過濾器的圖形 \n");
goto end;
}
// 創(chuàng)建和添加過濾器實(shí)例到現(xiàn)有的圖形谈宛,輸出
ret = avfilter_graph_create_filter(&sink_filter_context, sink_filter, "out", NULL, NULL, filterGraph);
if (ret < 0) {
perror(">>>>>>>>>>>>>? 無法創(chuàng)建過濾器的圖形 \n");
goto end;
}
// Set a binary option to an integer list. 從證書列表中查找flag選項(xiàng)次哈,得到視頻像素格式
ret = av_opt_set_int_list(sink_filter_context, "pix_fmts", pix_formats, AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN);
if (ret < 0) {
perror(">>>>>>>>>>>>? 不能設(shè)置輸出的像素格式 \n");
goto end;
}
// 過濾graph的終點(diǎn)
outputs->name = av_strdup("in");
outputs->filter_ctx = src_filter_context;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = sink_filter_context;
inputs->pad_idx = 0;
inputs->next = NULL;
// 將filters描述讀取成圖像信息
ret = avfilter_graph_parse_ptr(filterGraph, gp_filter_description(type), &inputs, &outputs, NULL);
if (ret < 0) {
perror(">>>>>>>>>>>>? 不能從描述信息中讀取圖形, 更正描述信息? ");
printf("description = %s \n",gp_filter_description(type));
goto end;
}
// 檢查有效性,并配置所有的鏈接和格式的圖形
ret = avfilter_graph_config(filterGraph, NULL);
if (ret < 0) {
perror(">>>>>>>>>>>? 圖形無效? \n");
goto end;
}
#if 此處為解碼處幀并對幀處理后得到新的幀數(shù)據(jù)即為過濾后的數(shù)據(jù)
AVFrame *frame = av_frame_alloc();
AVFrame *newFrame = av_frame_alloc();
// 向緩沖區(qū)加入一幀數(shù)據(jù)
ret = av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF);
// 從緩沖區(qū)獲取輸出的一幀數(shù)據(jù) newFrame即為過濾后的視頻幀數(shù)據(jù)
ret = av_buffersink_get_frame(buffersink_ctx, newFrame);
#endif
return 0;
end:
avfilter_inout_free(&inputs);
avfilter_inout_free(&outputs);
if (filterGraph != NULL) avfilter_graph_free(&filterGraph);
return -1;
}
```
##水印
```
AVFilterContext *gp_overlay_create(AVFilterGraph *filterGraph){
int ret = 0;
AVDictionary *overlayOptions;
// 創(chuàng)建水印的濾鏡吆录,根據(jù)名稱查找對應(yīng)的水印
AVFilter *overlayFilter = avfilter_get_by_name("overlay");
// 可視為水印的實(shí)例對象窑滞,用于管理overlayFilter <創(chuàng)建一個(gè)新的過濾器實(shí)例>
AVFilterContext *overlayContext = avfilter_graph_alloc_filter(filterGraph, overlayFilter, "overlay");
// 是否創(chuàng)建成功
if (!overlayContext){
perror(">>>>>>>>>>> 創(chuàng)建濾鏡失敗? \n");
return NULL;
}
// 設(shè)置位置? x軸,y軸
av_dict_set(&overlayOptions, "x", "0", 0);
av_dict_set(&overlayOptions, "y", "0", 0);
// main是主濾鏡,可以當(dāng)做是背板之上的恢筝,前景之下的哀卫;overlay可以當(dāng)做是背板
av_dict_set(&overlayOptions, "main_w", "100", 0);
av_dict_set(&overlayOptions, "main_h", "100", 0);
// 設(shè)置水印的覆蓋范圍
av_dict_set(&overlayOptions, "overlay_w", "120", 0);
av_dict_set(&overlayOptions, "overlay_h", "120", 0);
// 根據(jù)可選參數(shù)初始化過濾器實(shí)例
ret = avfilter_init_dict(overlayContext, &overlayOptions);
if (ret < 0) {
perror(">>>>>>>>>>> 初始化過濾器實(shí)例出錯(cuò)? \n");
return NULL;
}
// 釋放可選參數(shù)容器
av_dict_free(&overlayOptions);
// 過濾器是可以多個(gè)進(jìn)行鏈接的,可調(diào)用下面函數(shù)將水印的濾鏡添加到目標(biāo)濾鏡上,將兩個(gè)過濾器連接到一起撬槽; 默認(rèn)設(shè)置0 index of input pad
//avfilter_link(overlayContext, 0, <#AVFilterContext *dst#>, 0);
/*
// connect inputs and outputs
if (err >= 0) err = avfilter_link(buffersrc_ctx, 0, filter_ctx, 0);
if (err >= 0) err = avfilter_link(filter_ctx, 0, buffersink_ctx, 0);
if (err < 0) {
av_log(NULL, AV_LOG_ERROR, "error connecting filters\n");
return err;
}
err = avfilter_graph_config(filter_graph, NULL);
*/
return overlayContext;
}
```