summaryrefslogtreecommitdiffstats
path: root/chromium/media/filters/pipeline_integration_test.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/filters/pipeline_integration_test.cc')
-rw-r--r--chromium/media/filters/pipeline_integration_test.cc625
1 files changed, 492 insertions, 133 deletions
diff --git a/chromium/media/filters/pipeline_integration_test.cc b/chromium/media/filters/pipeline_integration_test.cc
index bdf33f22418..f991dc39977 100644
--- a/chromium/media/filters/pipeline_integration_test.cc
+++ b/chromium/media/filters/pipeline_integration_test.cc
@@ -9,6 +9,7 @@
#include "base/memory/scoped_ptr.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
+#include "media/base/cdm_promise.h"
#include "media/base/decoder_buffer.h"
#include "media/base/media_keys.h"
#include "media/base/media_switches.h"
@@ -17,8 +18,10 @@
#include "media/cdm/json_web_key.h"
#include "media/filters/chunk_demuxer.h"
+using testing::_;
using testing::AnyNumber;
using testing::AtMost;
+using testing::SaveArg;
namespace media {
@@ -33,6 +36,7 @@ const char kVideoOnlyWebM[] = "video/webm; codecs=\"vp8\"";
const char kMP4VideoType[] = "video/mp4";
const char kMP4AudioType[] = "audio/mp4";
#if defined(USE_PROPRIETARY_CODECS)
+const char kADTS[] = "audio/aac";
const char kMP4[] = "video/mp4; codecs=\"avc1.4D4041,mp4a.40.2\"";
const char kMP4Video[] = "video/mp4; codecs=\"avc1.4D4041\"";
const char kMP4VideoAVC3[] = "video/mp4; codecs=\"avc3.64001f\"";
@@ -57,19 +61,50 @@ const int kAppendWholeFile = -1;
// Constants for the Media Source config change tests.
const int kAppendTimeSec = 1;
const int kAppendTimeMs = kAppendTimeSec * 1000;
-const int k320WebMFileDurationMs = 2737;
-const int k640WebMFileDurationMs = 2763;
-const int kOpusEndTrimmingWebMFileDurationMs = 2771;
-const int kVP9WebMFileDurationMs = 2735;
-const int kVP8AWebMFileDurationMs = 2700;
+const int k320WebMFileDurationMs = 2736;
+const int k640WebMFileDurationMs = 2749;
+const int kOpusEndTrimmingWebMFileDurationMs = 2741;
+const int kVP9WebMFileDurationMs = 2736;
+const int kVP8AWebMFileDurationMs = 2733;
#if defined(USE_PROPRIETARY_CODECS)
const int k640IsoFileDurationMs = 2737;
const int k640IsoCencFileDurationMs = 2736;
const int k1280IsoFileDurationMs = 2736;
-const int k1280IsoAVC3FileDurationMs = 2735;
+const int k1280IsoAVC3FileDurationMs = 2736;
#endif // defined(USE_PROPRIETARY_CODECS)
+// Return a timeline offset for bear-320x240-live.webm.
+static base::Time kLiveTimelineOffset() {
+ // The file contians the following UTC timeline offset:
+ // 2012-11-10 12:34:56.789123456
+ // Since base::Time only has a resolution of microseconds,
+ // construct a base::Time for 2012-11-10 12:34:56.789123.
+ base::Time::Exploded exploded_time;
+ exploded_time.year = 2012;
+ exploded_time.month = 11;
+ exploded_time.day_of_month = 10;
+ exploded_time.hour = 12;
+ exploded_time.minute = 34;
+ exploded_time.second = 56;
+ exploded_time.millisecond = 789;
+ base::Time timeline_offset = base::Time::FromUTCExploded(exploded_time);
+
+ timeline_offset += base::TimeDelta::FromMicroseconds(123);
+
+ return timeline_offset;
+}
+
+// FFmpeg only supports time a resolution of seconds so this
+// helper function truncates a base::Time to seconds resolution.
+static base::Time TruncateToFFmpegTimeResolution(base::Time t) {
+ base::Time::Exploded exploded_time;
+ t.UTCExplode(&exploded_time);
+ exploded_time.millisecond = 0;
+
+ return base::Time::FromUTCExploded(exploded_time);
+}
+
// Note: Tests using this class only exercise the DecryptingDemuxerStream path.
// They do not exercise the Decrypting{Audio|Video}Decoder path.
class FakeEncryptedMedia {
@@ -79,21 +114,19 @@ class FakeEncryptedMedia {
public:
virtual ~AppBase() {}
- virtual void OnSessionCreated(uint32 session_id,
- const std::string& web_session_id) = 0;
-
- virtual void OnSessionMessage(uint32 session_id,
+ virtual void OnSessionMessage(const std::string& web_session_id,
const std::vector<uint8>& message,
- const std::string& destination_url) = 0;
+ const GURL& destination_url) = 0;
- virtual void OnSessionReady(uint32 session_id) = 0;
+ virtual void OnSessionReady(const std::string& web_session_id) = 0;
- virtual void OnSessionClosed(uint32 session_id) = 0;
+ virtual void OnSessionClosed(const std::string& web_session_id) = 0;
// Errors are not expected unless overridden.
- virtual void OnSessionError(uint32 session_id,
- MediaKeys::KeyError error_code,
- int system_code) {
+ virtual void OnSessionError(const std::string& web_session_id,
+ const std::string& error_name,
+ uint32 system_code,
+ const std::string& error_message) {
FAIL() << "Unexpected Key Error";
}
@@ -103,15 +136,9 @@ class FakeEncryptedMedia {
};
FakeEncryptedMedia(AppBase* app)
- : decryptor_(base::Bind(&FakeEncryptedMedia::OnSessionCreated,
- base::Unretained(this)),
- base::Bind(&FakeEncryptedMedia::OnSessionMessage,
- base::Unretained(this)),
- base::Bind(&FakeEncryptedMedia::OnSessionReady,
+ : decryptor_(base::Bind(&FakeEncryptedMedia::OnSessionMessage,
base::Unretained(this)),
base::Bind(&FakeEncryptedMedia::OnSessionClosed,
- base::Unretained(this)),
- base::Bind(&FakeEncryptedMedia::OnSessionError,
base::Unretained(this))),
app_(app) {}
@@ -120,28 +147,26 @@ class FakeEncryptedMedia {
}
// Callbacks for firing session events. Delegate to |app_|.
- void OnSessionCreated(uint32 session_id, const std::string& web_session_id) {
- app_->OnSessionCreated(session_id, web_session_id);
- }
-
- void OnSessionMessage(uint32 session_id,
+ void OnSessionMessage(const std::string& web_session_id,
const std::vector<uint8>& message,
- const std::string& destination_url) {
- app_->OnSessionMessage(session_id, message, destination_url);
+ const GURL& destination_url) {
+ app_->OnSessionMessage(web_session_id, message, destination_url);
}
- void OnSessionReady(uint32 session_id) {
- app_->OnSessionReady(session_id);
+ void OnSessionReady(const std::string& web_session_id) {
+ app_->OnSessionReady(web_session_id);
}
- void OnSessionClosed(uint32 session_id) {
- app_->OnSessionClosed(session_id);
+ void OnSessionClosed(const std::string& web_session_id) {
+ app_->OnSessionClosed(web_session_id);
}
- void OnSessionError(uint32 session_id,
- MediaKeys::KeyError error_code,
- int system_code) {
- app_->OnSessionError(session_id, error_code, system_code);
+ void OnSessionError(const std::string& web_session_id,
+ const std::string& error_name,
+ uint32 system_code,
+ const std::string& error_message) {
+ app_->OnSessionError(
+ web_session_id, error_name, system_code, error_message);
}
void NeedKey(const std::string& type,
@@ -154,44 +179,80 @@ class FakeEncryptedMedia {
scoped_ptr<AppBase> app_;
};
+enum PromiseResult { RESOLVED, REJECTED };
+
// Provides |kSecretKey| in response to needkey.
class KeyProvidingApp : public FakeEncryptedMedia::AppBase {
public:
- KeyProvidingApp() : current_session_id_(0) {}
+ KeyProvidingApp() {}
- virtual void OnSessionCreated(uint32 session_id,
- const std::string& web_session_id) OVERRIDE {
- EXPECT_GT(session_id, 0u);
- EXPECT_FALSE(web_session_id.empty());
+ void OnResolveWithSession(PromiseResult expected,
+ const std::string& web_session_id) {
+ EXPECT_EQ(expected, RESOLVED);
+ EXPECT_GT(web_session_id.length(), 0ul);
+ current_session_id_ = web_session_id;
+ }
+
+ void OnResolve(PromiseResult expected) {
+ EXPECT_EQ(expected, RESOLVED);
+ }
+
+ void OnReject(PromiseResult expected,
+ media::MediaKeys::Exception exception_code,
+ uint32 system_code,
+ const std::string& error_message) {
+ EXPECT_EQ(expected, REJECTED);
+ }
+
+ scoped_ptr<SimpleCdmPromise> CreatePromise(PromiseResult expected) {
+ scoped_ptr<media::SimpleCdmPromise> promise(new media::SimpleCdmPromise(
+ base::Bind(
+ &KeyProvidingApp::OnResolve, base::Unretained(this), expected),
+ base::Bind(
+ &KeyProvidingApp::OnReject, base::Unretained(this), expected)));
+ return promise.Pass();
+ }
+
+ scoped_ptr<NewSessionCdmPromise> CreateSessionPromise(
+ PromiseResult expected) {
+ scoped_ptr<media::NewSessionCdmPromise> promise(
+ new media::NewSessionCdmPromise(
+ base::Bind(&KeyProvidingApp::OnResolveWithSession,
+ base::Unretained(this),
+ expected),
+ base::Bind(
+ &KeyProvidingApp::OnReject, base::Unretained(this), expected)));
+ return promise.Pass();
}
- virtual void OnSessionMessage(uint32 session_id,
+ virtual void OnSessionMessage(const std::string& web_session_id,
const std::vector<uint8>& message,
- const std::string& default_url) OVERRIDE {
- EXPECT_GT(session_id, 0u);
+ const GURL& destination_url) OVERRIDE {
+ EXPECT_FALSE(web_session_id.empty());
EXPECT_FALSE(message.empty());
-
- current_session_id_ = session_id;
+ EXPECT_EQ(current_session_id_, web_session_id);
}
- virtual void OnSessionReady(uint32 session_id) OVERRIDE {
- EXPECT_GT(session_id, 0u);
+ virtual void OnSessionReady(const std::string& web_session_id) OVERRIDE {
+ EXPECT_EQ(current_session_id_, web_session_id);
}
- virtual void OnSessionClosed(uint32 session_id) OVERRIDE {
- EXPECT_GT(session_id, 0u);
+ virtual void OnSessionClosed(const std::string& web_session_id) OVERRIDE {
+ EXPECT_EQ(current_session_id_, web_session_id);
}
virtual void NeedKey(const std::string& type,
const std::vector<uint8>& init_data,
AesDecryptor* decryptor) OVERRIDE {
- if (current_session_id_ == 0u) {
- EXPECT_TRUE(
- decryptor->CreateSession(12, type, kInitData, arraysize(kInitData)));
+ if (current_session_id_.empty()) {
+ decryptor->CreateSession(type,
+ kInitData,
+ arraysize(kInitData),
+ MediaKeys::TEMPORARY_SESSION,
+ CreateSessionPromise(RESOLVED));
+ EXPECT_FALSE(current_session_id_.empty());
}
- EXPECT_EQ(current_session_id_, 12u);
-
// Clear Key really needs the key ID in |init_data|. For WebM, they are the
// same, but this is not the case for ISO CENC. Therefore, provide the
// correct key ID.
@@ -207,36 +268,103 @@ class KeyProvidingApp : public FakeEncryptedMedia::AppBase {
kSecretKey, arraysize(kSecretKey), key_id, key_id_length);
decryptor->UpdateSession(current_session_id_,
reinterpret_cast<const uint8*>(jwk.data()),
- jwk.size());
+ jwk.size(),
+ CreatePromise(RESOLVED));
}
- uint32 current_session_id_;
+ std::string current_session_id_;
};
-// Ignores needkey and does not perform a license request
-class NoResponseApp : public FakeEncryptedMedia::AppBase {
+class RotatingKeyProvidingApp : public KeyProvidingApp {
public:
- virtual void OnSessionCreated(uint32 session_id,
- const std::string& web_session_id) OVERRIDE {
- EXPECT_GT(session_id, 0u);
- EXPECT_FALSE(web_session_id.empty());
+ RotatingKeyProvidingApp() : num_distint_need_key_calls_(0) {}
+ virtual ~RotatingKeyProvidingApp() {
+ // Expect that NeedKey is fired multiple times with different |init_data|.
+ EXPECT_GT(num_distint_need_key_calls_, 1u);
}
- virtual void OnSessionMessage(uint32 session_id,
+ virtual void NeedKey(const std::string& type,
+ const std::vector<uint8>& init_data,
+ AesDecryptor* decryptor) OVERRIDE {
+ // Skip the request if the |init_data| has been seen.
+ if (init_data == prev_init_data_)
+ return;
+ prev_init_data_ = init_data;
+ ++num_distint_need_key_calls_;
+
+ decryptor->CreateSession(type,
+ vector_as_array(&init_data),
+ init_data.size(),
+ MediaKeys::TEMPORARY_SESSION,
+ CreateSessionPromise(RESOLVED));
+
+ std::vector<uint8> key_id;
+ std::vector<uint8> key;
+ EXPECT_TRUE(GetKeyAndKeyId(init_data, &key, &key_id));
+
+ // Convert key into a JSON structure and then add it.
+ std::string jwk = GenerateJWKSet(vector_as_array(&key),
+ key.size(),
+ vector_as_array(&key_id),
+ key_id.size());
+ decryptor->UpdateSession(current_session_id_,
+ reinterpret_cast<const uint8*>(jwk.data()),
+ jwk.size(),
+ CreatePromise(RESOLVED));
+ }
+
+ private:
+ bool GetKeyAndKeyId(std::vector<uint8> init_data,
+ std::vector<uint8>* key,
+ std::vector<uint8>* key_id) {
+ // For WebM, init_data is key_id; for ISO CENC, init_data should contain
+ // the key_id. We assume key_id is in the end of init_data here (that is
+ // only a reasonable assumption for WebM and clear key ISO CENC).
+ DCHECK_GE(init_data.size(), arraysize(kKeyId));
+ std::vector<uint8> key_id_from_init_data(
+ init_data.end() - arraysize(kKeyId), init_data.end());
+
+ key->assign(kSecretKey, kSecretKey + arraysize(kSecretKey));
+ key_id->assign(kKeyId, kKeyId + arraysize(kKeyId));
+
+ // The Key and KeyId for this testing key provider are created by left
+ // rotating kSecretKey and kKeyId. Note that this implementation is only
+ // intended for testing purpose. The actual key rotation algorithm can be
+ // much more complicated.
+ // Find out the rotating position from |key_id_from_init_data| and apply on
+ // |key|.
+ for (size_t pos = 0; pos < arraysize(kKeyId); ++pos) {
+ std::rotate(key_id->begin(), key_id->begin() + pos, key_id->end());
+ if (*key_id == key_id_from_init_data) {
+ std::rotate(key->begin(), key->begin() + pos, key->end());
+ return true;
+ }
+ }
+ return false;
+ }
+
+ std::vector<uint8> prev_init_data_;
+ uint32 num_distint_need_key_calls_;
+};
+
+// Ignores needkey and does not perform a license request
+class NoResponseApp : public FakeEncryptedMedia::AppBase {
+ public:
+ virtual void OnSessionMessage(const std::string& web_session_id,
const std::vector<uint8>& message,
- const std::string& default_url) OVERRIDE {
- EXPECT_GT(session_id, 0u);
+ const GURL& default_url) OVERRIDE {
+ EXPECT_FALSE(web_session_id.empty());
EXPECT_FALSE(message.empty());
- FAIL() << "Unexpected KeyMessage";
+ FAIL() << "Unexpected Message";
}
- virtual void OnSessionReady(uint32 session_id) OVERRIDE {
- EXPECT_GT(session_id, 0u);
+ virtual void OnSessionReady(const std::string& web_session_id) OVERRIDE {
+ EXPECT_FALSE(web_session_id.empty());
FAIL() << "Unexpected Ready";
}
- virtual void OnSessionClosed(uint32 session_id) OVERRIDE {
- EXPECT_GT(session_id, 0u);
+ virtual void OnSessionClosed(const std::string& web_session_id) OVERRIDE {
+ EXPECT_FALSE(web_session_id.empty());
FAIL() << "Unexpected Closed";
}
@@ -250,18 +378,19 @@ class NoResponseApp : public FakeEncryptedMedia::AppBase {
// Media Source API.
class MockMediaSource {
public:
- MockMediaSource(const std::string& filename, const std::string& mimetype,
+ MockMediaSource(const std::string& filename,
+ const std::string& mimetype,
int initial_append_size)
: file_path_(GetTestDataFilePath(filename)),
current_position_(0),
initial_append_size_(initial_append_size),
mimetype_(mimetype),
chunk_demuxer_(new ChunkDemuxer(
- base::Bind(&MockMediaSource::DemuxerOpened,
- base::Unretained(this)),
+ base::Bind(&MockMediaSource::DemuxerOpened, base::Unretained(this)),
base::Bind(&MockMediaSource::DemuxerNeedKey,
base::Unretained(this)),
- LogCB())),
+ LogCB(),
+ true)),
owned_chunk_demuxer_(chunk_demuxer_) {
file_data_ = ReadTestDataFile(filename);
@@ -284,7 +413,9 @@ class MockMediaSource {
void Seek(base::TimeDelta seek_time, int new_position, int seek_append_size) {
chunk_demuxer_->StartWaitingForSeek(seek_time);
- chunk_demuxer_->Abort(kSourceId);
+ chunk_demuxer_->Abort(
+ kSourceId,
+ base::TimeDelta(), kInfiniteDuration(), &last_timestamp_offset_);
DCHECK_GE(new_position, 0);
DCHECK_LT(new_position, file_data_->data_size());
@@ -297,16 +428,36 @@ class MockMediaSource {
DCHECK(chunk_demuxer_);
DCHECK_LT(current_position_, file_data_->data_size());
DCHECK_LE(current_position_ + size, file_data_->data_size());
+
chunk_demuxer_->AppendData(
- kSourceId, file_data_->data() + current_position_, size);
+ kSourceId, file_data_->data() + current_position_, size,
+ base::TimeDelta(), kInfiniteDuration(), &last_timestamp_offset_);
current_position_ += size;
}
- void AppendAtTime(const base::TimeDelta& timestampOffset,
- const uint8* pData, int size) {
- CHECK(chunk_demuxer_->SetTimestampOffset(kSourceId, timestampOffset));
- chunk_demuxer_->AppendData(kSourceId, pData, size);
- CHECK(chunk_demuxer_->SetTimestampOffset(kSourceId, base::TimeDelta()));
+ void AppendAtTime(base::TimeDelta timestamp_offset,
+ const uint8* pData,
+ int size) {
+ CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId));
+ chunk_demuxer_->AppendData(kSourceId, pData, size,
+ base::TimeDelta(), kInfiniteDuration(),
+ &timestamp_offset);
+ last_timestamp_offset_ = timestamp_offset;
+ }
+
+ void AppendAtTimeWithWindow(base::TimeDelta timestamp_offset,
+ base::TimeDelta append_window_start,
+ base::TimeDelta append_window_end,
+ const uint8* pData,
+ int size) {
+ CHECK(!chunk_demuxer_->IsParsingMediaSegment(kSourceId));
+ chunk_demuxer_->AppendData(kSourceId,
+ pData,
+ size,
+ append_window_start,
+ append_window_end,
+ &timestamp_offset);
+ last_timestamp_offset_ = timestamp_offset;
}
void EndOfStream() {
@@ -352,6 +503,7 @@ class MockMediaSource {
}
CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk);
+
AppendData(initial_append_size_);
}
@@ -362,6 +514,10 @@ class MockMediaSource {
need_key_cb_.Run(type, init_data);
}
+ base::TimeDelta last_timestamp_offset() const {
+ return last_timestamp_offset_;
+ }
+
private:
base::FilePath file_path_;
scoped_refptr<DecoderBuffer> file_data_;
@@ -371,6 +527,7 @@ class MockMediaSource {
ChunkDemuxer* chunk_demuxer_;
scoped_ptr<Demuxer> owned_chunk_demuxer_;
Demuxer::NeedKeyCB need_key_cb_;
+ base::TimeDelta last_timestamp_offset_;
};
class PipelineIntegrationTest
@@ -378,16 +535,17 @@ class PipelineIntegrationTest
public PipelineIntegrationTestBase {
public:
void StartPipelineWithMediaSource(MockMediaSource* source) {
- EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
- .Times(AtMost(1));
- EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
- .Times(AtMost(1));
+ EXPECT_CALL(*this, OnMetadata(_)).Times(AtMost(1))
+ .WillRepeatedly(SaveArg<0>(&metadata_));
+ EXPECT_CALL(*this, OnPrerollCompleted()).Times(AtMost(1));
pipeline_->Start(
CreateFilterCollection(source->GetDemuxer(), NULL),
base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)),
base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)),
QuitOnStatusCB(PIPELINE_OK),
- base::Bind(&PipelineIntegrationTest::OnBufferingState,
+ base::Bind(&PipelineIntegrationTest::OnMetadata,
+ base::Unretained(this)),
+ base::Bind(&PipelineIntegrationTest::OnPrerollCompleted,
base::Unretained(this)),
base::Closure());
@@ -402,17 +560,18 @@ class PipelineIntegrationTest
void StartPipelineWithEncryptedMedia(
MockMediaSource* source,
FakeEncryptedMedia* encrypted_media) {
- EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
- .Times(AtMost(1));
- EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
- .Times(AtMost(1));
+ EXPECT_CALL(*this, OnMetadata(_)).Times(AtMost(1))
+ .WillRepeatedly(SaveArg<0>(&metadata_));
+ EXPECT_CALL(*this, OnPrerollCompleted()).Times(AtMost(1));
pipeline_->Start(
CreateFilterCollection(source->GetDemuxer(),
encrypted_media->decryptor()),
base::Bind(&PipelineIntegrationTest::OnEnded, base::Unretained(this)),
base::Bind(&PipelineIntegrationTest::OnError, base::Unretained(this)),
QuitOnStatusCB(PIPELINE_OK),
- base::Bind(&PipelineIntegrationTest::OnBufferingState,
+ base::Bind(&PipelineIntegrationTest::OnMetadata,
+ base::Unretained(this)),
+ base::Bind(&PipelineIntegrationTest::OnPrerollCompleted,
base::Unretained(this)),
base::Closure());
@@ -462,6 +621,14 @@ TEST_F(PipelineIntegrationTest, BasicPlayback) {
ASSERT_TRUE(WaitUntilOnEnded());
}
+TEST_F(PipelineIntegrationTest, BasicPlaybackOpusOgg) {
+ ASSERT_TRUE(Start(GetTestDataFilePath("bear-opus.ogg"), PIPELINE_OK));
+
+ Play();
+
+ ASSERT_TRUE(WaitUntilOnEnded());
+}
+
TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) {
ASSERT_TRUE(Start(
GetTestDataFilePath("bear-320x240.webm"), PIPELINE_OK, kHashed));
@@ -472,6 +639,24 @@ TEST_F(PipelineIntegrationTest, BasicPlaybackHashed) {
EXPECT_EQ("f0be120a90a811506777c99a2cdf7cc1", GetVideoHash());
EXPECT_EQ("-3.59,-2.06,-0.43,2.15,0.77,-0.95,", GetAudioHash());
+ EXPECT_TRUE(demuxer_->GetTimelineOffset().is_null());
+}
+
+TEST_F(PipelineIntegrationTest, BasicPlaybackLive) {
+ ASSERT_TRUE(Start(
+ GetTestDataFilePath("bear-320x240-live.webm"), PIPELINE_OK, kHashed));
+
+ Play();
+
+ ASSERT_TRUE(WaitUntilOnEnded());
+
+ EXPECT_EQ("f0be120a90a811506777c99a2cdf7cc1", GetVideoHash());
+ EXPECT_EQ("-3.59,-2.06,-0.43,2.15,0.77,-0.95,", GetAudioHash());
+
+ // TODO: Fix FFmpeg code to return higher resolution time values so
+ // we don't have to truncate our expectations here.
+ EXPECT_EQ(TruncateToFFmpegTimeResolution(kLiveTimelineOffset()),
+ demuxer_->GetTimelineOffset());
}
TEST_F(PipelineIntegrationTest, F32PlaybackHashed) {
@@ -510,15 +695,34 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource) {
Play();
ASSERT_TRUE(WaitUntilOnEnded());
+
+ EXPECT_TRUE(demuxer_->GetTimelineOffset().is_null());
source.Abort();
Stop();
}
-// TODO(fgalligan): Enable after new vp9 files are landed.
-// http://crbug.com/259116
-TEST_F(PipelineIntegrationTest,
- DISABLED_BasicPlayback_MediaSource_VideoOnly_VP9_WebM) {
- MockMediaSource source("bear-vp9.webm", kWebMVP9, 32393);
+TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Live) {
+ MockMediaSource source("bear-320x240-live.webm", kWebM, 219221);
+ StartPipelineWithMediaSource(&source);
+ source.EndOfStream();
+
+ EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+ EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+ EXPECT_EQ(k320WebMFileDurationMs,
+ pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+ Play();
+
+ ASSERT_TRUE(WaitUntilOnEnded());
+
+ EXPECT_EQ(kLiveTimelineOffset(),
+ demuxer_->GetTimelineOffset());
+ source.Abort();
+ Stop();
+}
+
+TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VP9_WebM) {
+ MockMediaSource source("bear-vp9.webm", kWebMVP9, 67504);
StartPipelineWithMediaSource(&source);
source.EndOfStream();
@@ -535,7 +739,6 @@ TEST_F(PipelineIntegrationTest,
}
TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VP8A_WebM) {
- EXPECT_CALL(*this, OnSetOpaque(false)).Times(AnyNumber());
MockMediaSource source("bear-vp8a.webm", kVideoOnlyWebM, kAppendWholeFile);
StartPipelineWithMediaSource(&source);
source.EndOfStream();
@@ -553,7 +756,6 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VP8A_WebM) {
}
TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Opus_WebM) {
- EXPECT_CALL(*this, OnSetOpaque(false)).Times(AnyNumber());
MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM,
kAppendWholeFile);
StartPipelineWithMediaSource(&source);
@@ -572,12 +774,10 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Opus_WebM) {
// Flaky. http://crbug.com/304776
TEST_F(PipelineIntegrationTest, DISABLED_MediaSource_Opus_Seeking_WebM) {
- EXPECT_CALL(*this, OnSetOpaque(false)).Times(AnyNumber());
MockMediaSource source("bear-opus-end-trimming.webm", kOpusAudioOnlyWebM,
kAppendWholeFile);
StartHashedPipelineWithMediaSource(&source);
-
EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs,
@@ -711,16 +911,110 @@ TEST_F(PipelineIntegrationTest,
}
#if defined(USE_PROPRIETARY_CODECS)
+TEST_F(PipelineIntegrationTest, MediaSource_ADTS) {
+ MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
+ StartPipelineWithMediaSource(&source);
+ source.EndOfStream();
+
+ EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+ EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+ EXPECT_EQ(325, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+ Play();
+
+ EXPECT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_F(PipelineIntegrationTest, MediaSource_ADTS_TimestampOffset) {
+ MockMediaSource source("sfx.adts", kADTS, kAppendWholeFile);
+ StartHashedPipelineWithMediaSource(&source);
+ EXPECT_EQ(325, source.last_timestamp_offset().InMilliseconds());
+
+ // Trim off multiple frames off the beginning of the segment which will cause
+ // the first decoded frame to be incorrect if preroll isn't implemented.
+ const base::TimeDelta adts_preroll_duration =
+ base::TimeDelta::FromSecondsD(2.5 * 1024 / 44100);
+ const base::TimeDelta append_time =
+ source.last_timestamp_offset() - adts_preroll_duration;
+
+ scoped_refptr<DecoderBuffer> second_file = ReadTestDataFile("sfx.adts");
+ source.AppendAtTimeWithWindow(append_time,
+ append_time + adts_preroll_duration,
+ kInfiniteDuration(),
+ second_file->data(),
+ second_file->data_size());
+ source.EndOfStream();
+
+ EXPECT_EQ(592, source.last_timestamp_offset().InMilliseconds());
+ EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+ EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+ EXPECT_EQ(592, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+ Play();
+
+ EXPECT_TRUE(WaitUntilOnEnded());
+
+ // Verify preroll is stripped.
+ EXPECT_EQ("-0.06,0.97,-0.90,-0.70,-0.53,-0.34,", GetAudioHash());
+}
+
+TEST_F(PipelineIntegrationTest, BasicPlaybackHashed_MP3) {
+ ASSERT_TRUE(Start(GetTestDataFilePath("sfx.mp3"), PIPELINE_OK, kHashed));
+
+ Play();
+
+ ASSERT_TRUE(WaitUntilOnEnded());
+
+ // Verify codec delay and preroll are stripped.
+ EXPECT_EQ("3.05,2.87,3.00,3.32,3.58,4.08,", GetAudioHash());
+}
+
TEST_F(PipelineIntegrationTest, MediaSource_MP3) {
MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
- StartPipelineWithMediaSource(&source);
+ StartHashedPipelineWithMediaSource(&source);
source.EndOfStream();
+ EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+ EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+ EXPECT_EQ(313, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
Play();
EXPECT_TRUE(WaitUntilOnEnded());
+
+ // Verify that codec delay was stripped.
+ EXPECT_EQ("1.01,2.71,4.18,4.32,3.04,1.12,", GetAudioHash());
}
+TEST_F(PipelineIntegrationTest, MediaSource_MP3_TimestampOffset) {
+ MockMediaSource source("sfx.mp3", kMP3, kAppendWholeFile);
+ StartPipelineWithMediaSource(&source);
+ EXPECT_EQ(313, source.last_timestamp_offset().InMilliseconds());
+
+ // There are 576 silent frames at the start of this mp3. The second append
+ // should trim them off.
+ const base::TimeDelta mp3_preroll_duration =
+ base::TimeDelta::FromSecondsD(576.0 / 44100);
+ const base::TimeDelta append_time =
+ source.last_timestamp_offset() - mp3_preroll_duration;
+
+ scoped_refptr<DecoderBuffer> second_file = ReadTestDataFile("sfx.mp3");
+ source.AppendAtTimeWithWindow(append_time,
+ append_time + mp3_preroll_duration,
+ kInfiniteDuration(),
+ second_file->data(),
+ second_file->data_size());
+ source.EndOfStream();
+
+ EXPECT_EQ(613, source.last_timestamp_offset().InMilliseconds());
+ EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+ EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+ EXPECT_EQ(613, pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+ Play();
+
+ EXPECT_TRUE(WaitUntilOnEnded());
+}
TEST_F(PipelineIntegrationTest, MediaSource_MP3_Icecast) {
MockMediaSource source("icy_sfx.mp3", kMP3, kAppendWholeFile);
@@ -758,8 +1052,8 @@ TEST_F(PipelineIntegrationTest, MediaSource_ConfigChange_MP4) {
TEST_F(PipelineIntegrationTest,
MediaSource_ConfigChange_Encrypted_MP4_CENC_VideoOnly) {
- MockMediaSource source("bear-640x360-v_frag-cenc.mp4",
- kMP4Video, kAppendWholeFile);
+ MockMediaSource source("bear-640x360-v_frag-cenc.mp4", kMP4Video,
+ kAppendWholeFile);
FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
StartPipelineWithEncryptedMedia(&source, &encrypted_media);
@@ -783,6 +1077,33 @@ TEST_F(PipelineIntegrationTest,
Stop();
}
+TEST_F(PipelineIntegrationTest,
+ MediaSource_ConfigChange_Encrypted_MP4_CENC_KeyRotation_VideoOnly) {
+ MockMediaSource source("bear-640x360-v_frag-cenc-key_rotation.mp4", kMP4Video,
+ kAppendWholeFile);
+ FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp());
+ StartPipelineWithEncryptedMedia(&source, &encrypted_media);
+
+ scoped_refptr<DecoderBuffer> second_file =
+ ReadTestDataFile("bear-1280x720-v_frag-cenc-key_rotation.mp4");
+
+ source.AppendAtTime(base::TimeDelta::FromSeconds(kAppendTimeSec),
+ second_file->data(), second_file->data_size());
+
+ source.EndOfStream();
+
+ EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
+ EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
+ EXPECT_EQ(kAppendTimeMs + k1280IsoFileDurationMs,
+ pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
+
+ Play();
+
+ EXPECT_TRUE(WaitUntilOnEnded());
+ source.Abort();
+ Stop();
+}
+
// Config changes from clear to encrypted are not currently supported.
// TODO(ddorwin): Figure out why this CHECKs in AppendAtTime().
TEST_F(PipelineIntegrationTest,
@@ -818,8 +1139,8 @@ TEST_F(PipelineIntegrationTest,
// Config changes from encrypted to clear are not currently supported.
TEST_F(PipelineIntegrationTest,
MediaSource_ConfigChange_EncryptedThenClear_MP4_CENC) {
- MockMediaSource source("bear-640x360-v_frag-cenc.mp4",
- kMP4Video, kAppendWholeFile);
+ MockMediaSource source("bear-640x360-v_frag-cenc.mp4", kMP4Video,
+ kAppendWholeFile);
FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
StartPipelineWithEncryptedMedia(&source, &encrypted_media);
@@ -876,8 +1197,8 @@ TEST_F(PipelineIntegrationTest, EncryptedPlayback_WebM) {
}
TEST_F(PipelineIntegrationTest, EncryptedPlayback_ClearStart_WebM) {
- MockMediaSource source("bear-320x240-av_enc-av_clear-1s.webm",
- kWebM, kAppendWholeFile);
+ MockMediaSource source("bear-320x240-av_enc-av_clear-1s.webm", kWebM,
+ kAppendWholeFile);
FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
StartPipelineWithEncryptedMedia(&source, &encrypted_media);
@@ -892,8 +1213,8 @@ TEST_F(PipelineIntegrationTest, EncryptedPlayback_ClearStart_WebM) {
}
TEST_F(PipelineIntegrationTest, EncryptedPlayback_NoEncryptedFrames_WebM) {
- MockMediaSource source("bear-320x240-av_enc-av_clear-all.webm",
- kWebM, kAppendWholeFile);
+ MockMediaSource source("bear-320x240-av_enc-av_clear-all.webm", kWebM,
+ kAppendWholeFile);
FakeEncryptedMedia encrypted_media(new NoResponseApp());
StartPipelineWithEncryptedMedia(&source, &encrypted_media);
@@ -909,8 +1230,8 @@ TEST_F(PipelineIntegrationTest, EncryptedPlayback_NoEncryptedFrames_WebM) {
#if defined(USE_PROPRIETARY_CODECS)
TEST_F(PipelineIntegrationTest, EncryptedPlayback_MP4_CENC_VideoOnly) {
- MockMediaSource source("bear-1280x720-v_frag-cenc.mp4",
- kMP4Video, kAppendWholeFile);
+ MockMediaSource source("bear-1280x720-v_frag-cenc.mp4", kMP4Video,
+ kAppendWholeFile);
FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
StartPipelineWithEncryptedMedia(&source, &encrypted_media);
@@ -925,8 +1246,8 @@ TEST_F(PipelineIntegrationTest, EncryptedPlayback_MP4_CENC_VideoOnly) {
}
TEST_F(PipelineIntegrationTest, EncryptedPlayback_MP4_CENC_AudioOnly) {
- MockMediaSource source("bear-1280x720-a_frag-cenc.mp4",
- kMP4Audio, kAppendWholeFile);
+ MockMediaSource source("bear-1280x720-a_frag-cenc.mp4", kMP4Audio,
+ kAppendWholeFile);
FakeEncryptedMedia encrypted_media(new KeyProvidingApp());
StartPipelineWithEncryptedMedia(&source, &encrypted_media);
@@ -942,8 +1263,8 @@ TEST_F(PipelineIntegrationTest, EncryptedPlayback_MP4_CENC_AudioOnly) {
TEST_F(PipelineIntegrationTest,
EncryptedPlayback_NoEncryptedFrames_MP4_CENC_VideoOnly) {
- MockMediaSource source("bear-1280x720-v_frag-cenc_clear-all.mp4",
- kMP4Video, kAppendWholeFile);
+ MockMediaSource source("bear-1280x720-v_frag-cenc_clear-all.mp4", kMP4Video,
+ kAppendWholeFile);
FakeEncryptedMedia encrypted_media(new NoResponseApp());
StartPipelineWithEncryptedMedia(&source, &encrypted_media);
@@ -959,8 +1280,8 @@ TEST_F(PipelineIntegrationTest,
TEST_F(PipelineIntegrationTest,
EncryptedPlayback_NoEncryptedFrames_MP4_CENC_AudioOnly) {
- MockMediaSource source("bear-1280x720-a_frag-cenc_clear-all.mp4",
- kMP4Audio, kAppendWholeFile);
+ MockMediaSource source("bear-1280x720-a_frag-cenc_clear-all.mp4", kMP4Audio,
+ kAppendWholeFile);
FakeEncryptedMedia encrypted_media(new NoResponseApp());
StartPipelineWithEncryptedMedia(&source, &encrypted_media);
@@ -992,6 +1313,37 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_VideoOnly_MP4_AVC3) {
Stop();
}
+TEST_F(PipelineIntegrationTest, EncryptedPlayback_MP4_CENC_KeyRotation_Video) {
+ MockMediaSource source("bear-1280x720-v_frag-cenc-key_rotation.mp4",
+ kMP4Video, kAppendWholeFile);
+ FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp());
+ StartPipelineWithEncryptedMedia(&source, &encrypted_media);
+
+ source.EndOfStream();
+ ASSERT_EQ(PIPELINE_OK, pipeline_status_);
+
+ Play();
+
+ ASSERT_TRUE(WaitUntilOnEnded());
+ source.Abort();
+ Stop();
+}
+
+TEST_F(PipelineIntegrationTest, EncryptedPlayback_MP4_CENC_KeyRotation_Audio) {
+ MockMediaSource source("bear-1280x720-a_frag-cenc-key_rotation.mp4",
+ kMP4Audio, kAppendWholeFile);
+ FakeEncryptedMedia encrypted_media(new RotatingKeyProvidingApp());
+ StartPipelineWithEncryptedMedia(&source, &encrypted_media);
+
+ source.EndOfStream();
+ ASSERT_EQ(PIPELINE_OK, pipeline_status_);
+
+ Play();
+
+ ASSERT_TRUE(WaitUntilOnEnded());
+ source.Abort();
+ Stop();
+}
#endif
// TODO(acolwell): Fix flakiness http://crbug.com/117921
@@ -1051,7 +1403,7 @@ TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) {
TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) {
ASSERT_TRUE(TestSeekDuringRead("bear-320x240-video-only.webm", kVideoOnlyWebM,
32768,
- base::TimeDelta::FromMilliseconds(200),
+ base::TimeDelta::FromMilliseconds(167),
base::TimeDelta::FromMilliseconds(1668),
0x1C896, 65536));
}
@@ -1060,18 +1412,12 @@ TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_VideoOnly) {
TEST_F(PipelineIntegrationTest, BasicPlayback_AudioOnly_Opus_WebM) {
ASSERT_TRUE(Start(GetTestDataFilePath("bear-opus-end-trimming.webm"),
PIPELINE_OK));
- EXPECT_EQ(1u, pipeline_->GetBufferedTimeRanges().size());
- EXPECT_EQ(0, pipeline_->GetBufferedTimeRanges().start(0).InMilliseconds());
- EXPECT_EQ(kOpusEndTrimmingWebMFileDurationMs,
- pipeline_->GetBufferedTimeRanges().end(0).InMilliseconds());
Play();
ASSERT_TRUE(WaitUntilOnEnded());
}
// Verify that VP9 video in WebM containers can be played back.
-// TODO(fgalligan): Enable after new vp9 files are landed.
-// http://crbug.com/259116
-TEST_F(PipelineIntegrationTest, DISABLED_BasicPlayback_VideoOnly_VP9_WebM) {
+TEST_F(PipelineIntegrationTest, BasicPlayback_VideoOnly_VP9_WebM) {
ASSERT_TRUE(Start(GetTestDataFilePath("bear-vp9.webm"),
PIPELINE_OK));
Play();
@@ -1080,9 +1426,7 @@ TEST_F(PipelineIntegrationTest, DISABLED_BasicPlayback_VideoOnly_VP9_WebM) {
// Verify that VP9 video and Opus audio in the same WebM container can be played
// back.
-// TODO(fgalligan): Enable after new vp9 files are landed.
-// http://crbug.com/259116
-TEST_F(PipelineIntegrationTest, DISABLED_BasicPlayback_VP9_Opus_WebM) {
+TEST_F(PipelineIntegrationTest, BasicPlayback_VP9_Opus_WebM) {
ASSERT_TRUE(Start(GetTestDataFilePath("bear-vp9-opus.webm"),
PIPELINE_OK));
Play();
@@ -1091,7 +1435,6 @@ TEST_F(PipelineIntegrationTest, DISABLED_BasicPlayback_VP9_Opus_WebM) {
// Verify that VP8 video with alpha channel can be played back.
TEST_F(PipelineIntegrationTest, BasicPlayback_VP8A_WebM) {
- EXPECT_CALL(*this, OnSetOpaque(false)).Times(AnyNumber());
ASSERT_TRUE(Start(GetTestDataFilePath("bear-vp8a.webm"),
PIPELINE_OK));
Play();
@@ -1101,7 +1444,6 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_VP8A_WebM) {
// Verify that VP8A video with odd width/height can be played back.
TEST_F(PipelineIntegrationTest, BasicPlayback_VP8A_Odd_WebM) {
- EXPECT_CALL(*this, OnSetOpaque(false)).Times(AnyNumber());
ASSERT_TRUE(Start(GetTestDataFilePath("bear-vp8a-odd-dimensions.webm"),
PIPELINE_OK));
Play();
@@ -1118,4 +1460,21 @@ TEST_F(PipelineIntegrationTest,
ASSERT_TRUE(WaitUntilOnEnded());
}
+// Verify that VP9 video with 4:4:4 subsampling can be played back.
+TEST_F(PipelineIntegrationTest, P444_VP9_WebM) {
+ ASSERT_TRUE(Start(GetTestDataFilePath("bear-320x240-P444.webm"),
+ PIPELINE_OK));
+ Play();
+ ASSERT_TRUE(WaitUntilOnEnded());
+ EXPECT_EQ(last_video_frame_format_, VideoFrame::YV24);
+}
+
+// Verify that videos with an odd frame size playback successfully.
+TEST_F(PipelineIntegrationTest, BasicPlayback_OddVideoSize) {
+ ASSERT_TRUE(Start(GetTestDataFilePath("butterfly-853x480.webm"),
+ PIPELINE_OK));
+ Play();
+ ASSERT_TRUE(WaitUntilOnEnded());
+}
+
} // namespace media