Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(234)

Unified Diff: media/base/android/media_codec_decoder_unittest.cc

Issue 1176993005: Audio and video decoders for MediaCodecPlayer (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added tests for video decoder, video decoder now reports current time. Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: media/base/android/media_codec_decoder_unittest.cc
diff --git a/media/base/android/media_codec_decoder_unittest.cc b/media/base/android/media_codec_decoder_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..323f7471c600f2663c0c7424d3f7436e6e15a346
--- /dev/null
+++ b/media/base/android/media_codec_decoder_unittest.cc
@@ -0,0 +1,617 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/stringprintf.h"
+#include "base/thread_task_runner_handle.h"
+#include "base/timer/timer.h"
+#include "media/base/android/media_codec_audio_decoder.h"
+#include "media/base/android/media_codec_bridge.h"
+#include "media/base/android/media_codec_video_decoder.h"
+#include "media/base/decoder_buffer.h"
+#include "media/base/test_data_util.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gl/android/surface_texture.h"
+
+namespace media {
+
+// Helper macro to skip the test if MediaCodecBridge isn't available.
+#define SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE() \
+ do { \
+ if (!MediaCodecBridge::IsAvailable()) { \
+ VLOG(0) << "Could not run test - not supported on device."; \
+ return; \
+ } \
+ } while (0)
+
+namespace {
+
+const base::TimeDelta kDefaultTimeout = base::TimeDelta::FromMilliseconds(200);
+const base::TimeDelta kAudioFramePeriod = base::TimeDelta::FromMilliseconds(20);
+const base::TimeDelta kVideoFramePeriod = base::TimeDelta::FromMilliseconds(20);
+
+DemuxerConfigs CreateAudioConfigs(AudioCodec audio_codec,
+ const base::TimeDelta& duration) {
+ DemuxerConfigs configs;
+ configs.audio_codec = audio_codec;
+ configs.audio_channels = 2;
+ configs.is_audio_encrypted = false;
+ configs.duration = duration;
+
+ // Other codecs are not yet supported by this helper.
+ EXPECT_TRUE(audio_codec == kCodecAAC || audio_codec == kCodecVorbis);
+
+ switch (audio_codec) {
+ case kCodecVorbis: {
+ configs.audio_sampling_rate = 44100;
+ scoped_refptr<DecoderBuffer> buffer =
+ ReadTestDataFile("vorbis-extradata");
+ configs.audio_extra_data = std::vector<uint8>(
+ buffer->data(), buffer->data() + buffer->data_size());
+ } break;
+
+ case kCodecAAC: {
+ configs.audio_sampling_rate = 48000;
+ uint8 aac_extra_data[] = {0x13, 0x10};
+ configs.audio_extra_data =
+ std::vector<uint8>(aac_extra_data, aac_extra_data + 2);
+ } break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+
+ return configs;
+}
+
+DemuxerConfigs CreateVideoConfigs(const base::TimeDelta& duration) {
+ DemuxerConfigs configs;
+ configs.video_codec = kCodecH264;
+ configs.video_size = gfx::Size(320, 240);
+ configs.is_video_encrypted = false;
+ configs.duration = duration;
+ return configs;
+}
+
+} // namespace (anonymous)
+
+// DataFactory defines how the data will be generated.
+
+class DataFactory {
+ public:
+ DataFactory(const char* file_name_format,
+ const base::TimeDelta& duration,
+ const base::TimeDelta& frame_period);
+ virtual ~DataFactory() {}
+
+ void CreateChunk(DemuxerData* chunk, base::TimeDelta* delay);
+
+ base::TimeDelta last_pts() const { return last_pts_; }
+
+ protected:
+ virtual void FillAccessUnit(int index_in_chunk, AccessUnit* unit) = 0;
+
+ base::TimeDelta duration_;
+ base::TimeDelta frame_period_;
+ std::vector<uint8> packet_[4];
+ base::TimeDelta regular_pts_;
+ base::TimeDelta last_pts_;
+};
+
+class AudioFactory : public DataFactory {
+ public:
+ AudioFactory(const base::TimeDelta& duration);
+
+ protected:
+ void FillAccessUnit(int index_in_chunk, AccessUnit* unit) override;
+};
+
+class VideoFactory : public DataFactory {
+ public:
+ VideoFactory(const base::TimeDelta& duration);
+
+ protected:
+ void FillAccessUnit(int index_in_chunk, AccessUnit* unit) override;
+};
+
+DataFactory::DataFactory(const char* file_name_format,
+ const base::TimeDelta& duration,
+ const base::TimeDelta& frame_period)
+ : duration_(duration), frame_period_(frame_period) {
+ // Load packets
+ for (int i = 0; i < 4; ++i) {
+ scoped_refptr<DecoderBuffer> buffer =
+ ReadTestDataFile(base::StringPrintf(file_name_format, i));
+ packet_[i] = std::vector<uint8>(buffer->data(),
+ buffer->data() + buffer->data_size());
+ }
+}
+
+void DataFactory::CreateChunk(DemuxerData* chunk, base::TimeDelta* delay) {
+ DCHECK(chunk);
+ DCHECK(delay);
+
+ *delay = base::TimeDelta();
+
+ for (int i = 0; i < 4; ++i) {
+ chunk->access_units.push_back(AccessUnit());
+ AccessUnit& unit = chunk->access_units.back();
+ unit.status = DemuxerStream::kOk;
+
+ unit.timestamp = regular_pts_;
+ regular_pts_ += frame_period_;
+
+ if (unit.timestamp > duration_) {
+ unit.is_end_of_stream = true;
+ break; // EOS units have no data
+ }
+
+ FillAccessUnit(i, &unit);
+
+ // Maintain last PTS. FillAccessUnit can modify unit's PTS.
+ if (last_pts_ < unit.timestamp)
+ last_pts_ = unit.timestamp;
+ }
+}
+
+AudioFactory::AudioFactory(const base::TimeDelta& duration)
+ : DataFactory("vorbis-packet-%d", duration, kAudioFramePeriod) {
+}
+
+void AudioFactory::FillAccessUnit(int index_in_chunk, AccessUnit* unit) {
+ unit->data = packet_[index_in_chunk];
+
+ // Vorbis needs 4 extra bytes padding on Android to decode properly. Check
+ // NuMediaExtractor.cpp in Android source code.
+ uint8 padding[4] = {0xff, 0xff, 0xff, 0xff};
+ unit->data.insert(unit->data.end(), padding, padding + 4);
+}
+
+VideoFactory::VideoFactory(const base::TimeDelta& duration)
+ : DataFactory("h264-320x180-frame-%d", duration, kVideoFramePeriod) {
+}
+
+void VideoFactory::FillAccessUnit(int index_in_chunk, AccessUnit* unit) {
+ unit->data = packet_[index_in_chunk];
+
+ // The frames are taken from High profile and some are B-frames.
+ // The first 4 frames appear in the file in the following order:
+ //
+ // Frames: I P B P
+ // Decoding order: 0 1 2 3
+ // Presentation order: 0 2 1 4(3)
+ //
+ // I keep the last PTS to be 3 for simplicity.
+
+ // Swap pts for second and third frames.
+ if (index_in_chunk == 1) // second frame
+ unit->timestamp += frame_period_;
+ if (index_in_chunk == 2) // third frame
+ unit->timestamp -= frame_period_;
+
+ if (index_in_chunk == 0)
+ unit->is_key_frame = true;
+}
+
+// Class that computes statistics: number of calls, minimum and maximum values.
+// It is used for PTS statistics to verify that playback did actually happen.
+
+template <typename T>
+class Minimax {
+ public:
+ Minimax() : num_values_(0) {}
+ ~Minimax() {}
+
+ void AddValue(const T& value) {
+ ++num_values_;
+ if (value < min_)
+ min_ = value;
+ else if (max_ < value)
+ max_ = value;
+ }
+
+ const T& min() const { return min_; }
+ const T& max() const { return max_; }
+ int num_values() const { return num_values_; }
+
+ private:
+ T min_;
+ T max_;
+ int num_values_;
+};
+
+// The test fixture for MediaCodecDecoder
+
+class MediaCodecDecoderTest : public testing::Test {
+ public:
+ MediaCodecDecoderTest();
+ ~MediaCodecDecoderTest() override;
+
+ // Conditions we wait for.
+ bool is_prefetched() const { return is_prefetched_; }
+ bool is_stopped() const { return is_stopped_; }
+ bool is_starved() const { return is_starved_; }
+
+ // Prefetch callback has to be public.
+ void SetPrefetched() { is_prefetched_ = true; }
+
+ protected:
+ typedef base::Callback<bool()> Predicate;
+
+ typedef base::Callback<void(const DemuxerData&)> DataAvailableCallback;
+
+ // Waits for condition to become true or for timeout to expire.
+ // Returns true if the condition becomes true.
+ bool WaitForCondition(const Predicate& condition,
+ const base::TimeDelta& timeout = kDefaultTimeout);
+
+ void SetDataFactory(scoped_ptr<DataFactory> factory) {
+ data_factory_ = factory.Pass();
+ }
+
+ void CreateAudioDecoder();
+ void CreateVideoDecoder();
+ void SetVideoSurface();
+
+ // Decoder callbacks.
+ void OnDataRequested();
+ void OnStarvation() { is_starved_ = true; }
+ void OnStopDone() { is_stopped_ = true; }
+ void OnError() {}
+ void OnUpdateCurrentTime(base::TimeDelta now_playing,
+ base::TimeDelta last_buffered) {
+ pts_stat_.AddValue(now_playing);
+ }
+ void OnVideoSizeChanged(const gfx::Size& video_size) {}
+ void OnVideoCodecCreated() {}
+
+ scoped_ptr<MediaCodecDecoder> decoder_;
+ scoped_ptr<DataFactory> data_factory_;
+ Minimax<base::TimeDelta> pts_stat_;
+
+ private:
+ bool is_timeout_expired() const { return is_timeout_expired_; }
+ void SetTimeoutExpired(bool value) { is_timeout_expired_ = value; }
+
+ base::MessageLoop message_loop_;
+ bool is_timeout_expired_;
+
+ bool is_prefetched_;
+ bool is_stopped_;
+ bool is_starved_;
+
+ scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
+ DataAvailableCallback data_available_cb_;
+ scoped_refptr<gfx::SurfaceTexture> surface_texture_;
+
+ DISALLOW_COPY_AND_ASSIGN(MediaCodecDecoderTest);
+};
+
+MediaCodecDecoderTest::MediaCodecDecoderTest()
+ : is_timeout_expired_(false),
+ is_prefetched_(false),
+ is_stopped_(false),
+ is_starved_(false),
+ task_runner_(base::ThreadTaskRunnerHandle::Get()) {
+}
+
+MediaCodecDecoderTest::~MediaCodecDecoderTest() {
+}
+
+bool MediaCodecDecoderTest::WaitForCondition(const Predicate& condition,
+ const base::TimeDelta& timeout) {
+ // Let the message_loop_ process events.
+ // We start the timer and RunUntilIdle() until it signals.
+
+ SetTimeoutExpired(false);
+
+ base::Timer timer(false, false);
+ timer.Start(FROM_HERE, timeout,
+ base::Bind(&MediaCodecDecoderTest::SetTimeoutExpired,
+ base::Unretained(this), true));
+
+ do {
+ if (condition.Run()) {
+ timer.Stop();
+ return true;
+ }
+ message_loop_.RunUntilIdle();
+ } while (!is_timeout_expired());
+
+ DCHECK(!timer.IsRunning());
+ return false;
+}
+
+void MediaCodecDecoderTest::CreateAudioDecoder() {
+ decoder_ = scoped_ptr<MediaCodecDecoder>(new MediaCodecAudioDecoder(
+ task_runner_, base::Bind(&MediaCodecDecoderTest::OnDataRequested,
+ base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnStarvation, base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnStopDone, base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnError, base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnUpdateCurrentTime,
+ base::Unretained(this))));
+
+ data_available_cb_ = base::Bind(&MediaCodecDecoder::OnDemuxerDataAvailable,
+ base::Unretained(decoder_.get()));
+}
+
+void MediaCodecDecoderTest::CreateVideoDecoder() {
+ decoder_ = scoped_ptr<MediaCodecDecoder>(new MediaCodecVideoDecoder(
+ task_runner_, base::Bind(&MediaCodecDecoderTest::OnDataRequested,
+ base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnStarvation, base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnStopDone, base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnError, base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnUpdateCurrentTime,
+ base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnVideoSizeChanged,
+ base::Unretained(this)),
+ base::Bind(&MediaCodecDecoderTest::OnVideoCodecCreated,
+ base::Unretained(this))));
+
+ data_available_cb_ = base::Bind(&MediaCodecDecoder::OnDemuxerDataAvailable,
+ base::Unretained(decoder_.get()));
+}
+
+void MediaCodecDecoderTest::OnDataRequested() {
+ if (!data_factory_)
+ return;
+
+ DemuxerData data;
+ base::TimeDelta delay;
+ data_factory_->CreateChunk(&data, &delay);
+
+ task_runner_->PostDelayedTask(FROM_HERE, base::Bind(data_available_cb_, data),
+ delay);
+}
+
+void MediaCodecDecoderTest::SetVideoSurface() {
+ surface_texture_ = gfx::SurfaceTexture::Create(0);
+ gfx::ScopedJavaSurface surface(surface_texture_.get());
+ ASSERT_NE(nullptr, decoder_.get());
+ MediaCodecVideoDecoder* video_decoder =
+ static_cast<MediaCodecVideoDecoder*>(decoder_.get());
+ video_decoder->SetPendingSurface(surface.Pass());
+}
+
+TEST_F(MediaCodecDecoderTest, AudioPrefetch) {
+ CreateAudioDecoder();
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ SetDataFactory(scoped_ptr<DataFactory>(new AudioFactory(duration)));
+
+ decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
+ base::Unretained(this))));
+}
+
+TEST_F(MediaCodecDecoderTest, VideoPrefetch) {
+ CreateVideoDecoder();
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ SetDataFactory(scoped_ptr<DataFactory>(new VideoFactory(duration)));
+
+ decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
+ base::Unretained(this))));
+}
+
+TEST_F(MediaCodecDecoderTest, AudioConfigureNoParams) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateAudioDecoder();
+
+ // Cannot configure without config parameters.
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure());
+}
+
+TEST_F(MediaCodecDecoderTest, AudioConfigureValidParams) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateAudioDecoder();
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ decoder_->SetDemuxerConfigs(CreateAudioConfigs(kCodecVorbis, duration));
+
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
+}
+
+TEST_F(MediaCodecDecoderTest, VideoConfigureNoParams) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateVideoDecoder();
+
+ // Cannot configure without config parameters.
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure());
+}
+
+TEST_F(MediaCodecDecoderTest, VideoConfigureNoSurface) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateVideoDecoder();
+
+ // decoder_->Configure() searches back for the key frame.
+ // We have to prefetch decoder.
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ SetDataFactory(scoped_ptr<DataFactory>(new VideoFactory(duration)));
+
+ decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
+ base::Unretained(this))));
+
+ decoder_->SetDemuxerConfigs(CreateVideoConfigs(duration));
+
+ // Surface is not set, Configure() should fail.
+
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure());
+}
+
+TEST_F(MediaCodecDecoderTest, VideoConfigureInvalidSurface) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateVideoDecoder();
+
+ // decoder_->Configure() searches back for the key frame.
+ // We have to prefetch decoder.
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ SetDataFactory(scoped_ptr<DataFactory>(new VideoFactory(duration)));
+
+ decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
+ base::Unretained(this))));
+
+ decoder_->SetDemuxerConfigs(CreateVideoConfigs(duration));
+
+ // Prepare the surface.
+ scoped_refptr<gfx::SurfaceTexture> surface_texture(
+ gfx::SurfaceTexture::Create(0));
+ gfx::ScopedJavaSurface surface(surface_texture.get());
+
+ // Release the surface texture.
+ surface_texture = NULL;
+
+ MediaCodecVideoDecoder* video_decoder =
+ static_cast<MediaCodecVideoDecoder*>(decoder_.get());
+ video_decoder->SetPendingSurface(surface.Pass());
+
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_FAILURE, decoder_->Configure());
+}
+
+TEST_F(MediaCodecDecoderTest, VideoConfigureValidParams) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateVideoDecoder();
+
+ // decoder_->Configure() searches back for the key frame.
+ // We have to prefetch decoder.
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ SetDataFactory(scoped_ptr<DataFactory>(new VideoFactory(duration)));
+
+ decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
+ base::Unretained(this))));
+
+ decoder_->SetDemuxerConfigs(CreateVideoConfigs(duration));
+
+ SetVideoSurface();
+
+ // Now we can expect Configure() to succeed.
+
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
+}
+
+TEST_F(MediaCodecDecoderTest, AudioStartWithoutConfigure) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateAudioDecoder();
+
+ // Decoder has to be configured first.
+ EXPECT_FALSE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
+}
+
+TEST_F(MediaCodecDecoderTest, AudioStarvation) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateAudioDecoder();
+
+ // We do not set data factory and do not call Prefetch.
+ // In this case decoder shoudl report starvation right after Start.
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ decoder_->SetDemuxerConfigs(CreateAudioConfigs(kCodecVorbis, duration));
+
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
+
+ EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
+
+ EXPECT_TRUE(WaitForCondition(
+ base::Bind(&MediaCodecDecoderTest::is_starved, base::Unretained(this))));
+}
+
+TEST_F(MediaCodecDecoderTest, AudioPlayTillCompletion) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateAudioDecoder();
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(600);
+ SetDataFactory(scoped_ptr<DataFactory>(new AudioFactory(duration)));
+
+ // Prefetch to avoid starvation at the beginning of playback.
+ decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
+ base::Unretained(this))));
+
+ decoder_->SetDemuxerConfigs(CreateAudioConfigs(kCodecVorbis, duration));
+
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
+
+ EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
+
+ EXPECT_TRUE(WaitForCondition(
+ base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this)),
+ timeout));
+
+ EXPECT_TRUE(decoder_->IsStopped());
+ EXPECT_TRUE(decoder_->IsCompleted());
+
+ // It is hard to properly estimate minimum and maximum values because
+ // reported times are different from PTS.
+ EXPECT_EQ(25, pts_stat_.num_values());
+}
+
+TEST_F(MediaCodecDecoderTest, VideoPlayTillCompletion) {
+ SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
+
+ CreateVideoDecoder();
+
+ base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
+ base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(600);
+ SetDataFactory(scoped_ptr<DataFactory>(new VideoFactory(duration)));
+
+ // Prefetch
+ decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
+ base::Unretained(this)));
+
+ EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
+ base::Unretained(this))));
+
+ decoder_->SetDemuxerConfigs(CreateVideoConfigs(duration));
+
+ SetVideoSurface();
+
+ EXPECT_EQ(MediaCodecDecoder::CONFIG_OK, decoder_->Configure());
+
+ EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
+
+ EXPECT_TRUE(WaitForCondition(
+ base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this)),
+ timeout));
+
+ EXPECT_TRUE(decoder_->IsStopped());
+ EXPECT_TRUE(decoder_->IsCompleted());
+
+ EXPECT_EQ(26, pts_stat_.num_values());
+ EXPECT_EQ(data_factory_->last_pts(), pts_stat_.max());
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698