从MP4头信息中提取sps和pps数据

转自:https://blog.csdn.net/tracydawn123/article/details/31773153

 

一、MP4格式基本概念

MP4格式对应标准MPEG-4标准(ISO/IEC14496)

 


二、MP4封装格式核心概念

1  MP4封装格式对应标准为 ISO/IEC 14496-12(信息技术 视听对象编码的第12部分: ISO 基本媒体文件格式/Information technology Coding of audio-visual objects Part 12: ISO base media file format)

 附-- 标准免费下载: Freely Available Standards    http://standards.iso.org/ittf/PubliclyAvailableStandards/index.html

 

2  MP4封装格式是基于QuickTime容器格式定义,媒体描述与媒体数据分开,目前被广泛应用于封装h.264视频和ACC音频,是高清视频/HDV的代表。


 

3  MP4文件中所有数据都封装在box中(对应QuickTime中的atom),即MP4文件是由若干个box组成,每个box有长度和类型,每个box中还可以包含另外的子box(称container box)。

 一个MP4文件首先会有且只有一个“ftyp”类型的box,作为MP4格式的标志并包含关于文件的一些信息;之后会有且只有一个“moov”类型的box(Movie Box),它是一种container box,子box包含了媒体的metadata信息;MP4文件的媒体数据包含在“mdat”类型的box(Midia Data Box)中,该类型的box也是container box,可以有多个,也可以没有(当媒体数据全部引用其他文件时),媒体数据的结构由metadata进行描述。


 

4  MP4中box存储方式为大端模式。一般,标准的box开头会有四个字节的box size。

 

 

 

 

好的,铺垫做好了,下面直接进入正题!

 

近期本人在用自己的开发板对ffmpeg解码做一些改进,目标是将ffmpeg的解码部分交给硬件编解码单元VPU(Video Processing Unit)去做,而ffmpeg用来读取文件,并解析文件,以加快解码速度,并释放CPU资源(事实上单核ARM cotexA9解码1920x1088的视频,根本解不动啊...)。

 

这里补充两个概念:

解析:即解封装,封装格式有很多种,常见的如avi、mp4、rmvb等等。

解码:将压缩的多媒体数据格式还原,视频数据压缩格式也有很多,目前比较流行的有H.264、mpeg2、mpeg4等等。

 

使用VPU进行解码的时候,交给VPU的数据必须是严格的H.264数据,否则VPU是不认的。本以为ffmpeg的解析工作可以和VPU的解码完美配合,但事与愿违,ffmpeg解析后的一个packet(即一个视频帧)的格式为4byte的box大小+帧数据,而真正的H.264的数据为4byte的头(00 00 00 01)+帧数据。因此,每一帧的数据在丢给VPU之前,需要将头部的4bit用00 00 00 01替换。下图为ffmpeg提取的packet数据(我打印出来每个packet的前5位,和packet size)。

 

可以看到,每一个packet的前4byte是帧大小(mp4是大端存储),如第一组数据:0x14efa = 85754,即出去前4byte后的大小。

 

在使用VPU进行H.264解码的时候,还需要sps和pps数据,第一帧的数据头应当包含sps和pps。(至于这两个参数是干什么的,这里就不赘述了,网上的资料很多)。好的,下面来说如何找出sps和pps:

1、找到avcC,在mp4的包头中,包含了avcC这4个字母的asic码,对应的十六进制数分别是61 76 63 43。因此,需要在mp4包头中找出avcC。下图为用altraedit查看的mp4的包头数据。

可以看到,61 76 63 43对应了avcC。至于前面的mp4包头信息,可以参考mp4官方文档给出的解释,这里不再解释。

 

2、根据下表所示的信息,找出对应的sps和pps位:

 

 

因此,对应上表,可以得出如下信息:

 

avcC61 76 63 43
版本号1
AVCProfileIndication64
profile_compatibility0
AVCLevelIndication29
reserved(6bit)+NALU长度(2bit)ff
reserver(3bit)+sps个数(5bit)E1
sps长度00 0B
sps内容67 64 00 29 AC CE C0 78 02 26 40
pps个数01
pps长度00 04
pps内容68 EA 5B 83

 

 

好的,到此为止就在mp4的包头中找到了sps和pps数据,可以根据此方法写出你的code啦,很简单吧!

