]> git.sesse.net Git - ffmpeg/blob - libavcodec/vp9_superframe_bsf.c
Merge commit 'be1db21ba88fe86036fea9f8d2c1a5f47c2a0a7e'
[ffmpeg] / libavcodec / vp9_superframe_bsf.c
1 /*
2  * Vp9 invisible (alt-ref) frame to superframe merge bitstream filter
3  * Copyright (c) 2016 Ronald S. Bultje <rsbultje@gmail.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 "libavutil/avassert.h"
23 #include "avcodec.h"
24 #include "bsf.h"
25 #include "get_bits.h"
26
27 #define MAX_CACHE 8
28 typedef struct VP9BSFContext {
29     int n_cache;
30     struct CachedBuf {
31         uint8_t *data;
32         int size;
33     } cache[MAX_CACHE];
34 } VP9BSFContext;
35
36 static void stats(const struct CachedBuf *in, int n_in,
37                   unsigned *_max, unsigned *_sum)
38 {
39     int n;
40     unsigned max = 0, sum = 0;
41
42     for (n = 0; n < n_in; n++) {
43         unsigned sz = in[n].size;
44
45         if (sz > max)
46             max = sz;
47         sum += sz;
48     }
49
50     *_max = max;
51     *_sum = sum;
52 }
53
54 static int merge_superframe(const struct CachedBuf *in, int n_in, AVPacket *out)
55 {
56     unsigned max, sum, mag, marker, n, sz;
57     uint8_t *ptr;
58     int res;
59
60     stats(in, n_in, &max, &sum);
61     mag = av_log2(max) >> 3;
62     marker = 0xC0 + (mag << 3) + (n_in - 1);
63     sz = sum + 2 + (mag + 1) * n_in;
64     res = av_new_packet(out, sz);
65     if (res < 0)
66         return res;
67     ptr = out->data;
68     for (n = 0; n < n_in; n++) {
69         memcpy(ptr, in[n].data, in[n].size);
70         ptr += in[n].size;
71     }
72
73 #define wloop(mag, wr) \
74     for (n = 0; n < n_in; n++) { \
75         wr; \
76         ptr += mag + 1; \
77     }
78
79     // write superframe with marker 110[mag:2][nframes:3]
80     *ptr++ = marker;
81     switch (mag) {
82     case 0:
83         wloop(mag, *ptr = in[n].size);
84         break;
85     case 1:
86         wloop(mag, AV_WL16(ptr, in[n].size));
87         break;
88     case 2:
89         wloop(mag, AV_WL24(ptr, in[n].size));
90         break;
91     case 3:
92         wloop(mag, AV_WL32(ptr, in[n].size));
93         break;
94     }
95     *ptr++ = marker;
96     av_assert0(ptr == &out->data[out->size]);
97
98     return 0;
99 }
100
101 static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *out)
102 {
103     GetBitContext gb;
104     VP9BSFContext *s = ctx->priv_data;
105     AVPacket *in;
106     int res, invisible, profile, marker, uses_superframe_syntax = 0, n;
107
108     res = ff_bsf_get_packet(ctx, &in);
109     if (res < 0)
110         return res;
111
112     marker = in->data[in->size - 1];
113     if ((marker & 0xe0) == 0xc0) {
114         int nbytes = 1 + ((marker >> 3) & 0x3);
115         int n_frames = 1 + (marker & 0x7), idx_sz = 2 + n_frames * nbytes;
116
117         uses_superframe_syntax = in->size >= idx_sz && in->data[in->size - idx_sz] == marker;
118     }
119
120     if ((res = init_get_bits8(&gb, in->data, in->size)) < 0)
121         goto done;
122
123     get_bits(&gb, 2); // frame marker
124     profile  = get_bits1(&gb);
125     profile |= get_bits1(&gb) << 1;
126     if (profile == 3) profile += get_bits1(&gb);
127
128     if (get_bits1(&gb)) {
129         invisible = 0;
130     } else {
131         get_bits1(&gb); // keyframe
132         invisible = !get_bits1(&gb);
133     }
134
135     if (uses_superframe_syntax && s->n_cache > 0) {
136         av_log(ctx, AV_LOG_ERROR,
137                "Mixing of superframe syntax and naked VP9 frames not supported");
138         res = AVERROR_INVALIDDATA;
139         goto done;
140     } else if ((!invisible || uses_superframe_syntax) && !s->n_cache) {
141         // passthrough
142         av_packet_move_ref(out, in);
143         goto done;
144     } else if (s->n_cache + 1 >= MAX_CACHE) {
145         av_log(ctx, AV_LOG_ERROR,
146                "Too many invisible frames");
147         res = AVERROR_INVALIDDATA;
148         goto done;
149     }
150
151     s->cache[s->n_cache].size = in->size;
152     if (invisible && !uses_superframe_syntax) {
153         s->cache[s->n_cache].data = av_malloc(in->size);
154         if (!s->cache[s->n_cache].data) {
155             res = AVERROR(ENOMEM);
156             goto done;
157         }
158         memcpy(s->cache[s->n_cache++].data, in->data, in->size);
159         res = AVERROR(EAGAIN);
160         goto done;
161     }
162     av_assert0(s->n_cache > 0);
163
164     s->cache[s->n_cache].data = in->data;
165
166     // build superframe
167     if ((res = merge_superframe(s->cache, s->n_cache + 1, out)) < 0)
168         goto done;
169
170     for (n = 0; n < s->n_cache; n++)
171         av_freep(&s->cache[n].data);
172     s->n_cache = 0;
173
174     res = av_packet_copy_props(out, in);
175     if (res < 0)
176         goto done;
177
178 done:
179     if (res < 0)
180         av_packet_unref(out);
181     av_packet_free(&in);
182     return res;
183 }
184
185 static void vp9_superframe_close(AVBSFContext *ctx)
186 {
187     VP9BSFContext *s = ctx->priv_data;
188     int n;
189
190     // free cached data
191     for (n = 0; n < s->n_cache; n++)
192         av_freep(&s->cache[n].data);
193 }
194
195 static const enum AVCodecID codec_ids[] = {
196     AV_CODEC_ID_VP9, AV_CODEC_ID_NONE,
197 };
198
199 const AVBitStreamFilter ff_vp9_superframe_bsf = {
200     .name           = "vp9_superframe",
201     .priv_data_size = sizeof(VP9BSFContext),
202     .filter         = vp9_superframe_filter,
203     .close          = vp9_superframe_close,
204     .codec_ids      = codec_ids,
205 };