转自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. javaweb之jsp的九个隐含对象与基本语法

    1.在页面上可以不用声明直接使用的对象称为jsp页面的隐含对象.使用<% %>编写的java代码在_jspService方法中,如下: public void _jspService(fi ...

  2. thrift简介

    thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发.它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Python, PHP, Ruby, Erlang, Perl ...

  3. golang 应用的部署相关技术

    nohup命令 在 linux 下面部署,我们可以利用 nohup 命令,把应用部署在后端,如下所示: nohup ./yourapp & 这样你的应用就跑在了 Linux 系统的守护进程 n ...

  4. php基础部分(1)

    PHP 输出文本的基础指令:echo 和 print.echo和print的区别echo是PHP语句, print和print_r是函数,语句没有返回值,函数可以有返回值(即便没有用)echo 输出一 ...

  5. .NET开源工作流RoadFlow-流程运行-调试

    我们在设计好流程后,往往要先经过测试,把整个流程走遍,但是一个流程要多人审批才能走完,这样换别人账号来测试流程很麻烦. 所以我们设计了一种调试模式,即可以在流程运行时看到相关信息(错误时会显示错误信息 ...

  6. Spring Boot Async异步执行

    异步调用就是不用等待结果的返回就执行后面的逻辑,同步调用则需要等带结果再执行后面的逻辑. 通常我们使用异步操作都会去创建一个线程执行一段逻辑,然后把这个线程丢到线程池中去执行,代码如下: Execut ...

  7. python SQLAchemy常用语法

    SQLAlchemy是Python编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果. ...

  8. [poj 2479] Maximum sum -- 转载

    转自 CSND 想看更多的解题报告: http://blog.csdn.net/wangjian8006/article/details/7870410                         ...

  9. webstorm中使用git

    webstorm中使用git将代码放入tfs两种方式: 直接在tfs上建立仓库,复制仓库地址,然后在本地打开webstorm,然后git克隆这个仓库 使用git命令将本地项目上传到tfs git re ...

  10. centos 开机执行的命令

    centos开机执行的命令-------待验证,因为有可能涉及到root问题,没想明白怎么输入密码 1.增加rc.local可执行权限 chmod +x /etc/rc.d/rc.local 2.在里 ...