mirror of https://github.com/FFmpeg/FFmpeg.git
parent
f64d1100a5
commit
0cf949a011
5 changed files with 214 additions and 0 deletions
@ -0,0 +1,207 @@ |
||||
/*
|
||||
* VP9 invisible (alt-ref) frame to superframe merge bitstream filter |
||||
* Copyright (c) 2016 Ronald S. Bultje <rsbultje@gmail.com> |
||||
* |
||||
* This file is part of Libav. |
||||
* |
||||
* Libav is free software; you can redistribute it and/or |
||||
* modify it under the terms of the GNU Lesser General Public |
||||
* License as published by the Free Software Foundation; either |
||||
* version 2.1 of the License, or (at your option) any later version. |
||||
* |
||||
* Libav is distributed in the hope that it will be useful, |
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
||||
* Lesser General Public License for more details. |
||||
* |
||||
* You should have received a copy of the GNU Lesser General Public |
||||
* License along with Libav; if not, write to the Free Software |
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
||||
*/ |
||||
|
||||
#include "libavutil/avassert.h" |
||||
#include "avcodec.h" |
||||
#include "bsf.h" |
||||
#include "get_bits.h" |
||||
|
||||
#define MAX_CACHE 8 |
||||
typedef struct VP9BSFContext { |
||||
int n_cache; |
||||
struct CachedBuf { |
||||
uint8_t *data; |
||||
int size; |
||||
} cache[MAX_CACHE]; |
||||
} VP9BSFContext; |
||||
|
||||
static void stats(const struct CachedBuf *in, int n_in, |
||||
unsigned *_max, unsigned *_sum) |
||||
{ |
||||
int n; |
||||
unsigned max = 0, sum = 0; |
||||
|
||||
for (n = 0; n < n_in; n++) { |
||||
unsigned sz = in[n].size; |
||||
|
||||
if (sz > max) |
||||
max = sz; |
||||
sum += sz; |
||||
} |
||||
|
||||
*_max = max; |
||||
*_sum = sum; |
||||
} |
||||
|
||||
static int merge_superframe(const struct CachedBuf *in, int n_in, AVPacket *out) |
||||
{ |
||||
unsigned max, sum, mag, marker, n, sz; |
||||
uint8_t *ptr; |
||||
int res; |
||||
|
||||
stats(in, n_in, &max, &sum); |
||||
mag = av_log2(max) >> 3; |
||||
marker = 0xC0 + (mag << 3) + (n_in - 1); |
||||
sz = sum + 2 + (mag + 1) * n_in; |
||||
res = av_new_packet(out, sz); |
||||
if (res < 0) |
||||
return res; |
||||
ptr = out->data; |
||||
for (n = 0; n < n_in; n++) { |
||||
memcpy(ptr, in[n].data, in[n].size); |
||||
ptr += in[n].size; |
||||
} |
||||
|
||||
#define wloop(mag, wr) do { \ |
||||
for (n = 0; n < n_in; n++) { \
|
||||
wr; \
|
||||
ptr += mag + 1; \
|
||||
} \
|
||||
} while (0) |
||||
|
||||
// write superframe with marker 110[mag:2][nframes:3]
|
||||
*ptr++ = marker; |
||||
switch (mag) { |
||||
case 0: |
||||
wloop(mag, *ptr = in[n].size); |
||||
break; |
||||
case 1: |
||||
wloop(mag, AV_WL16(ptr, in[n].size)); |
||||
break; |
||||
case 2: |
||||
wloop(mag, AV_WL24(ptr, in[n].size)); |
||||
break; |
||||
case 3: |
||||
wloop(mag, AV_WL32(ptr, in[n].size)); |
||||
break; |
||||
} |
||||
*ptr++ = marker; |
||||
av_assert0(ptr == &out->data[out->size]); |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int vp9_superframe_filter(AVBSFContext *ctx, AVPacket *out) |
||||
{ |
||||
GetBitContext gb; |
||||
VP9BSFContext *s = ctx->priv_data; |
||||
AVPacket *in; |
||||
int res, invisible, profile, marker, uses_superframe_syntax = 0, n; |
||||
|
||||
res = ff_bsf_get_packet(ctx, &in); |
||||
if (res < 0) |
||||
return res; |
||||
|
||||
marker = in->data[in->size - 1]; |
||||
if ((marker & 0xe0) == 0xc0) { |
||||
int nbytes = 1 + ((marker >> 3) & 0x3); |
||||
int n_frames = 1 + (marker & 0x7), idx_sz = 2 + n_frames * nbytes; |
||||
|
||||
uses_superframe_syntax = in->size >= idx_sz && in->data[in->size - idx_sz] == marker; |
||||
} |
||||
|
||||
if ((res = init_get_bits8(&gb, in->data, in->size)) < 0) |
||||
goto done; |
||||
|
||||
get_bits(&gb, 2); // frame marker
|
||||
profile = get_bits1(&gb); |
||||
profile |= get_bits1(&gb) << 1; |
||||
if (profile == 3) profile += get_bits1(&gb); |
||||
|
||||
if (get_bits1(&gb)) { |
||||
invisible = 0; |
||||
} else { |
||||
get_bits1(&gb); // keyframe
|
||||
invisible = !get_bits1(&gb); |
||||
} |
||||
|
||||
if (uses_superframe_syntax && s->n_cache > 0) { |
||||
av_log(ctx, AV_LOG_ERROR, |
||||
"Mixing of superframe syntax and naked VP9 frames not supported"); |
||||
res = AVERROR(ENOSYS); |
||||
goto done; |
||||
} else if ((!invisible || uses_superframe_syntax) && !s->n_cache) { |
||||
// passthrough
|
||||
av_packet_move_ref(out, in); |
||||
goto done; |
||||
} else if (s->n_cache + 1 >= MAX_CACHE) { |
||||
av_log(ctx, AV_LOG_ERROR, |
||||
"Too many invisible frames"); |
||||
res = AVERROR_INVALIDDATA; |
||||
goto done; |
||||
} |
||||
|
||||
if (invisible) { |
||||
s->cache[s->n_cache].data = av_malloc(in->size); |
||||
if (!s->cache[s->n_cache].data) { |
||||
res = AVERROR(ENOMEM); |
||||
goto done; |
||||
} |
||||
memcpy(s->cache[s->n_cache].data, in->data, in->size); |
||||
s->cache[s->n_cache++].size = in->size; |
||||
res = AVERROR(EAGAIN); |
||||
goto done; |
||||
} |
||||
av_assert0(s->n_cache > 0); |
||||
|
||||
s->cache[s->n_cache].data = in->data; |
||||
s->cache[s->n_cache].size = in->size; |
||||
|
||||
// build superframe
|
||||
if ((res = merge_superframe(s->cache, s->n_cache + 1, out)) < 0) |
||||
goto done; |
||||
|
||||
for (n = 0; n < s->n_cache; n++) |
||||
av_freep(&s->cache[n].data); |
||||
s->n_cache = 0; |
||||
|
||||
res = av_packet_copy_props(out, in); |
||||
if (res < 0) |
||||
goto done; |
||||
|
||||
done: |
||||
if (res < 0) |
||||
av_packet_unref(out); |
||||
av_packet_free(&in); |
||||
return res; |
||||
} |
||||
|
||||
static void vp9_superframe_close(AVBSFContext *ctx) |
||||
{ |
||||
VP9BSFContext *s = ctx->priv_data; |
||||
int n; |
||||
|
||||
// free cached data
|
||||
for (n = 0; n < s->n_cache; n++) |
||||
av_freep(&s->cache[n].data); |
||||
} |
||||
|
||||
static const enum AVCodecID codec_ids[] = { |
||||
AV_CODEC_ID_VP9, AV_CODEC_ID_NONE, |
||||
}; |
||||
|
||||
const AVBitStreamFilter ff_vp9_superframe_bsf = { |
||||
.name = "vp9_superframe", |
||||
.priv_data_size = sizeof(VP9BSFContext), |
||||
.filter = vp9_superframe_filter, |
||||
.close = vp9_superframe_close, |
||||
.codec_ids = codec_ids, |
||||
}; |
Loading…
Reference in new issue