ref: a9a20a104018589de1efb58c61aeb84b327b54b7
parent: 165db2343a0183ab0c6201e271e3e732bb733051
author: hkuang <hkuang@google.com>
date: Fri Nov 7 11:42:27 EST 2014
Fix a bug in frame parallel decode and add a unit test for that. A flush bug is discovered during putting frame parallel decoder into Android. This test will expose that bug. Change-Id: Ia047f27972f4da0471649f79f1f91e7695297473
--- a/test/test-data.sha1
+++ b/test/test-data.sha1
@@ -665,3 +665,5 @@
47d7d409785afa33b123376de0c907336e6c7bd7 vp90-2-20-big_superframe-01.webm.md5
65ade6d2786209582c50d34cfe22b3cdb033abaf vp90-2-20-big_superframe-02.webm
7c0ed8d04c4d06c5411dd2e5de2411d37f092db5 vp90-2-20-big_superframe-02.webm.md5
+667ec8718c982aef6be07eb94f083c2efb9d2d16 vp90-2-07-frame_parallel-1.webm
+bfc82bf848e9c05020d61e3ffc1e62f25df81d19 vp90-2-07-frame_parallel-1.webm.md5
--- a/test/test.mk
+++ b/test/test.mk
@@ -31,6 +31,7 @@
LIBVPX_TEST_SRCS-$(CONFIG_VP9_DECODER) += external_frame_buffer_test.cc
LIBVPX_TEST_SRCS-$(CONFIG_VP9_DECODER) += user_priv_test.cc
+LIBVPX_TEST_SRCS-$(CONFIG_VP9_DECODER) += vp9_frame_parallel_test.cc
LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += active_map_test.cc
LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += borders_test.cc
LIBVPX_TEST_SRCS-$(CONFIG_VP9_ENCODER) += cpu_speed_test.cc
@@ -683,6 +684,8 @@
LIBVPX_TEST_DATA-$(CONFIG_VP9_DECODER) += vp90-2-06-bilinear.webm.md5
LIBVPX_TEST_DATA-$(CONFIG_VP9_DECODER) += vp90-2-07-frame_parallel.webm
LIBVPX_TEST_DATA-$(CONFIG_VP9_DECODER) += vp90-2-07-frame_parallel.webm.md5
+LIBVPX_TEST_DATA-$(CONFIG_VP9_DECODER) += vp90-2-07-frame_parallel-1.webm
+LIBVPX_TEST_DATA-$(CONFIG_VP9_DECODER) += vp90-2-07-frame_parallel-1.webm.md5
LIBVPX_TEST_DATA-$(CONFIG_VP9_DECODER) += vp90-2-08-tile-4x1.webm
LIBVPX_TEST_DATA-$(CONFIG_VP9_DECODER) += vp90-2-08-tile-4x1.webm.md5
LIBVPX_TEST_DATA-$(CONFIG_VP9_DECODER) += vp90-2-08-tile-4x4.webm
--- a/test/test_vectors.cc
+++ b/test/test_vectors.cc
@@ -154,7 +154,8 @@
"vp90-2-03-size-226x210.webm", "vp90-2-03-size-226x224.webm",
"vp90-2-03-size-226x226.webm", "vp90-2-03-deltaq.webm",
"vp90-2-05-resize.ivf", "vp90-2-06-bilinear.webm",
- "vp90-2-07-frame_parallel.webm", "vp90-2-08-tile_1x2_frame_parallel.webm",
+ "vp90-2-07-frame_parallel.webm", "vp90-2-07-frame_parallel-1.webm",
+ "vp90-2-08-tile_1x2_frame_parallel.webm",
"vp90-2-08-tile_1x2.webm", "vp90-2-08-tile_1x4_frame_parallel.webm",
"vp90-2-08-tile_1x4.webm", "vp90-2-08-tile_1x8_frame_parallel.webm",
"vp90-2-08-tile_1x8.webm", "vp90-2-08-tile-4x4.webm",
--- /dev/null
+++ b/test/vp9_frame_parallel_test.cc
@@ -1,0 +1,122 @@
+/*
+ * Copyright (c) 2014 The WebM project authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by a BSD-style license
+ * that can be found in the LICENSE file in the root of the source
+ * tree. An additional intellectual property rights grant can be found
+ * in the file PATENTS. All contributing project authors may
+ * be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include <cstdio>
+#include <cstdlib>
+#include <string>
+#include "third_party/googletest/src/include/gtest/gtest.h"
+#include "./vpx_config.h"
+#include "test/codec_factory.h"
+#include "test/decode_test_driver.h"
+#include "test/ivf_video_source.h"
+#include "test/md5_helper.h"
+#include "test/util.h"
+#if CONFIG_WEBM_IO
+#include "test/webm_video_source.h"
+#endif
+#include "vpx_mem/vpx_mem.h"
+
+namespace {
+
+using std::string;
+
+#if CONFIG_WEBM_IO
+
+struct FileList {
+ const char *name;
+ // md5 sum for decoded frames which does not include skipped frames.
+ const char *expected_md5;
+ const int pause_frame_num;
+};
+
+// Decodes |filename| with |num_threads|. Pause at the specified frame_num,
+// seek to next key frame and then continue decoding until the end. Return
+// the md5 of the decoded frames which does not include skipped frames.
+string DecodeFile(const string &filename, int num_threads, int pause_num) {
+ libvpx_test::WebMVideoSource video(filename);
+ video.Init();
+ int in_frames = 0;
+ int out_frames = 0;
+
+ vpx_codec_dec_cfg_t cfg = {0};
+ cfg.threads = num_threads;
+ vpx_codec_flags_t flags = 0;
+ flags |= VPX_CODEC_USE_FRAME_THREADING;
+ libvpx_test::VP9Decoder decoder(cfg, flags, 0);
+
+ libvpx_test::MD5 md5;
+ video.Begin();
+
+ do {
+ ++in_frames;
+ const vpx_codec_err_t res =
+ decoder.DecodeFrame(video.cxdata(), video.frame_size());
+ if (res != VPX_CODEC_OK) {
+ EXPECT_EQ(VPX_CODEC_OK, res) << decoder.DecodeError();
+ break;
+ }
+
+ // Pause at specified frame number.
+ if (in_frames == pause_num) {
+ // Flush the decoder and then seek to next key frame.
+ decoder.DecodeFrame(NULL, 0);
+ video.SeekToNextKeyFrame();
+ } else {
+ video.Next();
+ }
+
+ // Flush the decoder at the end of the video.
+ if (!video.cxdata())
+ decoder.DecodeFrame(NULL, 0);
+
+ libvpx_test::DxDataIterator dec_iter = decoder.GetDxData();
+ const vpx_image_t *img;
+
+ // Get decompressed data
+ while ((img = dec_iter.Next())) {
+ ++out_frames;
+ md5.Add(img);
+ }
+ } while (video.cxdata() != NULL);
+
+ EXPECT_EQ(in_frames, out_frames) <<
+ "Input frame count does not match output frame count";
+
+ return string(md5.Get());
+}
+
+void DecodeFiles(const FileList files[]) {
+ for (const FileList *iter = files; iter->name != NULL; ++iter) {
+ SCOPED_TRACE(iter->name);
+ for (int t = 2; t <= 8; ++t) {
+ EXPECT_EQ(iter->expected_md5,
+ DecodeFile(iter->name, t, iter->pause_frame_num))
+ << "threads = " << t;
+ }
+ }
+}
+
+TEST(VP9MultiThreadedFrameParallel, PauseSeekResume) {
+ // vp90-2-07-frame_parallel-1.webm is a 40 frame video file with
+ // one key frame for every ten frames.
+ static const FileList files[] = {
+ { "vp90-2-07-frame_parallel-1.webm",
+ "6ea7c3875d67252e7caf2bc6e75b36b1", 6},
+ { "vp90-2-07-frame_parallel-1.webm",
+ "4bb634160c7356a8d7d4299b6dc83a45", 12},
+ { "vp90-2-07-frame_parallel-1.webm",
+ "89772591e6ef461f9fa754f916c78ed8", 26},
+ { NULL, NULL, 0},
+ };
+ DecodeFiles(files);
+}
+
+#endif // CONFIG_WEBM_IO
+} // namespace
--- a/vp9/decoder/vp9_decoder.c
+++ b/vp9/decoder/vp9_decoder.c
@@ -272,6 +272,8 @@
cm->frame_refs[0].buf->corrupted = 1;
}
+ pbi->ready_for_new_data = 0;
+
// Check if the previous frame was a frame without any references to it.
// Release frame buffer if not decoding in frame parallel mode.
if (!pbi->frame_parallel_decode && cm->new_fb_idx >= 0
@@ -296,6 +298,7 @@
if (setjmp(cm->error.jmp)) {
cm->error.setjmp = 0;
+ pbi->ready_for_new_data = 1;
// We do not know if the missing frame(s) was supposed to update
// any of the reference buffers, but we act conservative and
@@ -354,8 +357,6 @@
vp9_swap_current_and_last_seg_map(cm);
}
- pbi->ready_for_new_data = 0;
-
cm->error.setjmp = 0;
return retcode;
}
@@ -370,11 +371,11 @@
if (pbi->ready_for_new_data == 1)
return ret;
+ pbi->ready_for_new_data = 1;
+
/* no raw frame to show!!! */
if (pbi->common.show_frame == 0)
return ret;
-
- pbi->ready_for_new_data = 1;
#if CONFIG_VP9_POSTPROC
ret = vp9_post_proc_frame(&pbi->common, sd, flags);
--- a/vp9/vp9_dx_iface.c
+++ b/vp9/vp9_dx_iface.c
@@ -443,6 +443,8 @@
&ctx->frame_workers[ctx->next_submit_worker_id],
&ctx->frame_workers[ctx->last_submit_worker_id]);
+ frame_worker_data->pbi->ready_for_new_data = 0;
+
// Copy the compressed data into worker's internal buffer.
// TODO(hkuang): Will all the workers allocate the same size
// as the size of the first intra frame be better? This will
@@ -757,10 +759,10 @@
(ctx->next_output_worker_id + 1) % ctx->num_frame_workers;
// Wait for the frame from worker thread.
winterface->sync(worker);
- ++ctx->available_threads;
if (vp9_get_raw_frame(frame_worker_data->pbi, &sd, &flags) == 0) {
VP9_COMMON *const cm = &frame_worker_data->pbi->common;
RefCntBuffer *const frame_bufs = cm->buffer_pool->frame_bufs;
+ ++ctx->available_threads;
release_last_output_frame(ctx);
ctx->last_show_frame = frame_worker_data->pbi->common.new_fb_idx;
yuvconfig2image(&ctx->img, &sd, frame_worker_data->user_priv);