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