Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / media / filters / pipeline_integration_test.cc
index b92c37f..bd026da 100644 (file)
@@ -9,19 +9,27 @@
 #include "base/memory/scoped_ptr.h"
 #include "base/strings/string_util.h"
 #include "build/build_config.h"
+#include "media/base/cdm_callback_promise.h"
 #include "media/base/decoder_buffer.h"
 #include "media/base/media_keys.h"
 #include "media/base/media_switches.h"
 #include "media/base/test_data_util.h"
 #include "media/cdm/aes_decryptor.h"
+#include "media/cdm/json_web_key.h"
 #include "media/filters/chunk_demuxer.h"
+#include "media/filters/renderer_impl.h"
+#include "testing/gmock/include/gmock/gmock.h"
 
+using testing::_;
 using testing::AnyNumber;
+using testing::AtLeast;
 using testing::AtMost;
+using testing::SaveArg;
 
 namespace media {
 
 const char kSourceId[] = "SourceId";
+const char kCencInitDataType[] = "cenc";
 const uint8 kInitData[] = { 0x69, 0x6e, 0x69, 0x74 };
 
 const char kWebM[] = "video/webm; codecs=\"vp8,vorbis\"";
@@ -29,9 +37,8 @@ const char kWebMVP9[] = "video/webm; codecs=\"vp9\"";
 const char kAudioOnlyWebM[] = "video/webm; codecs=\"vorbis\"";
 const char kOpusAudioOnlyWebM[] = "video/webm; codecs=\"opus\"";
 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\"";
@@ -56,20 +63,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 uint32 kOpusEndTrimmingWebMFileAudioBytes = 528676;
-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,53 +116,63 @@ class FakeEncryptedMedia {
    public:
     virtual ~AppBase() {}
 
-    virtual void KeyAdded(const std::string& session_id) = 0;
+    virtual void OnSessionMessage(const std::string& web_session_id,
+                                  const std::vector<uint8>& message,
+                                  const GURL& destination_url) = 0;
+
+    virtual void OnSessionClosed(const std::string& web_session_id) = 0;
+
+    virtual void OnSessionKeysChange(const std::string& web_session_id,
+                                     bool has_additional_usable_key) = 0;
 
     // Errors are not expected unless overridden.
-    virtual void KeyError(const std::string& 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";
     }
 
-    virtual void KeyMessage(const std::string& session_id,
-                            const std::vector<uint8>& message,
-                            const std::string& default_url) = 0;
-
     virtual void NeedKey(const std::string& type,
                          const std::vector<uint8>& init_data,
                          AesDecryptor* decryptor) = 0;
   };
 
   FakeEncryptedMedia(AppBase* app)
-      : decryptor_(base::Bind(&FakeEncryptedMedia::KeyAdded,
+      : decryptor_(base::Bind(&FakeEncryptedMedia::OnSessionMessage,
                               base::Unretained(this)),
-                   base::Bind(&FakeEncryptedMedia::KeyError,
+                   base::Bind(&FakeEncryptedMedia::OnSessionClosed,
                               base::Unretained(this)),
-                   base::Bind(&FakeEncryptedMedia::KeyMessage,
+                   base::Bind(&FakeEncryptedMedia::OnSessionKeysChange,
                               base::Unretained(this))),
-        app_(app) {
-  }
+        app_(app) {}
 
   AesDecryptor* decryptor() {
     return &decryptor_;
   }
 
-  // Callbacks for firing key events. Delegate to |app_|.
-  void KeyAdded(const std::string& session_id) {
-    app_->KeyAdded(session_id);
+  // Callbacks for firing session events. Delegate to |app_|.
+  void OnSessionMessage(const std::string& web_session_id,
+                        const std::vector<uint8>& message,
+                        const GURL& destination_url) {
+    app_->OnSessionMessage(web_session_id, message, destination_url);
   }
 
-  void KeyError(const std::string& session_id,
-                MediaKeys::KeyError error_code,
-                int system_code) {
-    app_->KeyError(session_id, error_code, system_code);
+  void OnSessionClosed(const std::string& web_session_id) {
+    app_->OnSessionClosed(web_session_id);
   }
 
-  void KeyMessage(const std::string& session_id,
-                  const std::vector<uint8>& message,
-                  const std::string& default_url) {
-    app_->KeyMessage(session_id, message, default_url);
+  void OnSessionKeysChange(const std::string& web_session_id,
+                           bool has_additional_usable_key) {
+    app_->OnSessionKeysChange(web_session_id, has_additional_usable_key);
+  }
+
+  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,
@@ -138,89 +185,220 @@ class FakeEncryptedMedia {
   scoped_ptr<AppBase> app_;
 };
 
+enum PromiseResult { RESOLVED, REJECTED };
+
 // Provides |kSecretKey| in response to needkey.
 class KeyProvidingApp : public FakeEncryptedMedia::AppBase {
  public:
-  virtual void KeyAdded(const std::string& session_id) OVERRIDE {
-    EXPECT_FALSE(session_id.empty());
+  KeyProvidingApp() {}
+
+  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);
   }
 
-  virtual void KeyMessage(const std::string& session_id,
-                          const std::vector<uint8>& message,
-                          const std::string& default_url) OVERRIDE {
-    EXPECT_FALSE(session_id.empty());
+  scoped_ptr<SimpleCdmPromise> CreatePromise(PromiseResult expected) {
+    scoped_ptr<media::SimpleCdmPromise> promise(new media::CdmCallbackPromise<>(
+        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::CdmCallbackPromise<std::string>(
+            base::Bind(&KeyProvidingApp::OnResolveWithSession,
+                       base::Unretained(this),
+                       expected),
+            base::Bind(
+                &KeyProvidingApp::OnReject, base::Unretained(this), expected)));
+    return promise.Pass();
+  }
+
+  void OnSessionMessage(const std::string& web_session_id,
+                        const std::vector<uint8>& message,
+                        const GURL& destination_url) override {
+    EXPECT_FALSE(web_session_id.empty());
     EXPECT_FALSE(message.empty());
+    EXPECT_EQ(current_session_id_, web_session_id);
+  }
 
-    current_session_id_ = session_id;
+  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 {
+  void OnSessionKeysChange(const std::string& web_session_id,
+                           bool has_additional_usable_key) override {
+    EXPECT_EQ(current_session_id_, web_session_id);
+    EXPECT_EQ(has_additional_usable_key, true);
+  }
+
+  void NeedKey(const std::string& type,
+               const std::vector<uint8>& init_data,
+               AesDecryptor* decryptor) override {
     if (current_session_id_.empty()) {
-      EXPECT_TRUE(decryptor->GenerateKeyRequest(type, kInitData,
-                                                arraysize(kInitData)));
+      decryptor->CreateSession(type,
+                               kInitData,
+                               arraysize(kInitData),
+                               MediaKeys::TEMPORARY_SESSION,
+                               CreateSessionPromise(RESOLVED));
+      EXPECT_FALSE(current_session_id_.empty());
     }
 
-    EXPECT_FALSE(current_session_id_.empty());
-
     // 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.
     const uint8* key_id = init_data.empty() ? NULL : &init_data[0];
     size_t key_id_length = init_data.size();
-    if (type == kMP4AudioType || type == kMP4VideoType) {
+    if (type == kCencInitDataType) {
       key_id = kKeyId;
       key_id_length = arraysize(kKeyId);
     }
 
-    decryptor->AddKey(kSecretKey, arraysize(kSecretKey),
-                      key_id, key_id_length, current_session_id_);
+    // Convert key into a JSON structure and then add it.
+    std::string jwk = GenerateJWKSet(
+        kSecretKey, arraysize(kSecretKey), key_id, key_id_length);
+    decryptor->UpdateSession(current_session_id_,
+                             reinterpret_cast<const uint8*>(jwk.data()),
+                             jwk.size(),
+                             CreatePromise(RESOLVED));
   }
 
   std::string current_session_id_;
 };
 
+class RotatingKeyProvidingApp : public KeyProvidingApp {
+ public:
+  RotatingKeyProvidingApp() : num_distint_need_key_calls_(0) {}
+  ~RotatingKeyProvidingApp() override {
+    // Expect that NeedKey is fired multiple times with different |init_data|.
+    EXPECT_GT(num_distint_need_key_calls_, 1u);
+  }
+
+  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 KeyAdded(const std::string& session_id) OVERRIDE {
-    EXPECT_FALSE(session_id.empty());
-    FAIL() << "Unexpected KeyAdded";
+  void OnSessionMessage(const std::string& web_session_id,
+                        const std::vector<uint8>& message,
+                        const GURL& default_url) override {
+    EXPECT_FALSE(web_session_id.empty());
+    EXPECT_FALSE(message.empty());
+    FAIL() << "Unexpected Message";
   }
 
-  virtual void KeyMessage(const std::string& session_id,
-                          const std::vector<uint8>& message,
-                          const std::string& default_url) OVERRIDE {
-    EXPECT_FALSE(session_id.empty());
-    EXPECT_FALSE(message.empty());
-    FAIL() << "Unexpected KeyMessage";
+  void OnSessionClosed(const std::string& web_session_id) override {
+    EXPECT_FALSE(web_session_id.empty());
+    FAIL() << "Unexpected Closed";
   }
 
-  virtual void NeedKey(const std::string& type,
-                       const std::vector<uint8>& init_data,
-                       AesDecryptor* decryptor) OVERRIDE {
+  void OnSessionKeysChange(const std::string& web_session_id,
+                           bool has_additional_usable_key) override {
+    EXPECT_FALSE(web_session_id.empty());
+    EXPECT_EQ(has_additional_usable_key, true);
   }
+
+  void NeedKey(const std::string& type,
+               const std::vector<uint8>& init_data,
+               AesDecryptor* decryptor) override {}
 };
 
 // Helper class that emulates calls made on the ChunkDemuxer by the
 // 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)),
-            base::Bind(&MockMediaSource::OnTextTrack,
-                       base::Unretained(this)),
-            LogCB())),
+            LogCB(),
+            true)),
         owned_chunk_demuxer_(chunk_demuxer_) {
 
     file_data_ = ReadTestDataFile(filename);
@@ -243,7 +421,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());
@@ -256,16 +436,42 @@ 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_,
+        base::Bind(&MockMediaSource::InitSegmentReceived,
+                   base::Unretained(this)));
     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,
+                               base::Bind(&MockMediaSource::InitSegmentReceived,
+                                          base::Unretained(this)));
+    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,
+                               base::Bind(&MockMediaSource::InitSegmentReceived,
+                                          base::Unretained(this)));
+    last_timestamp_offset_ = timestamp_offset;
   }
 
   void EndOfStream() {
@@ -311,6 +517,7 @@ class MockMediaSource {
     }
 
     CHECK_EQ(chunk_demuxer_->AddId(kSourceId, type, codecs), ChunkDemuxer::kOk);
+
     AppendData(initial_append_size_);
   }
 
@@ -321,12 +528,12 @@ class MockMediaSource {
     need_key_cb_.Run(type, init_data);
   }
 
-  scoped_ptr<TextTrack> OnTextTrack(TextKind kind,
-                                    const std::string& label,
-                                    const std::string& language) {
-    return scoped_ptr<TextTrack>();
+  base::TimeDelta last_timestamp_offset() const {
+    return last_timestamp_offset_;
   }
 
+  MOCK_METHOD0(InitSegmentReceived, void(void));
+
  private:
   base::FilePath file_path_;
   scoped_refptr<DecoderBuffer> file_data_;
@@ -336,6 +543,7 @@ class MockMediaSource {
   ChunkDemuxer* chunk_demuxer_;
   scoped_ptr<Demuxer> owned_chunk_demuxer_;
   Demuxer::NeedKeyCB need_key_cb_;
+  base::TimeDelta last_timestamp_offset_;
 };
 
 class PipelineIntegrationTest
@@ -343,19 +551,26 @@ class PipelineIntegrationTest
       public PipelineIntegrationTestBase {
  public:
   void StartPipelineWithMediaSource(MockMediaSource* source) {
-    EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
-        .Times(AtMost(1));
-    EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
+    EXPECT_CALL(*source, InitSegmentReceived()).Times(AtLeast(1));
+    EXPECT_CALL(*this, OnMetadata(_))
+        .Times(AtMost(1))
+        .WillRepeatedly(SaveArg<0>(&metadata_));
+    EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
         .Times(AtMost(1));
+    demuxer_ = source->GetDemuxer().Pass();
     pipeline_->Start(
-        CreateFilterCollection(source->GetDemuxer(), NULL),
+        demuxer_.get(),
+        CreateRenderer(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::Closure());
-
+        base::Bind(&PipelineIntegrationTest::OnBufferingStateChanged,
+                   base::Unretained(this)),
+        base::Closure(),
+        base::Bind(&PipelineIntegrationTest::OnAddTextTrack,
+                   base::Unretained(this)));
     message_loop_.Run();
   }
 
@@ -367,19 +582,26 @@ class PipelineIntegrationTest
   void StartPipelineWithEncryptedMedia(
       MockMediaSource* source,
       FakeEncryptedMedia* encrypted_media) {
-    EXPECT_CALL(*this, OnBufferingState(Pipeline::kHaveMetadata))
-        .Times(AtMost(1));
-    EXPECT_CALL(*this, OnBufferingState(Pipeline::kPrerollCompleted))
+    EXPECT_CALL(*source, InitSegmentReceived()).Times(AtLeast(1));
+    EXPECT_CALL(*this, OnMetadata(_))
+        .Times(AtMost(1))
+        .WillRepeatedly(SaveArg<0>(&metadata_));
+    EXPECT_CALL(*this, OnBufferingStateChanged(BUFFERING_HAVE_ENOUGH))
         .Times(AtMost(1));
+    demuxer_ = source->GetDemuxer().Pass();
     pipeline_->Start(
-        CreateFilterCollection(source->GetDemuxer(),
-                               encrypted_media->decryptor()),
+        demuxer_.get(),
+        CreateRenderer(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::OnBufferingStateChanged,
                    base::Unretained(this)),
-        base::Closure());
+        base::Closure(),
+        base::Bind(&PipelineIntegrationTest::OnAddTextTrack,
+                   base::Unretained(this)));
 
     source->set_need_key_cb(base::Bind(&FakeEncryptedMedia::NeedKey,
                                        base::Unretained(encrypted_media)));
@@ -427,6 +649,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));
@@ -437,6 +667,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) {
@@ -475,15 +723,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();
 
@@ -500,7 +767,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();
@@ -518,7 +784,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);
@@ -531,20 +796,16 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Opus_WebM) {
   Play();
 
   ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_EQ(kOpusEndTrimmingWebMFileAudioBytes,
-            pipeline_->GetStatistics().audio_bytes_decoded);
   source.Abort();
   Stop();
 }
 
 // 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,
@@ -678,16 +939,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);
@@ -725,8 +1080,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);
 
@@ -750,6 +1105,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,
@@ -785,8 +1167,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);
 
@@ -843,8 +1225,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);
 
