Commit d23845f3 authored by Alexander Strange's avatar Alexander Strange Committed by Ronald S. Bultje

vp3: Frame-based multithreading support

Decode times for big_buck_bunny_720p_stereo:

1 thread:
real    1m14.227s
user    1m13.104s
sys     0m1.108s

2 threads: (33% faster)
real    0m49.329s
user    1m33.735s
sys     0m1.834s

3 threads: (44% faster)
real    0m41.593s
user    1m44.884s
sys     0m1.967s
parent f2146944
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "vp3data.h" #include "vp3data.h"
#include "xiph.h" #include "xiph.h"
#include "thread.h"
#define FRAGMENT_PIXELS 8 #define FRAGMENT_PIXELS 8
...@@ -1318,6 +1319,15 @@ static void vp3_draw_horiz_band(Vp3DecodeContext *s, int y) ...@@ -1318,6 +1319,15 @@ static void vp3_draw_horiz_band(Vp3DecodeContext *s, int y)
int h, cy; int h, cy;
int offset[4]; int offset[4];
if (HAVE_PTHREADS && 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) if(s->avctx->draw_horiz_band==NULL)
return; return;
...@@ -1339,6 +1349,28 @@ static void vp3_draw_horiz_band(Vp3DecodeContext *s, int y) ...@@ -1339,6 +1349,28 @@ static void vp3_draw_horiz_band(Vp3DecodeContext *s, int y)
s->avctx->draw_horiz_band(s->avctx, &s->current_frame, offset, y, 3, h); 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. * Perform the final rendering for a particular slice of data.
* The slice number ranges from 0..(c_superblock_height - 1). * The slice number ranges from 0..(c_superblock_height - 1).
...@@ -1371,6 +1403,7 @@ static void render_slice(Vp3DecodeContext *s, int slice) ...@@ -1371,6 +1403,7 @@ static void render_slice(Vp3DecodeContext *s, int slice)
int fragment_width = s->fragment_width[!!plane]; int fragment_width = s->fragment_width[!!plane];
int fragment_height = s->fragment_height[!!plane]; int fragment_height = s->fragment_height[!!plane];
int fragment_start = s->fragment_start[plane]; int fragment_start = s->fragment_start[plane];
int do_await = !plane && HAVE_PTHREADS && (s->avctx->active_thread_type&FF_THREAD_FRAME);
if (!s->flipped_image) stride = -stride; if (!s->flipped_image) stride = -stride;
if (CONFIG_GRAY && plane && (s->avctx->flags & CODEC_FLAG_GRAY)) if (CONFIG_GRAY && plane && (s->avctx->flags & CODEC_FLAG_GRAY))
...@@ -1400,6 +1433,9 @@ static void render_slice(Vp3DecodeContext *s, int slice) ...@@ -1400,6 +1433,9 @@ static void render_slice(Vp3DecodeContext *s, int slice)
first_pixel = 8*y*stride + 8*x; 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 */ /* transform if this block was coded */
if (s->all_fragments[i].coding_method != MODE_COPY) { 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_USING_GOLDEN) ||
...@@ -1721,6 +1757,81 @@ vlc_fail: ...@@ -1721,6 +1757,81 @@ vlc_fail:
return -1; return -1;
} }
/// 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;
if (!s1->current_frame.data[0]
||s->width != s1->width
||s->height!= s1->height)
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]));
}
#define copy_fields(to, from, start_field, end_field) memcpy(&to->start_field, &from->start_field, (char*)&to->end_field - (char*)&to->start_field)
// 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->qscale_table, &s1->qscale_table, sizeof(s->qscale_table));
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;
}
/* /*
* This is the ffmpeg/libavcodec API frame decode function. * This is the ffmpeg/libavcodec API frame decode function.
*/ */
...@@ -1776,7 +1887,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, ...@@ -1776,7 +1887,7 @@ static int vp3_decode_frame(AVCodecContext *avctx,
s->current_frame.reference = 3; s->current_frame.reference = 3;
s->current_frame.pict_type = s->keyframe ? FF_I_TYPE : FF_P_TYPE; s->current_frame.pict_type = s->keyframe ? FF_I_TYPE : FF_P_TYPE;
if (avctx->get_buffer(avctx, &s->current_frame) < 0) { if (ff_thread_get_buffer(avctx, &s->current_frame) < 0) {
av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n"); av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
goto error; goto error;
} }
...@@ -1805,7 +1916,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, ...@@ -1805,7 +1916,7 @@ static int vp3_decode_frame(AVCodecContext *avctx,
s->golden_frame.reference = 3; s->golden_frame.reference = 3;
s->golden_frame.pict_type = FF_I_TYPE; s->golden_frame.pict_type = FF_I_TYPE;
if (avctx->get_buffer(avctx, &s->golden_frame) < 0) { if (ff_thread_get_buffer(avctx, &s->golden_frame) < 0) {
av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n"); av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
goto error; goto error;
} }
...@@ -1818,6 +1929,7 @@ static int vp3_decode_frame(AVCodecContext *avctx, ...@@ -1818,6 +1929,7 @@ static int vp3_decode_frame(AVCodecContext *avctx,
s->current_frame.qstride= 0; s->current_frame.qstride= 0;
memset(s->all_fragments, 0, s->fragment_count * sizeof(Vp3Fragment)); memset(s->all_fragments, 0, s->fragment_count * sizeof(Vp3Fragment));
ff_thread_finish_setup(avctx);
if (unpack_superblocks(s, &gb)){ if (unpack_superblocks(s, &gb)){
av_log(s->avctx, AV_LOG_ERROR, "error in unpack_superblocks\n"); av_log(s->avctx, AV_LOG_ERROR, "error in unpack_superblocks\n");
...@@ -1862,28 +1974,17 @@ static int vp3_decode_frame(AVCodecContext *avctx, ...@@ -1862,28 +1974,17 @@ static int vp3_decode_frame(AVCodecContext *avctx,
*data_size=sizeof(AVFrame); *data_size=sizeof(AVFrame);
*(AVFrame*)data= s->current_frame; *(AVFrame*)data= s->current_frame;
/* release the last frame, if it is allocated and if it is not the if (!HAVE_PTHREADS || !(s->avctx->active_thread_type&FF_THREAD_FRAME))
* golden frame */ update_frames(avctx);
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 */
return buf_size; return buf_size;
error: error:
if (s->current_frame.data[0]) ff_thread_report_progress(&s->current_frame, INT_MAX, 0);
if (!HAVE_PTHREADS || !(s->avctx->active_thread_type&FF_THREAD_FRAME))
avctx->release_buffer(avctx, &s->current_frame); avctx->release_buffer(avctx, &s->current_frame);
return -1; return -1;
} }
...@@ -1895,6 +1996,9 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx) ...@@ -1895,6 +1996,9 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx)
Vp3DecodeContext *s = avctx->priv_data; Vp3DecodeContext *s = avctx->priv_data;
int i; int i;
if (avctx->is_copy && !s->current_frame.data[0])
return 0;
av_free(s->superblock_coding); av_free(s->superblock_coding);
av_free(s->all_fragments); av_free(s->all_fragments);
av_free(s->coded_fragment_list[0]); av_free(s->coded_fragment_list[0]);
...@@ -1904,6 +2008,8 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx) ...@@ -1904,6 +2008,8 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx)
av_free(s->motion_val[0]); av_free(s->motion_val[0]);
av_free(s->motion_val[1]); av_free(s->motion_val[1]);
if (avctx->is_copy) return 0;
for (i = 0; i < 16; i++) { for (i = 0; i < 16; i++) {
free_vlc(&s->dc_vlc[i]); free_vlc(&s->dc_vlc[i]);
free_vlc(&s->ac_vlc_1[i]); free_vlc(&s->ac_vlc_1[i]);
...@@ -1919,9 +2025,9 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx) ...@@ -1919,9 +2025,9 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx)
/* release all frames */ /* release all frames */
if (s->golden_frame.data[0]) if (s->golden_frame.data[0])
avctx->release_buffer(avctx, &s->golden_frame); ff_thread_release_buffer(avctx, &s->golden_frame);
if (s->last_frame.data[0] && s->last_frame.type != FF_BUFFER_TYPE_COPY) if (s->last_frame.data[0] && s->last_frame.type != FF_BUFFER_TYPE_COPY)
avctx->release_buffer(avctx, &s->last_frame); ff_thread_release_buffer(avctx, &s->last_frame);
/* no need to release the current_frame since it will always be pointing /* no need to release the current_frame since it will always be pointing
* to the same frame as either the golden or last frame */ * to the same frame as either the golden or last frame */
...@@ -2232,9 +2338,10 @@ AVCodec ff_theora_decoder = { ...@@ -2232,9 +2338,10 @@ AVCodec ff_theora_decoder = {
NULL, NULL,
vp3_decode_end, vp3_decode_end,
vp3_decode_frame, vp3_decode_frame,
CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND, CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_FRAME_THREADS,
NULL, NULL,
.long_name = NULL_IF_CONFIG_SMALL("Theora"), .long_name = NULL_IF_CONFIG_SMALL("Theora"),
.update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context)
}; };
#endif #endif
...@@ -2247,7 +2354,8 @@ AVCodec ff_vp3_decoder = { ...@@ -2247,7 +2354,8 @@ AVCodec ff_vp3_decoder = {
NULL, NULL,
vp3_decode_end, vp3_decode_end,
vp3_decode_frame, vp3_decode_frame,
CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND, CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_FRAME_THREADS,
NULL, NULL,
.long_name = NULL_IF_CONFIG_SMALL("On2 VP3"), .long_name = NULL_IF_CONFIG_SMALL("On2 VP3"),
.update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context)
}; };
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment