历史[6]:
2001年,Apple推出QuickTime格式,后缀名为.qt和.mov。 2001 年,MPEG-4 第1 部分在MPEG-4 标准中添加了基于QuickTime 盒布局的容器格式。 2004 年,标准文档将编码和容器格式规范分开。 ISO-14496 第12 部分定义了容器格式的通用盒结构,即ISO媒体文件格式(ISO base media file format, ISO BMFF)。 ISO-14496 Part 14,基于Part12 进行了细化,定义了用于存储MPEG-4 内容的容器格式,即.mp4 格式。 ISO-14496 Part 15在Part 12的基础上,定义了如何封装AVC、HEVC和VVC的NALU。以下是每个标准文档的链接:
QuickTime:QuickTime/QTFFISO-14496 第12 部分:ISO/IEC 14496-12:2015ISO-14496 第14 部分:ISO/IEC 14496-14:2018。官网上这部分是付费的。 MP4文件由许多盒子组成,每个盒子包含不同的信息,并且这些盒子以树形结构组织。以下是主要框的简要说明:
图2 主框说明: 根节点下主要有ftyp、moov、mdat 三个节点。
ftyp:文件类型。描述要遵循的规范版本。moov box:媒体的元数据信息。mdat:特定媒体数据。说明:mp4中默认的写入字节顺序是Big-Endian。
2. mp4文件基本信息
mp4 文件分析工具:
mp4box.js:一个解析mp4的在线工具。 Bento4:包含mp4dump、mp4edit 和mp4encrypt 等工具。 MP4Box:与bento4类似,包含非常全面的工具。 l-smash: mp4盒子查看工具下图是使用mp4box.js打开mp4文件的界面:
image.pngmp4文件的基本信息
音频信息:
smplrate:采样率(采样率)。通道:通道数。比特率:比特率。 audiosamplenum:音频样本的数量。视频信息:
宽度、高度:视频的宽度/高度。 bitrate:比特率(码率),单位为秒。等于视频的总大小/持续时间。帧数:视频帧数。 fps:帧速率(每秒帧数)。 Total_time:时间长度,单位为ms。等于持续时间/时间尺度。 timescale:时间的粒度,1000表示1000个单位是1s。持续时间:时间粒度的数量。 videosamplenum:视频样本数。
3. 封装格式重要概念
3.1 box
mp4 文件由多个框组成。下图是盒子结构示意图。
图4 mp4_box.pngbox 由header 和body 组成。标题指定了盒子的大小和类型。 size 是整个盒子的大小,包括盒子标题。框类型,通常是四个ASCII 字符,如“ftyp”、“moov”等。这些框类型是预定义的,代表固定的含义。如果是“uuid”,则表示该box是用户自定义的扩展类型。如果框类型未定义,则应忽略它。如果header中的size为1,说明box长度需要更多的bit来描述,后面会有64位的largesize来描述box长度。如果size为0,则表示该box是文件的最后一个box,也是文件的结尾(也只存在于“mdat”类型的box中)。一个盒子可以容纳一个盒子,称为容器盒子。盒子有两种类型:盒子和全盒子。 FullBox 是Box 的扩展。 Header 中添加Version 和Flags 字段,定义如下:aligned(8) class Box (unsigned int(32) boxtype,
可选的unsigned int(8)[16] 扩展类型) {
无符号int(32) 大小;
无符号int(32) 类型=boxtype;
如果(大小==1){
无符号int(64) 大尺寸;
} 否则如果(大小==0){
//框延伸到文件末尾
}
if (boxtype=="uuid") {
无符号int(8)[16] 用户类型=扩展类型;
}
}FullBox 有版本和标志字段,
对齐(8)类FullBox(无符号整数(32)框类型,无符号整数(8)v,位(24)f)
扩展Box(boxtype) {
无符号int(8) 版本=v;
位(24)标志=f;
}
3.2 track
样本集合。对于媒体数据,轨道代表视频或音频序列。
3.3 sample
视频样本是一帧或一组连续的视频帧,音频样本是连续的音频。
3.4. sample table
表指示示例时序和物理布局。
3.5. chunk
由轨道的多个样本组成的单元。
在mp4 文件中,媒体内容位于moov 框中。一个moov 包含多个曲目。每个轨道都是一个随时间变化的媒体序列。轨道中的每个时间单元都是一个样本,样本按时间顺序排列。注意,一帧音频可以分解为多个音频样本,因此音频一般以样本为单位,而不是帧。
4. 重要box介绍
4.1 Sample Table Box(stbl)
“stbl”是mp4文件中最复杂的盒子,也是mp4文件格式的支柱。
stbl:样品台是一个容器盒。
语法:
类SampleTableBox 扩展Box("stbl") {
它的子框包括:
stsd:样本描述框,样本的描述信息。 stts:time to sample box,样本解码时间压缩表。 ctts:到样本箱的合成时间,样本的CTS和DTS时间差的压缩表。 stss:同步样本框,对于视频,关键帧的序列号。 stsz/stz2:样本大小框,每个样本的字节大小。 stsc:样本到块盒,样本-块映射表。 stco/co64:块偏移框,文件中的块偏移量。 Sample是媒体数据存储的单位。它存储在媒体块中。 chunk 和sample 的长度可以不同,如下图所示。
图5sample.jpg
4.2 Sample Description Box(stsd)
存储了编码类型和初始化解码器所需的信息。
有与特定轨道类型相关的信息。相同的轨道类型也可能具有不同的信息,例如使用不同的编码标准。
语法:
类SampleDescriptionBox (无符号int(32) handler_type)
扩展FullBox("stsd", 0, 0){
整数我;
无符号int(32) 条目计数;
for (i=1; i=条目计数; i++){
开关(处理程序类型){
case ‘soun’: //用于音轨
AudioSampleEntry();
休息;
case ‘vide’: //用于视频轨道
VisualSampleEntry();
休息;
case ‘hint’: //提示轨迹
提示样本条目();
休息;
case ‘meta’: //元数据轨道
MetadataSampleEntry();
休息;
}
}
}
}主要字段说明:
条目计数:条目数。handler_type:类型信息如“vide”、“soud”等,不同类型会提供不同的信息。对于音轨,使用“AudioSampleEntry”类型信息。
抽象类SampleEntry (无符号int(32) 格式)
扩展框(格式){
const unsigned int(8)[6] 保留=0;
无符号int(16) data_reference_index;
}
类AudioSampleEntry(编码名称) 扩展SampleEntry (编码名称){
const unsigned int(32)[2] 保留=0;
模板unsigned int(16) 通道数=2;
模板unsigned int(16) 样本大小=16;
无符号int(16) 预定义=0;
const 无符号int(16) 保留=0;
template unsigned int(32) 采样率={ 媒体默认采样率}16;
}format:视频或音频的编码格式。例如aac音频的格式值为mp4a。 AVC-1/H.264视频的格式值为avc1。 data_reference_index:使用此索引检索与当前示例描述关联的数据。数据引用存储在数据引用框中。通道数:通道数。样本大小:采样位数。默认值为16 位。采样率:采样率。 [16.16]格式化数据。对于视频轨道,请使用“VisualSampleEntry”类型信息。
类VisualSampleEntry(编码名称) 扩展SampleEntry (编码名称){
无符号int(16) 预定义=0;
const 无符号int(16) 保留=0;
无符号int(32)[3] 预定义=0;
无符号int(16) 宽度;
无符号int(16) 高度;
模板无符号int(32) 水平分辨率=0x00480000; //72 dpi
模板无符号int(32) 垂直分辨率=0x00480000; //72 dpi
const 无符号int(32) 保留=0;
模板无符号整数(16)frame_count=1;
string[32] 压缩机名称;
模板无符号int(16) 深度=0x0018;
int(16) 预定义=-1;
//派生规范中的其他框
CleanApertureBox拍手; //选修的
PixelAspectRatioBox pasp; //选修的
}width, height:像素宽度和高度。水平分辨率、垂直分辨率:每英寸像素值(dpi),数据格式为[16.16]。 frame_count:每个样本中的视频帧数,默认为1。一个样本中可以有多帧数据。
4.3 Decoding Time to Sample Box(stts)
包含该表的压缩版本,通过该表可以将解码时间映射到样本序列号。表中的每一项是连续相同的解码时间增量(Decode Delta)的数量和解码时间增量。通过累积时间增量,可以创建完整的采样时间表。
下面是解码时序和解码增量之间关系的说明:
图6 STTS_sample.PNGDT(解码时间):编码时间。 CT(composition time):创作时间。解码增量:编码时间增量。合成偏移:显示时间与解码时间之差,等于CT - DT。
计算方法:DT(n+1)=DT(n) + STTS(n) 其中STTS(n)
注意:这里的STTS(n) 是未压缩的Decode Delta 表。
DT(i)=SUM(对于delta(j) 的j=0 到i-1) 语法:
类TimeToSampleBox
扩展FullBox("stts", 版本=0, 0) {
无符号int(32) 条目计数;
整数我;
for (i=0; i 条目计数; i++) {
无符号整数(32)样本计数;
无符号int(32) 样本增量;
}
}重要字段说明:
Entry_count:表中的条目数。 sample_count: 相同时间长度的样本数。 Sample_delta:以时间刻度为单位的时间长度。
4.4 composition time to sample box(ctts)
该框提供了从解码时间到合成时间的偏移表,用于计算pts。
当解码时间和合成时间不同时,该表是必要的。如果box的版本等于0,则解码时间一定小于或等于合成时间,因此差值用无符号数表示。有如下公式:
注:CTTS(n)是未压缩表的第n个样本对应的偏移量。语法:
类CompositionOffsetBox
扩展FullBox("ctts", 版本=0, 0) {
无符号int(32) 条目计数;
整数我;
如果(版本==0){
for (i=0; i 条目计数; i++) {
无符号整数(32)样本计数;
无符号int(32) 样本偏移量;
}
}
否则如果(版本==1){
for (i=0; i 条目计数; i++) {
无符号整数(32)样本计数;
有符号int(32) 样本偏移量;
}
}
}主要字段说明:
Sample_count:连续相同偏移量的数量。 Sample_offset:CT 和DT 之间的偏移量。
4.5 sync sample box(stss)
它包含媒体中关键帧的示例表。如果该表不存在,则每个样本都是一个关键帧。
语法:
类SyncSampleBox
扩展FullBox("stss", 版本=0, 0) {
无符号int(32) 条目计数;
整数我;
for (i=0; i 条目计数; i++) {
无符号int(32) 样本数;
}
}主要字段说明:
Sample_number:媒体流中同步样本的序列号。
4.6 Sample Size Box(stsz/stz2)
包含样本数量和每个样本的字节大小。这个盒子比较大。
语法:
类SampleSizeBox 扩展FullBox("stsz", 版本=0, 0) {
无符号int(32) 样本大小;
无符号整数(32)样本计数;
如果(样本大小==0){
for (i=1; i=样本计数; i++) {
无符号int(32) 条目大小;
}
}
}主要字段说明:
Sample_size:指定默认样本字节大小。如果所有样本的大小不同,则该字段为0。sample_count:track中sample的数量。entry_size:每个sample的字节大小。Compact Sample SizeBox(stz2):压缩样本大小存储方法。
类CompactSampleSizeBox 扩展FullBox("stz2", 版本=0, 0) {
无符号int(24) 保留=0;
unsigned int(8) 字段大小;
无符号整数(32)样本计数;
for (i=1; i=样本计数; i++) {
无符号整数(字段大小)条目大小;
}
}field_size:指定表中条目的位大小,值为4、8或16。 (1)如果使用值4,则每个字节存储两个样本,大小为:
条目[i] 4 + 条目[i + 1]。 (2) 如果size不是用整数个字节填充,则最后一个字节未使用的部分用0填充。 Sample_count 是一个整数,给出下表中的条目数。 Entry_size 是一个整数,指定样本的大小,根据其编号进行索引。
4.7 Sample To Chunk Box(stsc)
介质中的样本被分成块。块可以具有不同的大小,并且块内的样本可以具有不同的大小。
通过stsc中的sample-chunk映射表可以找到包含指定样本的chunk,从而找到该样本。具有相同结构的块可以聚集在一起形成一个条目,该条目就是stsc映射表的条目。
语法:
类SampleToChunkBox
扩展FullBox("stsc", 版本=0, 0) {
无符号int(32) 条目计数;
for (i=1; i=条目计数; i++) {
无符号整数(32)first_chunk;
无符号整数(32)samples_per_chunk;
无符号int(32) 样本描述索引;
}
}主要字段说明:
first_chunk:一组chunk中第一个chunk的序号。块编号从1开始。samples_per_chunk:每个块中有多少个样本。 Sample_desc_idx:stsd 中样本desc 信息的索引。把一组相同结构的chunk放在一起进行管理,是为了压缩文件大小。用mp4box.js查看stsc box的信息如下:
image.png 第一组chunk的first_chunk序号为1,每个chunk中的样本数为1。因为第二组chunk的first_chunk序号为2,所以可以看出只有一个第一组块中的块。第二组chunk的first_chunk序号为2,每个chunk中的样本数为2。因为第三组chunk的first_chunk序号为24,所以可以看出有22个chunk,44个样本在第二组块中。
4.8 Chunk Offset Box(stco/co64)
Chunk Offset表存储了每个chunk在文件中的位置,这样就可以直接在文件中找到媒体数据,而无需解析box。
需要注意的是,一旦之前的框发生任何更改,该表将重新建立。语法:
类ChunkOffsetBox
扩展FullBox("stco", 版本=0, 0) {
无符号int(32) 条目计数;
对于(i=1;
i<= entry_count; i++) { unsigned int(32) chunk_offset; } }主要字段说明: chunk_offset:chunk在文件中的位置。stco 有两种形式,如果你的视频过大的话,就有可能造成 chunkoffset 超过 32bit 的限制。所以,这里针对大 Video 额外创建了一个 co64 的 Box。它的功效等价于 stco,也是用来表示 sample 在 mdat box 中的位置。只是,里面 chunk_offset 是 64bit 的。 aligned(8) class ChunkLargeOffsetBox extends FullBox(‘co64’, version = 0, 0) { unsigned int(32) entry_count; for (i=1; i<= entry_count; i++) { unsigned int(64) chunk_offset; } }5. 其他box介绍
以下是ISO/IEC 14496-12:2015文档给出的box的描述图: box.png5.1 File Type Box(ftyp)
File Type Box一般在文件的开头,用来指示该 mp4文件使用的标准规范。为了早期规范版本兼容,允许不包含ftyp box。 语法: class FileTypeBox extends Box(‘ftyp’) { unsigned int(32) major_brand;// is a brand identifier unsigned int(32) minor_version;// is an informative integer for the minor version of the major brand unsigned int(32) compatible_brands[]; //is a list, to the end of the box, of brands }没有ftyp box的文件应该处理成ftyp的major_brand为"mp41",minor_version为0,compatible_brands只包含一个"mp41"。5.2 Movie Box(moov)
Movie Box包含了文件媒体的metadata信息,“moov”是一个container box,具体内容信息在其子box中。一般情况下,“moov”会紧随着“ftyp”。 “moov”中包含1个“mvhd”和若干个“trak”。其中“mvhd”是header box,一般作为“moov”的第一个子box出现。“trak”包含了一个track的相关信息,是一个container box。 语法: class MovieBox extends Box(‘moov’){ }5.3 Movie Header Box(mvhd)
语法: class MovieHeaderBox extends FullBox(‘mvhd’, version, 0) { if (version==1) { unsigned int(64) creation_time; unsigned int(64) modification_time; unsigned int(32) timescale; unsigned int(64) duration; } else { // version==0 unsigned int(32) creation_time; unsigned int(32) modification_time; unsigned int(32) timescale; unsigned int(32) duration; } template int(32) rate = 0x00010000; // typically 1.0 template int(16) volume = 0x0100; // typically, full volume const bit(16) reserved = 0; const unsigned int(32)[2] reserved = 0; template int(32)[9] matrix = { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // Unity matrix bit(32)[6] pre_defined = 0; unsigned int(32) next_track_ID; }主要字段含义: version : box版本,0或1,一般为0。creation time : 创建时间(相对于UTC时间1904-01-01零点的秒数)。modification time : 修改时间 。timescale : 文件媒体在1秒时间内的刻度值,可理解为1秒长度的时间单元数。duration : 该track的时间长度,用duration和time scale值可以计算track时长,比如audio track的time scale = 8000, duration = 560128,时长为70.016,video track的time scale = 600, duration = 42000,时长为70。rate : 推荐播放速率,高16位和低16位分别为小数点整数部分和小数部分,即[16.16] 格式,该值为1.0(0x00010000)表示正常前向播放。volume : 推荐播放音量,[8.8] 格式,1.0(0x0100)表示最大音量。5.4 Track Box(trak)
Track Box是一个container box,其子box包含了该track的媒体数据引用和描述(hint track除外)。一个mp4文件可以包含多个track,且至少有一个track,track之间是独立,有自己的时间和空间信息。“trak”必须包含一个“tkhd”和一个“mdia”,此外还有很多可选的box。其中“tkhd”为track header box,“mdia”为media box,该box是一个包含一些track媒体数据信息box的container box。 语法: class TrackBox extends Box(‘trak’) { }5.5 Track Header Box(tkhd)
语法: class TrackHeaderBox extends FullBox(‘tkhd’, version, flags){ if (version==1) { unsigned int(64) creation_time; unsigned int(64) modification_time; unsigned int(32) track_ID; const unsigned int(32) reserved = 0; unsigned int(64) duration; } else { // version==0 unsigned int(32) creation_time; unsigned int(32) modification_time; unsigned int(32) track_ID; const unsigned int(32) reserved = 0; unsigned int(32) duration; } const unsigned int(32)[2] reserved = 0; template int(16) layer = 0; template int(16) alternate_group = 0; template int(16) volume = {if track_is_audio 0x0100 else 0}; const unsigned int(16) reserved = 0; template int(32)[9] matrix= { 0x00010000,0,0,0,0x00010000,0,0,0,0x40000000 }; // unity matrix unsigned int(32) width; unsigned int(32) height; }主要字段含义: version : box版本,0或1,一般为0。flags : 24-bit整数,按位或操作结果值,预定义的值(0x000001 ,track_enabled,表示track是有效的)、(0x000002,track_in_movie,表示该track在播放中被使用)、(0x000004,track_in_preview,表示track在预览时被使用。track id : track id号,不能重复且不能为0。duration : track的时间长度,计量单位timescale在mvhd中。volume : [8.8] 格式,如果为音频track,1.0(0x0100)表示最大音量;否则为0。width : 宽,[16.16] 格式值。height : 高,[16.16] 格式值,不必与sample的像素尺寸一致,用于播放时的展示宽高。5.6 Media Box(mdia)
Media Box也是个container box。 语法: class MediaBox extends Box(‘mdia’) { }其子box的结构和种类还是比较复杂的。 “mdia”定义了track媒体类型以及sample数据,描述sample信息。 一个“mdia”必须包含如下容器: 一个Media Header Atom(mdhd)一个Handler Reference(hdlr)一个media information(minf)和User Data下面依次看一下这几个box的结构。5.7 Media Header Box(mdhd)
mdhd 和 tkhd ,内容大致都是一样的。不过tkhd 通常是对指定的 track 设定相关属性和内容。而 mdhd 是针对于独立的 media 来设置的。不过两者一般都是一样的。 语法: class MediaHeaderBox extends FullBox(‘mdhd’, version, 0) { if (version==1) { unsigned int(64) creation_time; unsigned int(64) modification_time; unsigned int(32) timescale; unsigned int(64) duration; } else { // version==0 unsigned int(32) creation_time; unsigned int(32) modification_time; unsigned int(32) timescale; unsigned int(32) duration; } bit(1) pad = 0; unsigned int(5)[3] language; // ISO-639-2/T language code unsigned int(16) pre_defined = 0; }主要字段含义: version: box版本,0或1,一般为0。timescale: 同mvhd中的timescale。duration: track的时间长度。language: 媒体语言码。最高位为0,后面15位为3个字符(见ISO 639-2/T标准中定义)。5.8 Handler Reference Box(hdlr)
“hdlr”解释了媒体的播放过程信息,该box也可以被包含在meta box(meta)中。 语法: class HandlerBox extends FullBox(‘hdlr’, version = 0, 0) { unsigned int(32) pre_defined = 0; unsigned int(32) handler_type; const unsigned int(32)[3] reserved = 0; string name; }主要字段含义:handler type: 在media box中,该值为4个字符,会有以下取值:‘vide’ Video track ‘soun’ Audio track ‘hint’ Hint track ‘meta’ Timed Metadata track ‘auxv’ Auxiliary Video trackname: human-readable name for the track type,以‘ ’结尾的 UTF-8 字符串。用于调试后者检查的目的。5.9 Media Information Box(minf)
重要的容器 box,“minf”存储了解释track媒体数据的handler-specific信息,media handler用这些信息将媒体时间映射到媒体数据并进行处理。“minf”是一个container box,其实际内容由子box说明。 语法: class MediaInformationBox extends Box(‘minf’) { }“minf”中的信息格式和内容与媒体类型以及解释媒体数据的media handler密切相关,其他media handler不知道如何解释这些信息。 一般情况下,“minf”包含一个header box,一个“dinf”和一个“stbl”,其中,header box根据track type(即media handler type)分为“vmhd”、“smhd”、“hmhd”和“nmhd”,“dinf”为data information box,“stbl”为sample table box。下面分别介绍。5.10 Media Information Header Box(vmhd、smhd、hmhd、nmhd)
vmhd、smhd这两个box在解析时,非不可或缺的(有时候得看播放器),缺了的话,有可能会被认为格式不正确。Video Media Header Box(vmhd)语法: class VideoMediaHeaderBox extends FullBox(‘vmhd’, version = 0, 1) { template unsigned int(16) graphicsmode = 0; // copy, see below template unsigned int(16)[3] opcolor = {0, 0, 0}; }主要字段含义: graphics mode:视频合成模式,为0时拷贝原始图像,否则与opcolor进行合成。opcolor: 一组(red,green,blue),graphics modes使用。Sound Media Header Box(smhd) 语法: class SoundMediaHeaderBox extends FullBox(‘smhd’, version = 0, 0) { template int(16) balance = 0; const unsigned int(16) reserved = 0; }主要字段含义: balance:立体声平衡,[8.8] 格式值,一般为0表示中间,-1.0表示全部左声道,1.0表示全部右声道。Hint Media Header Box(hmhd) Null Media Header Box(nmhd) 非视音频媒体使用该box。5.11 Data Information Box(dinf)
“dinf”解释如何定位媒体信息,是一个container box。 语法: class DataInformationBox extends Box(‘dinf’) { }“dinf”一般包含一个“dref”(data reference box)。 “dref”下会包含若干个“url”或“urn”,这些box组成一个表,用来定位track数据。简单的说,track可以被分成若干段,每一段都可以根据“url”或“urn”指向的地址来获取数据,sample描述中会用这些片段的序号将这些片段组成一个完整的track。一般情况下,当数据被完全包含在文件中时,“url”或“urn”中的定位字符串是空的。 “dref”的语法: class DataEntryUrlBox (bit(24) flags) extends FullBox(‘url ’, version = 0, flags) { string location; } class DataEntryUrnBox (bit(24) flags) extends FullBox(‘urn ’, version = 0, flags) { string name; string location; } class DataReferenceBox extends FullBox(‘dref’, version = 0, 0) { unsigned int(32) entry_count; for (i=1; i<= entry_count; i++) { DataEntryBox(entry_version, entry_flags) data_entry; } }主要字段含义: entry count:“url”或“urn”表的元素个数。entry_version:entry格式的版本。entry_flags:当“url”或“urn”的box flag为1时,表示数据在该文件的Moov Box中。“url”或“urn”都是box,“url”的内容为location字符串,“urn”的内容为名称字符串和location字符串。6. 实用技术
6.1 moov移到mdat前面
ffmpeg默认情况下生成moov是在mdat写完成之后再写入,所以moov是在mdat的后面,使用faststart参数可以将moov移到mdat前面。 ./ffmpeg -i demo.flv -c copy output.mp4moov在mdat后面 image.png使用faststart参数ffmpeg -i out.flv -c copy -movflags faststart out2.mp4moov在mdat前面 image.png6.2 如何实现seek
stbl.png例如,我们需要seek到30s。 需要做如下工作:END,本文到此结束,如果可以帮助到大家,还望关注本站哦!
【MP4文件格式深度解析:核心技术及细节解读】相关文章:
2.米颠拜石
3.王羲之临池学书
8.郑板桥轶事十则
用户评论
这个标题听起来很有技术含量啊!
有18位网友表示赞同!
想了解一下MP4视频是如何工作的吗?
有6位网友表示赞同!
这方面的知识应该挺重要的吧,能看懂视频的内部结构。
有13位网友表示赞同!
感觉可以学到很多干货!学习一下MP4文件格式解析很有用。
有10位网友表示赞同!
我记得之前看过一些关于视频格式的文章,但没深入了解过。
有9位网友表示赞同!
这篇文章是不是要讲一些专业的编码技术?
有16位网友表示赞同!
对程序员来说,理解MP4是必须的呀!
有19位网友表示赞同!
不知道解析MP4文件需要哪些软件工具?
有18位网友表示赞同!
视频播放器是如何实现解码的呢?
有19位网友表示赞同!
这篇文章应该会讲解MP4的文件结构和各个组成部分吧?
有9位网友表示赞同!
学习一下MP4格式,对做影视作品的人也很重要。
有8位网友表示赞同!
感觉这种技术含量很高的知识能让我 broaden my horizons ,
有12位网友表示赞同!
视频压缩算法也是很有意思的,文章会不会提到这些?
有12位网友表示赞同!
如果能看懂MP4文件的内容,也许能修改视频格式吗?
有13位网友表示赞同!
我很想了解一下不同的视频格式有什么区别。
有14位网友表示赞同!
MP4文件解析是不是一个比较复杂的领域?
有6位网友表示赞同!
这篇文章的难易程度怎么样?適合初学者学习吗?
有12位网友表示赞同!
MP4文件在现实生活中应用场景还挺多的吧?
有11位网友表示赞同!
我觉得学习一下MP4格式解析,未来可以帮助我更好地理解视频和音频技术。
有20位网友表示赞同!