@@ -859,8 +1241,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);
 
@@ -876,8 +1258,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);
 
@@ -892,8 +1274,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);
 
@@ -909,8 +1291,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);
 
@@ -926,8 +1308,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);
 
@@ -959,6 +1341,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
@@ -1005,6 +1418,28 @@ TEST_F(PipelineIntegrationTest, DISABLED_SeekWhilePlaying) {
   ASSERT_TRUE(WaitUntilOnEnded());
 }
 
+#if defined(USE_PROPRIETARY_CODECS)
+TEST_F(PipelineIntegrationTest, Rotated_Metadata_0) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("bear_rotate_0.mp4"), PIPELINE_OK));
+  ASSERT_EQ(VIDEO_ROTATION_0, metadata_.video_rotation);
+}
+
+TEST_F(PipelineIntegrationTest, Rotated_Metadata_90) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("bear_rotate_90.mp4"), PIPELINE_OK));
+  ASSERT_EQ(VIDEO_ROTATION_90, metadata_.video_rotation);
+}
+
+TEST_F(PipelineIntegrationTest, Rotated_Metadata_180) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("bear_rotate_180.mp4"), PIPELINE_OK));
+  ASSERT_EQ(VIDEO_ROTATION_180, metadata_.video_rotation);
+}
+
+TEST_F(PipelineIntegrationTest, Rotated_Metadata_270) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("bear_rotate_270.mp4"), PIPELINE_OK));
+  ASSERT_EQ(VIDEO_ROTATION_270, metadata_.video_rotation);
+}
+#endif
+
 // Verify audio decoder & renderer can handle aborted demuxer reads.
 TEST_F(PipelineIntegrationTest, ChunkDemuxerAbortRead_AudioOnly) {
   ASSERT_TRUE(TestSeekDuringRead("bear-320x240-audio-only.webm", kAudioOnlyWebM,
@@ -1018,7 +1453,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));
 }
