[FFmpeg-devel] [PATCH 6/6] vp3: Frame-based multithreading support
Alexander Strange
astrange
Mon Nov 15 14:37:06 CET 2010
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
---
libavcodec/vp3.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++--------
1 files changed, 131 insertions(+), 23 deletions(-)
diff --git a/libavcodec/vp3.c b/libavcodec/vp3.c
index 8bf5cc5..d5b29d4 100644
--- a/libavcodec/vp3.c
+++ b/libavcodec/vp3.c
@@ -40,6 +40,7 @@
#include "vp3data.h"
#include "xiph.h"
+#include "thread.h"
#define FRAGMENT_PIXELS 8
@@ -1318,6 +1319,15 @@ static void vp3_draw_horiz_band(Vp3DecodeContext *s, int y)
int h, cy;
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)
return;
@@ -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);
}
+/**
+ * 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).
@@ -1371,6 +1403,7 @@ static void render_slice(Vp3DecodeContext *s, int slice)
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_PTHREADS && (s->avctx->active_thread_type&FF_THREAD_FRAME);
if (!s->flipped_image) stride = -stride;
if (CONFIG_GRAY && plane && (s->avctx->flags & CODEC_FLAG_GRAY))
@@ -1400,6 +1433,9 @@ static void render_slice(Vp3DecodeContext *s, int slice)
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) ||
@@ -1721,6 +1757,81 @@ vlc_fail:
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, 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.
*/
@@ -1776,7 +1887,7 @@ static int vp3_decode_frame(AVCodecContext *avctx,
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) {
+ if (ff_thread_get_buffer(avctx, &s->current_frame) < 0) {
av_log(s->avctx, AV_LOG_ERROR, "get_buffer() failed\n");
goto error;
}
@@ -1805,7 +1916,7 @@ static int vp3_decode_frame(AVCodecContext *avctx,
s->golden_frame.reference = 3;
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");
goto error;
}
@@ -1818,6 +1929,7 @@ static int vp3_decode_frame(AVCodecContext *avctx,
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");
@@ -1862,28 +1974,17 @@ static int vp3_decode_frame(AVCodecContext *avctx,
*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_PTHREADS || !(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_PTHREADS || !(s->avctx->active_thread_type&FF_THREAD_FRAME))
avctx->release_buffer(avctx, &s->current_frame);
+
return -1;
}
@@ -1895,6 +1996,9 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx)
Vp3DecodeContext *s = avctx->priv_data;
int i;
+ if (avctx->is_copy && !s->current_frame.data[0])
+ return 0;
+
av_free(s->superblock_coding);
av_free(s->all_fragments);
av_free(s->coded_fragment_list[0]);
@@ -1904,6 +2008,8 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx)
av_free(s->motion_val[0]);
av_free(s->motion_val[1]);
+ if (avctx->is_copy) return 0;
+
for (i = 0; i < 16; i++) {
free_vlc(&s->dc_vlc[i]);
free_vlc(&s->ac_vlc_1[i]);
@@ -1919,9 +2025,9 @@ static av_cold int vp3_decode_end(AVCodecContext *avctx)
/* release all frames */
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)
- 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
* to the same frame as either the golden or last frame */
@@ -2232,9 +2338,10 @@ AVCodec theora_decoder = {
NULL,
vp3_decode_end,
vp3_decode_frame,
- CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND,
+ CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_FRAME_THREADS,
NULL,
.long_name = NULL_IF_CONFIG_SMALL("Theora"),
+ .update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context)
};
#endif
@@ -2247,7 +2354,8 @@ AVCodec vp3_decoder = {
NULL,
vp3_decode_end,
vp3_decode_frame,
- CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND,
+ CODEC_CAP_DR1 | CODEC_CAP_DRAW_HORIZ_BAND | CODEC_CAP_FRAME_THREADS,
NULL,
.long_name = NULL_IF_CONFIG_SMALL("On2 VP3"),
+ .update_thread_context = ONLY_IF_THREADS_ENABLED(vp3_update_thread_context)
};
--
1.7.3.1
More information about the ffmpeg-devel
mailing list