ijkplayer框架簡析 -- FFmpeg流程
讀取媒體檔案中音視訊資料的基本功能
呼叫邏輯
char filepath[]="bigbuckbunny_480x272.h265"; AVPacket pkt; int ret = 0; AVCodecContext *pCodecCtx; AVCodec *pCodec; int videoindex = -1; //註冊複用器、編碼器等 av_register_all(); avformat_network_init(); //開啟檔案 AVFormatContext *pFormatCtx = avformat_alloc_context(); if(avformat_open_input(&pFormatCtx, filepath, NULL, NULL)!=0){ return -1; } //提取流資訊 if(avformat_find_stream_info(pFormatCtx, NULL)<0){ return -1; } //找AVCodec for(i=0; i<pFormatCtx->nb_streams; i++) { if(pFormatCtx->streams[i]->codec->codec_type==AVMEDIA_TYPE_VIDEO){ videoindex=i; break; } } if(videoindex==-1) { return -1; } pCodecCtx=pFormatCtx->streams[videoindex]->codec; pCodec=avcodec_find_decoder(pCodecCtx->codec_id); if(pCodec==NULL){ return -1; } //開打解碼器 if(avcodec_open2(pCodecCtx, pCodec,NULL)<0){ return -1; } //讀取音視訊資料 while(ret >= 0) { ret = av_read_frame(pFormatCtx, &pkt); }
av_register_all
作用是註冊codecs``demux
和protocols
。只有呼叫了該函式,才能使用複用器、編碼器等
#define REGISTER_MUXER(X, x)\ {\ extern AVOutputFormat ff_##x##_muxer;\ if (CONFIG_##X##_MUXER)\ av_register_output_format(&ff_##x##_muxer);\ } #define REGISTER_DEMUXER(X, x)\ {\ extern AVInputFormat ff_##x##_demuxer;\ if (CONFIG_##X##_DEMUXER)\ av_register_input_format(&ff_##x##_demuxer);\ } #define REGISTER_MUXDEMUX(X, x) REGISTER_MUXER(X, x); REGISTER_DEMUXER(X, x) REGISTER_MUXDEMUX(HEVC,hevc);
REGISTER_MUXDEMUX
實際上呼叫的是av_register_input_format()
和av_register_output_format()
,通過這兩個方法,將(解)複用器分別新增到了全域性變數first_iformat
與first_oformat
連結串列的最後位置
void av_register_input_format(AVInputFormat *format) { AVInputFormat **p = last_iformat; format->next = NULL; while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format)) p = &(*p)->next; last_iformat = &format->next; } void av_register_output_format(AVOutputFormat *format) { AVOutputFormat **p = last_oformat; format->next = NULL; while(*p || avpriv_atomic_ptr_cas((void * volatile *)p, NULL, format)) p = &(*p)->next; last_oformat = &format->next; }
avformat_open_input
完成媒體檔案的開啟和格式探測的功能。方法中呼叫了init_input
函式,在這裡完成了查詢流媒體協議和解複用器的工作
static int init_input(AVFormatContext *s, const char *filename, AVDictionary **options) { int ret; …… if ((ret = s->io_open(s, &s->pb, filename, AVIO_FLAG_READ | s->avio_flags, options)) < 0) return ret; if (s->iformat) return 0; return av_probe_input_buffer2(s->pb, &s->iformat, filename, s, 0, s->format_probesize); }
s->io_open
實際上呼叫的就是io_open_default()
,它最終呼叫到url_find_protocol
方法
static const struct URLProtocol *url_find_protocol(const char *filename) { const URLProtocol **protocols; …… protocols = ffurl_get_protocols(NULL, NULL); if (!protocols) return NULL; for (i = 0; protocols[i]; i++) { const URLProtocol *up = protocols[i]; if (!strcmp(proto_str, up->name)) { av_freep(&protocols); return up; } if (up->flags & URL_PROTOCOL_FLAG_NESTED_SCHEME && !strcmp(proto_nested, up->name)) { av_freep(&protocols); return up; } } av_freep(&protocols); return NULL; }
ffurl_get_protocols()
可以得到當前編譯的 FFmpeg 支援的所有流媒體協議,通過 url 的 scheme 和 protocol->name 相比較,得到正確的 protocol
av_probe_input_buffer2()
最終呼叫到av_probe_input_format3()
, 該方法遍歷所有的解複用器,即 first_iformat 連結串列中的所有節點,呼叫它們的read_probe()
函式計算匹配得分,函式最終返回計算找到的最匹配的解複用器