blob: 2b14820a9c0acb4cb2ebda3abdf34cc86ebcc8f8 [file] [log] [blame]
// 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/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_util.h"
#include "media/base/android/media_codec_video_decoder.h"
#include "media/base/android/media_statistics.h"
#include "media/base/android/sdk_media_codec_bridge.h"
#include "media/base/android/test_data_factory.h"
#include "media/base/android/test_statistics.h"
#include "media/base/timestamp_constants.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/android/surface_texture.h"
namespace media {
namespace {
const base::TimeDelta kDefaultTimeout = base::TimeDelta::FromMilliseconds(200);
const base::TimeDelta kAudioFramePeriod =
base::TimeDelta::FromSecondsD(1024.0 / 44100); // 1024 samples @ 44100 Hz
const base::TimeDelta kVideoFramePeriod = base::TimeDelta::FromMilliseconds(20);
// A helper function to calculate the expected number of frames.
int GetFrameCount(base::TimeDelta duration, base::TimeDelta frame_period) {
// A chunk has 4 access units. The last unit timestamp must exceed the
// duration. Last chunk has 3 regular access units and one stand-alone EOS
// unit that we do not count.
// Number of time intervals to exceed duration.
int num_intervals = duration / frame_period + 1.0;
// To cover these intervals we need one extra unit at the beginning.
int num_units = num_intervals + 1;
// Number of 4-unit chunks that hold these units:
int num_chunks = (num_units + 3) / 4;
// Altogether these chunks hold 4*num_chunks units, but we do not count
// the last EOS as a frame.
return 4 * num_chunks - 1;
}
class AudioFactory : public TestDataFactory {
public:
AudioFactory(base::TimeDelta duration);
DemuxerConfigs GetConfigs() const override;
protected:
void ModifyChunk(DemuxerData* chunk) override;
};
class VideoFactory : public TestDataFactory {
public:
VideoFactory(base::TimeDelta duration);
DemuxerConfigs GetConfigs() const override;
protected:
void ModifyChunk(DemuxerData* chunk) override;
};
AudioFactory::AudioFactory(base::TimeDelta duration)
: TestDataFactory("aac-44100-packet-%d", duration, kAudioFramePeriod) {
}
DemuxerConfigs AudioFactory::GetConfigs() const {
return TestDataFactory::CreateAudioConfigs(kCodecAAC, duration_);
}
void AudioFactory::ModifyChunk(DemuxerData* chunk) {
DCHECK(chunk);
for (AccessUnit& unit : chunk->access_units) {
if (!unit.data.empty())
unit.is_key_frame = true;
}
}
VideoFactory::VideoFactory(base::TimeDelta duration)
: TestDataFactory("h264-320x180-frame-%d", duration, kVideoFramePeriod) {
}
DemuxerConfigs VideoFactory::GetConfigs() const {
return TestDataFactory::CreateVideoConfigs(kCodecH264, duration_,
gfx::Size(320, 180));
}
void VideoFactory::ModifyChunk(DemuxerData* 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.
// If the chunk contains EOS, it should not break the presentation order.
// For instance, the following chunk is ok:
//
// Frames: I P B EOS
// Decoding order: 0 1 2 -
// Presentation order: 0 2 1 -
//
// while this one might cause decoder to block:
//
// Frames: I P EOS
// Decoding order: 0 1 -
// Presentation order: 0 2 - <------- might wait for the B frame forever
//
// With current base class implementation that always has EOS at the 4th
// place we are covered (http://crbug.com/526755)
DCHECK(chunk);
DCHECK(chunk->access_units.size() == 4);
// Swap pts for second and third frames. Make first frame a key frame.
base::TimeDelta tmp = chunk->access_units[1].timestamp;
chunk->access_units[1].timestamp = chunk->access_units[2].timestamp;
chunk->access_units[2].timestamp = tmp;
chunk->access_units[0].is_key_frame = true;
}
} // namespace (anonymous)
// 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_; }
void SetPrefetched(bool value) { is_prefetched_ = value; }
void SetStopped(bool value) { is_stopped_ = value; }
void SetStarved(bool value) { is_starved_ = value; }
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<TestDataFactory> factory) {
data_factory_ = factory.Pass();
}
DemuxerConfigs GetConfigs() const {
// ASSERT_NE does not compile here because it expects void return value.
EXPECT_NE(nullptr, data_factory_.get());
return data_factory_->GetConfigs();
}
void CreateAudioDecoder();
void CreateVideoDecoder();
void SetVideoSurface();
void SetStopRequestAtTime(const base::TimeDelta& time) {
stop_request_time_ = time;
}
// Decoder callbacks.
void OnDataRequested();
void OnStarvation() { is_starved_ = true; }
void OnDecoderDrained() {}
void OnStopDone() { is_stopped_ = true; }
void OnKeyRequired() {}
void OnError() { DVLOG(0) << "MediaCodecDecoderTest::" << __FUNCTION__; }
void OnUpdateCurrentTime(base::TimeDelta now_playing,
base::TimeDelta last_buffered,
bool postpone) {
// Add the |last_buffered| value for PTS. For video it is the same as
// |now_playing| and is equal to PTS, for audio |last_buffered| should
// exceed PTS.
if (postpone)
return;
pts_stat_.AddValue(last_buffered);
if (stop_request_time_ != kNoTimestamp() &&
now_playing >= stop_request_time_) {
stop_request_time_ = kNoTimestamp();
decoder_->RequestToStop();
}
}
void OnVideoSizeChanged(const gfx::Size& video_size) {
video_size_ = video_size;
}
void OnVideoCodecCreated() {}
scoped_ptr<MediaCodecDecoder> decoder_;
scoped_ptr<TestDataFactory> data_factory_;
Minimax<base::TimeDelta> pts_stat_;
gfx::Size video_size_;
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_;
base::TimeDelta stop_request_time_;
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
FrameStatistics frame_statistics_;
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),
stop_request_time_(kNoTimestamp()),
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_, &frame_statistics_,
base::Bind(&MediaCodecDecoderTest::OnDataRequested,
base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnStarvation, base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnDecoderDrained,
base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnStopDone, base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnKeyRequired, 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_, &frame_statistics_,
base::Bind(&MediaCodecDecoderTest::OnDataRequested,
base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnStarvation, base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnDecoderDrained,
base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnStopDone, base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnKeyRequired, base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnError, base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnUpdateCurrentTime,
base::Unretained(this)),
base::Bind(&MediaCodecDecoderTest::OnVideoSizeChanged,
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;
if (!data_factory_->CreateChunk(&data, &delay))
return;
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->SetVideoSurface(surface.Pass());
}
TEST_F(MediaCodecDecoderTest, AudioPrefetch) {
CreateAudioDecoder();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
SetDataFactory(scoped_ptr<TestDataFactory>(new AudioFactory(duration)));
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
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<VideoFactory>(new VideoFactory(duration)));
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
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::kConfigFailure, decoder_->Configure(nullptr));
}
TEST_F(MediaCodecDecoderTest, AudioConfigureValidParams) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
CreateAudioDecoder();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
scoped_ptr<AudioFactory> factory(new AudioFactory(duration));
decoder_->SetDemuxerConfigs(factory->GetConfigs());
EXPECT_EQ(MediaCodecDecoder::kConfigOk, decoder_->Configure(nullptr));
}
TEST_F(MediaCodecDecoderTest, VideoConfigureNoParams) {
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<VideoFactory>(new VideoFactory(duration)));
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
SetVideoSurface();
// Cannot configure without config parameters.
EXPECT_EQ(MediaCodecDecoder::kConfigFailure, decoder_->Configure(nullptr));
}
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<VideoFactory>(new VideoFactory(duration)));
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
decoder_->SetDemuxerConfigs(GetConfigs());
// Surface is not set, Configure() should fail.
EXPECT_EQ(MediaCodecDecoder::kConfigFailure, decoder_->Configure(nullptr));
}
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<VideoFactory>(new VideoFactory(duration)));
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
decoder_->SetDemuxerConfigs(GetConfigs());
// 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->SetVideoSurface(surface.Pass());
EXPECT_EQ(MediaCodecDecoder::kConfigFailure, decoder_->Configure(nullptr));
}
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<VideoFactory>(new VideoFactory(duration)));
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
decoder_->SetDemuxerConfigs(GetConfigs());
SetVideoSurface();
// Now we can expect Configure() to succeed.
EXPECT_EQ(MediaCodecDecoder::kConfigOk, decoder_->Configure(nullptr));
}
TEST_F(MediaCodecDecoderTest, AudioStartWithoutConfigure) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
CreateAudioDecoder();
// Decoder has to be prefetched and configured before the start.
// Wrong state: not prefetched
EXPECT_FALSE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
// Do the prefetch.
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
SetDataFactory(scoped_ptr<AudioFactory>(new AudioFactory(duration)));
// Prefetch to avoid starvation at the beginning of playback.
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
// Still, decoder is not configured.
EXPECT_FALSE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
}
// http://crbug.com/518900
TEST_F(MediaCodecDecoderTest, AudioPlayTillCompletion) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
DVLOG(0) << "AudioPlayTillCompletion started";
CreateAudioDecoder();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(1500);
SetDataFactory(scoped_ptr<AudioFactory>(new AudioFactory(duration)));
// Prefetch to avoid starvation at the beginning of playback.
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
decoder_->SetDemuxerConfigs(GetConfigs());
EXPECT_EQ(MediaCodecDecoder::kConfigOk, decoder_->Configure(nullptr));
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());
// Last buffered timestamp should be no less than PTS.
// The number of hits in pts_stat_ depends on the preroll implementation.
// We might not report the time for the first buffer after preroll that
// is written to the audio track. pts_stat_.num_values() is either 21 or 22.
EXPECT_LE(21, pts_stat_.num_values());
EXPECT_LE(data_factory_->last_pts(), pts_stat_.max());
DVLOG(0) << "AudioPlayTillCompletion stopping";
}
TEST_F(MediaCodecDecoderTest, VideoPlayTillCompletion) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
CreateVideoDecoder();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
// The first output frame might come out with significant delay. Apparently
// the codec does initial configuration at this time. We increase the timeout
// to leave a room of 1 second for this initial configuration.
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(1500);
SetDataFactory(scoped_ptr<VideoFactory>(new VideoFactory(duration)));
// Prefetch
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
decoder_->SetDemuxerConfigs(GetConfigs());
SetVideoSurface();
EXPECT_EQ(MediaCodecDecoder::kConfigOk, decoder_->Configure(nullptr));
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());
int expected_video_frames = GetFrameCount(duration, kVideoFramePeriod);
EXPECT_EQ(expected_video_frames, pts_stat_.num_values());
EXPECT_EQ(data_factory_->last_pts(), pts_stat_.max());
}
TEST_F(MediaCodecDecoderTest, VideoStopAndResume) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
CreateVideoDecoder();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(500);
base::TimeDelta stop_request_time = base::TimeDelta::FromMilliseconds(200);
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(1000);
SetDataFactory(scoped_ptr<VideoFactory>(new VideoFactory(duration)));
// Prefetch
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
decoder_->SetDemuxerConfigs(GetConfigs());
SetVideoSurface();
EXPECT_EQ(MediaCodecDecoder::kConfigOk, decoder_->Configure(nullptr));
SetStopRequestAtTime(stop_request_time);
// Start from the beginning.
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_FALSE(decoder_->IsCompleted());
base::TimeDelta last_pts = pts_stat_.max();
EXPECT_GE(last_pts, stop_request_time);
// Resume playback from last_pts:
SetPrefetched(false);
SetStopped(false);
// Prefetch again.
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
// Then start.
EXPECT_TRUE(decoder_->Start(last_pts));
// Wait till completion.
EXPECT_TRUE(WaitForCondition(
base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this)),
timeout));
EXPECT_TRUE(decoder_->IsStopped());
EXPECT_TRUE(decoder_->IsCompleted());
// We should not skip frames in this process.
int expected_video_frames = GetFrameCount(duration, kVideoFramePeriod);
EXPECT_EQ(expected_video_frames, pts_stat_.num_values());
EXPECT_EQ(data_factory_->last_pts(), pts_stat_.max());
}
// http://crbug.com/518900
TEST_F(MediaCodecDecoderTest, DISABLED_AudioStarvationAndStop) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
CreateAudioDecoder();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(200);
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(400);
AudioFactory* factory = new AudioFactory(duration);
factory->SetStarvationMode(true);
SetDataFactory(scoped_ptr<AudioFactory>(factory));
// Prefetch.
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
// Configure.
decoder_->SetDemuxerConfigs(GetConfigs());
EXPECT_EQ(MediaCodecDecoder::kConfigOk, decoder_->Configure(nullptr));
// Start.
EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
// Wait for starvation.
EXPECT_TRUE(WaitForCondition(
base::Bind(&MediaCodecDecoderTest::is_starved, base::Unretained(this)),
timeout));
EXPECT_FALSE(decoder_->IsStopped());
EXPECT_FALSE(decoder_->IsCompleted());
EXPECT_GT(pts_stat_.num_values(), 0);
// After starvation we should be able to stop decoder.
decoder_->RequestToStop();
EXPECT_TRUE(WaitForCondition(
base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this))));
EXPECT_TRUE(decoder_->IsStopped());
EXPECT_FALSE(decoder_->IsCompleted());
}
TEST_F(MediaCodecDecoderTest, VideoFirstUnitIsReconfig) {
SKIP_TEST_IF_MEDIA_CODEC_BRIDGE_IS_NOT_AVAILABLE();
// Test that the kConfigChanged unit that comes before the first data unit
// gets processed, i.e. is not lost.
CreateVideoDecoder();
base::TimeDelta duration = base::TimeDelta::FromMilliseconds(200);
base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(1000);
SetDataFactory(scoped_ptr<VideoFactory>(new VideoFactory(duration)));
// Ask factory to produce initial configuration unit. The configuraton will
// be factory.GetConfigs().
data_factory_->RequestInitialConfigs();
// Create am alternative configuration (we just alter video size).
DemuxerConfigs alt_configs = data_factory_->GetConfigs();
alt_configs.video_size = gfx::Size(100, 100);
// Pass the alternative configuration to decoder.
decoder_->SetDemuxerConfigs(alt_configs);
// Prefetch.
decoder_->Prefetch(base::Bind(&MediaCodecDecoderTest::SetPrefetched,
base::Unretained(this), true));
EXPECT_TRUE(WaitForCondition(base::Bind(&MediaCodecDecoderTest::is_prefetched,
base::Unretained(this))));
// Current implementation reports the new video size after
// SetDemuxerConfigs(), verify that it is alt size.
EXPECT_EQ(alt_configs.video_size, video_size_);
SetVideoSurface();
// Configure.
EXPECT_EQ(MediaCodecDecoder::kConfigOk, decoder_->Configure(nullptr));
// Start.
EXPECT_TRUE(decoder_->Start(base::TimeDelta::FromMilliseconds(0)));
// Wait for completion.
EXPECT_TRUE(WaitForCondition(
base::Bind(&MediaCodecDecoderTest::is_stopped, base::Unretained(this)),
timeout));
EXPECT_TRUE(decoder_->IsStopped());
EXPECT_TRUE(decoder_->IsCompleted());
EXPECT_EQ(data_factory_->last_pts(), pts_stat_.max());
// Check that the reported video size is the one from the in-stream configs.
EXPECT_EQ(data_factory_->GetConfigs().video_size, video_size_);
}
} // namespace media