]> git.sesse.net Git - ffmpeg/blob - libavcodec/mpeg4_unpack_bframes_bsf.c
Merge commit 'f89ec87afaf0d1abb6d450253b0b348fd554533b'
[ffmpeg] / libavcodec / mpeg4_unpack_bframes_bsf.c
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
22 #include "avcodec.h"
23 #include "bsf.h"
24 #include "mpeg4video.h"
25
26 typedef struct UnpackBFramesBSFContext {
27     AVPacket *b_frame;
28 } UnpackBFramesBSFContext;
29
30 /* search next start code */
31 static unsigned int find_startcode(const uint8_t *buf, int buf_size, int *pos)
32 {
33     unsigned int startcode = 0xFF;
34
35     for (; *pos < buf_size;) {
36         startcode = ((startcode << 8) | buf[*pos]) & 0xFFFFFFFF;
37         *pos +=1;
38         if ((startcode & 0xFFFFFF00) != 0x100)
39             continue;  /* no startcode */
40         return startcode;
41     }
42
43     return 0;
44 }
45
46 /* determine the position of the packed marker in the userdata,
47  * the number of VOPs and the position of the second VOP */
48 static void scan_buffer(const uint8_t *buf, int buf_size,
49                         int *pos_p, int *nb_vop, int *pos_vop2) {
50     unsigned int startcode;
51     int pos, i;
52
53     for (pos = 0; pos < buf_size;) {
54         startcode = find_startcode(buf, buf_size, &pos);
55
56         if (startcode == USER_DATA_STARTCODE && pos_p) {
57             /* check if the (DivX) userdata string ends with 'p' (packed) */
58             for (i = 0; i < 255 && pos + i + 1 < buf_size; i++) {
59                 if (buf[pos + i] == 'p' && buf[pos + i + 1] == '\0') {
60                     *pos_p = pos + i;
61                     break;
62                 }
63             }
64         } else if (startcode == VOP_STARTCODE && nb_vop) {
65             *nb_vop += 1;
66             if (*nb_vop == 2 && pos_vop2) {
67                 *pos_vop2 = pos - 4; /* subtract 4 bytes startcode */
68             }
69         }
70     }
71 }
72
73 static int mpeg4_unpack_bframes_filter(AVBSFContext *ctx, AVPacket *out)
74 {
75     UnpackBFramesBSFContext *s = ctx->priv_data;
76     int pos_p = -1, nb_vop = 0, pos_vop2 = -1, ret = 0;
77     AVPacket *in;
78
79     ret = ff_bsf_get_packet(ctx, &in);
80     if (ret < 0)
81         return ret;
82
83     scan_buffer(in->data, in->size, &pos_p, &nb_vop, &pos_vop2);
84     av_log(ctx, AV_LOG_DEBUG, "Found %d VOP startcode(s) in this packet.\n", nb_vop);
85
86     if (pos_vop2 >= 0) {
87         if (s->b_frame->data) {
88             av_log(ctx, AV_LOG_WARNING,
89                    "Missing one N-VOP packet, discarding one B-frame.\n");
90             av_packet_unref(s->b_frame);
91         }
92         /* store the packed B-frame in the BSFContext */
93         ret = av_packet_ref(s->b_frame, in);
94         if (ret < 0) {
95             goto fail;
96         }
97         s->b_frame->size -= pos_vop2;
98         s->b_frame->data += pos_vop2;
99     }
100
101     if (nb_vop > 2) {
102         av_log(ctx, AV_LOG_WARNING,
103        "Found %d VOP headers in one packet, only unpacking one.\n", nb_vop);
104     }
105
106     if (nb_vop == 1 && s->b_frame->data) {
107         /* use frame from BSFContext */
108         av_packet_move_ref(out, s->b_frame);
109
110         /* use properties from current input packet */
111         ret = av_packet_copy_props(out, in);
112         if (ret < 0) {
113             goto fail;
114         }
115
116         if (in->size <= MAX_NVOP_SIZE) {
117             /* N-VOP */
118             av_log(ctx, AV_LOG_DEBUG, "Skipping N-VOP.\n");
119         } else {
120             /* copy packet into BSFContext */
121             av_packet_move_ref(s->b_frame, in);
122         }
123     } else if (nb_vop >= 2) {
124         /* use first frame of the packet */
125         av_packet_move_ref(out, in);
126         out->size = pos_vop2;
127     } else if (pos_p >= 0) {
128         ret = av_packet_make_writable(in);
129         if (ret < 0)
130             goto fail;
131         av_log(ctx, AV_LOG_DEBUG, "Updating DivX userdata (remove trailing 'p').\n");
132         av_packet_move_ref(out, in);
133         /* remove 'p' (packed) from the end of the (DivX) userdata string */
134         out->data[pos_p] = '\0';
135     } else {
136         /* copy packet */
137         av_packet_move_ref(out, in);
138     }
139
140 fail:
141     if (ret < 0)
142         av_packet_unref(out);
143     av_packet_free(&in);
144
145     return ret;
146 }
147
148 static int mpeg4_unpack_bframes_init(AVBSFContext *ctx)
149 {
150     UnpackBFramesBSFContext *s = ctx->priv_data;
151
152     s->b_frame = av_packet_alloc();
153     if (!s->b_frame)
154         return AVERROR(ENOMEM);
155
156     if (ctx->par_in->extradata) {
157         int pos_p_ext = -1;
158         scan_buffer(ctx->par_in->extradata, ctx->par_in->extradata_size, &pos_p_ext, NULL, NULL);
159         if (pos_p_ext >= 0) {
160             av_log(ctx, AV_LOG_DEBUG,
161                    "Updating DivX userdata (remove trailing 'p') in extradata.\n");
162             ctx->par_out->extradata[pos_p_ext] = '\0';
163         }
164     }
165
166     return 0;
167 }
168
169 static void mpeg4_unpack_bframes_flush(AVBSFContext *bsfc)
170 {
171     UnpackBFramesBSFContext *ctx = bsfc->priv_data;
172     av_packet_unref(ctx->b_frame);
173 }
174
175 static void mpeg4_unpack_bframes_close(AVBSFContext *bsfc)
176 {
177     UnpackBFramesBSFContext *ctx = bsfc->priv_data;
178     av_packet_free(&ctx->b_frame);
179 }
180
181 static const enum AVCodecID codec_ids[] = {
182     AV_CODEC_ID_MPEG4, AV_CODEC_ID_NONE,
183 };
184
185 const AVBitStreamFilter ff_mpeg4_unpack_bframes_bsf = {
186     .name           = "mpeg4_unpack_bframes",
187     .priv_data_size = sizeof(UnpackBFramesBSFContext),
188     .init           = mpeg4_unpack_bframes_init,
189     .filter         = mpeg4_unpack_bframes_filter,
190     .flush          = mpeg4_unpack_bframes_flush,
191     .close          = mpeg4_unpack_bframes_close,
192     .codec_ids      = codec_ids,
193 };