Códec de audio y video: notas sobre el formato de encapsulación MP4

1. Introducción:
el formato de encapsulación MP4 se ha convertido en uno de los formatos de encapsulación de medios más comunes debido a sus características multiplataforma. Un archivo MP4 consta de varios cuadros, cada cuadro almacena información diferente y el anidamiento se produce entre los cuadros. Hay muchas cajas MP4, pero las cajas de nivel superior más importantes son las siguientes:

ftyp : cuadro de tipo de archivo, describe la especificación MP4 y la versión que cumple el archivo
moov : cuadro de película, información de metadatos del medio, solo hay uno
mdat : cuadro de datos de medios, almacena los datos de medios reales, generalmente hay varios

Cada cuadro consta de dos partes: el encabezado del cuadro y el cuerpo del cuadro.
encabezado del cuadro: metadatos del cuadro, como tipo de cuadro, tamaño del cuadro.
cuerpo del cuadro: la parte de datos del cuadro, el contenido real almacenado está relacionado con el tipo de cuadro, como los datos multimedia almacenados en la parte del cuerpo de mdat.
Cuando hay otras cajas anidadas en el cuerpo de la caja, dicha caja se denomina caja contenedora.

Dos, la caja importante:

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相对于文件整体的偏移量

3. Análisis de cuadros relacionados con MP4 en FFmpeg:
en el código fuente de FFmpeg, el demuxer para analizar el formato MP4 es mov, y la ruta del archivo es:

libavformat/mov.c

Eche un vistazo a la definición de cada miembro de la estructura:

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,
};

El análisis de cada cuadro se realiza en 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;
    }
	...
}

Eche un vistazo a 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;
            }
            ...
		}
		...
	}
	...
}

La matriz de análisis de cuadros predeterminada para archivos MP4 es 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 },	
...
}

4. Herramienta de análisis de cajas MP4:
1.mp4info:
inserte la descripción de la imagen aquí
Es conveniente obtener información básica de la caja, pero no puede mostrar completamente alguna información clave en la caja.

2. Explorador MP4:
inserte la descripción de la imagen aquí
enumerará la información clave de cada cuadro con más detalle, lo que nos ayudará a analizar el flujo de código y la dirección de descarga .

5. Análisis de algunos problemas comunes de MP4:
1. El tiempo de reproducción de Samba y otros accesos compartidos es más largo:
esto se debe principalmente a que el box-moov para grabar metadatos está al final del archivo y es necesario descargar los metadatos. solo después de descargar toda la fuente, analice la información relacionada con la decodificación.
inserte la descripción de la imagen aquí
La solución a este problema es mover moov al principio de la fuente. Para fuentes locales, puede usar FFmpeg para transcodificar:

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

Para videos cortos, se recomienda cambiar uniformemente el moov al cargar, como vibrato, etc. Para transmisiones en las que el moov en línea ya está al final del archivo, puede considerar usar la transcodificación en la nube para comenzar a transmitir en segundos.

Supongo que te gusta

Origin blog.csdn.net/achina2011jy/article/details/122090574
Recomendado
Clasificación