@@ -1029,14 +1464,10 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_AudioOnly_Opus_WebM) {
                     PIPELINE_OK));
   Play();
   ASSERT_TRUE(WaitUntilOnEnded());
-  EXPECT_EQ(kOpusEndTrimmingWebMFileAudioBytes,
-            pipeline_->GetStatistics().audio_bytes_decoded);
 }
 
 // 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();
@@ -1045,9 +1476,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();
@@ -1056,7 +1485,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();
@@ -1064,12 +1492,124 @@ TEST_F(PipelineIntegrationTest, BasicPlayback_VP8A_WebM) {
   EXPECT_EQ(last_video_frame_format_, VideoFrame::YV12A);
 }
 
+// Verify that VP8A video with odd width/height can be played back.
+TEST_F(PipelineIntegrationTest, BasicPlayback_VP8A_Odd_WebM) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("bear-vp8a-odd-dimensions.webm"),
+                    PIPELINE_OK));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_EQ(last_video_frame_format_, VideoFrame::YV12A);
+}
+
+// Verify that VP9 video with odd width/height can be played back.
+TEST_F(PipelineIntegrationTest, BasicPlayback_VP9_Odd_WebM) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("bear-vp9-odd-dimensions.webm"),
+                    PIPELINE_OK));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
 // Verify that VP8 video with inband text track can be played back.
 TEST_F(PipelineIntegrationTest, BasicPlayback_VP8_WebVTT_WebM) {
+  EXPECT_CALL(*this, OnAddTextTrack(_, _));
   ASSERT_TRUE(Start(GetTestDataFilePath("bear-vp8-webvtt.webm"),
                     PIPELINE_OK));
   Play();
   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());
