/*
* Copyright (C) 2003-2004 the ffmpeg project
*
- * This file is part of FFmpeg.
+ * This file is part of Libav.
*
- * FFmpeg is free software; you can redistribute it and/or
+ * 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.
*
- * FFmpeg is distributed in the hope that it will be useful,
+ * 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 FFmpeg; if not, write to the Free Software
+ * License along with Libav; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
#include <string.h>
-#include "libavcore/imgutils.h"
+#include "libavutil/imgutils.h"
#include "avcodec.h"
#include "dsputil.h"
#include "get_bits.h"
#include "vp3data.h"
#include "xiph.h"
+#include "thread.h"
#define FRAGMENT_PIXELS 8
static av_cold int vp3_decode_end(AVCodecContext *avctx);
+static void vp3_decode_flush(AVCodecContext *avctx);
//FIXME split things out into their own arrays
typedef struct Vp3Fragment {
/* these arrays need to be on 16-byte boundaries since SSE2 operations
* index into them */
- DECLARE_ALIGNED(16, int16_t, qmat)[3][2][3][64]; //<qmat[qpi][is_inter][plane]
+ DECLARE_ALIGNED(16, int16_t, qmat)[3][2][3][64]; ///< qmat[qpi][is_inter][plane]
/* This table contains superblock_count * 16 entries. Each set of 16
* numbers corresponds to the fragment indexes 0..15 of the superblock.
* is coded. */
unsigned char *macroblock_coding;
- uint8_t edge_emu_buffer[9*2048]; //FIXME dynamic alloc
- int8_t qscale_table[2048]; //FIXME dynamic alloc (width+15)/16
+ uint8_t *edge_emu_buffer;
/* Huffman decode */
int hti;
s->qmat[qpi][inter][plane][0] = s->qmat[0][inter][plane][0];
}
}
-
- memset(s->qscale_table, (FFMAX(s->qmat[0][0][0][1], s->qmat[0][0][1][1])+8)/16, 512); //FIXME finetune
}
/*
/* decode a VLC into a token */
token = get_vlc2(gb, vlc_table, 11, 3);
/* use the token to get a zero run, a coefficient, and an eob run */
- if (token <= 6) {
+ if ((unsigned) token <= 6U) {
eob_run = eob_run_base[token];
if (eob_run_get_bits[token])
eob_run += get_bits(gb, eob_run_get_bits[token]);
coeff_i += eob_run;
eob_run = 0;
}
- } else {
+ } else if (token >= 0) {
bits_to_get = coeff_get_bits[token];
if (bits_to_get)
bits_to_get = get_bits(gb, bits_to_get);
for (i = coeff_index+1; i <= coeff_index+zero_run; i++)
s->num_coded_frags[plane][i]--;
coeff_i++;
+ } else {
+ av_log(s->avctx, AV_LOG_ERROR,
+ "Invalid token %d\n", token);
+ return -1;
}
}
/* unpack the Y plane DC coefficients */
residual_eob_run = unpack_vlcs(s, gb, &s->dc_vlc[dc_y_table], 0,
0, residual_eob_run);
+ if (residual_eob_run < 0)
+ return residual_eob_run;
/* reverse prediction of the Y-plane DC coefficients */
reverse_dc_prediction(s, 0, s->fragment_width[0], s->fragment_height[0]);
/* unpack the C plane DC coefficients */
residual_eob_run = unpack_vlcs(s, gb, &s->dc_vlc[dc_c_table], 0,
1, residual_eob_run);
+ if (residual_eob_run < 0)
+ return residual_eob_run;
residual_eob_run = unpack_vlcs(s, gb, &s->dc_vlc[dc_c_table], 0,
2, residual_eob_run);
+ if (residual_eob_run < 0)
+ return residual_eob_run;
/* reverse prediction of the C-plane DC coefficients */
if (!(s->avctx->flags & CODEC_FLAG_GRAY))
for (i = 1; i <= 63; i++) {
residual_eob_run = unpack_vlcs(s, gb, y_tables[i], i,
0, residual_eob_run);
+ if (residual_eob_run < 0)
+ return residual_eob_run;
residual_eob_run = unpack_vlcs(s, gb, c_tables[i], i,
1, residual_eob_run);
+ if (residual_eob_run < 0)
+ return residual_eob_run;
residual_eob_run = unpack_vlcs(s, gb, c_tables[i], i,
2, residual_eob_run);
+ if (residual_eob_run < 0)
+ return residual_eob_run;
}
return 0;
int h, cy;
int offset[4];
+ if (HAVE_THREADS && s->avctx->active_thread_type&FF_THREAD_FRAME) {
+ int y_flipped = s->flipped_image ? s->avctx->height-y : y;
+
+ // At the end of the frame, report INT_MAX instead of the height of the frame.
+ // This makes the other threads' ff_thread_await_progress() calls cheaper, because
+ // they don't have to clip their values.
+ ff_thread_report_progress(&s->current_frame, y_flipped==s->avctx->height ? INT_MAX : y_flipped-1, 0);
+ }
+
if(s->avctx->draw_horiz_band==NULL)
return;
s->avctx->draw_horiz_band(s->avctx, &s->current_frame, offset, y, 3, h);
}
+/**
+ * Wait for the reference frame of the current fragment.
+ * The progress value is in luma pixel rows.
+ */
+static void await_reference_row(Vp3DecodeContext *s, Vp3Fragment *fragment, int motion_y, int y)
+{
+ AVFrame *ref_frame;
+ int ref_row;
+ int border = motion_y&1;
+
+ if (fragment->coding_method == MODE_USING_GOLDEN ||
+ fragment->coding_method == MODE_GOLDEN_MV)
+ ref_frame = &s->golden_frame;
+ else
+ ref_frame = &s->last_frame;
+
+ ref_row = y + (motion_y>>1);
+ ref_row = FFMAX(FFABS(ref_row), ref_row + 8 + border);
+
+ ff_thread_await_progress(ref_frame, ref_row, 0);
+}
+
/*
* Perform the final rendering for a particular slice of data.
* The slice number ranges from 0..(c_superblock_height - 1).
*/
static void render_slice(Vp3DecodeContext *s, int slice)
{
- int x, y, i, j;
+ int x, y, i, j, fragment;
LOCAL_ALIGNED_16(DCTELEM, block, [64]);
int motion_x = 0xdeadbeef, motion_y = 0xdeadbeef;
int motion_halfpel_index;
int fragment_width = s->fragment_width[!!plane];
int fragment_height = s->fragment_height[!!plane];
int fragment_start = s->fragment_start[plane];
+ int do_await = !plane && HAVE_THREADS && (s->avctx->active_thread_type&FF_THREAD_FRAME);
if (!s->flipped_image) stride = -stride;
if (CONFIG_GRAY && plane && (s->avctx->flags & CODEC_FLAG_GRAY))
continue;
-
- if(FFABS(stride) > 2048)
- return; //various tables are fixed size
-
/* for each superblock row in the slice (both of them)... */
for (; sb_y < slice_height; sb_y++) {
for (j = 0; j < 16; j++) {
x = 4*sb_x + hilbert_offset[j][0];
y = 4*sb_y + hilbert_offset[j][1];
+ fragment = y*fragment_width + x;
- i = fragment_start + y*fragment_width + x;
+ i = fragment_start + fragment;
// bounds check
if (x >= fragment_width || y >= fragment_height)
first_pixel = 8*y*stride + 8*x;
+ if (do_await && s->all_fragments[i].coding_method != MODE_INTRA)
+ await_reference_row(s, &s->all_fragments[i], motion_val[fragment][1], (16*y) >> s->chroma_y_shift);
+
/* transform if this block was coded */
if (s->all_fragments[i].coding_method != MODE_COPY) {
if ((s->all_fragments[i].coding_method == MODE_USING_GOLDEN) ||
if ((s->all_fragments[i].coding_method > MODE_INTRA) &&
(s->all_fragments[i].coding_method != MODE_USING_GOLDEN)) {
int src_x, src_y;
- motion_x = motion_val[y*fragment_width + x][0];
- motion_y = motion_val[y*fragment_width + x][1];
+ motion_x = motion_val[fragment][0];
+ motion_y = motion_val[fragment][1];
src_x= (motion_x>>1) + 8*x;
src_y= (motion_y>>1) + 8*y;
if(src_x<0 || src_y<0 || src_x + 9 >= plane_width || src_y + 9 >= plane_height){
uint8_t *temp= s->edge_emu_buffer;
- if(stride<0) temp -= 9*stride;
- else temp += 9*stride;
+ if(stride<0) temp -= 8*stride;
- ff_emulated_edge_mc(temp, motion_source, stride, 9, 9, src_x, src_y, plane_width, plane_height);
+ s->dsp.emulated_edge_mc(temp, motion_source, stride, 9, 9, src_x, src_y, plane_width, plane_height);
motion_source= temp;
}
}
vp3_draw_horiz_band(s, FFMIN((32 << s->chroma_y_shift) * (slice + 1) -16, s->height-16));
}
-/*
- * This is the ffmpeg/libavcodec API init function.
- */
+/// Allocate tables for per-frame data in Vp3DecodeContext
+static av_cold int allocate_tables(AVCodecContext *avctx)
+{
+ Vp3DecodeContext *s = avctx->priv_data;
+ int y_fragment_count, c_fragment_count;
+
+ y_fragment_count = s->fragment_width[0] * s->fragment_height[0];
+ c_fragment_count = s->fragment_width[1] * s->fragment_height[1];
+
+ s->superblock_coding = av_malloc(s->superblock_count);
+ s->all_fragments = av_malloc(s->fragment_count * sizeof(Vp3Fragment));
+ s->coded_fragment_list[0] = av_malloc(s->fragment_count * sizeof(int));
+ s->dct_tokens_base = av_malloc(64*s->fragment_count * sizeof(*s->dct_tokens_base));
+ s->motion_val[0] = av_malloc(y_fragment_count * sizeof(*s->motion_val[0]));
+ s->motion_val[1] = av_malloc(c_fragment_count * sizeof(*s->motion_val[1]));
+
+ /* work out the block mapping tables */
+ s->superblock_fragments = av_malloc(s->superblock_count * 16 * sizeof(int));
+ s->macroblock_coding = av_malloc(s->macroblock_count + 1);
+
+ if (!s->superblock_coding || !s->all_fragments || !s->dct_tokens_base ||
+ !s->coded_fragment_list[0] || !s->superblock_fragments || !s->macroblock_coding ||
+ !s->motion_val[0] || !s->motion_val[1]) {
+ vp3_decode_end(avctx);
+ return -1;
+ }
+
+ init_block_mapping(s);
+
+ return 0;
+}
+
static av_cold int vp3_decode_init(AVCodecContext *avctx)
{
Vp3DecodeContext *s = avctx->priv_data;
s->superblock_count = s->y_superblock_count + (s->c_superblock_count * 2);
s->u_superblock_start = s->y_superblock_count;
s->v_superblock_start = s->u_superblock_start + s->c_superblock_count;
- s->superblock_coding = av_malloc(s->superblock_count);
s->macroblock_width = (s->width + 15) / 16;
s->macroblock_height = (s->height + 15) / 16;
s->fragment_start[1] = y_fragment_count;
s->fragment_start[2] = y_fragment_count + c_fragment_count;
- s->all_fragments = av_malloc(s->fragment_count * sizeof(Vp3Fragment));
- s->coded_fragment_list[0] = av_malloc(s->fragment_count * sizeof(int));
- s->dct_tokens_base = av_malloc(64*s->fragment_count * sizeof(*s->dct_tokens_base));
- s->motion_val[0] = av_malloc(y_fragment_count * sizeof(*s->motion_val[0]));
- s->motion_val[1] = av_malloc(c_fragment_count * sizeof(*s->motion_val[1]));
-
- if (!s->superblock_coding || !s->all_fragments || !s->dct_tokens_base ||
- !s->coded_fragment_list[0] || !s->motion_val[0] || !s->motion_val[1]) {
- vp3_decode_end(avctx);
- return -1;
- }
-
if (!s->theora_tables)
{
for (i = 0; i < 64; i++) {
&motion_vector_vlc_table[0][1], 2, 1,
&motion_vector_vlc_table[0][0], 2, 1, 0);
- /* work out the block mapping tables */
- s->superblock_fragments = av_malloc(s->superblock_count * 16 * sizeof(int));
- s->macroblock_coding = av_malloc(s->macroblock_count + 1);
- if (!s->superblock_fragments || !s->macroblock_coding) {
- vp3_decode_end(avctx);
- return -1;
- }
- init_block_mapping(s);
-
for (i = 0; i < 3; i++) {
s->current_frame.data[i] = NULL;
s->last_frame.data[i] = NULL;
s->golden_frame.data[i] = NULL;
}
- return 0;
+ return allocate_tables(avctx);
vlc_fail:
av_log(avctx, AV_LOG_FATAL, "Invalid huffman table\n");
return -1;
}
-/*
- * This is the ffmpeg/libavcodec API frame decode function.
- */
+/// Release and shuffle frames after decode finishes
+static void update_frames(AVCodecContext *avctx)
+{
+ Vp3DecodeContext *s = avctx->priv_data;
+
+ /* release the last frame, if it is allocated and if it is not the
+ * golden frame */
+ if (s->last_frame.data[0] && s->last_frame.type != FF_BUFFER_TYPE_COPY)
+ ff_thread_release_buffer(avctx, &s->last_frame);
+
+ /* shuffle frames (last = current) */
+ s->last_frame= s->current_frame;
+
+ if (s->keyframe) {
+ if (s->golden_frame.data[0])
+ ff_thread_release_buffer(avctx, &s->golden_frame);
+ s->golden_frame = s->current_frame;
+ s->last_frame.type = FF_BUFFER_TYPE_COPY;
+ }
+
+ s->current_frame.data[0]= NULL; /* ensure that we catch any access to this released frame */
+}
+
+static int vp3_update_thread_context(AVCodecContext *dst, const AVCodecContext *src)
+{
+ Vp3DecodeContext *s = dst->priv_data, *s1 = src->priv_data;
+ int qps_changed = 0, i, err;
+
+#define copy_fields(to, from, start_field, end_field) memcpy(&to->start_field, &from->start_field, (char*)&to->end_field - (char*)&to->start_field)
+
+ if (!s1->current_frame.data[0]
+ ||s->width != s1->width
+ ||s->height!= s1->height) {
+ if (s != s1)
+ copy_fields(s, s1, golden_frame, current_frame);
+ return -1;
+ }
+
+ if (s != s1) {
+ // init tables if the first frame hasn't been decoded
+ if (!s->current_frame.data[0]) {
+ int y_fragment_count, c_fragment_count;
+ s->avctx = dst;
+ err = allocate_tables(dst);
+ if (err)
+ return err;
+ y_fragment_count = s->fragment_width[0] * s->fragment_height[0];
+ c_fragment_count = s->fragment_width[1] * s->fragment_height[1];
+ memcpy(s->motion_val[0], s1->motion_val[0], y_fragment_count * sizeof(*s->motion_val[0]));
+ memcpy(s->motion_val[1], s1->motion_val[1], c_fragment_count * sizeof(*s->motion_val[1]));
+ }
+
+ // copy previous frame data
+ copy_fields(s, s1, golden_frame, dsp);
+
+ // copy qscale data if necessary
+ for (i = 0; i < 3; i++) {
+ if (s->qps[i] != s1->qps[1]) {
+ qps_changed = 1;
+ memcpy(&s->qmat[i], &s1->qmat[i], sizeof(s->qmat[i]));
+ }
+ }
+
+ if (s->qps[0] != s1->qps[0])
+ memcpy(&s->bounding_values_array, &s1->bounding_values_array, sizeof(s->bounding_values_array));
+
+ if (qps_changed)
+ copy_fields(s, s1, qps, superblock_count);
+#undef copy_fields
+ }
+
+ update_frames(dst);
+
+ return 0;
+}
+
static int vp3_decode_frame(AVCodecContext *avctx,
void *data, int *data_size,
AVPacket *avpkt)
int buf_size = avpkt->size;
Vp3DecodeContext *s = avctx->priv_data;
GetBitContext gb;
- static int counter = 0;
int i;
init_get_bits(&gb, buf, buf_size * 8);
if (s->avctx->debug & FF_DEBUG_PICT_INFO)
av_log(s->avctx, AV_LOG_INFO, " VP3 %sframe #%d: Q index = %d\n",
- s->keyframe?"key":"", counter, s->qps[0]);
- counter++;
+ s->keyframe?"key":"", avctx->frame_number+1, s->qps[0]);
s->skip_loop_filter = !s->filter_limit_values[s->qps[0]] ||
avctx->skip_loop_filter >= (s->keyframe ? AVDISCARD_ALL : AVDISCARD_NONKEY);
return buf_size;
s->current_frame.reference = 3;
- s->current_frame.pict_type = s->keyframe ? FF_I_TYPE : FF_P_TYPE;
- if (avctx->get_buffer(avctx, &s->current_frame) < 0) {
+ s->current_frame.pict_type = s->keyframe ? AV_PICTURE_TYPE_I : AV_PICTURE_TYPE_P;
+ if (ff_thread_get_buffer(avctx, &s->current_frame) < 0) {
av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
goto error;
}
+ if (!s->edge_emu_buffer)
+ s->edge_emu_buffer = av_malloc(9*FFABS(s->current_frame.linesize[0]));
+
if (s->keyframe) {
if (!s->theora)
{
if (s->version)
{
s->version = get_bits(&gb, 5);
- if (counter == 1)
+ if (avctx->frame_number == 0)
av_log(s->avctx, AV_LOG_DEBUG, "VP version: %d\n", s->version);
}
}
av_log(s->avctx, AV_LOG_WARNING, "vp3: first frame not a keyframe\n");
s->golden_frame.reference = 3;
- s->golden_frame.pict_type = FF_I_TYPE;
- if (avctx->get_buffer(avctx, &s->golden_frame) < 0) {
+ s->golden_frame.pict_type = AV_PICTURE_TYPE_I;
+ if (ff_thread_get_buffer(avctx, &s->golden_frame) < 0) {
av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
goto error;
}
s->last_frame = s->golden_frame;
s->last_frame.type = FF_BUFFER_TYPE_COPY;
+ ff_thread_report_progress(&s->last_frame, INT_MAX, 0);
}
}
- s->current_frame.qscale_table= s->qscale_table; //FIXME allocate individual tables per AVFrame
- s->current_frame.qstride= 0;
-
memset(s->all_fragments, 0, s->fragment_count * sizeof(Vp3Fragment));
+ ff_thread_finish_setup(avctx);
if (unpack_superblocks(s, &gb)){
av_log(s->avctx, AV_LOG_ERROR, "error in unpack_superblocks\n");
*data_size=sizeof(AVFrame);
*(AVFrame*)data= s->current_frame;
- /* release the last frame, if it is allocated and if it is not the
- * golden frame */
- if (s->last_frame.data[0] && s->last_frame.type != FF_BUFFER_TYPE_COPY)
- avctx->release_buffer(avctx, &s->last_frame);
-
- /* shuffle frames (last = current) */
- s->last_frame= s->current_frame;
-
- if (s->keyframe) {
- if (s->golden_frame.data[0])
- avctx->release_buffer(avctx, &s->golden_frame);
- s->golden_frame = s->current_frame;
- s->last_frame.type = FF_BUFFER_TYPE_COPY;
- }
-
- s->current_frame.data[0]= NULL; /* ensure that we catch any access to this released frame */
+ if (!HAVE_THREADS || !(s->avctx->active_thread_type&FF_THREAD_FRAME))
+ update_frames(avctx);
return buf_size;
error:
- if (s->current_frame.data[0])
+ ff_thread_report_progress(&s->current_frame, INT_MAX, 0);
+
+ if (!HAVE_THREADS || !(s->avctx->active_thread_type&FF_THREAD_FRAME))
avctx->release_buffer(avctx, &s->current_frame);
+
return -1;
}
-/*
- * This is the ffmpeg/libavcodec API module cleanup function.
- */
static av_cold int vp3_decode_end(AVCodecContext *avctx)
{
Vp3DecodeContext *s = avctx->priv_data;
av_free(s->macroblock_coding);
av_free(s->motion_val[0]);
av_free(s->motion_val[1]);
+ av_free(s->edge_emu_buffer);
+
+ if (avctx->is_copy) return 0;
for (i = 0; i < 16; i++) {
free_vlc(&s->dc_vlc[i]);
free_vlc(&s->motion_vector_vlc);
/* release all frames */
- if (s->golden_frame.data[0])
- avctx->release_buffer(avctx, &s->golden_frame);
- if (s->last_frame.data[0] && s->last_frame.type != FF_BUFFER_TYPE_COPY)
- avctx->release_buffer(avctx, &s->last_frame);
- /* no need to release the current_frame since it will always be pointing
- * to the same frame as either the golden or last frame */
+ vp3_decode_flush(avctx);
return 0;
}
if (s->theora >= 0x030200) {
n = get_bits(gb, 3);
/* loop filter limit values table */
- if (n) {
- for (i = 0; i < 64; i++) {
- s->filter_limit_values[i] = get_bits(gb, n);
- if (s->filter_limit_values[i] > 127) {
- av_log(avctx, AV_LOG_ERROR, "filter limit value too large (%i > 127), clamping\n", s->filter_limit_values[i]);
- s->filter_limit_values[i] = 127;
- }
- }
- }
+ if (n)
+ for (i = 0; i < 64; i++)
+ s->filter_limit_values[i] = get_bits(gb, n);
}
if (s->theora >= 0x030200)
return -1;
}
- if (ff_split_xiph_headers(avctx->extradata, avctx->extradata_size,
+ if (avpriv_split_xiph_headers(avctx->extradata, avctx->extradata_size,
42, header_start, header_len) < 0) {
av_log(avctx, AV_LOG_ERROR, "Corrupt extradata\n");
return -1;
return vp3_decode_init(avctx);
}
-AVCodec theora_decoder = {
- "theora",
- AVMEDIA_TYPE_VIDEO,
- CODEC_ID_THEORA,
- sizeof(Vp3DecodeContext),
- theora_decode_init,
- NULL,
- vp3_decode_end,
- vp3_decode_frame,
- CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND,
- NULL,
+static void vp3_decode_flush(AVCodecContext *avctx)
+{
+ Vp3DecodeContext *s = avctx->priv_data;
+
+ if (s->golden_frame.data[0]) {
+ if (s->golden_frame.data[0] == s->last_frame.data[0])
+ memset(&s->last_frame, 0, sizeof(AVFrame));
+ if (s->current_frame.data[0] == s->golden_frame.data[0])
+ memset(&s->current_frame, 0, sizeof(AVFrame));
+ ff_thread_release_buffer(avctx, &s->golden_frame);
+ }
+ if (s->last_frame.data[0]) {
+ if (s->current_frame.data[0] == s->last_frame.data[0])
+ memset(&s->current_frame, 0, sizeof(AVFrame));
+ ff_thread_release_buffer(avctx, &s->last_frame);
+ }
+ if (s->current_frame.data[0])
+ ff_thread_release_buffer(avctx, &s->current_frame);
+}
+
+static int vp3_init_thread_copy(AVCodecContext *avctx)
+{
+ Vp3DecodeContext *s = avctx->priv_data;
+
+ s->superblock_coding = NULL;
+ s->all_fragments = NULL;
+ s->coded_fragment_list[0] = NULL;
+ s->dct_tokens_base = NULL;
+ s->superblock_fragments = NULL;
+ s->macroblock_coding = NULL;
+ s->motion_val[0] = NULL;
+ s->motion_val[1] = NULL;
+ s->edge_emu_buffer = NULL;
+
+ return 0;
+}
+
+AVCodec ff_theora_decoder = {
+ .name = "theora",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = CODEC_ID_THEORA,
+ .priv_data_size = sizeof(Vp3DecodeContext),
+ .init = theora_decode_init,
+ .close = vp3_decode_end,
+ .decode = vp3_decode_frame,
+ .capabilities = CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_FRAME_THREADS,
+ .flush = vp3_decode_flush,
.long_name = NULL_IF_CONFIG_SMALL("Theora"),
+ .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp3_init_thread_copy),
+ .update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context)
};
#endif
-AVCodec vp3_decoder = {
- "vp3",
- AVMEDIA_TYPE_VIDEO,
- CODEC_ID_VP3,
- sizeof(Vp3DecodeContext),
- vp3_decode_init,
- NULL,
- vp3_decode_end,
- vp3_decode_frame,
- CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND,
- NULL,
+AVCodec ff_vp3_decoder = {
+ .name = "vp3",
+ .type = AVMEDIA_TYPE_VIDEO,
+ .id = CODEC_ID_VP3,
+ .priv_data_size = sizeof(Vp3DecodeContext),
+ .init = vp3_decode_init,
+ .close = vp3_decode_end,
+ .decode = vp3_decode_frame,
+ .capabilities = CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_FRAME_THREADS,
+ .flush = vp3_decode_flush,
.long_name = NULL_IF_CONFIG_SMALL("On2 VP3"),
+ .init_thread_copy = ONLY_IF_THREADS_ENABLED(vp3_init_thread_copy),
+ .update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context)
};