#《FFMPEG》 學(xué)習(xí) 第一章

##初始化

```

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;

}

```

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末此改,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子侄柔,更是在濱河造成了極大的恐慌共啃,老刑警劉巖鼓寺,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異勋磕,居然都是意外死亡妈候,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進(jìn)店門挂滓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來苦银,“玉大人,你說我怎么就攤上這事赶站♂B玻” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵贝椿,是天一觀的道長想括。 經(jīng)常有香客問我,道長烙博,這世上最難降的妖魔是什么瑟蜈? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮渣窜,結(jié)果婚禮上铺根,老公的妹妹穿的比我還像新娘。我一直安慰自己乔宿,他們只是感情好位迂,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著详瑞,像睡著了一般掂林。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上坝橡,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天泻帮,我揣著相機(jī)與錄音,去河邊找鬼驳庭。 笑死刑顺,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的饲常。 我是一名探鬼主播蹲堂,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贝淤!你這毒婦竟也來了柒竞?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤播聪,失蹤者是張志新(化名)和其女友劉穎朽基,沒想到半個(gè)月后布隔,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稼虎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年衅檀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片霎俩。...
    茶點(diǎn)故事閱讀 38,117評論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡哀军,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出打却,到底是詐尸還是另有隱情杉适,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布柳击,位于F島的核電站猿推,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏捌肴。R本人自食惡果不足惜蹬叭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望哭靖。 院中可真熱鬧具垫,春花似錦、人聲如沸试幽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽铺坞。三九已至,卻和暖如春洲胖,著一層夾襖步出監(jiān)牢的瞬間济榨,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工绿映, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留擒滑,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓叉弦,卻偏偏與公主長得像丐一,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子淹冰,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容