+}
+
+// Verify that OPUS audio in a webm which reports a 44.1kHz sample rate plays
+// correctly at 48kHz
+TEST_F(PipelineIntegrationTest, BasicPlayback_Opus441kHz) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("sfx-opus-441.webm"), PIPELINE_OK));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  EXPECT_EQ(48000,
+            demuxer_->GetStream(DemuxerStream::AUDIO)
+                ->audio_decoder_config()
+                .samples_per_second());
+}
+
+// Same as above but using MediaSource.
+TEST_F(PipelineIntegrationTest, BasicPlayback_MediaSource_Opus441kHz) {
+  MockMediaSource source(
+      "sfx-opus-441.webm", kOpusAudioOnlyWebM, kAppendWholeFile);
+  StartPipelineWithMediaSource(&source);
+  source.EndOfStream();
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  source.Abort();
+  Stop();
+  EXPECT_EQ(48000,
+            demuxer_->GetStream(DemuxerStream::AUDIO)
+                ->audio_decoder_config()
+                .samples_per_second());
+}
+
+// Ensures audio-only playback with missing or negative timestamps works.  Tests
+// the common live-streaming case for chained ogg.  See http://crbug.com/396864.
+TEST_F(PipelineIntegrationTest, BasicPlaybackChainedOgg) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("double-sfx.ogg"), PIPELINE_OK));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  ASSERT_EQ(base::TimeDelta(), demuxer_->GetStartTime());
+}
+
+// Ensures audio-video playback with missing or negative timestamps fails softly
+// instead of crashing.  See http://crbug.com/396864.
+TEST_F(PipelineIntegrationTest, BasicPlaybackChainedOggVideo) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("double-bear.ogv"), PIPELINE_OK));
+  Play();
+  EXPECT_EQ(PIPELINE_ERROR_DECODE, WaitUntilEndedOrError());
+  ASSERT_EQ(base::TimeDelta(), demuxer_->GetStartTime());
+}
+
+// Tests that we signal ended even when audio runs longer than video track.
+TEST_F(PipelineIntegrationTest, BasicPlaybackAudioLongerThanVideo) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("bear_audio_longer_than_video.ogv"),
+                    PIPELINE_OK));
+  // Audio track is 2000ms. Video track is 1001ms. Duration should be higher
+  // of the two.
+  EXPECT_EQ(2000, pipeline_->GetMediaDuration().InMilliseconds());
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+// Tests that we signal ended even when audio runs shorter than video track.
+TEST_F(PipelineIntegrationTest, BasicPlaybackAudioShorterThanVideo) {
+  ASSERT_TRUE(Start(GetTestDataFilePath("bear_audio_shorter_than_video.ogv"),
+                    PIPELINE_OK));
+  // Audio track is 500ms. Video track is 1001ms. Duration should be higher of
+  // the two.
+  EXPECT_EQ(1001, pipeline_->GetMediaDuration().InMilliseconds());
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+}
+
+TEST_F(PipelineIntegrationTest, BasicPlaybackPositiveStartTime) {
+  ASSERT_TRUE(
+      Start(GetTestDataFilePath("nonzero-start-time.webm"), PIPELINE_OK));
+  Play();
+  ASSERT_TRUE(WaitUntilOnEnded());
+  ASSERT_EQ(base::TimeDelta::FromMicroseconds(396000),
+            demuxer_->GetStartTime());
+}
+
 }  // namespace media