出自: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. 每天一个linux命令(2):file 命令

    作用:查看文件类型信息 格式:file[OPTIONS...] [FILE...] 主要参数: --help 显示帮助信息 -v,--version 输出版本信息并退出 -b,--brief     ...

  2. 每日英语:Surviving a Conference Call

    The conference call is one of the most familiar rituals of office life -- and one of the most hated. ...

  3. VS调试dll

    (ps:编译dll的工程最好配置一下预编译宏:“配置属性”/“C或C++”/“预处理器”/“预处理器定义”,选择编辑,在最下面加一行“_CRT_SECURE_NO_WARNINGS”) 1. 右键dl ...

  4. vue实现点击区域外部的区域,关闭该区域

    var _this = this; document.addEventListener('click',function(e){ console.log(_this.$refs.configforms ...

  5. shell快捷方式总结

    Linux使用Shell时总是有各种不太方便,需要对输入的命令行做编辑.跳转.这里整理下我遇到的Shell下快捷键. 注意下文中所有的ctrl表示键盘中的控制按键,所有的ctrl + x的格式都是同时 ...

  6. cat正常,cat重定向到文件可能是乱码;解决办法

    文件cat正常,cat重定向到文件可能是乱码:解决办法如下 .vimrc文件中增加如下两行 set fileencoding=utf8 set fileencodings=utf8 据一位老神医说,文 ...

  7. maven relativePath

    父项目的pom.xml文件的相对路径.默认值为../pom.xml.maven首先从当前构建项目开始查找父项目的pom文件,然后从本地仓库,最有从远程仓库.RelativePath允许你选择一个不同的 ...

  8. JAVA-JSP内置对象之session对象设置并获得session生命周期

    相关资料:<21天学通Java Web开发> session对象设置并获得session生命周期1.通过session对象的setMaxInactiveInterval()方法可以设置se ...

  9. 猥琐百度杯猥琐CTF

    其实不难,但是作为我这个代码菜鸡+脑洞菜鸡+黑阔菜鸡而言确实挺难. 题目源码: <?php error_reporting(0); session_start(); require('./fla ...

  10. AJAX开发技术

    AJAX技术 AJAX(Asynchronous JavaScript and XML,异步JavaScript和XML),AJAX并不是一项新的技术,它产生的主要目的是用于页面的局部刷新,从之前的代 ...