--------------------- 本文来自 Freesnail_ 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/tracydawn123/article/details/31773153?utm_source=copy

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
使用GStreamer API提取H.264的SPSPPS信息,需要使用h264parse元素来解析H.264码流,并从提取SPSPPS。以下是使用GStreamer API提取H.264SPSPPS信息的示例代码: ```c #include <gst/gst.h> int main(int argc, char *argv[]) { GstElement *pipeline, *h264parse; GstCaps *caps; GstBuffer *sps_buf, *pps_buf; GstMapInfo map_info; gboolean ret; /* Initialize GStreamer */ gst_init(&argc, &argv); /* Create the elements */ pipeline = gst_pipeline_new("mypipeline"); h264parse = gst_element_factory_make("h264parse", "myh264parse"); /* Set the caps for h264parse element */ caps = gst_caps_new_simple("video/x-h264", "stream-format", G_TYPE_STRING, "byte-stream", NULL); g_object_set(G_OBJECT(h264parse), "caps", caps, NULL); gst_caps_unref(caps); /* Add h264parse element to the pipeline */ gst_bin_add(GST_BIN(pipeline), h264parse); /* Link the elements */ if (!gst_element_link_many(h264parse, NULL)) { g_printerr("Failed to link elements\n"); return -1; } /* Start the pipeline */ gst_element_set_state(pipeline, GST_STATE_PLAYING); /* Wait for the SPS and PPS buffers to be emitted */ ret = gst_element_get_state(h264parse, NULL, NULL, GST_CLOCK_TIME_NONE); if (ret != GST_STATE_CHANGE_SUCCESS) { g_printerr("Failed to get state of h264parse element\n"); return -1; } ret = gst_element_query_position(h264parse, GST_FORMAT_TIME, NULL); if (ret != TRUE) { g_printerr("Failed to query position of h264parse element\n"); return -1; } ret = gst_element_query_duration(h264parse, GST_FORMAT_TIME, NULL); if (ret != TRUE) { g_printerr("Failed to query duration of h264parse element\n"); return -1; } ret = gst_element_query_latency(h264parse, GST_FORMAT_TIME, NULL, NULL); if (ret != TRUE) { g_printerr("Failed to query latency of h264parse element\n"); return -1; } gst_element_send_event(h264parse, gst_event_new_seek(1.0, GST_FORMAT_TIME, GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE, GST_SEEK_TYPE_NONE, 0, GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE)); ret = gst_element_get_state(h264parse, NULL, NULL, GST_CLOCK_TIME_NONE); if (ret != GST_STATE_CHANGE_SUCCESS) { g_printerr("Failed to get state of h264parse element\n"); return -1; } sps_buf = gst_buffer_new(); pps_buf = gst_buffer_new(); ret = gst_element_query_convert(h264parse, GST_FORMAT_TIME, GST_FORMAT_BUFFER, GST_SECOND, &sps_buf); if (ret != TRUE) { g_printerr("Failed to query convert SPS buffer\n"); return -1; } ret = gst_element_query_convert(h264parse, GST_FORMAT_TIME, GST_FORMAT_BUFFER, 2 * GST_SECOND, &pps_buf); if (ret != TRUE) { g_printerr("Failed to query convert PPS buffer\n"); return -1; } /* Extract the SPS and PPS data */ gst_buffer_map(sps_buf, &map_info, GST_MAP_READ); /* map_info.data contains SPS data */ gst_buffer_unmap(sps_buf, &map_info); gst_buffer_map(pps_buf, &map_info, GST_MAP_READ); /* map_info.data contains PPS data */ gst_buffer_unmap(pps_buf, &map_info); /* Stop and cleanup the pipeline */ gst_element_set_state(pipeline, GST_STATE_NULL); gst_object_unref(pipeline); return 0; } ``` 在上述代码,我们创建了一个GStreamer管道,其包含了h264parse元素。我们设置了h264parse的caps,然后将h264parse元素添加到管道,并启动了管道。 我们使用gst_element_send_event()来发送一个seek事件,让h264parse元素从H.264码流提取SPSPPS信息。我们使用gst_element_query_convert()来获取SPSPPS的缓冲区,并使用gst_buffer_map()和gst_buffer_unmap()来提取SPSPPS数据。 请注意,在实际使用,你需要根据需要修改代码,并添加适当的错误处理和释放资源的代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值