转自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. js对flv提取h264、aac音视频流

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

  6. ffmpeg architecture(上)

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

  7. ffmpeg 转换 mp4 成 flv

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

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

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

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

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

随机推荐

  1. Effective C++ .09 不在构造和析构过程中调用virtual函数

    看过C++对象模型的话就可以知道,在构造基类时,完整的vtable没有建立起来(表项没有被相应的子类函数替换),因而无法调用到子类的函数(即构造函数中的virtual函数是本类里的方法,不是virtu ...

  2. flask-login的简单实用

    # encoding: utf-8 from flask import Flask, Blueprint from flask_login import (LoginManager, login_re ...

  3. npm安装指定版本

    今天犯了一个低级错误,在npm安装依赖时,命令写成下了格式 npm i --save iview 2.0.0 要安装指定版本应该使用 npm i --save iview@2.0.0 谨记

  4. 动态赋值poster,无法显示

    vue操作video的poster属性时,动态给poster赋值,在chrome下是无法显示的 解决办法 在赋值后,找到video元素.load()下就会看到封面图了

  5. Apache服务器运维笔记(3)----容器部分

    1.<IfModule>容器 <IfModule>容器作用于模块,它会首先判断模块是否载入,然后再决定是否进行处理,也就是说只有当判断结果为真时才会执行容器内的指令,相反如果为 ...

  6. 02_linux常用指令

    [用户管理] 查看当前用户     whoami 新建用户     sudo adduser higginCui 查看新建用户     ls /home 使用新建用户登录     su -l higg ...

  7. 【Python】Sublime text 3 搭建Python IDE

    背景: 最经遇到一件很苦恼的事情,就是在Sublime text 3中写的Python代码直接挪到python原生的ide中老是报格式的错误(有时让人讨厌的缩进),没有办法,看到Sublime tex ...

  8. GTX650Ti,GT610安装黑苹果之经验与步骤

    安装这两个显卡的黑苹果都是10.9以上的版本,一个是10.9.2,一个是10.9.4,最后都完美.主板一个是Z77,一个是H61. 1. 开始安装完以后,显卡不工作,能够安全模式进去. 2. 删除Ap ...

  9. javascprit form表单提交前验证以及ajax返回json

    1.今天要做一个手机验证码验证的功能.需求是前端页面点击发送 短信验证码,后台接收后通过ajax返回到前端,之后前端在提交时候进行验证.思路很简单,不过做的过程还是学到不少的东西. 1.ajax请求后 ...

  10. java实现哈弗曼树和哈夫曼树压缩

    本篇博文将介绍什么是哈夫曼树,并且如何在java语言中构建一棵哈夫曼树,怎么利用哈夫曼树实现对文件的压缩和解压.首先,先来了解下什么哈夫曼树. 一.哈夫曼树 哈夫曼树属于二叉树,即树的结点最多拥有2个 ...