出自:http://blog.csdn.net/gavinr/article/details/7183499
 
1.获取数据
ffmpeg读取mp4中的H264数据,并不能直接得到NALU,文件中也没有储存0x00000001的分隔符。下面这张图为packet.data中的数据

从图中可以发现,packet中的数据起始处没有分隔符(0x00000001), 也不是0x65、0x67、0x68、0x41等字节,所以可以肯定这不是标准的nalu。

其实,前4个字0x000032ce表示的是nalu的长度,从第5个字节开始才是nalu的数据。所以直接将前4个字节替换为0x00000001即可得到标准的nalu数据。

2.获取pps及sps

pps及sps不能从packet获得,而是保存在AVCodecContext的extradata数据域中。如下:

如何从extradata中解析出sps及pps呢?ffmpeg中提供了一个流过滤器"h264_mp4toannexb"完成这项工作,关键代码如下

  1. //h264_mp4toannexb_bsf.c
  2. static int h264_mp4toannexb_filter(AVBitStreamFilterContext *bsfc,
  3. AVCodecContext *avctx, const char *args,
  4. uint8_t  **poutbuf, int *poutbuf_size,
  5. const uint8_t *buf, int      buf_size,
  6. int keyframe) {
  7. H264BSFContext *ctx = bsfc->priv_data;
  8. uint8_t unit_type;
  9. int32_t nal_size;
  10. uint32_t cumul_size = 0;
  11. const uint8_t *buf_end = buf + buf_size;
  12. /* nothing to filter */
  13. if (!avctx->extradata || avctx->extradata_size < 6) {
  14. *poutbuf = (uint8_t*) buf;
  15. *poutbuf_size = buf_size;
  16. return 0;
  17. }
  18. //
  19. //从extradata中分析出SPS、PPS
  20. //
  21. /* retrieve sps and pps NAL units from extradata */
  22. if (!ctx->extradata_parsed) {
  23. uint16_t unit_size;
  24. uint64_t total_size = 0;
  25. uint8_t *out = NULL, unit_nb, sps_done = 0, sps_seen = 0, pps_seen = 0;
  26. const uint8_t *extradata = avctx->extradata+4;  //跳过前4个字节
  27. static const uint8_t nalu_header[4] = {0, 0, 0, 1};
  28. /* retrieve length coded size */
  29. ctx->length_size = (*extradata++ & 0x3) + 1;    //用于指示表示编码数据长度所需字节数
  30. if (ctx->length_size == 3)
  31. return AVERROR(EINVAL);
  32. /* retrieve sps and pps unit(s) */
  33. unit_nb = *extradata++ & 0x1f; /* number of sps unit(s) */
  34. if (!unit_nb) {
  35. goto pps;
  36. } else {
  37. sps_seen = 1;
  38. }
  39. while (unit_nb--) {
  40. void *tmp;
  41. unit_size = AV_RB16(extradata);
  42. total_size += unit_size+4;
  43. if (total_size > INT_MAX - FF_INPUT_BUFFER_PADDING_SIZE ||
  44. extradata+2+unit_size > avctx->extradata+avctx->extradata_size) {
  45. av_free(out);
  46. return AVERROR(EINVAL);
  47. }
  48. tmp = av_realloc(out, total_size + FF_INPUT_BUFFER_PADDING_SIZE);
  49. if (!tmp) {
  50. av_free(out);
  51. return AVERROR(ENOMEM);
  52. }
  53. out = tmp;
  54. memcpy(out+total_size-unit_size-4, nalu_header, 4);
  55. memcpy(out+total_size-unit_size,   extradata+2, unit_size);
  56. extradata += 2+unit_size;
  57. pps:
  58. if (!unit_nb && !sps_done++) {
  59. unit_nb = *extradata++; /* number of pps unit(s) */
  60. if (unit_nb)
  61. pps_seen = 1;
  62. }
  63. }
  64. if(out)
  65. memset(out + total_size, 0, FF_INPUT_BUFFER_PADDING_SIZE);
  66. if (!sps_seen)
  67. av_log(avctx, AV_LOG_WARNING, "Warning: SPS NALU missing or invalid. The resulting stream may not play.\n");
  68. if (!pps_seen)
  69. av_log(avctx, AV_LOG_WARNING, "Warning: PPS NALU missing or invalid. The resulting stream may not play.\n");
  70. av_free(avctx->extradata);
  71. avctx->extradata      = out;
  72. avctx->extradata_size = total_size;
  73. ctx->first_idr        = 1;
  74. ctx->extradata_parsed = 1;
  75. }
  76. *poutbuf_size = 0;
  77. *poutbuf = NULL;
  78. do {
  79. if (buf + ctx->length_size > buf_end)
  80. goto fail;  //buf为NULL时,以下代码将不再执行
  81. //
  82. //用于保存数据长度的字节数,是在分析原extradata计算出来的
  83. //
  84. if (ctx->length_size == 1) {
  85. nal_size = buf[0];
  86. } else if (ctx->length_size == 2) {
  87. nal_size = AV_RB16(buf);
  88. } else
  89. nal_size = AV_RB32(buf);
  90. buf += ctx->length_size;
  91. unit_type = *buf & 0x1f;
  92. if (buf + nal_size > buf_end || nal_size < 0)
  93. goto fail;
  94. /* prepend only to the first type 5 NAL unit of an IDR picture */
  95. if (ctx->first_idr && unit_type == 5) {
  96. //
  97. //copy IDR 帧时,需要将sps及pps一同拷贝
  98. //
  99. if (alloc_and_copy(poutbuf, poutbuf_size,
  100. avctx->extradata, avctx->extradata_size,
  101. buf, nal_size) < 0)
  102. goto fail;
  103. ctx->first_idr = 0;
  104. } else {
  105. //
  106. //非IDR帧,没有sps及pps
  107. if (alloc_and_copy(poutbuf, poutbuf_size,
  108. NULL, 0,
  109. buf, nal_size) < 0)
  110. goto fail;
  111. if (!ctx->first_idr && unit_type == 1)
  112. ctx->first_idr = 1;
  113. }
  114. buf += nal_size;
  115. cumul_size += nal_size + ctx->length_size;
  116. } while (cumul_size < buf_size);
  117. return 1;
  118. fail:
  119. av_freep(poutbuf);
  120. *poutbuf_size = 0;
  121. return AVERROR(EINVAL);
  122. }

