出自: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. 【qt】QT 的信号与槽机制

    QT 是一个跨平台的 C++ GUI 应用构架,它提供了丰富的窗口部件集,具有面向对象.易于扩展.真正的组件编程等特点,更为引人注目的是目前 Linux 上最为流行的 KDE 桌面环境就是建立在 QT ...

  2. 【C/C++】关于隐式转换·面试题分析

    题目 以下两个程序片段A 和B ,问哪个能进入循环? 片段A: unsigned short i; unsigned ; ; i < index-; i++) { ........ } 片段B: ...

  3. 【ARM】2410裸机系列-ADC数模转换

    开发环境   1.硬件平台:FS2410 2.主机:Ubuntu 12.04 ADC寄存器配置       1.初始化ADC(ADCCON) 设置预分频,预分频因子,选择A/D转换通道,并选择正常模式 ...

  4. 【Bayesian】贝叶斯决策方法(Bayesian Decision Method)

    已知某条件概率,如何得到两个事件交换后的概率,也就是在已知P(A|B)的情况下如何求得P(B|A).这里先解释什么是条件概率: 表示事件B已经发生的前提下,事件A发生的概率,叫做事件B发生下事件A的条 ...

  5. Android之Activity切换

    ●假如有Activity01和Activity02,从Activity01切换到Activity02并传递参数. Activity01中: button.setOnClickListener(new  ...

  6. JAVA-JSP内置对象之request对象的其他方法

    相关资料:<21天学通Java Web开发> request对象的其他方法1.request对象除了可以用来获得请求参数,还可以用来获得HTTP标头及其他信息. 方法           ...

  7. mysql防止误删除的方法

    为了防止在更新和删除的时候,没有写where条件而对全部数据进行操作,mysql提供了一个参数来防止此情况的发生 需要在启动mysql的时候,增加参数--i-am-a-dummy含义是我是新手,或者使 ...

  8. [转]你所不知的 CSS ::before 和 ::after 伪元素用法

    SS 有两个说不上常用的伪类 :before 和 :after,偶尔会被人用来添加些自定义格式什么的,但是它们的功用不仅于此.前几天发现了 Creative Link Effects 这个非常有意思的 ...

  9. 微服务之springCloud-docker-feign配置(五)

    简介 上一节我们讨论了怎么用feign声明式调用cloud的生产者,这节我们讨论一下feign配置,通过编写配置类,我们可以自定义feign的日志级别,日志扫描目录,可以通过feign调用服务在eur ...

  10. Android——Fragment+Editext总结

    原文地址: android Fragment中没有onTouchEvent解决方法 Android--点击EditText的时候弹出软键盘,点击EditText之外空白处软键盘消失,android-- ...