You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

167 lines
5.5KB

  1. /*
  2. * Bitstream filter for unpacking DivX-style packed B-frames in MPEG-4 (divx_packed)
  3. * Copyright (c) 2015 Andreas Cadhalpun <Andreas.Cadhalpun@googlemail.com>
  4. *
  5. * This file is part of FFmpeg.
  6. *
  7. * FFmpeg is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * FFmpeg is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with FFmpeg; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. */
  21. #include "avcodec.h"
  22. #include "bsf.h"
  23. #include "internal.h"
  24. #include "mpeg4video.h"
  25. typedef struct UnpackBFramesBSFContext {
  26. AVBufferRef *b_frame_ref;
  27. } UnpackBFramesBSFContext;
  28. /* determine the position of the packed marker in the userdata,
  29. * the number of VOPs and the position of the second VOP */
  30. static void scan_buffer(const uint8_t *buf, int buf_size,
  31. int *pos_p, int *nb_vop, int *pos_vop2) {
  32. uint32_t startcode;
  33. const uint8_t *end = buf + buf_size, *pos = buf;
  34. while (pos < end) {
  35. startcode = -1;
  36. pos = avpriv_find_start_code(pos, end, &startcode);
  37. if (startcode == USER_DATA_STARTCODE && pos_p) {
  38. /* check if the (DivX) userdata string ends with 'p' (packed) */
  39. for (int i = 0; i < 255 && pos + i + 1 < end; i++) {
  40. if (pos[i] == 'p' && pos[i + 1] == '\0') {
  41. *pos_p = pos + i - buf;
  42. break;
  43. }
  44. }
  45. } else if (startcode == VOP_STARTCODE && nb_vop) {
  46. *nb_vop += 1;
  47. if (*nb_vop == 2 && pos_vop2) {
  48. *pos_vop2 = pos - buf - 4; /* subtract 4 bytes startcode */
  49. }
  50. }
  51. }
  52. }
  53. static int mpeg4_unpack_bframes_filter(AVBSFContext *ctx, AVPacket *pkt)
  54. {
  55. UnpackBFramesBSFContext *s = ctx->priv_data;
  56. int pos_p = -1, nb_vop = 0, pos_vop2 = -1, ret = 0;
  57. ret = ff_bsf_get_packet_ref(ctx, pkt);
  58. if (ret < 0)
  59. return ret;
  60. scan_buffer(pkt->data, pkt->size, &pos_p, &nb_vop, &pos_vop2);
  61. av_log(ctx, AV_LOG_DEBUG, "Found %d VOP startcode(s) in this packet.\n", nb_vop);
  62. if (pos_vop2 >= 0) {
  63. if (s->b_frame_ref) {
  64. av_log(ctx, AV_LOG_WARNING,
  65. "Missing one N-VOP packet, discarding one B-frame.\n");
  66. av_buffer_unref(&s->b_frame_ref);
  67. }
  68. /* store a reference to the packed B-frame's data in the BSFContext */
  69. s->b_frame_ref = av_buffer_ref(pkt->buf);
  70. if (!s->b_frame_ref) {
  71. ret = AVERROR(ENOMEM);
  72. goto fail;
  73. }
  74. s->b_frame_ref->data = pkt->data + pos_vop2;
  75. s->b_frame_ref->size = pkt->size - pos_vop2;
  76. }
  77. if (nb_vop > 2) {
  78. av_log(ctx, AV_LOG_WARNING,
  79. "Found %d VOP headers in one packet, only unpacking one.\n", nb_vop);
  80. }
  81. if (nb_vop == 1 && s->b_frame_ref) {
  82. AVBufferRef *tmp = pkt->buf;
  83. /* make tmp accurately reflect the packet's data */
  84. tmp->data = pkt->data;
  85. tmp->size = pkt->size;
  86. /* replace data in packet with stored data */
  87. pkt->buf = s->b_frame_ref;
  88. pkt->data = s->b_frame_ref->data;
  89. pkt->size = s->b_frame_ref->size;
  90. /* store reference to data into BSFContext */
  91. s->b_frame_ref = tmp;
  92. if (s->b_frame_ref->size <= MAX_NVOP_SIZE) {
  93. /* N-VOP - discard stored data */
  94. av_log(ctx, AV_LOG_DEBUG, "Skipping N-VOP.\n");
  95. av_buffer_unref(&s->b_frame_ref);
  96. }
  97. } else if (nb_vop >= 2) {
  98. /* use first frame of the packet */
  99. pkt->size = pos_vop2;
  100. } else if (pos_p >= 0) {
  101. ret = av_packet_make_writable(pkt);
  102. if (ret < 0)
  103. goto fail;
  104. av_log(ctx, AV_LOG_DEBUG, "Updating DivX userdata (remove trailing 'p').\n");
  105. /* remove 'p' (packed) from the end of the (DivX) userdata string */
  106. pkt->data[pos_p] = '\0';
  107. } else {
  108. /* use packet as is */
  109. }
  110. fail:
  111. if (ret < 0)
  112. av_packet_unref(pkt);
  113. return ret;
  114. }
  115. static int mpeg4_unpack_bframes_init(AVBSFContext *ctx)
  116. {
  117. if (ctx->par_in->extradata) {
  118. int pos_p_ext = -1;
  119. scan_buffer(ctx->par_in->extradata, ctx->par_in->extradata_size, &pos_p_ext, NULL, NULL);
  120. if (pos_p_ext >= 0) {
  121. av_log(ctx, AV_LOG_DEBUG,
  122. "Updating DivX userdata (remove trailing 'p') in extradata.\n");
  123. ctx->par_out->extradata[pos_p_ext] = '\0';
  124. }
  125. }
  126. return 0;
  127. }
  128. static void mpeg4_unpack_bframes_close_flush(AVBSFContext *bsfc)
  129. {
  130. UnpackBFramesBSFContext *ctx = bsfc->priv_data;
  131. av_buffer_unref(&ctx->b_frame_ref);
  132. }
  133. static const enum AVCodecID codec_ids[] = {
  134. AV_CODEC_ID_MPEG4, AV_CODEC_ID_NONE,
  135. };
  136. const AVBitStreamFilter ff_mpeg4_unpack_bframes_bsf = {
  137. .name = "mpeg4_unpack_bframes",
  138. .priv_data_size = sizeof(UnpackBFramesBSFContext),
  139. .init = mpeg4_unpack_bframes_init,
  140. .filter = mpeg4_unpack_bframes_filter,
  141. .flush = mpeg4_unpack_bframes_close_flush,
  142. .close = mpeg4_unpack_bframes_close_flush,
  143. .codec_ids = codec_ids,
  144. };