一般情况下,extradata中包含一个sps、一个pps 的nalu, 从上面的代码中容易看出extradata的数据格式。分析后的sps及pps依然储存在extradata域中,并添加了起始符。从代码中还可以看出,上面的函数会将sps、pps及packet中的数据,都copy到poutbuf指示的内存中,如果不需要copy到指定内存,直接给buf参数传入空值即可。

3.使用ffmpeg的流过滤器获取sps及pps
流过滤器"h264_mp4toannexb", 在av_register_all()函数中会被注册。用法示例如下:

  1. int ParseH264ExtraDataInMp4(int stream_id)
  2. {
  3. uint8_t *dummy = NULL;
  4. int dummy_size;
  5. AVBitStreamFilterContext* bsfc =  av_bitstream_filter_init("h264_mp4toannexb");
  6. if(bsfc == NULL)
  7. {
  8. return -1;
  9. }
  10. av_bitstream_filter_filter(
  11. bsfc, format_ctx_->streams[stream_id]->codec, NULL, &dummy, &dummy_size, NULL, 0, 0);
  1. av_bitstream_filter_close(bsfc);
  2. return 0;
  3. }

(转)ffmpeg 从mp4上提取H264的nalu的更多相关文章

  1. ffmpeg 从mp4上提取H264的nalu

    转自http://blog.csdn.net/gavinr/article/details/7183499 1.获取数据 ffmpeg读取mp4中的H264数据,并不能直接得到NALU,文件中也没有储 ...

  2. 嵌入式 H264—MP4格式及在MP4文件中提取H264的SPS、PPS及码流

    一.MP4格式基本概念 MP4格式对应标准MPEG-4标准(ISO/IEC14496) 二.MP4封装格式核心概念 1  MP4封装格式对应标准为 ISO/IEC 14496-12(信息技术 视听对象 ...

  3. [转]【流媒體】H264—MP4格式及在MP4文件中提取H264的SPS、PPS及码流

    [流媒體]H264—MP4格式及在MP4文件中提取H264的SPS.PPS及码流 SkySeraph Apr 1st 2012  Email:skyseraph00@163.com 一.MP4格式基本 ...

  4. 多媒体开发之---H264—MP4格式及在MP4文件中提取H264的SPS、PPS及码流

    一.MP4格式基本概念 MP4格式对应标准MPEG-4标准(ISO/IEC14496) 二.MP4封装格式核心概念 1  MP4封装格式对应标准为 ISO/IEC 14496-12(信息技术 视听对象 ...

  5. ffmpeg architecture(上)

    ffmpeg architecture(上) 目录 介绍 视频-您看到的是什么! 音频-您在听什么! 编解码器-缩小数据 容器-音频和视频的舒适场所 FFmpeg-命令行 FFmpeg命令行工具101 ...

  6. ffmpeg 转换 mp4 成 flv

    参考资料: https://addpipe.com/blog/flv-to-mp4/ ffmpeg -i demo.mp4 -c:v libx264 -crf 19 demo.flv 或者 ffmpe ...

  7. [转载]用 FFMPEG 合并 MP4 视频

    因为 ffmpeg 是支持切分 mp4 视频的,所以我就理所当然的以为 ffmpeg 是支持视频合并.直到今天同事找我问方法,才发现一直以为的方法是错误的, mp4 不支持直接 concate(丢人了 ...

  8. js对flv提取h264、aac音视频流

    FLV提取里面的h264视频流 FLV和MP4支持的编码 流媒体和媒体文件的区别 流媒体是指将一连串的多媒体资料压缩后,经过互联网分段发送资料,在互联网上即时传输影音以供观赏的一种技术与过程,此技术使 ...

  9. ps流提取H264并解码播放

    因为需要从海康ps流中提取H264数据并进行解码播放,才有了这篇文章.因为是视频编解码领域的纯入门新手,个别理解或者方法有误,需要自行判断,不过相关方法已经测试通过,对于 像我这样的新手还是有一定的借 ...

随机推荐

  1. javascript基础拾遗(八)

    1.原型继承 如何让一个对象继承另一个对象? function Language(name){ this.name = name this.score = 8.0 } Language.prototy ...

  2. Effective Spark RDDs with Alluxio【转】

    转自:http://kaimingwan.com/post/alluxio/effective-spark-rdds-with-alluxio 1. 介绍 2. 引言 3. Alluxio and S ...

  3. vue如何在路由跳转的时候更新组件

    项目中在路由跳转的时候碰到一个问题,没有更新视图,如何解决呢: https://segmentfault.com/a/1190000008879966 http://www.tuicool.com/a ...

  4. vue中$router和$route的区别

    $router是VueRouter的实例,在script标签中想要导航到不同的URL,使用$router.push方法. 返回上一个历史history用$router.to(-1) $route为当前 ...

  5. Android下基于PCM的音频渲染

    环境准备 请按照我之前的文章-Android下基于SDL的位图渲染,安装必要的开发环境. 实践篇 这里主要参考Beginning SDL 2.0(6) 音频渲染及wav播放,只不过将源从WAV文件改成 ...

  6. 通用性好的win2003序列号: (推荐先用这个里面的)

    通用性好的win2003序列号: (推荐先用这个里面的)FJ8DH-TQPYG-9KFHQ-88CB2-Y7V3Y GRD4P-FTQQF-JCDM8-4P6JK-PFG7MJD7JX-KCDTH-7 ...

  7. Airlaunch 快捷设置代码分享

    Airlaunch 快捷设置代码分享设置:prefs:root=SETTING蜂窝网络:prefs:root=MOBILE_DATA_SETTINGS_IDWIFI:prefs:root=WIFI定位 ...

  8. LeetCode: Largest Rectangle in Histogram 解题报告

    Largest Rectangle in Histogram Given n non-negative integers representing the histogram's bar height ...

  9. 【Linux】crontab 每隔1小时 2小时的执行job写法

    crontab -l crontab -e 每五分钟执行  */5 * * * * 每小时执行     0 * * * * 每2小时执行     0 */2 * * * 每天执行        0 0 ...

  10. Python学习笔记(4):容器、迭代对象、迭代器、生成器、生成器表达式

    在了解Python的数据结构时,容器(container).可迭代对象(iterable).迭代器(iterator).生成器(generator).列表/集合/字典推导式(list,set,dict ...