如下是avformat_open_input与 avformat_find_stream_info 之后调用av_dump_format的不同
- Duration: 00:22:17.75, bitrate: N/A
- Stream #0:0(eng): Video: h264 (avc1 / 0x31637661), none, 470x352, 520 kb/s, 15 fps, 23.98 tbr, 24002 tbn (default)
- Duration: 00:22:17.75, start: 0.000000, bitrate: 610 kb/s
- Stream #0:0(eng): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 470x352 [SAR 1:1 DAR 235:176], 520 kb/s, 15 fps, 23.98 tbr, 24002 tbn, 47.96 tbc (default)
发现有很多参数只是调用avformat_open_input是获取不了的,所以这儿需要再调用avformat_find_stream_info
2. 匹配解码器的过程
avformat_find_stream_info 对每一个trak都执行如下
-->find_decoder ::匹配解码器
-->avcodec_find_decoder
-->find_encdec
- a.解码器的注册过程
- avcodec_register_all
- -->REGISTER_DECODER(H264, h264);
- 将解码器都放在first_avcodec至last_avcodec的链表中
- b. 媒体文件的解码器
- 在mov_read_header中解析出format=avc1,然后查表知avc1对应的code_id=AV_CODEC_ID_H264
- c.解码器的匹配
- find_encdec查找链表,并找到id=AV_CODEC_ID_H264的AVCodec,返回H264的AVCodec
3. avformat_find_stream_info
- int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options)
- {
- int i, count, ret = 0, j;
- int64_t read_size;
- AVStream *st;
- AVPacket pkt1, *pkt;
- int64_t old_offset = avio_tell(ic->pb);
- // new streams might appear, no options for those
- int orig_nb_streams = ic->nb_streams;
- int flush_codecs;
- int64_t max_analyze_duration = ic->max_analyze_duration;
- int64_t max_stream_analyze_duration;
- int64_t max_subtitle_analyze_duration;
- int64_t probesize = ic->probesize;
- flush_codecs = probesize > 0;
- av_opt_set(ic, "skip_clear", "1", AV_OPT_SEARCH_CHILDREN);
- max_stream_analyze_duration = max_analyze_duration;
- max_subtitle_analyze_duration = max_analyze_duration;
- if (!max_analyze_duration) {
- max_stream_analyze_duration = max_analyze_duration = 5*AV_TIME_BASE; //设初值
- max_subtitle_analyze_duration = 30*AV_TIME_BASE; //设初值
- if (!strcmp(ic->iformat->name, "flv"))
- max_stream_analyze_duration = 90*AV_TIME_BASE;
- }
- for (i = 0; i < ic->nb_streams; i++) {
- const AVCodec *codec;
- AVDictionary *thread_opt = NULL;
- st = ic->streams[i];
- if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO ||
- st->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
- if (!st->codec->time_base.num)
- st->codec->time_base = st->time_base; //这儿把codec的time_base设为mp4的box中读到的time_base=0x5dc2
- }
- // only for the split stuff
- if (!st->parser && !(ic->flags & AVFMT_FLAG_NOPARSE) && st->request_probe <= 0) {
- st->parser = av_parser_init(st->codec->codec_id);
- if (st->parser) {
- if (st->need_parsing == AVSTREAM_PARSE_HEADERS) {
- st->parser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
- } else if (st->need_parsing == AVSTREAM_PARSE_FULL_RAW) {
- st->parser->flags |= PARSER_FLAG_USE_CODEC_TS;
- }
- } else if (st->need_parsing) {
- av_log(ic, AV_LOG_VERBOSE, "parser not found for codec "
- "%s, packets or times may be invalid.\n",
- avcodec_get_name(st->codec->codec_id));
- }
- }
- //mov_read_header中解析出format=avc1,然后查表知avc1对应的code_id=AV_CODEC_ID_H264返回H264的AVCodec
- codec = find_decoder(ic, st, st->codec->codec_id);
- av_dict_set(options ? &options[i] : &thread_opt, "threads", "1", 0);
- //打开解码器
- if (!has_codec_parameters(st, NULL) && st->request_probe <= 0) {
- if (codec && !st->codec->codec)
- {
- avcodec_open2(st->codec, codec, options ? &options[i] : &thread_opt);
- }
- }
- }
- for (i = 0; i < ic->nb_streams; i++) {
- #if FF_API_R_FRAME_RATE
- ic->streams[i]->info->last_dts = AV_NOPTS_VALUE;
- #endif
- ic->streams[i]->info->fps_first_dts = AV_NOPTS_VALUE;
- ic->streams[i]->info->fps_last_dts = AV_NOPTS_VALUE;
- }
- count = 0;
- read_size = 0;
- for (;;) {
- int analyzed_all_streams;
- analyzed_all_streams = 0;
- if (i == ic->nb_streams) {
- analyzed_all_streams = 1;
- if (!(ic->ctx_flags & AVFMTCTX_NOHEADER)) { //这儿是死循环的结束条件:如果所有的streams都分析好了,就结束
- ret = count;
- flush_codecs = 0;
- break;
- }
- }
- if (read_size >= probesize) { //这儿判断读到的数据read_size是否超过了预设值probeszie,要跳出不再读了
- ret = count;
- break;
- }
- ret = read_frame_internal(ic, &pkt1); //从媒体文件中读取一帧,这儿是mp4格式就是读取一个sample
- pkt = &pkt1;
- if (!(ic->flags & AVFMT_FLAG_NOBUFFER)) { //把这一帧加入到队列中
- ret = add_to_pktbuf(&ic->internal->packet_buffer, pkt, &ic->internal->packet_buffer_end, 0);
- }
- st = ic->streams[pkt->stream_index];
- if (!(st->disposition & AV_DISPOSITION_ATTACHED_PIC))
- read_size += pkt->size; //read_size是己读到的数据size
- if (pkt->dts != AV_NOPTS_VALUE && st->codec_info_nb_frames > 1) {
- if (st->info->fps_last_dts != AV_NOPTS_VALUE &&
- st->info->fps_last_dts >= pkt->dts) {
- st->info->fps_first_dts = st->info->fps_last_dts = AV_NOPTS_VALUE;
- }
- if (st->info->fps_last_dts != AV_NOPTS_VALUE &&
- st->info->fps_last_dts_idx > st->info->fps_first_dts_idx &&
- (pkt->dts - st->info->fps_last_dts) / 1000 >
- (st->info->fps_last_dts - st->info->fps_first_dts) /
- (st->info->fps_last_dts_idx - st->info->fps_first_dts_idx)) {
- st->info->fps_first_dts = st->info->fps_last_dts = AV_NOPTS_VALUE;
- }
- if (st->info->fps_first_dts == AV_NOPTS_VALUE) {
- st->info->fps_first_dts = pkt->dts;
- st->info->fps_first_dts_idx = st->codec_info_nb_frames;
- }
- st->info->fps_last_dts = pkt->dts;
- st->info->fps_last_dts_idx = st->codec_info_nb_frames;
- }
- if (st->codec_info_nb_frames>1) {
- //这儿一直为0,没有超过1,不管它
- }
- #if FF_API_R_FRAME_RATE
- if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO)
- ff_rfps_add_frame(ic, st, pkt->dts);
- #endif
- if (st->parser && st->parser->parser->split && !st->codec->extradata) {
- int i = st->parser->parser->split(st->codec, pkt->data, pkt->size);
- if (i > 0 && i < FF_MAX_EXTRADATA_SIZE) {
- if (ff_alloc_extradata(st->codec, i))
- return AVERROR(ENOMEM);
- memcpy(st->codec->extradata, pkt->data, st->codec->extradata_size);
- }
- }
- //video调用avcodec_decode_video2,audio调用avcodec_decode_audio4去解码这一帧
- //有些参数,如vidoe的pix_fmt是需要调用h264_decode_frame才可以获取其pix_fmt的
- try_decode_frame(ic, st, pkt, (options && i < orig_nb_streams) ? &options[i] : NULL);
- if (ic->flags & AVFMT_FLAG_NOBUFFER)
- av_packet_unref(pkt);
- st->codec_info_nb_frames++;
- count++;
- }
- if (flush_codecs) {
- .. //flush_codecs=0
- }
- // close codecs which were opened in try_decode_frame()
- for (i = 0; i < ic->nb_streams; i++) {
- st = ic->streams[i];
- avcodec_close(st->codec);
- }
- ff_rfps_calculate(ic);
- for (i = 0; i < ic->nb_streams; i++) {
- st = ic->streams[i];
- if (st->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
- if (st->codec->codec_id == AV_CODEC_ID_RAWVIDEO && !st->codec->codec_tag && !st->codec->bits_per_coded_sample) {
- uint32_t tag= avcodec_pix_fmt_to_codec_tag(st->codec->pix_fmt);
- if (avpriv_find_pix_fmt(avpriv_get_raw_pix_fmt_tags(), tag) == st->codec->pix_fmt)
- st->codec->codec_tag= tag;
- }
- /* estimate average framerate if not set by demuxer */
- if (st->info->codec_info_duration_fields &&
- !st->avg_frame_rate.num &&
- st->info->codec_info_duration) {
- int best_fps = 0;
- double best_error = 0.01;
- if (st->info->codec_info_duration >= INT64_MAX / st->time_base.num / 2||
- st->info->codec_info_duration_fields >= INT64_MAX / st->time_base.den ||
- st->info->codec_info_duration < 0)
- continue;
- av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
- st->info->codec_info_duration_fields * (int64_t) st->time_base.den,
- st->info->codec_info_duration * 2 * (int64_t) st->time_base.num, 60000);
- for (j = 0; j < MAX_STD_TIMEBASES; j++) {
- AVRational std_fps = { get_std_framerate(j), 12 * 1001 };
- double error = fabs(av_q2d(st->avg_frame_rate) /
- av_q2d(std_fps) - 1);
- if (error < best_error) {
- best_error = error;
- best_fps = std_fps.num;
- }
- }
- if (best_fps)
- av_reduce(&st->avg_frame_rate.num, &st->avg_frame_rate.den,
- best_fps, 12 * 1001, INT_MAX);
- }
- if (!st->r_frame_rate.num) {
- if ( st->codec->time_base.den * (int64_t) st->time_base.num
- <= st->codec->time_base.num * st->codec->ticks_per_frame * (int64_t) st->time_base.den) {
- st->r_frame_rate.num = st->codec->time_base.den;
- st->r_frame_rate.den = st->codec->time_base.num * st->codec->ticks_per_frame;
- } else {
- st->r_frame_rate.num = st->time_base.den;
- st->r_frame_rate.den = st->time_base.num;
- }
- }
- if (st->display_aspect_ratio.num && st->display_aspect_ratio.den) {
- AVRational hw_ratio = { st->codec->height, st->codec->width };
- st->sample_aspect_ratio = av_mul_q(st->display_aspect_ratio,
- hw_ratio);
- }
- } else if (st->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
- if (!st->codec->bits_per_coded_sample)
- st->codec->bits_per_coded_sample =
- av_get_bits_per_sample(st->codec->codec_id);
- // set stream disposition based on audio service type
- switch (st->codec->audio_service_type) {
- case AV_AUDIO_SERVICE_TYPE_EFFECTS:
- st->disposition = AV_DISPOSITION_CLEAN_EFFECTS;
- break;
- case AV_AUDIO_SERVICE_TYPE_VISUALLY_IMPAIRED:
- st->disposition = AV_DISPOSITION_VISUAL_IMPAIRED;
- break;
- case AV_AUDIO_SERVICE_TYPE_HEARING_IMPAIRED:
- st->disposition = AV_DISPOSITION_HEARING_IMPAIRED;
- break;
- case AV_AUDIO_SERVICE_TYPE_COMMENTARY:
- st->disposition = AV_DISPOSITION_COMMENT;
- break;
- case AV_AUDIO_SERVICE_TYPE_KARAOKE:
- st->disposition = AV_DISPOSITION_KARAOKE;
- break;
- }
- }
- }
- if (probesize)
- estimate_timings(ic, old_offset);
- if (ret >= 0 && ic->nb_streams)
- ret = -1;
- for (i = 0; i < ic->nb_streams; i++) {
- const char *errmsg;
- st = ic->streams[i];
- if (!has_codec_parameters(st, &errmsg)) {
- char buf[256];
- avcodec_string(buf, sizeof(buf), st->codec, 0);
- } else {
- ret = 0;
- }
- }
- compute_chapters_end(ic);
- find_stream_info_err:
- for (i = 0; i < ic->nb_streams; i++) {
- st = ic->streams[i];
- if (ic->streams[i]->codec->codec_type != AVMEDIA_TYPE_AUDIO)
- ic->streams[i]->codec->thread_count = 0;
- if (st->info)
- av_freep(&st->info->duration_error);
- av_freep(&ic->streams[i]->info);
- }
- return ret;
- }
为了获取媒体中的stream参数,这儿动真格了:
read_frame_internal -->从媒体文件中读取sample数据
add_to_pktbuf -->加入buf队列
try_decode_frame -->解码sample数据
最后又计算了一堆参数。
4.一些参数的获取
4.1 bitrate的计算
计算公式 bitrate=(filesize*8)/duration,单位是b/s
- 在函数update_stream_timings中
- double bitrate = (double) filesize * 8.0 * AV_TIME_BASE / (double) ic->duration;
- 为什么写得这么复杂呢?
- 从moov中读出duration=0x146995(单位是time_units)
- a.媒体文件在播放过程中用的时间单位,是由timescale计算出来的
- 一个time units=1s/timescale=1ms,这儿1个time_unts=1ms
- b.通过duration可计算媒体的播放时间 0x146995*1ms=1337749ms=1337.749s,对上了
- filesize=102119991,duration=0x4fbc6e08=1337749000
- bitrate=(filesize*8)/duration
- 因为这儿是ms,同时bitrate的单位是kb/s
- (102119991*8)/1000 / (1337749/1000) 即=102119991*8/1337749=610kb/s
4.2 关于duration
- 在函数mov_read_mvhd中有:
- c->fc->duration = av_rescale(c->duration, AV_TIME_BASE, c->time_scale);
- 把c->duration=1337749扩大了1000倍
4.3关于vidoe的pix_fmt
- 从mp4的box中是无法解析出这个pix_fmt的,需要调用try_decode_frame-->h264_decode_frame
- 然后才得知这个pix_fmt=0=yuv420p
4.4关于audio的channels与channel_layout与name=stereo
a. channels的得出很简单
mp4的box-->stsd直接就读到 0002-->audio的channels=0x02 ;;双声道
b. channel_layout=3得出有点复杂
avformat_find_stream_info
-->aac_decode_init
-->output_configure
-->sniff_channel_order
得出layout=3,也就是说这个layout是解码器给出的,跟解码器相关
c.如何得出name=stereo
av_dump_format
-->dump_stream_format
--> avcodec_string
--> av_get_channel_layout_string
- void av_bprint_channel_layout(struct AVBPrint *bp,
- int nb_channels, uint64_t channel_layout)
- {
- //匹配channel_layout_map的nb_channels与layout,然后返回name
- for (i = 0; i < FF_ARRAY_ELEMS(channel_layout_map); i++)
- if (nb_channels == channel_layout_map[i].nb_channels && //channels=2
- channel_layout == channel_layout_map[i].layout) { //channel_layout=3
- av_bprintf(bp, "%s", channel_layout_map[i].name);
- return;
- }
- }
-
static const struct {
const char *name;
int nb_channels;
uint64_t layout;
} channel_layout_map[] = {
{ "mono", 1, AV_CH_LAYOUT_MONO },
{ "stereo", 2, AV_CH_LAYOUT_STEREO },
{ "2.1", 3, AV_CH_LAYOUT_2POINT1 },
{ "3.0", 3, AV_CH_LAYOUT_SURROUND },
...
};
#define AV_CH_LAYOUT_STEREO (AV_CH_FRONT_LEFT|AV_CH_FRONT_RIGHT)
#define AV_CH_FRONT_LEFT 0x00000001
#define AV_CH_FRONT_RIGHT 0x00000002
可知AV_CH_LAYOUT_STEREO=0x03
4.6 audio的sample_format的确定
跟上面的 channel_layout=3的确定一样是在aac_decode_init中设置
avctx->sample_fmt = AV_SAMPLE_FMT_FLTP; //< float, planar