mirror of https://git.ffmpeg.org/ffmpeg.git
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
(cherry picked from commit d23845f311
)
This commit is contained in:
parent
f94317fd07
commit
4a5a16898f
154
libavcodec/vp3.c
154
libavcodec/vp3.c
|
@ -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)
|
||||||
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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
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:
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
|
|
||||||
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,
|
||||||
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,
|
||||||
*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)
|
||||||
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)
|
||||||
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)
|
||||||
|
|
||||||
/* 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 = {
|
||||||
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 = {
|
||||||
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)
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue