-/**
+/*
* VP8 compatible video decoder
*
* Copyright (C) 2010 David Conrad
#include "libavutil/imgutils.h"
#include "avcodec.h"
+#include "internal.h"
#include "vp8.h"
#include "vp8data.h"
#include "rectangle.h"
int ret;
if ((ret = ff_thread_get_buffer(s->avctx, f)) < 0)
return ret;
- if (!s->maps_are_invalid && s->num_maps_to_be_freed) {
+ if (s->num_maps_to_be_freed && !s->maps_are_invalid) {
f->ref_index[0] = s->segmentation_maps[--s->num_maps_to_be_freed];
} else if (!(f->ref_index[0] = av_mallocz(s->mb_width * s->mb_height))) {
ff_thread_release_buffer(s->avctx, f);
return 0;
}
-static void vp8_release_frame(VP8Context *s, AVFrame *f, int is_close)
+static void vp8_release_frame(VP8Context *s, AVFrame *f, int prefer_delayed_free, int can_direct_free)
{
- if (!is_close) {
- if (f->ref_index[0]) {
- assert(s->num_maps_to_be_freed < FF_ARRAY_ELEMS(s->segmentation_maps));
- s->segmentation_maps[s->num_maps_to_be_freed++] = f->ref_index[0];
+ if (f->ref_index[0]) {
+ if (prefer_delayed_free) {
+ /* Upon a size change, we want to free the maps but other threads may still
+ * be using them, so queue them. Upon a seek, all threads are inactive so
+ * we want to cache one to prevent re-allocation in the next decoding
+ * iteration, but the rest we can free directly. */
+ int max_queued_maps = can_direct_free ? 1 : FF_ARRAY_ELEMS(s->segmentation_maps);
+ if (s->num_maps_to_be_freed < max_queued_maps) {
+ s->segmentation_maps[s->num_maps_to_be_freed++] = f->ref_index[0];
+ } else if (can_direct_free) /* vp8_decode_flush(), but our queue is full */ {
+ av_free(f->ref_index[0]);
+ } /* else: MEMLEAK (should never happen, but better that than crash) */
f->ref_index[0] = NULL;
+ } else /* vp8_decode_free() */ {
+ av_free(f->ref_index[0]);
}
- } else {
- av_freep(&f->ref_index[0]);
}
ff_thread_release_buffer(s->avctx, f);
}
-static void vp8_decode_flush_impl(AVCodecContext *avctx, int force, int is_close)
+static void vp8_decode_flush_impl(AVCodecContext *avctx,
+ int prefer_delayed_free, int can_direct_free, int free_mem)
{
VP8Context *s = avctx->priv_data;
int i;
- if (!avctx->is_copy || force) {
+ if (!avctx->internal->is_copy) {
for (i = 0; i < 5; i++)
if (s->frames[i].data[0])
- vp8_release_frame(s, &s->frames[i], is_close);
+ vp8_release_frame(s, &s->frames[i], prefer_delayed_free, can_direct_free);
}
memset(s->framep, 0, sizeof(s->framep));
- free_buffers(s);
- s->maps_are_invalid = 1;
+ if (free_mem) {
+ free_buffers(s);
+ s->maps_are_invalid = 1;
+ }
}
static void vp8_decode_flush(AVCodecContext *avctx)
{
- vp8_decode_flush_impl(avctx, 0, 0);
+ vp8_decode_flush_impl(avctx, 1, 1, 0);
}
static int update_dimensions(VP8Context *s, int width, int height)
if (av_image_check_size(width, height, 0, s->avctx))
return AVERROR_INVALIDDATA;
- vp8_decode_flush_impl(s->avctx, 1, 0);
+ vp8_decode_flush_impl(s->avctx, 1, 0, 1);
avcodec_set_dimensions(s->avctx, width, height);
}
VP56RangeCoder *c = &s->c;
int i;
- for (i = 0; i < 4; i++)
- s->lf_delta.ref[i] = vp8_rac_get_sint(c, 6);
+ for (i = 0; i < 4; i++) {
+ if (vp8_rac_get(c)) {
+ s->lf_delta.ref[i] = vp8_rac_get_uint(c, 6);
+
+ if (vp8_rac_get(c))
+ s->lf_delta.ref[i] = -s->lf_delta.ref[i];
+ }
+ }
+
+ for (i = MODE_I4x4; i <= VP8_MVMODE_SPLIT; i++) {
+ if (vp8_rac_get(c)) {
+ s->lf_delta.mode[i] = vp8_rac_get_uint(c, 6);
- for (i = MODE_I4x4; i <= VP8_MVMODE_SPLIT; i++)
- s->lf_delta.mode[i] = vp8_rac_get_sint(c, 6);
+ if (vp8_rac_get(c))
+ s->lf_delta.mode[i] = -s->lf_delta.mode[i];
+ }
+ }
}
static int setup_partitions(VP8Context *s, const uint8_t *buf, int buf_size)
if (s->segmentation.update_map)
*segment = vp8_rac_get_tree(c, vp8_segmentid_tree, s->prob->segmentid);
- else
+ else if (s->segmentation.enabled)
*segment = ref ? *ref : *segment;
s->segment = *segment;
VP8Context *s = avctx->priv_data;
int ret, mb_x, mb_y, i, y, referenced;
enum AVDiscard skip_thresh;
- AVFrame *av_uninit(curframe), *prev_frame = s->framep[VP56_FRAME_CURRENT];
+ AVFrame *av_uninit(curframe), *prev_frame;
release_queued_segmaps(s, 0);
if ((ret = decode_frame_header(s, avpkt->data, avpkt->size)) < 0)
- return ret;
+ goto err;
+
+ prev_frame = s->framep[VP56_FRAME_CURRENT];
referenced = s->update_last || s->update_golden == VP56_FRAME_CURRENT
|| s->update_altref == VP56_FRAME_CURRENT;
if (avctx->skip_frame >= skip_thresh) {
s->invisible = 1;
+ memcpy(&s->next_framep[0], &s->framep[0], sizeof(s->framep[0]) * 4);
goto skip_decode;
}
s->deblock_filter = s->filter.level && avctx->skip_loop_filter < skip_thresh;
&s->frames[i] != s->framep[VP56_FRAME_PREVIOUS] &&
&s->frames[i] != s->framep[VP56_FRAME_GOLDEN] &&
&s->frames[i] != s->framep[VP56_FRAME_GOLDEN2])
- vp8_release_frame(s, &s->frames[i], 0);
+ vp8_release_frame(s, &s->frames[i], 1, 0);
// find a free buffer
for (i = 0; i < 5; i++)
abort();
}
if (curframe->data[0])
- ff_thread_release_buffer(avctx, curframe);
+ vp8_release_frame(s, curframe, 1, 0);
+
+ // Given that arithmetic probabilities are updated every frame, it's quite likely
+ // that the values we have on a random interframe are complete junk if we didn't
+ // start decode on a keyframe. So just don't display anything rather than junk.
+ if (!s->keyframe && (!s->framep[VP56_FRAME_PREVIOUS] ||
+ !s->framep[VP56_FRAME_GOLDEN] ||
+ !s->framep[VP56_FRAME_GOLDEN2])) {
+ av_log(avctx, AV_LOG_WARNING, "Discarding interframe without a prior keyframe!\n");
+ ret = AVERROR_INVALIDDATA;
+ goto err;
+ }
curframe->key_frame = s->keyframe;
curframe->pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
curframe->reference = referenced ? 3 : 0;
if ((ret = vp8_alloc_frame(s, curframe))) {
av_log(avctx, AV_LOG_ERROR, "get_buffer() failed!\n");
- return ret;
+ goto err;
}
// check if golden and altref are swapped
ff_thread_finish_setup(avctx);
- // Given that arithmetic probabilities are updated every frame, it's quite likely
- // that the values we have on a random interframe are complete junk if we didn't
- // start decode on a keyframe. So just don't display anything rather than junk.
- if (!s->keyframe && (!s->framep[VP56_FRAME_PREVIOUS] ||
- !s->framep[VP56_FRAME_GOLDEN] ||
- !s->framep[VP56_FRAME_GOLDEN2])) {
- av_log(avctx, AV_LOG_WARNING, "Discarding interframe without a prior keyframe!\n");
- return AVERROR_INVALIDDATA;
- }
-
s->linesize = curframe->linesize[0];
s->uvlinesize = curframe->linesize[1];
}
ff_thread_report_progress(curframe, INT_MAX, 0);
+ memcpy(&s->framep[0], &s->next_framep[0], sizeof(s->framep[0]) * 4);
+
skip_decode:
// if future frames don't use the updated probabilities,
// reset them to the values we saved
if (!s->update_probabilities)
s->prob[0] = s->prob[1];
- memcpy(&s->framep[0], &s->next_framep[0], sizeof(s->framep[0]) * 4);
-
if (!s->invisible) {
*(AVFrame*)data = *curframe;
*data_size = sizeof(AVFrame);
}
return avpkt->size;
+err:
+ memcpy(&s->next_framep[0], &s->framep[0], sizeof(s->framep[0]) * 4);
+ return ret;
}
static av_cold int vp8_decode_init(AVCodecContext *avctx)
s->avctx = avctx;
avctx->pix_fmt = PIX_FMT_YUV420P;
- dsputil_init(&s->dsp, avctx);
- ff_h264_pred_init(&s->hpc, CODEC_ID_VP8, 8);
+ ff_dsputil_init(&s->dsp, avctx);
+ ff_h264_pred_init(&s->hpc, CODEC_ID_VP8, 8, 1);
ff_vp8dsp_init(&s->vp8dsp);
return 0;
static av_cold int vp8_decode_free(AVCodecContext *avctx)
{
- vp8_decode_flush_impl(avctx, 0, 1);
+ vp8_decode_flush_impl(avctx, 0, 1, 1);
release_queued_segmaps(avctx->priv_data, 1);
return 0;
}
if (s->macroblocks_base &&
(s_src->mb_width != s->mb_width || s_src->mb_height != s->mb_height)) {
free_buffers(s);
+ s->maps_are_invalid = 1;
+ s->mb_width = s_src->mb_width;
+ s->mb_height = s_src->mb_height;
}
s->prob[0] = s_src->prob[!s_src->update_probabilities];
}
AVCodec ff_vp8_decoder = {
- .name = "vp8",
- .type = AVMEDIA_TYPE_VIDEO,
- .id = CODEC_ID_VP8,
- .priv_data_size = sizeof(VP8Context),
- .init = vp8_decode_init,
- .close = vp8_decode_free,
- .decode = vp8_decode_frame,
- .capabilities = CODEC_CAP_DR1 | CODEC_CAP_FRAME_THREADS,
- .flush = vp8_decode_flush,
- .long_name = NULL_IF_CONFIG_SMALL("On2 VP8"),
+ .name = "vp8",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = CODEC_ID_VP8,
+ .priv_data_size = sizeof(VP8Context),
+ .init = vp8_decode_init,
+ .close = vp8_decode_free,
+ .decode = vp8_decode_frame,
+ .capabilities = CODEC_CAP_DR1 | CODEC_CAP_FRAME_THREADS,
+ .flush = vp8_decode_flush,
+ .long_name = NULL_IF_CONFIG_SMALL("On2 VP8"),
.init_thread_copy = ONLY_IF_THREADS_ENABLED(vp8_decode_init_thread_copy),
.update_thread_context = ONLY_IF_THREADS_ENABLED(vp8_decode_update_thread_context),
};