ffmpeg在看media文件信息的时候特别方便,通过-i参数指定input文件,就可以得到下面这样的输出,当然通过ffprobe也是一样的,只是ffmpeg用的太习惯了而已。
$ ffmpeg -i ~/h264.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/home/hui/h264.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Lavf55.37.102
Duration: 00:00:18.04, start: 0.000000, bitrate: 390 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720, 389 kb/s, 25 fps, 25 tbr, 1200k tbn, 50 tbc (default)
Metadata:
handler_name : VideoHandler
下面是对这个处理过程的一个简单的分析,首先是调用栈:
av_demuxer_iterate
av_probe_input_format3 format.c 165 0x7ffff70bf3dc
av_probe_input_format2 format.c 208 0x7ffff70bf5ca
av_probe_input_buffer2 format.c 280 0x7ffff70bf8b5
init_input utils.c 446 0x7ffff722ed5d
avformat_open_input utils.c 576 0x7ffff722f26a
open_input_file ffmpeg_opt.c 1105 0x555555563c13
open_files ffmpeg_opt.c 3280 0x55555556dba3
ffmpeg_parse_options ffmpeg_opt.c 3320 0x55555556dd57
main ffmpeg.c 4874 0x55555558d9d8
从ffmpeg的main函数开始,最后进入av_probe_input_format3中调用av_demuxer_iterate,av_demuxer_iterate遍历所有的demuxer,进行打分,最后返回打分最高的fmt,完成了container format的识别。
const AVInputFormat *av_demuxer_iterate(void **opaque)
{
static const uintptr_t size = sizeof(demuxer_list)/sizeof(demuxer_list[0]) - 1;
uintptr_t i = (uintptr_t)*opaque;
const AVInputFormat *f = NULL;
if (i < size) {
f = demuxer_list[i];
} else if (outdev_list) {
f = indev_list[i - size];
}
if (f)
*opaque = (void*)(i + 1);
return f;
}
这部分是demuxer_list的列表定义,为了方便阅读,省略掉很多:
static const AVInputFormat * const demuxer_list[] = { &ff_aa_demuxer, &ff_aac_demuxer, &ff_ac3_demuxer, &ff_acm_demuxer, &ff_act_demuxer, &ff_adf_demuxer, &ff_adp_demuxer, &ff_ads_demuxer, &ff_adx_demuxer, &ff_aea_demuxer, &ff_afc_demuxer, &ff_aiff_demuxer, &ff_aix_demuxer, &ff_amr_demuxer, &ff_amrnb_demuxer, &ff_amrwb_demuxer, &ff_anm_demuxer, &ff_apc_demuxer, &ff_ape_demuxer, &ff_m4v_demuxer, &ff_matroska_demuxer, &ff_mgsts_demuxer, &ff_microdvd_demuxer, &ff_mjpeg_demuxer, &ff_mjpeg_2000_demuxer, &ff_mlp_demuxer, &ff_mlv_demuxer, &ff_mm_demuxer, &ff_mmf_demuxer, &ff_mov_demuxer, &ff_mp3_demuxer, &ff_mpc_demuxer, &ff_mpc8_demuxer, &ff_mpegps_demuxer, &ff_mpegts_demuxer, &ff_mpegtsraw_demuxer, &ff_mpegvideo_demuxer, &ff_mpjpeg_demuxer, &ff_image_tiff_pipe_demuxer, &ff_image_webp_pipe_demuxer, &ff_image_xpm_pipe_demuxer, &ff_image_xwd_pipe_demuxer, NULL };
av_probe_input_format3的代码分析:
ff_const59 AVInputFormat *av_probe_input_format3(ff_const59 AVProbeData *pd, int is_opened,
int *score_ret)
{
AVProbeData lpd = *pd;
const AVInputFormat *fmt1 = NULL;
ff_const59 AVInputFormat *fmt = NULL;
int score, score_max = 0;
void *i = 0;
const static uint8_t zerobuffer[AVPROBE_PADDING_SIZE];
enum nodat {
NO_ID3,
ID3_ALMOST_GREATER_PROBE,
ID3_GREATER_PROBE,
ID3_GREATER_MAX_PROBE,
} nodat = NO_ID3;
if (!lpd.buf)
lpd.buf = (unsigned char *) zerobuffer;
if (lpd.buf_size > 10 && ff_id3v2_match(lpd.buf, ID3v2_DEFAULT_MAGIC)) {
int id3len = ff_id3v2_tag_len(lpd.buf);
if (lpd.buf_size > id3len + 16) {
if (lpd.buf_size < 2LL*id3len + 16)
nodat = ID3_ALMOST_GREATER_PROBE;
lpd.buf += id3len;
lpd.buf_size -= id3len;
} else if (id3len >= PROBE_BUF_MAX) {
nodat = ID3_GREATER_MAX_PROBE;
} else
nodat = ID3_GREATER_PROBE;
}
// 遍历所有的demuxer
while ((fmt1 = av_demuxer_iterate(&i))) {
if (!is_opened == !(fmt1->flags & AVFMT_NOFILE) && strcmp(fmt1->name, "image2"))
continue;
score = 0;
if (fmt1->read_probe) {
// 调用demuxer的read_probe函数计算score
score = fmt1->read_probe(&lpd);
if (score)
av_log(NULL, AV_LOG_TRACE, "Probing %s score:%d size:%d\n", fmt1->name, score, lpd.buf_size);
if (fmt1->extensions && av_match_ext(lpd.filename, fmt1->extensions)) {
switch (nodat) {
case NO_ID3:
score = FFMAX(score, 1);
break;
case ID3_GREATER_PROBE:
case ID3_ALMOST_GREATER_PROBE:
score = FFMAX(score, AVPROBE_SCORE_EXTENSION / 2 - 1);
break;
case ID3_GREATER_MAX_PROBE:
score = FFMAX(score, AVPROBE_SCORE_EXTENSION);
break;
}
}
} else if (fmt1->extensions) {
if (av_match_ext(lpd.filename, fmt1->extensions))
score = AVPROBE_SCORE_EXTENSION;
}
if (av_match_name(lpd.mime_type, fmt1->mime_type)) {
if (AVPROBE_SCORE_MIME > score) {
score = AVPROBE_SCORE_MIME;
}
}
// 记录分值最高的format
if (score > score_max) {
score_max = score;
fmt = (AVInputFormat*)fmt1;
} else if (score == score_max)
fmt = NULL;
}
// 返回score_max对应的fmt
if (nodat == ID3_GREATER_PROBE)
score_max = FFMIN(AVPROBE_SCORE_EXTENSION / 2 - 1, score_max);
*score_ret = score_max;
return fmt;
}
在前面probe到具体的format之后,就调用真正的demuxer来处理,这里是iso base的文件类型,进入mov对应的处理逻辑,下面是调用栈:
mov_read_ftyp mov.c 1120 0x7ffff71261bd
mov_read_default mov.c 6929 0x7ffff713864d
mov_read_header mov.c 7468 0x7ffff713a38b
avformat_open_input utils.c 634 0x7ffff722f56b
open_input_file ffmpeg_opt.c 1105 0x555555563c13
open_files ffmpeg_opt.c 3280 0x55555556dba3
ffmpeg_parse_options ffmpeg_opt.c 3320 0x55555556dd57
main ffmpeg.c 4874 0x55555558d9d8