本文參考雷神博文:https://blog.csdn.net/leixiaohua1020润努。
FFMPEG打開媒體的的過程開始于avformat_open_input宏多,因此該函數(shù)的重要性不可忽視
//參數(shù)ps統(tǒng)領(lǐng)整個上下文只洒,
//會返回一個AVFormatContext的實例.
//參數(shù)filename是媒體文件名或URL.
//參數(shù)fmt是要打開的媒體格式的操作結(jié)構(gòu)祝钢,因為是讀窖杀,所以是inputFormat.此處可以
//傳入一個使用者定義的inputFormat角骤,對應(yīng)命令行中的 -f xxx段,如果指定了它器赞,
//在打開文件中就不會探測文件的實際格式了垢袱,以它為準(zhǔn)了.
//參數(shù)options是對某種格式的一些操作,是為了在命令行中可以對不同的格式傳入
//特殊的操作參數(shù)而建的港柜, 為了了解流程,完全可以無視它.
ps:函數(shù)調(diào)用成功之后處理過的AVFormatContext結(jié)構(gòu)體。
file:打開的視音頻流的URL夏醉。
fmt:強(qiáng)制指定AVFormatContext中AVInputFormat的爽锥。這個參數(shù)一般情況下可以設(shè)置為NULL,這樣FFmpeg可以自動檢測AVInputFormat畔柔。
dictionay:附加的一些選項氯夷,一般情況下可以設(shè)置為NULL
在該函數(shù)中,F(xiàn)FMPEG完成了:
輸入輸出結(jié)構(gòu)體AVIOContext的初始化靶擦;
輸入數(shù)據(jù)的協(xié)議(例如RTMP腮考,或者file)的識別(通過一套評分機(jī)制):1判斷文件名的后綴 2讀取文件頭的數(shù)據(jù)進(jìn)行比對;
使用獲得最高分的文件協(xié)議對應(yīng)的URLProtocol玄捕,通過函數(shù)指針的方式踩蔚,與FFMPEG連接(非專業(yè)用詞);
剩下的就是調(diào)用該URLProtocol的函數(shù)進(jìn)行open,read等操作了
int avformat_open_input(AVFormatContext **ps, const char *filename,
AVInputFormat *fmt, AVDictionary **options)
{
AVFormatContext *s = *ps;
int i, ret = 0;
AVDictionary *tmp = NULL;
ID3v2ExtraMeta *id3v2_extra_meta = NULL;
//預(yù)防之前沒有申請空間這里再申請一次枚粘,如果還是失敗就直接return
if (!s && !(s = avformat_alloc_context()))
return AVERROR(ENOMEM);
if (!s->av_class) {
av_log(NULL, AV_LOG_ERROR, "Input context has not been properly allocated by avformat_alloc_context() and is not NULL either\n");
return AVERROR(EINVAL);
}
//如果指定了輸入的格式馅闽,這樣直接使用
if (fmt)
s->iformat = fmt;
if (options)
av_dict_copy(&tmp, *options, 0);
if (s->pb) // must be before any goto fail
s->flags |= AVFMT_FLAG_CUSTOM_IO;
if ((ret = av_opt_set_dict(s, &tmp)) < 0)
goto fail;
//復(fù)制文件名或者流的url給url指針,strdup申請了空間放置字符串并返回
if (!(s->url = av_strdup(filename ? filename : ""))) {
ret = AVERROR(ENOMEM);
goto fail;
}
#if FF_API_FORMAT_FILENAME
FF_DISABLE_DEPRECATION_WARNINGS
//標(biāo)準(zhǔn)庫函數(shù)strlcpy馍迄,是更加安全版本的strcpy函數(shù)福也,把filename復(fù)制到s->filename
av_strlcpy(s->filename, filename ? filename : "", sizeof(s->filename));
FF_ENABLE_DEPRECATION_WARNINGS
#endif
//該函數(shù)主要是初始化AVInputFormat結(jié)構(gòu)體,主要是調(diào)用av_probe_input_buffer2函數(shù)探測碼流格式 這個函數(shù)對里面的函數(shù)指針seek跟close等賦值
//執(zhí)行完此函數(shù)后攀圈,s->pb和s->iformat都已經(jīng)指向了有效實例.pb是用于讀寫數(shù)據(jù)的暴凑,它
//把媒體數(shù)據(jù)當(dāng)做流來讀寫,不管是什么媒體格式赘来,而iformat把pb讀出來的流按某種媒體格
//式進(jìn)行分析现喳,也就是說pb在底層,iformat在上層.
//絕大部分初始化工作都是在這里做的
if ((ret = init_input(s, filename, &tmp)) < 0)
goto fail;
/**
* format probing score.
* The maximal score is AVPROBE_SCORE_MAX, its set when the demuxer probes
* the format.
* - encoding: unused
* - decoding: set by avformat, read by user
*/
s->probe_score = ret;
if (!s->protocol_whitelist && s->pb && s->pb->protocol_whitelist) {
s->protocol_whitelist = av_strdup(s->pb->protocol_whitelist);
if (!s->protocol_whitelist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
if (!s->protocol_blacklist && s->pb && s->pb->protocol_blacklist) {
s->protocol_blacklist = av_strdup(s->pb->protocol_blacklist);
if (!s->protocol_blacklist) {
ret = AVERROR(ENOMEM);
goto fail;
}
}
if (s->format_whitelist && av_match_list(s->iformat->name, s->format_whitelist, ',') <= 0) {
av_log(s, AV_LOG_ERROR, "Format not on whitelist \'%s\'\n", s->format_whitelist);
ret = AVERROR(EINVAL);
goto fail;
}
//直接將文件指針指向 s->skip_initial_bytes位置
avio_skip(s->pb, s->skip_initial_bytes);
//很多靜態(tài)圖像文件格式撕捍,都被當(dāng)作一個格式處理拿穴,比如要打開.jpeg文件,需要的格式
//名為image2
/* Check filename in case an image number is expected. */
//檢查文件名忧风,在期望的圖像編號的情況下檢查文件名默色,后續(xù)再了解此處細(xì)節(jié)
if (s->iformat->flags & AVFMT_NEEDNUMBER) {
if (!av_filename_number_test(filename)) {
ret = AVERROR(EINVAL);
goto fail;
}
}
s->duration = s->start_time = AV_NOPTS_VALUE;
/* Allocate private data. */
//分配私有數(shù)據(jù),此結(jié)構(gòu)的size在定義AVInputFormat時已指定了
if (s->iformat->priv_data_size > 0) {
if (!(s->priv_data = av_mallocz(s->iformat->priv_data_size))) {
ret = AVERROR(ENOMEM);
goto fail;
}
//這里的用處還沒細(xì)看狮腿,先不管
if (s->iformat->priv_class) {
*(const AVClass **) s->priv_data = s->iformat->priv_class;
av_opt_set_defaults(s->priv_data);
if ((ret = av_opt_set_dict(s->priv_data, &tmp)) < 0)
goto fail;
}
}
/* e.g. AVFMT_NOFILE formats will not have a AVIOContext */
//從MP3文件讀取id3
if (s->pb)
ff_id3v2_read_dict(s->pb, &s->internal->id3v2_meta, ID3v2_DEFAULT_MAGIC, &id3v2_extra_meta);
//讀一下媒體的頭部腿宰,根據(jù)流的數(shù)量分配流結(jié)構(gòu)并初始化,把文件指針指向數(shù)據(jù)區(qū)開始處等.
read_header函數(shù)指針在init_input函數(shù)里面賦值
//讀取多媒體數(shù)據(jù)文件頭缘厢,根據(jù)視音頻流創(chuàng)建相應(yīng)的AVStream吃度。
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->iformat->read_header)
if ((ret = s->iformat->read_header(s)) < 0)
goto fail;
if (!s->metadata) {
s->metadata = s->internal->id3v2_meta;
s->internal->id3v2_meta = NULL;
} else if (s->internal->id3v2_meta) {
int level = AV_LOG_WARNING;
if (s->error_recognition & AV_EF_COMPLIANT)
level = AV_LOG_ERROR;
av_log(s, level, "Discarding ID3 tags because more suitable tags were found.\n");
av_dict_free(&s->internal->id3v2_meta);
if (s->error_recognition & AV_EF_EXPLODE)
return AVERROR_INVALIDDATA;
}
if (id3v2_extra_meta) {
if (!strcmp(s->iformat->name, "mp3") || !strcmp(s->iformat->name, "aac") ||
!strcmp(s->iformat->name, "tta")) {
if ((ret = ff_id3v2_parse_apic(s, &id3v2_extra_meta)) < 0)
goto fail;
if ((ret = ff_id3v2_parse_chapters(s, &id3v2_extra_meta)) < 0)
goto fail;
if ((ret = ff_id3v2_parse_priv(s, &id3v2_extra_meta)) < 0)
goto fail;
} else
av_log(s, AV_LOG_DEBUG, "demuxer does not support additional id3 data, skipping\n");
}
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
//附屬照片?贴硫?
if ((ret = avformat_queue_attached_pictures(s)) < 0)
goto fail;
// s->internal->data_offset保存數(shù)據(jù)區(qū)開始的位置
if (!(s->flags&AVFMT_FLAG_PRIV_OPT) && s->pb && !s->internal->data_offset)
s->internal->data_offset = avio_tell(s->pb);
s->internal->raw_packet_buffer_remaining_size = RAW_PACKET_BUFFER_SIZE;
//更新一下結(jié)構(gòu)體參數(shù)
update_stream_avctx(s);
for (i = 0; i < s->nb_streams; i++)
s->streams[i]->internal->orig_codec_id = s->streams[i]->codecpar->codec_id;
if (options) {
av_dict_free(options);
*options = tmp;
}
*ps = s;
return 0;
fail:
ff_id3v2_free_extra_meta(&id3v2_extra_meta);
av_dict_free(&tmp);
if (s->pb && !(s->flags & AVFMT_FLAG_CUSTOM_IO))
avio_closep(&s->pb);
avformat_free_context(s);
*ps = NULL;
return ret;
}
下面是這個函數(shù)的調(diào)用結(jié)構(gòu)圖:
init_input()
此函數(shù)分析在ffmpeg源碼分析3-init_input
AVInputFormat-> read_header()
在調(diào)用完init_input()完成基本的初始化并且推測得到相應(yīng)的AVInputFormat之后椿每,avformat_open_input()會調(diào)用AVInputFormat的read_header()方法讀取媒體文件的文件頭并且完成相關(guān)的初始化工作伊者。read_header()是一個位于AVInputFormat結(jié)構(gòu)體中的一個函數(shù)指針,對于不同的封裝格式间护,會調(diào)用不同的read_header()的實現(xiàn)函數(shù)亦渗。舉個例子,當(dāng)輸入視頻的封裝格式為MOV的時候汁尺,會調(diào)用mov的AVInputFormat中的read_header()法精。mov的AVInputFormat定義位于libavformat\mov.c文件中,如下所示痴突。
AVInputFormat ff_mov_demuxer = {
.name = "mov,mp4,m4a,3gp,3g2,mj2",
.long_name = NULL_IF_CONFIG_SMALL("QuickTime / MOV"),
.priv_class = &mov_class,
.priv_data_size = sizeof(MOVContext),
.extensions = "mov,mp4,m4a,3gp,3g2,mj2",
.read_probe = mov_probe,
.read_header = mov_read_header,
.read_packet = mov_read_packet,
.read_close = mov_read_close,
.read_seek = mov_read_seek,
.flags = AVFMT_NO_BYTE_SEEK,
};
可以看出read_header()指向了mov_read_header()函數(shù)搂蜓。mov_read_header()的實現(xiàn)同樣位于libavformat\mov.c文件中,如下所示辽装。
static int mov_read_header(AVFormatContext *s)
{
MOVContext *mov = s->priv_data;
AVIOContext *pb = s->pb;
int j, err;
MOVAtom atom = { AV_RL32("root") };
int i;
if (mov->decryption_key_len != 0 && mov->decryption_key_len != AES_CTR_KEY_SIZE) {
av_log(s, AV_LOG_ERROR, "Invalid decryption key len %d expected %d\n",
mov->decryption_key_len, AES_CTR_KEY_SIZE);
return AVERROR(EINVAL);
}
mov->fc = s;
mov->trak_index = -1;
/* .mov and .mp4 aren't streamable anyway (only progressive download if moov is before mdat) */
if (pb->seekable & AVIO_SEEKABLE_NORMAL)
atom.size = avio_size(pb);
else
atom.size = INT64_MAX;
/* check MOV header */
do {
if (mov->moov_retry)
avio_seek(pb, 0, SEEK_SET);
if ((err = mov_read_default(mov, pb, atom)) < 0) {
av_log(s, AV_LOG_ERROR, "error reading header\n");
mov_read_close(s);
return err;
}
} while ((pb->seekable & AVIO_SEEKABLE_NORMAL) && !mov->found_moov && !mov->moov_retry++);
if (!mov->found_moov) {
av_log(s, AV_LOG_ERROR, "moov atom not found\n");
mov_read_close(s);
return AVERROR_INVALIDDATA;
}
av_log(mov->fc, AV_LOG_TRACE, "on_parse_exit_offset=%"PRId64"\n", avio_tell(pb));
if (pb->seekable & AVIO_SEEKABLE_NORMAL) {
if (mov->nb_chapter_tracks > 0 && !mov->ignore_chapters)
mov_read_chapters(s);
for (i = 0; i < s->nb_streams; i++)
if (s->streams[i]->codecpar->codec_tag == AV_RL32("tmcd")) {
mov_read_timecode_track(s, s->streams[i]);
} else if (s->streams[i]->codecpar->codec_tag == AV_RL32("rtmd")) {
mov_read_rtmd_track(s, s->streams[i]);
}
}
/* copy timecode metadata from tmcd tracks to the related video streams */
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
if (sc->timecode_track > 0) {
AVDictionaryEntry *tcr;
int tmcd_st_id = -1;
for (j = 0; j < s->nb_streams; j++)
if (s->streams[j]->id == sc->timecode_track)
tmcd_st_id = j;
if (tmcd_st_id < 0 || tmcd_st_id == i)
continue;
tcr = av_dict_get(s->streams[tmcd_st_id]->metadata, "timecode", NULL, 0);
if (tcr)
av_dict_set(&st->metadata, "timecode", tcr->value, 0);
}
}
export_orphan_timecode(s);
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
fix_timescale(mov, sc);
if(st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO && st->codecpar->codec_id == AV_CODEC_ID_AAC) {
st->skip_samples = sc->start_pad;
}
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO && sc->nb_frames_for_fps > 0 && sc->duration_for_fps > 0)
av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
sc->time_scale*(int64_t)sc->nb_frames_for_fps, sc->duration_for_fps, INT_MAX);
if (st->codecpar->codec_type == AVMEDIA_TYPE_SUBTITLE) {
if (st->codecpar->width <= 0 || st->codecpar->height <= 0) {
st->codecpar->width = sc->width;
st->codecpar->height = sc->height;
}
if (st->codecpar->codec_id == AV_CODEC_ID_DVD_SUBTITLE) {
if ((err = mov_rewrite_dvd_sub_extradata(st)) < 0)
return err;
}
}
if (mov->handbrake_version &&
mov->handbrake_version <= 1000000*0 + 1000*10 + 2 && // 0.10.2
st->codecpar->codec_id == AV_CODEC_ID_MP3
) {
av_log(s, AV_LOG_VERBOSE, "Forcing full parsing for mp3 stream\n");
st->need_parsing = AVSTREAM_PARSE_FULL;
}
}
if (mov->trex_data) {
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
if (st->duration > 0) {
if (sc->data_size > INT64_MAX / sc->time_scale / 8) {
av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %"PRId64" * 8 * %d\n",
sc->data_size, sc->time_scale);
mov_read_close(s);
return AVERROR_INVALIDDATA;
}
st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale / st->duration;
}
}
}
if (mov->use_mfra_for > 0) {
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
if (sc->duration_for_fps > 0) {
if (sc->data_size > INT64_MAX / sc->time_scale / 8) {
av_log(s, AV_LOG_ERROR, "Overflow during bit rate calculation %"PRId64" * 8 * %d\n",
sc->data_size, sc->time_scale);
mov_read_close(s);
return AVERROR_INVALIDDATA;
}
st->codecpar->bit_rate = sc->data_size * 8 * sc->time_scale /
sc->duration_for_fps;
}
}
}
for (i = 0; i < mov->bitrates_count && i < s->nb_streams; i++) {
if (mov->bitrates[i]) {
s->streams[i]->codecpar->bit_rate = mov->bitrates[i];
}
}
ff_rfps_calculate(s);
for (i = 0; i < s->nb_streams; i++) {
AVStream *st = s->streams[i];
MOVStreamContext *sc = st->priv_data;
switch (st->codecpar->codec_type) {
case AVMEDIA_TYPE_AUDIO:
err = ff_replaygain_export(st, s->metadata);
if (err < 0) {
mov_read_close(s);
return err;
}
break;
case AVMEDIA_TYPE_VIDEO:
if (sc->display_matrix) {
err = av_stream_add_side_data(st, AV_PKT_DATA_DISPLAYMATRIX, (uint8_t*)sc->display_matrix,
sizeof(int32_t) * 9);
if (err < 0)
return err;
sc->display_matrix = NULL;
}
if (sc->stereo3d) {
err = av_stream_add_side_data(st, AV_PKT_DATA_STEREO3D,
(uint8_t *)sc->stereo3d,
sizeof(*sc->stereo3d));
if (err < 0)
return err;
sc->stereo3d = NULL;
}
if (sc->spherical) {
err = av_stream_add_side_data(st, AV_PKT_DATA_SPHERICAL,
(uint8_t *)sc->spherical,
sc->spherical_size);
if (err < 0)
return err;
sc->spherical = NULL;
}
if (sc->mastering) {
err = av_stream_add_side_data(st, AV_PKT_DATA_MASTERING_DISPLAY_METADATA,
(uint8_t *)sc->mastering,
sizeof(*sc->mastering));
if (err < 0)
return err;
sc->mastering = NULL;
}
if (sc->coll) {
err = av_stream_add_side_data(st, AV_PKT_DATA_CONTENT_LIGHT_LEVEL,
(uint8_t *)sc->coll,
sc->coll_size);
if (err < 0)
return err;
sc->coll = NULL;
}
break;
}
}
ff_configure_buffers_for_index(s, AV_TIME_BASE);
for (i = 0; i < mov->frag_index.nb_items; i++)
if (mov->frag_index.item[i].moof_offset <= mov->fragment.moof_offset)
mov->frag_index.item[i].headers_read = 1;
return 0;
}
可以看出帮碰,函數(shù)讀取了mov的文件頭并且判斷其中是否包含視頻流和音頻流,創(chuàng)建相應(yīng)的視頻流和音頻流如迟。
經(jīng)過上面的步驟AVInputFormat的read_header()完成了視音頻流對應(yīng)的AVStream的創(chuàng)建收毫。至此,avformat_open_input()中的主要代碼分析完畢殷勘。