MP4中提取H.264码流

来源:互联网 发布:阿里云客服可以兼职吗 编辑:程序博客网 时间:2024/06/16 22:43


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"完成这项工作,关键代码如下

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

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




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

[cpp] view plaincopyprint?
  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);
int ParseH264ExtraDataInMp4(int stream_id) { uint8_t *dummy = NULL; int dummy_size; AVBitStreamFilterContext<wbr>* bsfc = av_bitstream_filter_init("h264_mp4toannexb"); if(bsfc == NULL) { return -1; } av_bitstream_filter_filter( bsfc, format_ctx_->streams[stream_id]->codec, NULL, &dummy, &dummy_size, NULL, 0, 0);</wbr>
[cpp] view plaincopyprint?
  1. av_bitstream_filter_close(bsfc);
  2. return 0;
  3. }
 av_bitstream_filter_close(bsfc); return 0; }
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 福袋不支持退货怎么办 不支持跨区下单怎么办 支付宝被占用怎么办 淘宝东西失效了怎么办 访客突然下降了怎么办 淘宝店铺广告违规怎么办 苹果手机网速差怎么办 支付宝账号忘记怎么办 支付宝无法登录怎么办 什么是淘宝空间不足怎么办 支付宝授权失败怎么办 买家旺旺被打标一次怎么办 淘宝网上忘记密码怎么办 登淘宝忘记密码怎么办 手机淘宝闪退怎么办 证券账户忘了怎么办 资金账户忘了怎么办 公积金账户忘了怎么办 淘宝买家号违规怎么办? 淘宝号显示违规怎么办 淘宝网登录不了怎么办 支付宝刷脸登录失败怎么办 blue超出注册限制怎么办 电脑不识别光盘怎么办 4g按钮打不开怎么办 我电脑没有光驱怎么办 电脑360浏览器打不开怎么办 注销淘宝账号手机号怎么办 苹果手机崩溃了怎么办 淘宝被限制使用怎么办 公司注册成功后怎么办 一建注册不成功怎么办 志愿者注册没成功怎么办 品牌注册成功后怎么办 outlook密码忘了怎么办 淘宝被买家限制怎么办 淘宝限制下单怎么办 买家淘宝号违规怎么办 手机淘宝反应慢怎么办 手机淘宝占内存怎么办 手机淘宝有违规怎么办