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.

180 lines
5.6KB

  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. AVPacket *b_frame;
  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 *out)
  54. {
  55. UnpackBFramesBSFContext *s = ctx->priv_data;
  56. int pos_p = -1, nb_vop = 0, pos_vop2 = -1, ret = 0;
  57. AVPacket *in;
  58. ret = ff_bsf_get_packet(ctx, &in);
  59. if (ret < 0)
  60. return ret;
  61. scan_buffer(in->data, in->size, &pos_p, &nb_vop, &pos_vop2);
  62. av_log(ctx, AV_LOG_DEBUG, "Found %d VOP startcode(s) in this packet.\n", nb_vop);
  63. if (pos_vop2 >= 0) {
  64. if (s->b_frame->data) {
  65. av_log(ctx, AV_LOG_WARNING,
  66. "Missing one N-VOP packet, discarding one B-frame.\n");
  67. av_packet_unref(s->b_frame);
  68. }
  69. /* store the packed B-frame in the BSFContext */
  70. ret = av_packet_ref(s->b_frame, in);
  71. if (ret < 0) {
  72. goto fail;
  73. }
  74. s->b_frame->size -= pos_vop2;
  75. s->b_frame->data += 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->data) {
  82. /* use frame from BSFContext */
  83. av_packet_move_ref(out, s->b_frame);
  84. /* use properties from current input packet */
  85. ret = av_packet_copy_props(out, in);
  86. if (ret < 0) {
  87. goto fail;
  88. }
  89. if (in->size <= MAX_NVOP_SIZE) {
  90. /* N-VOP */
  91. av_log(ctx, AV_LOG_DEBUG, "Skipping N-VOP.\n");
  92. } else {
  93. /* copy packet into BSFContext */
  94. av_packet_move_ref(s->b_frame, in);
  95. }
  96. } else if (nb_vop >= 2) {
  97. /* use first frame of the packet */
  98. av_packet_move_ref(out, in);
  99. out->size = pos_vop2;
  100. } else if (pos_p >= 0) {
  101. ret = av_packet_make_writable(in);
  102. if (ret < 0)
  103. goto fail;
  104. av_log(ctx, AV_LOG_DEBUG, "Updating DivX userdata (remove trailing 'p').\n");
  105. av_packet_move_ref(out, in);
  106. /* remove 'p' (packed) from the end of the (DivX) userdata string */
  107. out->data[pos_p] = '\0';
  108. } else {
  109. /* copy packet */
  110. av_packet_move_ref(out, in);
  111. }
  112. fail:
  113. if (ret < 0)
  114. av_packet_unref(out);
  115. av_packet_free(&in);
  116. return ret;
  117. }
  118. static int mpeg4_unpack_bframes_init(AVBSFContext *ctx)
  119. {
  120. UnpackBFramesBSFContext *s = ctx->priv_data;
  121. s->b_frame = av_packet_alloc();
  122. if (!s->b_frame)
  123. return AVERROR(ENOMEM);
  124. if (ctx->par_in->extradata) {
  125. int pos_p_ext = -1;
  126. scan_buffer(ctx->par_in->extradata, ctx->par_in->extradata_size, &pos_p_ext, NULL, NULL);
  127. if (pos_p_ext >= 0) {
  128. av_log(ctx, AV_LOG_DEBUG,
  129. "Updating DivX userdata (remove trailing 'p') in extradata.\n");
  130. ctx->par_out->extradata[pos_p_ext] = '\0';
  131. }
  132. }
  133. return 0;
  134. }
  135. static void mpeg4_unpack_bframes_flush(AVBSFContext *bsfc)
  136. {
  137. UnpackBFramesBSFContext *ctx = bsfc->priv_data;
  138. av_packet_unref(ctx->b_frame);
  139. }
  140. static void mpeg4_unpack_bframes_close(AVBSFContext *bsfc)
  141. {
  142. UnpackBFramesBSFContext *ctx = bsfc->priv_data;
  143. av_packet_free(&ctx->b_frame);
  144. }
  145. static const enum AVCodecID codec_ids[] = {
  146. AV_CODEC_ID_MPEG4, AV_CODEC_ID_NONE,
  147. };
  148. const AVBitStreamFilter ff_mpeg4_unpack_bframes_bsf = {
  149. .name = "mpeg4_unpack_bframes",
  150. .priv_data_size = sizeof(UnpackBFramesBSFContext),
  151. .init = mpeg4_unpack_bframes_init,
  152. .filter = mpeg4_unpack_bframes_filter,
  153. .flush = mpeg4_unpack_bframes_flush,
  154. .close = mpeg4_unpack_bframes_close,
  155. .codec_ids = codec_ids,
  156. };