音视频编解码:MP4封装格式笔记

一、简介:
MP4封装格式以其跨平台特性而成为当前最常见的媒体封装格式之一。MP4文件由多个box组成,每个box存储不同的信息,且box之间会出现嵌套。MP4的box有很多,但最重要的顶层box主要有如下三个:

ftyp:File Type Box,描述文件遵从的MP4规范与版本
moov:Movie Box,媒体的metadata信息,有且仅有一个
mdat:Media Data Box,存放实际的媒体数据,一般有多个

每个box有两部分组成:box header 和 box body。
box header:box的元数据,比如box type、box size。
box body:box的数据部分,实际存储的内容跟box类型有关,比如mdat中body部分存储的媒体数据。
当box body中嵌套其他box时,这样的box叫做container box。

二、重要的box:

ftyp
mdat
moov
	-mvhd
		-(time_scale):1s包含的时间单位
		-(duration):影片时长,等于最长trak的duration
	-trak
		-tkhd:单个track的metadata
			-(id):当前track的唯一标识
			-(duration):当前track的持续时间,FFmpeg忽略了此值
			-(width):视频宽
			-(height):视频高
		-mdia:描述当前track的一些信息
			-hdlr:声明当前的track类型
				*vide:视频track
				*soun:音频track
				*m1a :MP2
				*subp/clcp:字幕
			-stbl:媒体数据的索引及时间信息(非常重要)
				-stsd:确认当前trak的format,匹配FFmpeg中的codec_id和codec_type等
			-stts:每个帧的时长
			-stss:该trak中关键帧的个数及序号
			-ctts:记录dts与pts的差值,仅B帧存在的码流才需要
			-stsc:每个chunk的sample数
			-stsz:当前trak包含的sample数
			-stco:chunk在文件中的偏移量
				-chunk_offsets:每个chunk相对于文件整体的偏移量

三、FFmpeg中对MP4相关box的解析:
FFmpeg源码中,解析MP4格式的demuxer是mov,文件所在路径为:

libavformat/mov.c

看一下结构体各成员定义:

const 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,psp,m4b,ism,ismv,isma,f4v",
    .flags_internal = FF_FMT_INIT_CLEANUP,
    .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 | AVFMT_SEEK_TO_PTS | AVFMT_SHOW_IDS,
};

各个box的解析是在mov_read_header中完成的:

static int mov_read_header(AVFormatContext *s)
{
    MOVContext *mov = s->priv_data;
    AVIOContext *pb = s->pb;
    int j, err;
    /* atmo为box解析中的最小单位 */
    MOVAtom atom = { AV_RL32("root") };
	...
    /* check MOV header */
    do {
        if (mov->moov_retry)
            avio_seek(pb, 0, SEEK_SET);
        /* 读取box中内容,有嵌套的话持续往下读 */
        if ((err = mov_read_default(mov, pb, atom)) < 0) {
            av_log(s, AV_LOG_ERROR, "error reading header\n");
            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");
        return AVERROR_INVALIDDATA;
    }
	...
}

看一下mov_read_default:

static int mov_read_default(MOVContext *c, AVIOContext *pb, MOVAtom atom)
{
    int64_t total_size = 0;
    MOVAtom a;
    int i;
	/* 记录atom的嵌套层数 */
    if (c->atom_depth > 10) {
        av_log(c->fc, AV_LOG_ERROR, "Atoms too deeply nested\n");
        return AVERROR_INVALIDDATA;
    }
    c->atom_depth ++;

	if (atom.size < 0)
	atom.size = INT64_MAX;
    while (total_size <= atom.size - 8 && !avio_feof(pb)) {
    	/* parse函数指针用于指向各个解析box */
		int (*parse)(MOVContext*, AVIOContext*, MOVAtom) = NULL;
		...
		/* 遍历各个数组,找根据type找到对应的box函数进行解析 */
		for (i = 0; mov_default_parse_table[i].type; i++)
            if (mov_default_parse_table[i].type == a.type) {
                parse = mov_default_parse_table[i].parse;
                break;
            }
        ...
        if (!parse) { /* skip leaf atoms data */
            avio_skip(pb, a.size);
        } else {
			int64_t start_pos = avio_tell(pb);
            int64_t left;
            /* 调用对应的box解析函数 */
            int err = parse(c, pb, a);
            if (err < 0) {
                c->atom_depth --;
                return err;
            }
            ...
		}
		...
	}
	...
}

MP4文件默认的box解析数组为mov_default_parse_table:

static const MOVParseTableEntry mov_default_parse_table[] = {
{ MKTAG('A','C','L','R'), mov_read_aclr },
{ MKTAG('A','P','R','G'), mov_read_avid },
{ MKTAG('A','A','L','P'), mov_read_avid },
{ MKTAG('A','R','E','S'), mov_read_ares },
{ MKTAG('a','v','s','s'), mov_read_avss },
{ MKTAG('a','v','1','C'), mov_read_glbl },	
...
}

四、MP4的box解析工具:
1.mp4info:
在这里插入图片描述
方便获取一些box的基本信息,但无法完全显示box中一些关键信息。

2.MP4 exploer:
在这里插入图片描述
会比较详细的列出每个box的关键信息,有助于我们分析码流,下载地址

五、MP4的一些常见问题分析:
1.Samba等共享访问时起播时间较长:
这个主要是因为记录metadata的box-moov在文件末尾的,需要将整个片源都下载下来之后才会获取metadata,解析出解码相关信息。
在这里插入图片描述
这种问题的解决方案为将moov移动到片源开始位置,对于本地片源,可以使用FFmpeg进行转码:

ffmpeg -i xxx.mp4 -codec copy -movflags faststart output.mp4

对于短视频类,建议在上传的时候统一进行moov的位移,如抖音等,对于线上moov已经在文件末尾的码流,可以考虑使用云端转码,以实现秒起播。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
学习Linux音视频解码的路线可以参考以下步骤: 1. 了解AVS标准:AVS是中国的第二代信源码标准,用于数字音视频码压缩。你可以先了解AVS的基本原理和技术特点,以及它在数字音视频产业中的应用。 2. 学习音视频解码基础知识:音视频解码是将原始音视频数据进行压缩和解压缩的过程。你可以学习音视频码的基本概念、常用的音视频码算法和标准。 3. 掌握Linux操作系统:Linux是一个广泛使用的开源操作系统,它在音视频领域有很多应用。你可以学习Linux的基本操作和命令,熟悉Linux环境下的开发和程工具。 4. 学习流媒体技术:流媒体是指通过网络传输音视频数据的技术。你可以学习流媒体的基本原理、协议和常用的流媒体服务器软件。 5. 掌握音视频解码工具:Linux上有很多开源的音视频解码工具,如FFmpeg、GStreamer等。你可以学习这些工具的使用方法和参数配置,了解它们在音视频解码中的应用。 6. 实践项目:通过实践项目来巩固所学的知识。你可以选择一些开源的音视频项目,如视频播放器、音频辑器等,进行学习和实践。 总之,学习Linux音视频解码需要掌握AVS标准、音视频解码基础知识、Linux操作系统、流媒体技术和音视频解码工具,并通过实践项目来应用所学的知识。这样就能建立起一个相对完整的学习路线。<span class="em">1</span><span class="em">2</span><span class="em">3</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值