diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2016-08-01 12:59:39 +0200 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2016-08-04 12:40:43 +0000 |
commit | 28b1110370900897ab652cb420c371fab8857ad4 (patch) | |
tree | 41b32127d23b0df4f2add2a27e12dc87bddb260e /chromium/chromecast/media | |
parent | 399c965b6064c440ddcf4015f5f8e9d131c7a0a6 (diff) | |
download | qtwebengine-chromium-28b1110370900897ab652cb420c371fab8857ad4.tar.gz |
BASELINE: Update Chromium to 53.0.2785.41
Also adds a few extra files for extensions.
Change-Id: Iccdd55d98660903331cf8b7b29188da781830af4
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/chromecast/media')
48 files changed, 1268 insertions, 596 deletions
diff --git a/chromium/chromecast/media/BUILD.gn b/chromium/chromecast/media/BUILD.gn index c79adce6d46..fc6291d6c0f 100644 --- a/chromium/chromecast/media/BUILD.gn +++ b/chromium/chromecast/media/BUILD.gn @@ -22,6 +22,7 @@ test("cast_media_unittests") { "cma/base/balanced_media_task_runner_unittest.cc", "cma/base/buffering_controller_unittest.cc", "cma/base/buffering_frame_provider_unittest.cc", + "cma/base/decoder_buffer_adapter_unittest.cc", "cma/base/demuxer_stream_adapter_unittest.cc", "cma/base/demuxer_stream_for_test.cc", "cma/base/demuxer_stream_for_test.h", diff --git a/chromium/chromecast/media/audio/cast_audio_manager.cc b/chromium/chromecast/media/audio/cast_audio_manager.cc index e3b56d9c769..d5367f6692b 100644 --- a/chromium/chromecast/media/audio/cast_audio_manager.cc +++ b/chromium/chromecast/media/audio/cast_audio_manager.cc @@ -73,28 +73,32 @@ CastAudioManager::CreateMediaPipelineBackend( } ::media::AudioOutputStream* CastAudioManager::MakeLinearOutputStream( - const ::media::AudioParameters& params) { + const ::media::AudioParameters& params, + const ::media::AudioManager::LogCallback& log_callback) { DCHECK_EQ(::media::AudioParameters::AUDIO_PCM_LINEAR, params.format()); return new CastAudioOutputStream(params, this); } ::media::AudioOutputStream* CastAudioManager::MakeLowLatencyOutputStream( const ::media::AudioParameters& params, - const std::string& device_id) { + const std::string& device_id, + const ::media::AudioManager::LogCallback& log_callback) { DCHECK_EQ(::media::AudioParameters::AUDIO_PCM_LOW_LATENCY, params.format()); return new CastAudioOutputStream(params, this); } ::media::AudioInputStream* CastAudioManager::MakeLinearInputStream( const ::media::AudioParameters& params, - const std::string& device_id) { + const std::string& device_id, + const ::media::AudioManager::LogCallback& log_callback) { LOG(WARNING) << "No support for input audio devices"; return nullptr; } ::media::AudioInputStream* CastAudioManager::MakeLowLatencyInputStream( const ::media::AudioParameters& params, - const std::string& device_id) { + const std::string& device_id, + const ::media::AudioManager::LogCallback& log_callback) { LOG(WARNING) << "No support for input audio devices"; return nullptr; } diff --git a/chromium/chromecast/media/audio/cast_audio_manager.h b/chromium/chromecast/media/audio/cast_audio_manager.h index 213e52c82a5..346a7f73b0a 100644 --- a/chromium/chromecast/media/audio/cast_audio_manager.h +++ b/chromium/chromecast/media/audio/cast_audio_manager.h @@ -45,16 +45,20 @@ class CastAudioManager : public ::media::AudioManagerBase { private: // AudioManagerBase implementation. ::media::AudioOutputStream* MakeLinearOutputStream( - const ::media::AudioParameters& params) override; + const ::media::AudioParameters& params, + const ::media::AudioManager::LogCallback& log_callback) override; ::media::AudioOutputStream* MakeLowLatencyOutputStream( const ::media::AudioParameters& params, - const std::string& device_id) override; + const std::string& device_id, + const ::media::AudioManager::LogCallback& log_callback) override; ::media::AudioInputStream* MakeLinearInputStream( const ::media::AudioParameters& params, - const std::string& device_id) override; + const std::string& device_id, + const ::media::AudioManager::LogCallback& log_callback) override; ::media::AudioInputStream* MakeLowLatencyInputStream( const ::media::AudioParameters& params, - const std::string& device_id) override; + const std::string& device_id, + const ::media::AudioManager::LogCallback& log_callback) override; ::media::AudioParameters GetPreferredOutputStreamParameters( const std::string& output_device_id, const ::media::AudioParameters& input_params) override; diff --git a/chromium/chromecast/media/audio/cast_audio_output_stream.cc b/chromium/chromecast/media/audio/cast_audio_output_stream.cc index 3af92654267..215827fd9e1 100644 --- a/chromium/chromecast/media/audio/cast_audio_output_stream.cc +++ b/chromium/chromecast/media/audio/cast_audio_output_stream.cc @@ -168,6 +168,7 @@ CastAudioOutputStream::CastAudioOutputStream( audio_manager_(audio_manager), volume_(1.0), source_callback_(nullptr), + timestamp_helper_(audio_params_.sample_rate()), backend_(new Backend()), buffer_duration_(audio_params.GetBufferDuration()), push_in_progress_(false), @@ -203,6 +204,7 @@ bool CastAudioOutputStream::Open() { audio_bus_ = ::media::AudioBus::Create(audio_params_); decoder_buffer_ = new DecoderBufferAdapter( new ::media::DecoderBuffer(audio_params_.GetBytesPerBuffer())); + timestamp_helper_.SetBaseTimestamp(base::TimeDelta()); VLOG(1) << __FUNCTION__ << " : " << this; return true; @@ -279,6 +281,8 @@ void CastAudioOutputStream::PushBuffer() { frame_count * audio_params_.GetBytesPerFrame()); audio_bus_->ToInterleaved(frame_count, audio_params_.bits_per_sample() / 8, decoder_buffer_->writable_data()); + decoder_buffer_->set_timestamp(timestamp_helper_.GetTimestamp()); + timestamp_helper_.AddFrames(frame_count); auto completion_cb = base::Bind(&CastAudioOutputStream::OnPushBufferComplete, weak_factory_.GetWeakPtr()); diff --git a/chromium/chromecast/media/audio/cast_audio_output_stream.h b/chromium/chromecast/media/audio/cast_audio_output_stream.h index fd2c6042afe..47764360289 100644 --- a/chromium/chromecast/media/audio/cast_audio_output_stream.h +++ b/chromium/chromecast/media/audio/cast_audio_output_stream.h @@ -10,6 +10,7 @@ #include "base/time/time.h" #include "media/audio/audio_io.h" #include "media/base/audio_parameters.h" +#include "media/base/audio_timestamp_helper.h" namespace chromecast { namespace media { @@ -44,6 +45,7 @@ class CastAudioOutputStream : public ::media::AudioOutputStream { AudioSourceCallback* source_callback_; std::unique_ptr<::media::AudioBus> audio_bus_; scoped_refptr<media::DecoderBufferBase> decoder_buffer_; + ::media::AudioTimestampHelper timestamp_helper_; std::unique_ptr<Backend> backend_; const base::TimeDelta buffer_duration_; bool push_in_progress_; diff --git a/chromium/chromecast/media/audio/cast_audio_output_stream_unittest.cc b/chromium/chromecast/media/audio/cast_audio_output_stream_unittest.cc index 77db4c26dd5..e65aa25631c 100644 --- a/chromium/chromecast/media/audio/cast_audio_output_stream_unittest.cc +++ b/chromium/chromecast/media/audio/cast_audio_output_stream_unittest.cc @@ -4,6 +4,7 @@ #include "chromecast/media/audio/cast_audio_output_stream.h" +#include "base/memory/ptr_util.h" #include "base/run_loop.h" #include "chromecast/base/metrics/cast_metrics_test_helper.h" #include "chromecast/media/audio/cast_audio_manager.h" @@ -104,8 +105,8 @@ class FakeMediaPipelineBackend : public MediaPipelineBackend { // MediaPipelineBackend implementation: AudioDecoder* CreateAudioDecoder() override { DCHECK(!audio_decoder_); - audio_decoder_ = new FakeAudioDecoder(); - return audio_decoder_; + audio_decoder_ = base::MakeUnique<FakeAudioDecoder>(); + return audio_decoder_.get(); } VideoDecoder* CreateVideoDecoder() override { NOTREACHED(); @@ -137,11 +138,11 @@ class FakeMediaPipelineBackend : public MediaPipelineBackend { bool SetPlaybackRate(float rate) override { return true; } State state() const { return state_; } - FakeAudioDecoder* decoder() const { return audio_decoder_; } + FakeAudioDecoder* decoder() const { return audio_decoder_.get(); } private: State state_; - FakeAudioDecoder* audio_decoder_; + std::unique_ptr<FakeAudioDecoder> audio_decoder_; }; class FakeAudioSourceCallback @@ -235,8 +236,9 @@ class CastAudioOutputStreamTest : public ::testing::Test { } ::media::AudioOutputStream* CreateStream() { - return audio_manager_->MakeAudioOutputStream(GetAudioParams(), - kDefaultDeviceId); + return audio_manager_->MakeAudioOutputStream( + GetAudioParams(), kDefaultDeviceId, + ::media::AudioManager::LogCallback()); } // Runs the messsage loop for duration equivalent to the given number of diff --git a/chromium/chromecast/media/base/decrypt_context_impl.cc b/chromium/chromecast/media/base/decrypt_context_impl.cc index 945ba9672a3..cc04405785f 100644 --- a/chromium/chromecast/media/base/decrypt_context_impl.cc +++ b/chromium/chromecast/media/base/decrypt_context_impl.cc @@ -4,12 +4,23 @@ #include "chromecast/media/base/decrypt_context_impl.h" +#include <memory> #include <vector> +#include "base/bind.h" +#include "base/logging.h" #include "chromecast/public/media/cast_decoder_buffer.h" namespace chromecast { namespace media { +namespace { +void BufferDecryptCB(bool* called, bool* ret, bool success) { + DCHECK(called); + DCHECK(ret); + *called = true; + *ret = success; +} +} DecryptContextImpl::DecryptContextImpl(CastKeySystem key_system) : key_system_(key_system) {} @@ -23,11 +34,26 @@ CastKeySystem DecryptContextImpl::GetKeySystem() { bool DecryptContextImpl::Decrypt(CastDecoderBuffer* buffer, std::vector<uint8_t>* output) { output->resize(buffer->data_size()); - return Decrypt(buffer, output->data()); + return Decrypt(buffer, output->data(), 0); } -bool DecryptContextImpl::Decrypt(CastDecoderBuffer* buffer, uint8_t* output) { - return false; +bool DecryptContextImpl::Decrypt(CastDecoderBuffer* buffer, + uint8_t* output, + size_t data_offset) { + bool called = false; + bool success = false; + DecryptAsync(buffer, output, data_offset, + base::Bind(&BufferDecryptCB, &called, &success)); + CHECK(called) << "Sync Decrypt isn't supported"; + + return success; +} + +void DecryptContextImpl::DecryptAsync(CastDecoderBuffer* buffer, + uint8_t* output, + size_t data_offset, + const DecryptCB& decrypt_cb) { + decrypt_cb.Run(false); } bool DecryptContextImpl::CanDecryptToBuffer() const { diff --git a/chromium/chromecast/media/base/decrypt_context_impl.h b/chromium/chromecast/media/base/decrypt_context_impl.h index bb83a646dd6..e41ff48930c 100644 --- a/chromium/chromecast/media/base/decrypt_context_impl.h +++ b/chromium/chromecast/media/base/decrypt_context_impl.h @@ -5,11 +5,13 @@ #ifndef CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_IMPL_H_ #define CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_IMPL_H_ +#include <stddef.h> #include <stdint.h> -#include "base/macros.h" -#include "base/memory/ref_counted.h" -#include "chromecast/media/base/key_systems_common.h" +#include <memory> + +#include "base/callback.h" +#include "chromecast/public/media/cast_key_system.h" #include "chromecast/public/media/decrypt_context.h" namespace chromecast { @@ -21,6 +23,8 @@ namespace media { // decryption context. class DecryptContextImpl : public DecryptContext { public: + using DecryptCB = base::Callback<void(bool)>; + explicit DecryptContextImpl(CastKeySystem key_system); ~DecryptContextImpl() override; @@ -29,12 +33,25 @@ class DecryptContextImpl : public DecryptContext { bool Decrypt(CastDecoderBuffer* buffer, std::vector<uint8_t>* output) final; - // TODO(yucliu): replace DecryptContext::Decrypt with this one in next + // TODO(smcgruer): Replace DecryptContext::Decrypt with this one in next // public api releasing. - // Decrypts the given buffer. Returns true/false for success/failure, - // and places the decrypted data in |output| if successful. - // Decrypted data in |output| has the same length as |buffer|. - virtual bool Decrypt(CastDecoderBuffer* buffer, uint8_t* output); + // Decrypts the given buffer. Returns true/false for success/failure. + // + // The decrypted data will be of size |buffer.data_size()| and there must be + // enough space in |output| to store that data. + // + // If non-zero, |data_offset| specifies an offset to be applied to |output| + // before the decrypted data is written. + virtual bool Decrypt(CastDecoderBuffer* buffer, + uint8_t* output, + size_t data_offset); + + // Similar as the above one. Decryption success or not will be returned in + // |decrypt_cb|. |decrypt_cb| will be called on caller's thread. + virtual void DecryptAsync(CastDecoderBuffer* buffer, + uint8_t* output, + size_t data_offset, + const DecryptCB& decrypt_cb); // Returns whether the data can be decrypted into user memory. // If the key system doesn't support secure output or the app explicitly @@ -46,7 +63,10 @@ class DecryptContextImpl : public DecryptContext { private: CastKeySystem key_system_; - DISALLOW_COPY_AND_ASSIGN(DecryptContextImpl); + // TODO(smcgruer): Restore macro usage next public API release. + // DISALLOW_COPY_AND_ASSIGN(DecryptContextImpl); + DecryptContextImpl(const DecryptContextImpl&) = delete; + void operator=(const DecryptContextImpl&) = delete; }; } // namespace media diff --git a/chromium/chromecast/media/base/decrypt_context_impl_clearkey.cc b/chromium/chromecast/media/base/decrypt_context_impl_clearkey.cc index f1f83419b3a..c4539cbe44a 100644 --- a/chromium/chromecast/media/base/decrypt_context_impl_clearkey.cc +++ b/chromium/chromecast/media/base/decrypt_context_impl_clearkey.cc @@ -7,6 +7,7 @@ #include <openssl/aes.h> #include <string.h> +#include <memory> #include <string> #include <vector> @@ -26,8 +27,16 @@ DecryptContextImplClearKey::DecryptContextImplClearKey( DecryptContextImplClearKey::~DecryptContextImplClearKey() {} -bool DecryptContextImplClearKey::Decrypt(CastDecoderBuffer* buffer, - uint8_t* output) { +void DecryptContextImplClearKey::DecryptAsync(CastDecoderBuffer* buffer, + uint8_t* output, + size_t data_offset, + const DecryptCB& decrypt_cb) { + decrypt_cb.Run(DoDecrypt(buffer, output, data_offset)); +} + +bool DecryptContextImplClearKey::DoDecrypt(CastDecoderBuffer* buffer, + uint8_t* output, + size_t data_offset) { DCHECK(buffer); DCHECK(output); @@ -38,6 +47,9 @@ bool DecryptContextImplClearKey::Decrypt(CastDecoderBuffer* buffer, if (!decrypt_config || decrypt_config->iv().size() == 0) return false; + // Apply the |data_offset|, if requested. + output += data_offset; + // Get the key. std::string raw_key; if (!key_->GetRawKey(&raw_key)) { diff --git a/chromium/chromecast/media/base/decrypt_context_impl_clearkey.h b/chromium/chromecast/media/base/decrypt_context_impl_clearkey.h index 8e0706e287e..d21a188bf98 100644 --- a/chromium/chromecast/media/base/decrypt_context_impl_clearkey.h +++ b/chromium/chromecast/media/base/decrypt_context_impl_clearkey.h @@ -5,6 +5,10 @@ #ifndef CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_IMPL_CLEARKEY_H_ #define CHROMECAST_MEDIA_BASE_DECRYPT_CONTEXT_IMPL_CLEARKEY_H_ +#include <stddef.h> + +#include <vector> + #include "base/macros.h" #include "chromecast/media/base/decrypt_context_impl.h" @@ -21,13 +25,18 @@ class DecryptContextImplClearKey : public DecryptContextImpl { explicit DecryptContextImplClearKey(crypto::SymmetricKey* key); ~DecryptContextImplClearKey() override; - // DecryptContext implementation. - bool Decrypt(CastDecoderBuffer* buffer, uint8_t* output) override; - // DecryptContextImpl implementation. + void DecryptAsync(CastDecoderBuffer* buffer, + uint8_t* output, + size_t data_offset, + const DecryptCB& decrypt_cb) override; + bool CanDecryptToBuffer() const override; private: + bool DoDecrypt(CastDecoderBuffer* buffer, + uint8_t* output, + size_t data_offset); crypto::SymmetricKey* const key_; DISALLOW_COPY_AND_ASSIGN(DecryptContextImplClearKey); diff --git a/chromium/chromecast/media/base/media_resource_tracker.cc b/chromium/chromecast/media/base/media_resource_tracker.cc index 29fb3b27e6f..79b4c12f5d0 100644 --- a/chromium/chromecast/media/base/media_resource_tracker.cc +++ b/chromium/chromecast/media/base/media_resource_tracker.cc @@ -15,8 +15,7 @@ namespace media { MediaResourceTracker::MediaResourceTracker( const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner) - : delegate_(nullptr), - media_use_count_(0), + : media_use_count_(0), media_lib_initialized_(false), delete_on_finalize_(false), ui_task_runner_(ui_task_runner), @@ -28,11 +27,6 @@ MediaResourceTracker::MediaResourceTracker( MediaResourceTracker::~MediaResourceTracker() {} -void MediaResourceTracker::SetDelegate(Delegate* delegate) { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); - delegate_ = delegate; -} - void MediaResourceTracker::InitializeMediaLib() { DCHECK(ui_task_runner_->BelongsToCurrentThread()); media_task_runner_->PostTask( @@ -53,7 +47,6 @@ void MediaResourceTracker::FinalizeMediaLib( void MediaResourceTracker::FinalizeAndDestroy() { DCHECK(ui_task_runner_->BelongsToCurrentThread()); - delegate_ = nullptr; media_task_runner_->PostTask( FROM_HERE, @@ -67,41 +60,18 @@ void MediaResourceTracker::IncrementUsageCount() { DCHECK(media_lib_initialized_); DCHECK(finalize_completion_cb_.is_null()); media_use_count_++; - - if (media_use_count_ == 1) { - ui_task_runner_->PostTask( - FROM_HERE, - base::Bind(&MediaResourceTracker::CallDelegateStartOnUiThread, - base::Unretained(this))); - } } void MediaResourceTracker::DecrementUsageCount() { DCHECK(media_task_runner_->BelongsToCurrentThread()); media_use_count_--; - if (media_use_count_ == 0) { - ui_task_runner_->PostTask( - FROM_HERE, base::Bind(&MediaResourceTracker::CallDelegateStopOnUiThread, - base::Unretained(this))); - - if (delete_on_finalize_ || !finalize_completion_cb_.is_null()) + if (media_use_count_ == 0 && + (delete_on_finalize_ || !finalize_completion_cb_.is_null())) { CallFinalizeOnMediaThread(); } } -void MediaResourceTracker::CallDelegateStartOnUiThread() { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); - if (delegate_) - delegate_->OnStartUsingMedia(); -} - -void MediaResourceTracker::CallDelegateStopOnUiThread() { - DCHECK(ui_task_runner_->BelongsToCurrentThread()); - if (delegate_) - delegate_->OnStopUsingMedia(); -} - void MediaResourceTracker::CallInitializeOnMediaThread() { DCHECK(media_task_runner_->BelongsToCurrentThread()); if (media_lib_initialized_) diff --git a/chromium/chromecast/media/base/media_resource_tracker.h b/chromium/chromecast/media/base/media_resource_tracker.h index e714a163984..52a3011fddd 100644 --- a/chromium/chromecast/media/base/media_resource_tracker.h +++ b/chromium/chromecast/media/base/media_resource_tracker.h @@ -27,34 +27,15 @@ namespace media { // * This class interacts on both UI and media threads (task runners required // by ctor to perform thread hopping and checks). See function-level comments // on which thread to use for which operations. -// * All interaction with delegate is performed on UI thread. // * The application should instantiate a single MediaResourceTracker instance. // Destruction should be performed by calling FinalizeAndDestroy from the UI // thread. class MediaResourceTracker { public: - class Delegate { - public: - // Called on UI thread when media usage starts (i.e. count steps 0->1). - // Does not mean Initialize has happened. - virtual void OnStartUsingMedia() = 0; - - // Called on UI thread when media usage stops (i.e. count steps 1->0). - // Does not mean Finalize has happened. - virtual void OnStopUsingMedia() = 0; - - protected: - virtual ~Delegate() {} - }; - MediaResourceTracker( const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner); - // Sets the delegate to receive start/stop media usage notifications. Must - // call on UI thread. Set to nullptr to clear existing delegate. - void SetDelegate(Delegate* delegate); - // Media resource acquire implementation. Must call on ui thread; runs // CastMediaShlib::Initialize on media thread. Safe to call even if media lib // already initialized. @@ -73,7 +54,7 @@ class MediaResourceTracker { // (2) Calls CastMediaShlib::Finalize on media thread // (3) Deletes this object // Must be called on UI thread. No further calls should be made on UI thread - // after this. Delegate is implicitly cleared. + // after this. void FinalizeAndDestroy(); // Users of media resource (e.g. CMA pipeline) should call these when they @@ -86,10 +67,6 @@ class MediaResourceTracker { friend class TestMediaResourceTracker; virtual ~MediaResourceTracker(); - // Tasks posted to UI thread - void CallDelegateStartOnUiThread(); - void CallDelegateStopOnUiThread(); - // Tasks posted to media thread void CallInitializeOnMediaThread(); void MaybeCallFinalizeOnMediaThread(const base::Closure& completion_cb); @@ -100,9 +77,6 @@ class MediaResourceTracker { virtual void DoInitializeMediaLib(); virtual void DoFinalizeMediaLib(); - // Accessed on UI thread - Delegate* delegate_; - // Accessed on media thread + ctor size_t media_use_count_; bool media_lib_initialized_; diff --git a/chromium/chromecast/media/base/media_resource_tracker_unittest.cc b/chromium/chromecast/media/base/media_resource_tracker_unittest.cc index dd83f7f8bd7..01eac0341d6 100644 --- a/chromium/chromecast/media/base/media_resource_tracker_unittest.cc +++ b/chromium/chromecast/media/base/media_resource_tracker_unittest.cc @@ -20,7 +20,9 @@ namespace chromecast { namespace media { void RunUntilIdle(base::TaskRunner* task_runner) { - base::WaitableEvent completion_event(false, false); + base::WaitableEvent completion_event( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); task_runner->PostTask(FROM_HERE, base::Bind(&base::WaitableEvent::Signal, base::Unretained(&completion_event))); @@ -28,16 +30,12 @@ void RunUntilIdle(base::TaskRunner* task_runner) { } // Collection of mocks to verify MediaResourceTracker takes the correct actions. -class MediaResourceTrackerTestMocks : public MediaResourceTracker::Delegate { +class MediaResourceTrackerTestMocks { public: MOCK_METHOD0(Initialize, void()); // CastMediaShlib::Initialize MOCK_METHOD0(Finalize, void()); // CastMediaShlib::Finalize MOCK_METHOD0(Destroyed, void()); // ~CastMediaResourceTracker MOCK_METHOD0(FinalizeCallback, void()); // callback to Finalize - - // MediaResourceTracker::Delegate implementation: - MOCK_METHOD0(OnStartUsingMedia, void()); - MOCK_METHOD0(OnStopUsingMedia, void()); }; class TestMediaResourceTracker : public MediaResourceTracker { @@ -83,7 +81,6 @@ class MediaResourceTrackerTest : public ::testing::Test { resource_tracker_ = new TestMediaResourceTracker( test_mocks_.get(), message_loop_->task_runner(), media_task_runner_); - resource_tracker_->SetDelegate(test_mocks_.get()); } void TearDown() override { media_thread_.reset(); } @@ -173,7 +170,6 @@ TEST_F(MediaResourceTrackerTest, FinalizeResourceInUse) { resource_tracker_->InitializeMediaLib(); IncrementMediaUsageCount(); - EXPECT_CALL(*test_mocks_, OnStartUsingMedia()).Times(1); RunUntilIdle(media_task_runner_.get()); base::RunLoop().RunUntilIdle(); @@ -188,7 +184,6 @@ TEST_F(MediaResourceTrackerTest, FinalizeResourceInUse) { DecrementMediaUsageCount(); EXPECT_CALL(*test_mocks_, FinalizeCallback()).Times(1); - EXPECT_CALL(*test_mocks_, OnStopUsingMedia()).Times(1); RunUntilIdle(media_task_runner_.get()); base::RunLoop().RunUntilIdle(); @@ -205,7 +200,6 @@ TEST_F(MediaResourceTrackerTest, DestroyWaitForNoUsers) { resource_tracker_->InitializeMediaLib(); IncrementMediaUsageCount(); - EXPECT_CALL(*test_mocks_, OnStartUsingMedia()).Times(1); RunUntilIdle(media_task_runner_.get()); base::RunLoop().RunUntilIdle(); @@ -214,8 +208,6 @@ TEST_F(MediaResourceTrackerTest, DestroyWaitForNoUsers) { resource_tracker_->FinalizeAndDestroy(); RunUntilIdle(media_task_runner_.get()); - // Note, OnStop delegate call should not be made during shutdown - EXPECT_CALL(*test_mocks_, OnStopUsingMedia()).Times(0); EXPECT_CALL(*test_mocks_, Finalize()).Times(1); EXPECT_CALL(*test_mocks_, Destroyed()).Times(1); DecrementMediaUsageCount(); @@ -231,7 +223,6 @@ TEST_F(MediaResourceTrackerTest, DestroyWithPendingFinalize) { resource_tracker_->InitializeMediaLib(); IncrementMediaUsageCount(); - EXPECT_CALL(*test_mocks_, OnStartUsingMedia()).Times(1); RunUntilIdle(media_task_runner_.get()); base::RunLoop().RunUntilIdle(); @@ -246,7 +237,6 @@ TEST_F(MediaResourceTrackerTest, DestroyWithPendingFinalize) { EXPECT_CALL(*test_mocks_, Finalize()).Times(1); EXPECT_CALL(*test_mocks_, Destroyed()).Times(1); EXPECT_CALL(*test_mocks_, FinalizeCallback()).Times(1); - EXPECT_CALL(*test_mocks_, OnStopUsingMedia()).Times(0); DecrementMediaUsageCount(); diff --git a/chromium/chromecast/media/cdm/BUILD.gn b/chromium/chromecast/media/cdm/BUILD.gn index cc9fa01f64a..c6a21eba54e 100644 --- a/chromium/chromecast/media/cdm/BUILD.gn +++ b/chromium/chromecast/media/cdm/BUILD.gn @@ -7,12 +7,18 @@ import("//chromecast/chromecast.gni") # GYP target: chromecast/media.gyp:media_cdm source_set("cdm") { sources = [ - "browser_cdm_cast.cc", - "browser_cdm_cast.h", + "cast_cdm.cc", + "cast_cdm.h", + "cast_cdm_context.cc", + "cast_cdm_context.h", + "cast_cdm_proxy.cc", + "cast_cdm_proxy.h", "chromecast_init_data.cc", "chromecast_init_data.h", ] + configs += [ "//media/mojo/services:mojo_media_config" ] + deps = [ "//base", "//chromecast/media/base", diff --git a/chromium/chromecast/media/cdm/browser_cdm_cast.cc b/chromium/chromecast/media/cdm/browser_cdm_cast.cc deleted file mode 100644 index 65c05b5974f..00000000000 --- a/chromium/chromecast/media/cdm/browser_cdm_cast.cc +++ /dev/null @@ -1,252 +0,0 @@ -// Copyright 2014 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 "chromecast/media/cdm/browser_cdm_cast.h" - -#include <utility> - -#include "base/bind.h" -#include "base/location.h" -#include "base/memory/ptr_util.h" -#include "base/single_thread_task_runner.h" -#include "base/threading/thread_task_runner_handle.h" -#include "chromecast/media/base/media_resource_tracker.h" -#include "media/base/cdm_key_information.h" -#include "media/base/cdm_promise.h" -#include "media/cdm/player_tracker_impl.h" - -namespace chromecast { -namespace media { - -namespace { - -// media::CdmPromiseTemplate implementation that wraps a promise so as to -// allow passing to other threads. -template <typename... T> -class CdmPromiseInternal : public ::media::CdmPromiseTemplate<T...> { - public: - CdmPromiseInternal(std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise) - : task_runner_(base::ThreadTaskRunnerHandle::Get()), - promise_(std::move(promise)) {} - - ~CdmPromiseInternal() final { - if (IsPromiseSettled()) - return; - - DCHECK(promise_); - RejectPromiseOnDestruction(); - } - - // CdmPromiseTemplate<> implementation. - void resolve(const T&... result) final; - - void reject(::media::MediaKeys::Exception exception, - uint32_t system_code, - const std::string& error_message) final { - MarkPromiseSettled(); - task_runner_->PostTask( - FROM_HERE, - base::Bind(&::media::CdmPromiseTemplate<T...>::reject, - base::Owned(promise_.release()), - exception, system_code, error_message)); - } - - private: - using ::media::CdmPromiseTemplate<T...>::IsPromiseSettled; - using ::media::CdmPromiseTemplate<T...>::MarkPromiseSettled; - using ::media::CdmPromiseTemplate<T...>::RejectPromiseOnDestruction; - - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise_; -}; - -template <typename... T> -void CdmPromiseInternal<T...>::resolve(const T&... result) { - MarkPromiseSettled(); - task_runner_->PostTask( - FROM_HERE, - base::Bind(&::media::CdmPromiseTemplate<T...>::resolve, - base::Owned(promise_.release()), - result...)); -} - -template <typename... T> -std::unique_ptr<CdmPromiseInternal<T...>> BindPromiseToCurrentLoop( - std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise) { - return base::WrapUnique(new CdmPromiseInternal<T...>(std::move(promise))); -} - -} // namespace - -BrowserCdmCast::BrowserCdmCast(MediaResourceTracker* media_resource_tracker) - : media_resource_tracker_(media_resource_tracker) { - DCHECK(media_resource_tracker); - thread_checker_.DetachFromThread(); -} - -BrowserCdmCast::~BrowserCdmCast() { - DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(player_tracker_impl_.get()); - player_tracker_impl_->NotifyCdmUnset(); - media_resource_tracker_->DecrementUsageCount(); -} - -void BrowserCdmCast::Initialize( - const ::media::SessionMessageCB& session_message_cb, - const ::media::SessionClosedCB& session_closed_cb, - const ::media::LegacySessionErrorCB& legacy_session_error_cb, - const ::media::SessionKeysChangeCB& session_keys_change_cb, - const ::media::SessionExpirationUpdateCB& session_expiration_update_cb) { - DCHECK(thread_checker_.CalledOnValidThread()); - - media_resource_tracker_->IncrementUsageCount(); - player_tracker_impl_.reset(new ::media::PlayerTrackerImpl()); - - session_message_cb_ = session_message_cb; - session_closed_cb_ = session_closed_cb; - legacy_session_error_cb_ = legacy_session_error_cb; - session_keys_change_cb_ = session_keys_change_cb; - session_expiration_update_cb_ = session_expiration_update_cb; - - InitializeInternal(); -} - -int BrowserCdmCast::RegisterPlayer(const base::Closure& new_key_cb, - const base::Closure& cdm_unset_cb) { - DCHECK(thread_checker_.CalledOnValidThread()); - return player_tracker_impl_->RegisterPlayer(new_key_cb, cdm_unset_cb); -} - -void BrowserCdmCast::UnregisterPlayer(int registration_id) { - DCHECK(thread_checker_.CalledOnValidThread()); - player_tracker_impl_->UnregisterPlayer(registration_id); -} - -void BrowserCdmCast::OnSessionMessage( - const std::string& session_id, - const std::vector<uint8_t>& message, - const GURL& destination_url, - ::media::MediaKeys::MessageType message_type) { - session_message_cb_.Run(session_id, - message_type, - message, - destination_url); -} - -void BrowserCdmCast::OnSessionClosed(const std::string& session_id) { - session_closed_cb_.Run(session_id); -} - -void BrowserCdmCast::OnSessionKeysChange(const std::string& session_id, - bool newly_usable_keys, - ::media::CdmKeysInfo keys_info) { - session_keys_change_cb_.Run(session_id, newly_usable_keys, - std::move(keys_info)); - - if (newly_usable_keys) - player_tracker_impl_->NotifyNewKey(); -} - -void BrowserCdmCast::KeyIdAndKeyPairsToInfo( - const ::media::KeyIdAndKeyPairs& keys, - ::media::CdmKeysInfo* keys_info) { - DCHECK(keys_info); - for (const std::pair<std::string, std::string>& key : keys) { - std::unique_ptr<::media::CdmKeyInformation> cdm_key_information( - new ::media::CdmKeyInformation(key.first, - ::media::CdmKeyInformation::USABLE, 0)); - keys_info->push_back(cdm_key_information.release()); - } -} - -// A macro runs current member function on |task_runner_| thread. -#define FORWARD_ON_CDM_THREAD(param_fn, ...) \ - task_runner_->PostTask( \ - FROM_HERE, \ - base::Bind(&BrowserCdmCast::param_fn, \ - base::Unretained(browser_cdm_cast_.get()), ##__VA_ARGS__)) - -BrowserCdmCastUi::BrowserCdmCastUi( - const scoped_refptr<BrowserCdmCast>& browser_cdm_cast, - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) - : browser_cdm_cast_(browser_cdm_cast), task_runner_(task_runner) {} - -BrowserCdmCastUi::~BrowserCdmCastUi() { - DCHECK(thread_checker_.CalledOnValidThread()); - browser_cdm_cast_->AddRef(); - BrowserCdmCast* raw_cdm = browser_cdm_cast_.get(); - browser_cdm_cast_ = nullptr; - task_runner_->ReleaseSoon(FROM_HERE, raw_cdm); -} - -BrowserCdmCast* BrowserCdmCastUi::browser_cdm_cast() const { - DCHECK(thread_checker_.CalledOnValidThread()); - return browser_cdm_cast_.get(); -} - -void BrowserCdmCastUi::SetServerCertificate( - const std::vector<uint8_t>& certificate, - std::unique_ptr<::media::SimpleCdmPromise> promise) { - DCHECK(thread_checker_.CalledOnValidThread()); - FORWARD_ON_CDM_THREAD( - SetServerCertificate, certificate, - base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); -} - -void BrowserCdmCastUi::CreateSessionAndGenerateRequest( - ::media::MediaKeys::SessionType session_type, - ::media::EmeInitDataType init_data_type, - const std::vector<uint8_t>& init_data, - std::unique_ptr<::media::NewSessionCdmPromise> promise) { - DCHECK(thread_checker_.CalledOnValidThread()); - FORWARD_ON_CDM_THREAD( - CreateSessionAndGenerateRequest, session_type, init_data_type, init_data, - base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); -} - -void BrowserCdmCastUi::LoadSession( - ::media::MediaKeys::SessionType session_type, - const std::string& session_id, - std::unique_ptr<::media::NewSessionCdmPromise> promise) { - DCHECK(thread_checker_.CalledOnValidThread()); - FORWARD_ON_CDM_THREAD( - LoadSession, session_type, session_id, - base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); -} - -void BrowserCdmCastUi::UpdateSession( - const std::string& session_id, - const std::vector<uint8_t>& response, - std::unique_ptr<::media::SimpleCdmPromise> promise) { - DCHECK(thread_checker_.CalledOnValidThread()); - FORWARD_ON_CDM_THREAD( - UpdateSession, session_id, response, - base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); -} - -void BrowserCdmCastUi::CloseSession( - const std::string& session_id, - std::unique_ptr<::media::SimpleCdmPromise> promise) { - DCHECK(thread_checker_.CalledOnValidThread()); - FORWARD_ON_CDM_THREAD( - CloseSession, session_id, - base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); -} - -void BrowserCdmCastUi::RemoveSession( - const std::string& session_id, - std::unique_ptr<::media::SimpleCdmPromise> promise) { - DCHECK(thread_checker_.CalledOnValidThread()); - FORWARD_ON_CDM_THREAD( - RemoveSession, session_id, - base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); -} - -// A default empty implementation for subclasses that don't need to provide -// any key system specific initialization. -void BrowserCdmCast::InitializeInternal() { -} - -} // namespace media -} // namespace chromecast diff --git a/chromium/chromecast/media/cdm/cast_cdm.cc b/chromium/chromecast/media/cdm/cast_cdm.cc new file mode 100644 index 00000000000..30965c11314 --- /dev/null +++ b/chromium/chromecast/media/cdm/cast_cdm.cc @@ -0,0 +1,108 @@ +// Copyright 2014 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 "chromecast/media/cdm/cast_cdm.h" + +#include <utility> + +#include "base/bind.h" +#include "base/location.h" +#include "base/memory/ptr_util.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "chromecast/media/base/media_resource_tracker.h" +#include "media/base/cdm_key_information.h" +#include "media/base/decryptor.h" +#include "media/cdm/player_tracker_impl.h" + +namespace chromecast { +namespace media { + +CastCdm::CastCdm(MediaResourceTracker* media_resource_tracker) + : media_resource_tracker_(media_resource_tracker), + cast_cdm_context_(new CastCdmContext(this)) { + DCHECK(media_resource_tracker); + thread_checker_.DetachFromThread(); +} + +CastCdm::~CastCdm() { + DCHECK(thread_checker_.CalledOnValidThread()); + DCHECK(player_tracker_impl_.get()); + player_tracker_impl_->NotifyCdmUnset(); + media_resource_tracker_->DecrementUsageCount(); +} + +void CastCdm::Initialize( + const ::media::SessionMessageCB& session_message_cb, + const ::media::SessionClosedCB& session_closed_cb, + const ::media::LegacySessionErrorCB& legacy_session_error_cb, + const ::media::SessionKeysChangeCB& session_keys_change_cb, + const ::media::SessionExpirationUpdateCB& session_expiration_update_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + + media_resource_tracker_->IncrementUsageCount(); + player_tracker_impl_.reset(new ::media::PlayerTrackerImpl()); + + session_message_cb_ = session_message_cb; + session_closed_cb_ = session_closed_cb; + legacy_session_error_cb_ = legacy_session_error_cb; + session_keys_change_cb_ = session_keys_change_cb; + session_expiration_update_cb_ = session_expiration_update_cb; + + InitializeInternal(); +} + +int CastCdm::RegisterPlayer(const base::Closure& new_key_cb, + const base::Closure& cdm_unset_cb) { + DCHECK(thread_checker_.CalledOnValidThread()); + return player_tracker_impl_->RegisterPlayer(new_key_cb, cdm_unset_cb); +} + +void CastCdm::UnregisterPlayer(int registration_id) { + DCHECK(thread_checker_.CalledOnValidThread()); + player_tracker_impl_->UnregisterPlayer(registration_id); +} + +::media::CdmContext* CastCdm::GetCdmContext() { + return cast_cdm_context_.get(); +} + +void CastCdm::OnSessionMessage(const std::string& session_id, + const std::vector<uint8_t>& message, + const GURL& destination_url, + ::media::MediaKeys::MessageType message_type) { + session_message_cb_.Run(session_id, message_type, message, destination_url); +} + +void CastCdm::OnSessionClosed(const std::string& session_id) { + session_closed_cb_.Run(session_id); +} + +void CastCdm::OnSessionKeysChange(const std::string& session_id, + bool newly_usable_keys, + ::media::CdmKeysInfo keys_info) { + session_keys_change_cb_.Run(session_id, newly_usable_keys, + std::move(keys_info)); + + if (newly_usable_keys) + player_tracker_impl_->NotifyNewKey(); +} + +void CastCdm::KeyIdAndKeyPairsToInfo(const ::media::KeyIdAndKeyPairs& keys, + ::media::CdmKeysInfo* keys_info) { + DCHECK(keys_info); + for (const std::pair<std::string, std::string>& key : keys) { + std::unique_ptr<::media::CdmKeyInformation> cdm_key_information( + new ::media::CdmKeyInformation(key.first, + ::media::CdmKeyInformation::USABLE, 0)); + keys_info->push_back(cdm_key_information.release()); + } +} + +// A default empty implementation for subclasses that don't need to provide +// any key system specific initialization. +void CastCdm::InitializeInternal() {} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cdm/browser_cdm_cast.h b/chromium/chromecast/media/cdm/cast_cdm.h index 1f65bcdfebe..e9c7512abed 100644 --- a/chromium/chromecast/media/cdm/browser_cdm_cast.h +++ b/chromium/chromecast/media/cdm/cast_cdm.h @@ -2,8 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef CHROMECAST_MEDIA_CDM_BROWSER_CDM_CAST_H_ -#define CHROMECAST_MEDIA_CDM_BROWSER_CDM_CAST_H_ +#ifndef CHROMECAST_MEDIA_CDM_cast_cdm_H_ +#define CHROMECAST_MEDIA_CDM_cast_cdm_H_ #include <stdint.h> @@ -17,7 +17,9 @@ #include "base/sequenced_task_runner_helpers.h" #include "base/threading/thread_checker.h" #include "chromecast/media/base/media_resource_tracker.h" +#include "chromecast/media/cdm/cast_cdm_context.h" #include "chromecast/public/media/cast_key_status.h" +#include "media/base/cdm_context.h" #include "media/base/media_keys.h" #include "media/base/player_tracker.h" #include "media/cdm/json_web_key.h" @@ -34,17 +36,16 @@ namespace chromecast { namespace media { class DecryptContextImpl; -// BrowserCdmCast is an extension of MediaKeys that provides common +// CastCdm is an extension of MediaKeys that provides common // functionality across CDM implementations. // All these additional functions are synchronous so: // - either both the CDM and the media pipeline must be running on the same // thread, -// - or BrowserCdmCast implementations must use some locks. +// - or CastCdm implementations must use some locks. // -class BrowserCdmCast : public ::media::MediaKeys, - public ::media::PlayerTracker { +class CastCdm : public ::media::MediaKeys { public: - explicit BrowserCdmCast(MediaResourceTracker* media_resource_tracker); + explicit CastCdm(MediaResourceTracker* media_resource_tracker); void Initialize( const ::media::SessionMessageCB& session_message_cb, @@ -53,14 +54,12 @@ class BrowserCdmCast : public ::media::MediaKeys, const ::media::SessionKeysChangeCB& session_keys_change_cb, const ::media::SessionExpirationUpdateCB& session_expiration_update_cb); - // ::media::PlayerTracker implementation. int RegisterPlayer(const base::Closure& new_key_cb, - const base::Closure& cdm_unset_cb) override; - void UnregisterPlayer(int registration_id) override; + const base::Closure& cdm_unset_cb); + void UnregisterPlayer(int registration_id); // Returns the decryption context needed to decrypt frames encrypted with - // |key_id|. - // Returns null if |key_id| is not available. + // |key_id|. Returns null if |key_id| is not available. virtual std::unique_ptr<DecryptContextImpl> GetDecryptContext( const std::string& key_id) const = 0; @@ -70,8 +69,11 @@ class BrowserCdmCast : public ::media::MediaKeys, CastKeyStatus key_status, uint32_t system_code) = 0; + // ::media::MediaKeys implementation. + ::media::CdmContext* GetCdmContext() override; + protected: - ~BrowserCdmCast() override; + ~CastCdm() override; void OnSessionMessage(const std::string& session_id, const std::vector<uint8_t>& message, @@ -86,8 +88,6 @@ class BrowserCdmCast : public ::media::MediaKeys, ::media::CdmKeysInfo* key_info); private: - friend class BrowserCdmCastUi; - // Allow subclasses to override to provide key sysytem specific // initialization. virtual void InitializeInternal(); @@ -100,59 +100,14 @@ class BrowserCdmCast : public ::media::MediaKeys, MediaResourceTracker* media_resource_tracker_; std::unique_ptr<::media::PlayerTrackerImpl> player_tracker_impl_; + std::unique_ptr<CastCdmContext> cast_cdm_context_; base::ThreadChecker thread_checker_; - DISALLOW_COPY_AND_ASSIGN(BrowserCdmCast); -}; - -// MediaKeys implementation that lives on the UI thread and forwards all calls -// to a BrowserCdmCast instance on the CMA thread. This is used to simplify the -// UI-CMA threading interaction. -class BrowserCdmCastUi : public ::media::MediaKeys { - public: - BrowserCdmCastUi( - const scoped_refptr<BrowserCdmCast>& browser_cdm_cast, - const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); - - BrowserCdmCast* browser_cdm_cast() const; - - private: - ~BrowserCdmCastUi() override; - - // ::media::MediaKeys implementation: - void SetServerCertificate( - const std::vector<uint8_t>& certificate, - std::unique_ptr<::media::SimpleCdmPromise> promise) override; - void CreateSessionAndGenerateRequest( - ::media::MediaKeys::SessionType session_type, - ::media::EmeInitDataType init_data_type, - const std::vector<uint8_t>& init_data, - std::unique_ptr<::media::NewSessionCdmPromise> promise) override; - void LoadSession( - ::media::MediaKeys::SessionType session_type, - const std::string& session_id, - std::unique_ptr<::media::NewSessionCdmPromise> promise) override; - void UpdateSession( - const std::string& session_id, - const std::vector<uint8_t>& response, - std::unique_ptr<::media::SimpleCdmPromise> promise) override; - void CloseSession( - const std::string& session_id, - std::unique_ptr<::media::SimpleCdmPromise> promise) override; - void RemoveSession( - const std::string& session_id, - std::unique_ptr<::media::SimpleCdmPromise> promise) override; - - scoped_refptr<BrowserCdmCast> browser_cdm_cast_; - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - - base::ThreadChecker thread_checker_; - - DISALLOW_COPY_AND_ASSIGN(BrowserCdmCastUi); + DISALLOW_COPY_AND_ASSIGN(CastCdm); }; } // namespace media } // namespace chromecast -#endif // CHROMECAST_MEDIA_CDM_BROWSER_CDM_CAST_H_ +#endif // CHROMECAST_MEDIA_CDM_cast_cdm_H_ diff --git a/chromium/chromecast/media/cdm/cast_cdm_context.cc b/chromium/chromecast/media/cdm/cast_cdm_context.cc new file mode 100644 index 00000000000..742516cab4e --- /dev/null +++ b/chromium/chromecast/media/cdm/cast_cdm_context.cc @@ -0,0 +1,54 @@ +// Copyright 2016 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 "chromecast/media/cdm/cast_cdm_context.h" + +#include "base/logging.h" +#include "chromecast/media/base/decrypt_context_impl.h" +#include "chromecast/media/cdm/cast_cdm.h" +#include "chromecast/public/media/decrypt_context.h" + +namespace chromecast { +namespace media { + +CastCdmContext::CastCdmContext(CastCdm* cast_cdm) : cast_cdm_(cast_cdm) { + DCHECK(cast_cdm_); +} + +CastCdmContext::~CastCdmContext() {} + +::media::Decryptor* CastCdmContext::GetDecryptor() { + // Subclasses providing CdmContext for a ClearKey CDM implementation must + // override this method to provide the Decryptor. Subclasses providing DRM + // implementations should return nullptr here. + return nullptr; +} + +int CastCdmContext::GetCdmId() const { + // This is a local CDM module. + return ::media::CdmContext::kInvalidCdmId; +} + +int CastCdmContext::RegisterPlayer(const base::Closure& new_key_cb, + const base::Closure& cdm_unset_cb) { + return cast_cdm_->RegisterPlayer(new_key_cb, cdm_unset_cb); +} + +void CastCdmContext::UnregisterPlayer(int registration_id) { + cast_cdm_->UnregisterPlayer(registration_id); +} + +std::unique_ptr<DecryptContextImpl> CastCdmContext::GetDecryptContext( + const std::string& key_id) { + return cast_cdm_->GetDecryptContext(key_id); +} + +void CastCdmContext::SetKeyStatus(const std::string& key_id, + CastKeyStatus key_status, + uint32_t system_code) { + cast_cdm_->SetKeyStatus(key_id, key_status, system_code); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cdm/cast_cdm_context.h b/chromium/chromecast/media/cdm/cast_cdm_context.h new file mode 100644 index 00000000000..70fa26ba940 --- /dev/null +++ b/chromium/chromecast/media/cdm/cast_cdm_context.h @@ -0,0 +1,64 @@ +// Copyright 2016 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. + +#ifndef CHROMECAST_MEDIA_CDM_CAST_CDM_CONTEXT_H_ +#define CHROMECAST_MEDIA_CDM_CAST_CDM_CONTEXT_H_ + +#include <memory> +#include <string> + +#include "chromecast/public/media/cast_key_status.h" +#include "media/base/cdm_context.h" + +namespace media { +class Decryptor; +} + +namespace chromecast { +namespace media { + +class CastCdm; +class DecryptContextImpl; + +// This class exposes only what's needed by CastRenderer. +class CastCdmContext : public ::media::CdmContext { + public: + explicit CastCdmContext(CastCdm* cast_cdm); + ~CastCdmContext() override; + + // ::media::CdmContext implementation. + ::media::Decryptor* GetDecryptor() override; + int GetCdmId() const override; + + // Register a player with this CDM. |new_key_cb| will be called when a new + // key is available. |cdm_unset_cb| will be called when the CDM is destroyed. + int RegisterPlayer(const base::Closure& new_key_cb, + const base::Closure& cdm_unset_cb); + + // Unregiester a player with this CDM. |registration_id| should be the id + // returned by RegisterPlayer(). + void UnregisterPlayer(int registration_id); + + // Returns the decryption context needed to decrypt frames encrypted with + // |key_id|. Returns null if |key_id| is not available. + std::unique_ptr<DecryptContextImpl> GetDecryptContext( + const std::string& key_id); + + // Notifies that key status has changed (e.g. if expiry is detected by + // hardware decoder). + void SetKeyStatus(const std::string& key_id, + CastKeyStatus key_status, + uint32_t system_code); + + private: + // The CastCdm object which owns |this|. + CastCdm* const cast_cdm_; + + DISALLOW_COPY_AND_ASSIGN(CastCdmContext); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CDM_CAST_CDM_CONTEXT_H_
\ No newline at end of file diff --git a/chromium/chromecast/media/cdm/cast_cdm_proxy.cc b/chromium/chromecast/media/cdm/cast_cdm_proxy.cc new file mode 100644 index 00000000000..eeadabcb05b --- /dev/null +++ b/chromium/chromecast/media/cdm/cast_cdm_proxy.cc @@ -0,0 +1,167 @@ +// Copyright 2014 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 "chromecast/media/cdm/cast_cdm_proxy.h" + +#include <utility> + +#include "base/bind.h" +#include "base/location.h" +#include "base/memory/ptr_util.h" +#include "base/single_thread_task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "media/base/cdm_key_information.h" +#include "media/base/cdm_promise.h" + +namespace chromecast { +namespace media { + +namespace { + +// media::CdmPromiseTemplate implementation that wraps a promise so as to +// allow passing to other threads. +template <typename... T> +class CdmPromiseInternal : public ::media::CdmPromiseTemplate<T...> { + public: + CdmPromiseInternal(std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise) + : task_runner_(base::ThreadTaskRunnerHandle::Get()), + promise_(std::move(promise)) {} + + ~CdmPromiseInternal() final { + if (IsPromiseSettled()) + return; + + DCHECK(promise_); + RejectPromiseOnDestruction(); + } + + // CdmPromiseTemplate<> implementation. + void resolve(const T&... result) final; + + void reject(::media::MediaKeys::Exception exception, + uint32_t system_code, + const std::string& error_message) final { + MarkPromiseSettled(); + task_runner_->PostTask( + FROM_HERE, base::Bind(&::media::CdmPromiseTemplate<T...>::reject, + base::Owned(promise_.release()), exception, + system_code, error_message)); + } + + private: + using ::media::CdmPromiseTemplate<T...>::IsPromiseSettled; + using ::media::CdmPromiseTemplate<T...>::MarkPromiseSettled; + using ::media::CdmPromiseTemplate<T...>::RejectPromiseOnDestruction; + + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise_; +}; + +template <typename... T> +void CdmPromiseInternal<T...>::resolve(const T&... result) { + MarkPromiseSettled(); + task_runner_->PostTask( + FROM_HERE, base::Bind(&::media::CdmPromiseTemplate<T...>::resolve, + base::Owned(promise_.release()), result...)); +} + +template <typename... T> +std::unique_ptr<CdmPromiseInternal<T...>> BindPromiseToCurrentLoop( + std::unique_ptr<::media::CdmPromiseTemplate<T...>> promise) { + return base::WrapUnique(new CdmPromiseInternal<T...>(std::move(promise))); +} + +} // namespace + +// A macro runs current member function on |task_runner_| thread. +#define FORWARD_ON_CDM_THREAD(param_fn, ...) \ + task_runner_->PostTask( \ + FROM_HERE, base::Bind(&CastCdm::param_fn, \ + base::Unretained(cast_cdm_.get()), ##__VA_ARGS__)) + +CastCdmProxy::CastCdmProxy( + const scoped_refptr<CastCdm>& cast_cdm, + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner) + : cast_cdm_(cast_cdm), task_runner_(task_runner) {} + +CastCdmProxy::~CastCdmProxy() { + DCHECK(thread_checker_.CalledOnValidThread()); + cast_cdm_->AddRef(); + CastCdm* raw_cdm = cast_cdm_.get(); + cast_cdm_ = nullptr; + task_runner_->ReleaseSoon(FROM_HERE, raw_cdm); +} + +CastCdm* CastCdmProxy::cast_cdm() const { + DCHECK(thread_checker_.CalledOnValidThread()); + return cast_cdm_.get(); +} + +void CastCdmProxy::SetServerCertificate( + const std::vector<uint8_t>& certificate, + std::unique_ptr<::media::SimpleCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + SetServerCertificate, certificate, + base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); +} + +void CastCdmProxy::CreateSessionAndGenerateRequest( + ::media::MediaKeys::SessionType session_type, + ::media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + std::unique_ptr<::media::NewSessionCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + CreateSessionAndGenerateRequest, session_type, init_data_type, init_data, + base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); +} + +void CastCdmProxy::LoadSession( + ::media::MediaKeys::SessionType session_type, + const std::string& session_id, + std::unique_ptr<::media::NewSessionCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + LoadSession, session_type, session_id, + base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); +} + +void CastCdmProxy::UpdateSession( + const std::string& session_id, + const std::vector<uint8_t>& response, + std::unique_ptr<::media::SimpleCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + UpdateSession, session_id, response, + base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); +} + +void CastCdmProxy::CloseSession( + const std::string& session_id, + std::unique_ptr<::media::SimpleCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + CloseSession, session_id, + base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); +} + +void CastCdmProxy::RemoveSession( + const std::string& session_id, + std::unique_ptr<::media::SimpleCdmPromise> promise) { + DCHECK(thread_checker_.CalledOnValidThread()); + FORWARD_ON_CDM_THREAD( + RemoveSession, session_id, + base::Passed(BindPromiseToCurrentLoop(std::move(promise)))); +} + +::media::CdmContext* CastCdmProxy::GetCdmContext() { + // This will be recast as a CastCdmService pointer before being passed to the + // media pipeline. The returned object should only be called on the CMA + // renderer thread. + return cast_cdm_->GetCdmContext(); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cdm/cast_cdm_proxy.h b/chromium/chromecast/media/cdm/cast_cdm_proxy.h new file mode 100644 index 00000000000..f17c4c199da --- /dev/null +++ b/chromium/chromecast/media/cdm/cast_cdm_proxy.h @@ -0,0 +1,71 @@ +// Copyright 2016 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. + +#ifndef CHROMECAST_MEDIA_CDM_CAST_CDM_PROXY_H_ +#define CHROMECAST_MEDIA_CDM_CAST_CDM_PROXY_H_ + +#include <stdint.h> + +#include "base/threading/thread_checker.h" +#include "chromecast/media/cdm/cast_cdm.h" + +namespace base { +class SingleThreadTaskRunner; +} + +namespace chromecast { +namespace media { + +// MediaKeys implementation that lives on the UI thread and forwards all calls +// to a CastCdm instance on the CMA thread. This is used to simplify the +// UI-CMA threading interaction. +// TODO(slan): Remove this class when CMA is deprecated. +class CastCdmProxy : public ::media::MediaKeys { + public: + CastCdmProxy(const scoped_refptr<CastCdm>& cast_cdm, + const scoped_refptr<base::SingleThreadTaskRunner>& task_runner); + + // Returns the CDM instance which lives on the CMA thread. + CastCdm* cast_cdm() const; + + private: + ~CastCdmProxy() override; + + // ::media::MediaKeys implementation: + void SetServerCertificate( + const std::vector<uint8_t>& certificate, + std::unique_ptr<::media::SimpleCdmPromise> promise) override; + void CreateSessionAndGenerateRequest( + ::media::MediaKeys::SessionType session_type, + ::media::EmeInitDataType init_data_type, + const std::vector<uint8_t>& init_data, + std::unique_ptr<::media::NewSessionCdmPromise> promise) override; + void LoadSession( + ::media::MediaKeys::SessionType session_type, + const std::string& session_id, + std::unique_ptr<::media::NewSessionCdmPromise> promise) override; + void UpdateSession( + const std::string& session_id, + const std::vector<uint8_t>& response, + std::unique_ptr<::media::SimpleCdmPromise> promise) override; + void CloseSession( + const std::string& session_id, + std::unique_ptr<::media::SimpleCdmPromise> promise) override; + void RemoveSession( + const std::string& session_id, + std::unique_ptr<::media::SimpleCdmPromise> promise) override; + ::media::CdmContext* GetCdmContext() override; + + scoped_refptr<CastCdm> cast_cdm_; + scoped_refptr<base::SingleThreadTaskRunner> task_runner_; + + base::ThreadChecker thread_checker_; + + DISALLOW_COPY_AND_ASSIGN(CastCdmProxy); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CDM_CAST_CDM_PROXY_H_ diff --git a/chromium/chromecast/media/cma/DEPS b/chromium/chromecast/media/cma/DEPS new file mode 100644 index 00000000000..b0625031df6 --- /dev/null +++ b/chromium/chromecast/media/cma/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "-chromecast/media/cdm", + "+chromecast/media/cdm/cast_cdm_context.h", +] diff --git a/chromium/chromecast/media/cma/backend/alsa/BUILD.gn b/chromium/chromecast/media/cma/backend/alsa/BUILD.gn index f0554e13a2b..ec28069cc64 100644 --- a/chromium/chromecast/media/cma/backend/alsa/BUILD.gn +++ b/chromium/chromecast/media/cma/backend/alsa/BUILD.gn @@ -4,6 +4,7 @@ import("//build/buildflag_header.gni") import("//build/config/chromecast_build.gni") +import("//chromecast/chromecast.gni") import("//media/media_options.gni") import("//testing/test.gni") @@ -24,6 +25,7 @@ shared_library("libcast_media_1.0_audio") { "cast_media_shlib.cc", "media_codec_support_cast_audio.cc", ] + deps = [ ":alsa_cma_backend", "//base", @@ -50,8 +52,11 @@ source_set("alsa_cma_backend") { "stream_mixer_alsa_input_impl.h", ] + libs = [ "asound" ] + deps = [ ":alsa_features", + ":audio_filter_includes", "//base", "//chromecast/base", "//chromecast/media/cma/backend", @@ -61,6 +66,34 @@ source_set("alsa_cma_backend") { "//media", "//media:shared_memory_support", ] + + if (chromecast_branding == "public") { + deps += [ ":audio_filter_null" ] + } else { + deps += [ "//chromecast/internal/media/cma/backend/alsa:filter" ] + } +} + +source_set("audio_filter_null") { + sources = [ + "audio_filter_factory_default.cc", + ] + + deps = [ + ":audio_filter_includes", + ] +} + +source_set("audio_filter_includes") { + sources = [ + "audio_filter_factory.h", + "audio_filter_interface.h", + ] + + deps = [ + "//base", + "//media", + ] } # GYP target: chromecast/media/media.gyp:chromecast_alsa_features diff --git a/chromium/chromecast/media/cma/backend/alsa/audio_filter_factory.h b/chromium/chromecast/media/cma/backend/alsa/audio_filter_factory.h new file mode 100644 index 00000000000..5f8dcda132e --- /dev/null +++ b/chromium/chromecast/media/cma/backend/alsa/audio_filter_factory.h @@ -0,0 +1,28 @@ +// Copyright 2016 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. + +#ifndef CHROMECAST_MEDIA_CMA_BACKEND_ALSA_AUDIO_FILTER_FACTORY_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_ALSA_AUDIO_FILTER_FACTORY_H_ + +#include <memory> + +#include "chromecast/media/cma/backend/alsa/audio_filter_interface.h" + +namespace chromecast { +namespace media { + +class AudioFilterFactory { + public: + // FilterType specifies the usage of the created filter. + enum FilterType { PRE_LOOPBACK_FILTER, POST_LOOPBACK_FILTER }; + + // Creates a new AudioFilterInterface. + static std::unique_ptr<AudioFilterInterface> MakeAudioFilter( + FilterType filter_type); +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_ALSA_FILTER_AUDIO_FILTER_FACTORY_H_ diff --git a/chromium/chromecast/media/cma/backend/alsa/audio_filter_factory_default.cc b/chromium/chromecast/media/cma/backend/alsa/audio_filter_factory_default.cc new file mode 100644 index 00000000000..3333c9a263e --- /dev/null +++ b/chromium/chromecast/media/cma/backend/alsa/audio_filter_factory_default.cc @@ -0,0 +1,18 @@ +// Copyright 2016 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 "chromecast/media/cma/backend/alsa/audio_filter_factory.h" + +// An AudioFilterFactory that just returns nullptrs + +namespace chromecast { +namespace media { + +std::unique_ptr<AudioFilterInterface> AudioFilterFactory::MakeAudioFilter( + AudioFilterFactory::FilterType filter_type) { + return nullptr; +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/backend/alsa/audio_filter_interface.h b/chromium/chromecast/media/cma/backend/alsa/audio_filter_interface.h new file mode 100644 index 00000000000..afcb4d59dc4 --- /dev/null +++ b/chromium/chromecast/media/cma/backend/alsa/audio_filter_interface.h @@ -0,0 +1,28 @@ +// Copyright 2016 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. + +#ifndef CHROMECAST_MEDIA_CMA_BACKEND_ALSA_AUDIO_FILTER_INTERFACE_H_ +#define CHROMECAST_MEDIA_CMA_BACKEND_ALSA_AUDIO_FILTER_INTERFACE_H_ + +#include <stdint.h> + +#include "media/base/sample_format.h" + +namespace chromecast { +namespace media { + +class AudioFilterInterface { + public: + virtual ~AudioFilterInterface() = default; + virtual bool SetSampleRateAndFormat(int sample_rate, + ::media::SampleFormat sample_format) = 0; + + // Process data frames. Must be interleaved. |data| will be overwritten. + virtual bool ProcessInterleaved(uint8_t* data, int frames) = 0; +}; + +} // namespace media +} // namespace chromecast + +#endif // CHROMECAST_MEDIA_CMA_BACKEND_ALSA_AUDIO_FILTER_INTERFACE_H_ diff --git a/chromium/chromecast/media/cma/backend/alsa/cast_media_shlib.cc b/chromium/chromecast/media/cma/backend/alsa/cast_media_shlib.cc index e6e21ccd51a..46f4712d7bb 100644 --- a/chromium/chromecast/media/cma/backend/alsa/cast_media_shlib.cc +++ b/chromium/chromecast/media/cma/backend/alsa/cast_media_shlib.cc @@ -10,6 +10,7 @@ #include "base/logging.h" #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" +#include "chromecast/base/init_command_line_shlib.h" #include "chromecast/base/task_runner_impl.h" #include "chromecast/media/cma/backend/alsa/media_pipeline_backend_alsa.h" #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa.h" @@ -88,8 +89,7 @@ std::unique_ptr<base::ThreadTaskRunnerHandle> g_thread_task_runner_handle; } // namespace void CastMediaShlib::Initialize(const std::vector<std::string>& argv) { - base::CommandLine::Init(0, nullptr); - base::CommandLine::ForCurrentProcess()->InitFromArgv(argv); + chromecast::InitCommandLineShlib(argv); g_video_plane = new DefaultVideoPlane(); @@ -98,8 +98,6 @@ void CastMediaShlib::Initialize(const std::vector<std::string>& argv) { } void CastMediaShlib::Finalize() { - base::CommandLine::Reset(); - if (g_hardware_controls) snd_hctl_close(g_hardware_controls); snd_ctl_elem_value_free(g_rate_offset_ppm); diff --git a/chromium/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc b/chromium/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc index 7038d5bd3b5..cdd867339e1 100644 --- a/chromium/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc +++ b/chromium/chromecast/media/cma/backend/alsa/stream_mixer_alsa.cc @@ -14,9 +14,11 @@ #include "base/memory/weak_ptr.h" #include "base/single_thread_task_runner.h" #include "base/strings/string_number_conversions.h" +#include "base/threading/platform_thread.h" #include "base/threading/thread_task_runner_handle.h" #include "chromecast/base/chromecast_switches.h" #include "chromecast/media/cma/backend/alsa/alsa_wrapper.h" +#include "chromecast/media/cma/backend/alsa/audio_filter_factory.h" #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input_impl.h" #include "media/base/audio_bus.h" #include "media/base/media_switches.h" @@ -124,8 +126,9 @@ bool GetSwitchValueAsInt(const std::string& switch_name, } const base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); - if (!command_line->HasSwitch(switch_name)) + if (!command_line->HasSwitch(switch_name)) { return false; + } int arg_value; if (!base::StringToInt(command_line->GetSwitchValueASCII(switch_name), @@ -144,8 +147,9 @@ bool GetSwitchValueAsNonNegativeInt(const std::string& switch_name, << " must have a non-negative default value"; DCHECK(value); - if (!GetSwitchValueAsInt(switch_name, default_value, value)) + if (!GetSwitchValueAsInt(switch_name, default_value, value)) { return false; + } if (*value < 0) { LOG(DFATAL) << "--" << switch_name << " must have a non-negative value"; @@ -204,8 +208,9 @@ StreamMixerAlsa::StreamMixerAlsa() if (single_threaded_for_test_) { mixer_task_runner_ = base::ThreadTaskRunnerHandle::Get(); } else { - // TODO(kmackay) Start thread with higher priority? - mixer_thread_->Start(); + base::Thread::Options options; + options.priority = base::ThreadPriority::REALTIME_AUDIO; + mixer_thread_->StartWithOptions(options); mixer_task_runner_ = mixer_thread_->task_runner(); } @@ -221,10 +226,23 @@ StreamMixerAlsa::StreamMixerAlsa() int fixed_samples_per_second; GetSwitchValueAsNonNegativeInt(switches::kAlsaFixedOutputSampleRate, kInvalidSampleRate, &fixed_samples_per_second); - if (fixed_samples_per_second != kInvalidSampleRate) + if (fixed_samples_per_second != kInvalidSampleRate) { LOG(INFO) << "Setting fixed sample rate to " << fixed_samples_per_second; + } + fixed_output_samples_per_second_ = fixed_samples_per_second; + low_sample_rate_cutoff_ = + chromecast::GetSwitchValueBoolean(switches::kAlsaEnableUpsampling, false) + ? kLowSampleRateCutoff + : 0; + + // Create filters + pre_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( + AudioFilterFactory::PRE_LOOPBACK_FILTER); + post_loopback_filter_ = AudioFilterFactory::MakeAudioFilter( + AudioFilterFactory::POST_LOOPBACK_FILTER); + DefineAlsaParameters(); } @@ -309,7 +327,7 @@ unsigned int StreamMixerAlsa::DetermineOutputRate(unsigned int requested_rate) { // because some common AV receivers don't support optical out at these // frequencies. See b/26385501 unsigned int first_choice_sample_rate = requested_rate; - if (requested_rate < kLowSampleRateCutoff) { + if (requested_rate < low_sample_rate_cutoff_) { first_choice_sample_rate = output_samples_per_second_ != kInvalidSampleRate ? output_samples_per_second_ : kFallbackSampleRate; @@ -438,6 +456,7 @@ int StreamMixerAlsa::SetAlsaPlaybackParams() { << alsa_buffer_size_ << " frames). Audio playback will not start."; } + RETURN_ERROR_CODE(PcmSwParamsSetAvailMin, pcm_, swparams, alsa_avail_min_); RETURN_ERROR_CODE(PcmSwParamsSetTstampMode, pcm_, swparams, SND_PCM_TSTAMP_ENABLE); @@ -500,6 +519,18 @@ void StreamMixerAlsa::Start() { return; } } + + // Initialize filters + if (pre_loopback_filter_) { + pre_loopback_filter_->SetSampleRateAndFormat( + output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); + } + + if (post_loopback_filter_) { + post_loopback_filter_->SetSampleRateAndFormat( + output_samples_per_second_, ::media::SampleFormat::kSampleFormatS32); + } + RETURN_REPORT_ERROR(PcmPrepare, pcm_); RETURN_REPORT_ERROR(PcmStatusMalloc, &pcm_status_); @@ -512,6 +543,10 @@ void StreamMixerAlsa::Start() { } void StreamMixerAlsa::Stop() { + for (auto* observer : loopback_observers_) { + observer->OnLoopbackInterrupted(); + } + alsa_->PcmStatusFree(pcm_status_); pcm_status_ = nullptr; alsa_->PcmHwParamsFree(pcm_hw_params_); @@ -519,26 +554,31 @@ void StreamMixerAlsa::Stop() { state_ = kStateUninitialized; output_samples_per_second_ = kInvalidSampleRate; - if (!pcm_) + if (!pcm_) { return; + } // If |pcm_| is RUNNING, drain all pending data. if (alsa_->PcmState(pcm_) == SND_PCM_STATE_RUNNING) { int err = alsa_->PcmDrain(pcm_); - if (err < 0) + if (err < 0) { LOG(ERROR) << "snd_pcm_drain error: " << alsa_->StrError(err); + } } else { int err = alsa_->PcmDrop(pcm_); - if (err < 0) + if (err < 0) { LOG(ERROR) << "snd_pcm_drop error: " << alsa_->StrError(err); + } } } void StreamMixerAlsa::Close() { Stop(); - if (!pcm_) + if (!pcm_) { return; + } + LOG(INFO) << "snd_pcm_close: handle=" << pcm_; int err = alsa_->PcmClose(pcm_); if (err < 0) { @@ -561,8 +601,10 @@ void StreamMixerAlsa::SignalError() { void StreamMixerAlsa::SetAlsaWrapperForTest( std::unique_ptr<AlsaWrapper> alsa_wrapper) { - if (alsa_) + if (alsa_) { Close(); + } + alsa_ = std::move(alsa_wrapper); } @@ -579,8 +621,9 @@ void StreamMixerAlsa::ClearInputsForTest() { void StreamMixerAlsa::AddInput(std::unique_ptr<InputQueue> input) { RUN_ON_MIXER_THREAD(&StreamMixerAlsa::AddInput, base::Passed(std::move(input))); - if (!alsa_) + if (!alsa_) { alsa_.reset(new AlsaWrapper()); + } DCHECK(input); // If the new input is a primary one, we may need to change the output @@ -611,11 +654,14 @@ void StreamMixerAlsa::CheckChangeOutputRate(int input_samples_per_second) { if (!pcm_ || input_samples_per_second == requested_output_samples_per_second_ || input_samples_per_second == output_samples_per_second_ || - input_samples_per_second < static_cast<int>(kLowSampleRateCutoff)) + input_samples_per_second < static_cast<int>(low_sample_rate_cutoff_)) { return; + } + for (auto&& input : inputs_) { - if (input->primary() && !input->IsDeleting()) + if (input->primary() && !input->IsDeleting()) { return; + } } // Move all current inputs to the ignored list @@ -683,10 +729,14 @@ void StreamMixerAlsa::CheckClose() { } void StreamMixerAlsa::OnFramesQueued() { - if (state_ != kStateNormalPlayback) + if (state_ != kStateNormalPlayback) { return; - if (retry_write_frames_timer_->IsRunning()) + } + + if (retry_write_frames_timer_->IsRunning()) { return; + } + retry_write_frames_timer_->Start( FROM_HERE, base::TimeDelta(), base::Bind(&StreamMixerAlsa::WriteFrames, base::Unretained(this))); @@ -703,8 +753,10 @@ void StreamMixerAlsa::WriteFrames() { bool StreamMixerAlsa::TryWriteFrames() { DCHECK(mixer_task_runner_->BelongsToCurrentThread()); - if (state_ != kStateNormalPlayback) + if (state_ != kStateNormalPlayback) { return false; + } + int chunk_size = output_samples_per_second_ * kMaxWriteSizeMs / 1000; std::vector<InputQueue*> active_inputs; for (auto&& input : inputs_) { @@ -720,24 +772,30 @@ bool StreamMixerAlsa::TryWriteFrames() { if (active_inputs.empty()) { // No inputs have any data to provide. - if (!inputs_.empty()) + if (!inputs_.empty()) { return false; // If there are some inputs, don't fill with silence. + } // If we have no inputs, fill with silence to avoid underrun. chunk_size = kPreventUnderrunChunkSize; - if (!mixed_ || mixed_->frames() < chunk_size) + if (!mixed_ || mixed_->frames() < chunk_size) { mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); + } + mixed_->Zero(); WriteMixedPcm(*mixed_, chunk_size); return true; } // If |mixed_| has not been allocated, or it is too small, allocate a buffer. - if (!mixed_ || mixed_->frames() < chunk_size) + if (!mixed_ || mixed_->frames() < chunk_size) { mixed_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); + } + // If |temp_| has not been allocated, or is too small, allocate a buffer. - if (!temp_ || temp_->frames() < chunk_size) + if (!temp_ || temp_->frames() < chunk_size) { temp_ = ::media::AudioBus::Create(kNumOutputChannels, chunk_size); + } mixed_->ZeroFramesPartial(0, chunk_size); @@ -767,29 +825,43 @@ void StreamMixerAlsa::WriteMixedPcm(const ::media::AudioBus& mixed, size_t interleaved_size = static_cast<size_t>(frames * kNumOutputChannels) * BytesPerOutputFormatSample(); - if (interleaved_.size() < interleaved_size) + if (interleaved_.size() < interleaved_size) { interleaved_.resize(interleaved_size); + } int64_t expected_playback_time = rendering_delay_.timestamp_microseconds + rendering_delay_.delay_microseconds; mixed.ToInterleaved(frames, BytesPerOutputFormatSample(), interleaved_.data()); + // Filter, send to observers, and post filter + if (pre_loopback_filter_) { + pre_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames); + } + for (CastMediaShlib::LoopbackAudioObserver* observer : loopback_observers_) { observer->OnLoopbackAudio(expected_playback_time, kSampleFormatS32, output_samples_per_second_, kNumOutputChannels, interleaved_.data(), interleaved_size); } + if (post_loopback_filter_) { + post_loopback_filter_->ProcessInterleaved(interleaved_.data(), frames); + } + // If the PCM has been drained it will be in SND_PCM_STATE_SETUP and need // to be prepared in order for playback to work. - if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) + if (alsa_->PcmState(pcm_) == SND_PCM_STATE_SETUP) { RETURN_REPORT_ERROR(PcmPrepare, pcm_); + } int frames_left = frames; uint8_t* data = &interleaved_[0]; while (frames_left) { int frames_or_error; while ((frames_or_error = alsa_->PcmWritei(pcm_, data, frames_left)) < 0) { + for (auto* observer : loopback_observers_) { + observer->OnLoopbackInterrupted(); + } RETURN_REPORT_ERROR(PcmRecover, pcm_, frames_or_error, kPcmRecoverIsSilent); } diff --git a/chromium/chromecast/media/cma/backend/alsa/stream_mixer_alsa.h b/chromium/chromecast/media/cma/backend/alsa/stream_mixer_alsa.h index 58a51ec1e1b..6366db6324d 100644 --- a/chromium/chromecast/media/cma/backend/alsa/stream_mixer_alsa.h +++ b/chromium/chromecast/media/cma/backend/alsa/stream_mixer_alsa.h @@ -16,6 +16,7 @@ #include "base/memory/ref_counted.h" #include "base/threading/thread.h" #include "base/timer/timer.h" +#include "chromecast/media/cma/backend/alsa/audio_filter_interface.h" #include "chromecast/media/cma/backend/alsa/media_pipeline_backend_alsa.h" #include "chromecast/media/cma/backend/alsa/stream_mixer_alsa_input.h" #include "chromecast/public/cast_media_shlib.h" @@ -195,6 +196,7 @@ class StreamMixerAlsa { scoped_refptr<base::SingleThreadTaskRunner> mixer_task_runner_; unsigned int fixed_output_samples_per_second_; + unsigned int low_sample_rate_cutoff_; int requested_output_samples_per_second_; int output_samples_per_second_; snd_pcm_t* pcm_; @@ -232,6 +234,9 @@ class StreamMixerAlsa { std::vector<CastMediaShlib::LoopbackAudioObserver*> loopback_observers_; + std::unique_ptr<AudioFilterInterface> pre_loopback_filter_; + std::unique_ptr<AudioFilterInterface> post_loopback_filter_; + DISALLOW_COPY_AND_ASSIGN(StreamMixerAlsa); }; diff --git a/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc b/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc index ec663c9accc..011ac946e16 100644 --- a/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc +++ b/chromium/chromecast/media/cma/backend/audio_video_pipeline_device_unittest.cc @@ -54,6 +54,9 @@ const base::TimeDelta kMonitorLoopDelay = base::TimeDelta::FromMilliseconds(20); const int64_t kStartPts = 1000 * 1000; // Amount that PTS is allowed to progress past the time that Pause() was called. const int kPausePtsSlackMs = 75; +// Number of effects streams to open simultaneously when also playing a +// non-effects stream. +const int kNumEffectsStreams = 1; void IgnoreEos() {} @@ -627,7 +630,6 @@ void AudioVideoPipelineDeviceTest::PauseBeforeEos() { } void AudioVideoPipelineDeviceTest::AddEffectsStreams() { - const int kNumEffectsStreams = 3; for (int i = 0; i < kNumEffectsStreams; ++i) { MediaPipelineDeviceParams params( MediaPipelineDeviceParams::kModeIgnorePts, diff --git a/chromium/chromecast/media/cma/backend/multizone_backend_unittest.cc b/chromium/chromecast/media/cma/backend/multizone_backend_unittest.cc index c6248c408ae..c7400bf8078 100644 --- a/chromium/chromecast/media/cma/backend/multizone_backend_unittest.cc +++ b/chromium/chromecast/media/cma/backend/multizone_backend_unittest.cc @@ -5,6 +5,7 @@ #include <stdint.h> #include <stdlib.h> +#include <algorithm> #include <limits> #include <memory> #include <vector> @@ -42,7 +43,7 @@ const int64_t kPushTimeUs = 2 * kMicrosecondsPerSecond; const int64_t kStartPts = 0; const int64_t kRenderingDelayGracePeriodUs = 250 * 1000; const int64_t kMaxRenderingDelayErrorUs = 200; -const int kNumEffectsStreams = 3; +const int kNumEffectsStreams = 1; void IgnoreEos() {} @@ -57,6 +58,22 @@ class BufferFeeder : public MediaPipelineBackend::Decoder::Delegate { void Start(); void Stop(); + int64_t max_rendering_delay_error_us() { + return max_rendering_delay_error_us_; + } + + int64_t max_positive_rendering_delay_error_us() { + return max_positive_rendering_delay_error_us_; + } + + int64_t max_negative_rendering_delay_error_us() { + return max_negative_rendering_delay_error_us_; + } + + int64_t average_rendering_delay_error_us() { + return total_rendering_delay_error_us_ / sample_count_; + } + private: void FeedBuffer(); @@ -84,6 +101,11 @@ class BufferFeeder : public MediaPipelineBackend::Decoder::Delegate { const AudioConfig config_; const bool effects_only_; const base::Closure eos_cb_; + int64_t max_rendering_delay_error_us_; + int64_t max_positive_rendering_delay_error_us_; + int64_t max_negative_rendering_delay_error_us_; + int64_t total_rendering_delay_error_us_; + size_t sample_count_; bool feeding_completed_; std::unique_ptr<TaskRunnerImpl> task_runner_; std::unique_ptr<MediaPipelineBackend> backend_; @@ -138,6 +160,11 @@ BufferFeeder::BufferFeeder(const AudioConfig& config, : config_(config), effects_only_(effects_only), eos_cb_(eos_cb), + max_rendering_delay_error_us_(0), + max_positive_rendering_delay_error_us_(0), + max_negative_rendering_delay_error_us_(0), + total_rendering_delay_error_us_(0), + sample_count_(0), feeding_completed_(false), task_runner_(new TaskRunnerImpl()), decoder_(nullptr), @@ -228,8 +255,17 @@ void BufferFeeder::OnPushBufferComplete(BufferStatus status) { if (pushed_us_ > kRenderingDelayGracePeriodUs) { int64_t error = next_push_playback_timestamp_ - expected_next_push_playback_timestamp; - EXPECT_LT(std::abs(error), kMaxRenderingDelayErrorUs) - << "Bad rendering delay after " << pushed_us_ << " us"; + max_rendering_delay_error_us_ = + std::max(max_rendering_delay_error_us_, std::abs(error)); + total_rendering_delay_error_us_ += std::abs(error); + if (error >= 0) { + max_positive_rendering_delay_error_us_ = + std::max(max_positive_rendering_delay_error_us_, error); + } else { + max_negative_rendering_delay_error_us_ = + std::min(max_negative_rendering_delay_error_us_, error); + } + sample_count_++; } } pushed_us_ += last_push_length_us_; @@ -250,7 +286,7 @@ MultizoneBackendTest::~MultizoneBackendTest() {} void MultizoneBackendTest::Initialize(int sample_rate) { AudioConfig config; config.codec = kCodecPCM; - config.sample_format = kSampleFormatPlanarF32; + config.sample_format = kSampleFormatS32; config.channel_number = 2; config.bytes_per_channel = 4; config.samples_per_second = sample_rate; @@ -291,6 +327,15 @@ void MultizoneBackendTest::OnEndOfStream() { feeder->Stop(); base::MessageLoop::current()->QuitWhenIdle(); + + EXPECT_LT(audio_feeder_->max_rendering_delay_error_us(), + kMaxRenderingDelayErrorUs) + << "Max positive rendering delay error: " + << audio_feeder_->max_positive_rendering_delay_error_us() + << "\nMax negative rendering delay error: " + << audio_feeder_->max_negative_rendering_delay_error_us() + << "\nAverage rendering delay error: " + << audio_feeder_->average_rendering_delay_error_us(); } TEST_P(MultizoneBackendTest, RenderingDelay) { diff --git a/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc b/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc index fb8e2aaee48..9ccd7ca0653 100644 --- a/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc +++ b/chromium/chromecast/media/cma/base/balanced_media_task_runner_unittest.cc @@ -9,8 +9,10 @@ #include <vector> #include "base/bind.h" +#include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -122,10 +124,9 @@ void BalancedMediaTaskRunnerTest::SetupTest( } void BalancedMediaTaskRunnerTest::ProcessAllTasks() { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&BalancedMediaTaskRunnerTest::OnTestTimeout, - base::Unretained(this)), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&BalancedMediaTaskRunnerTest::OnTestTimeout, + base::Unretained(this)), base::TimeDelta::FromSeconds(5)); ScheduleTask(); } diff --git a/chromium/chromecast/media/cma/base/cast_decrypt_config_impl.cc b/chromium/chromecast/media/cma/base/cast_decrypt_config_impl.cc index 51ef53eb87c..0c6761298f9 100644 --- a/chromium/chromecast/media/cma/base/cast_decrypt_config_impl.cc +++ b/chromium/chromecast/media/cma/base/cast_decrypt_config_impl.cc @@ -4,25 +4,16 @@ #include "chromecast/media/cma/base/cast_decrypt_config_impl.h" -#include "media/base/decrypt_config.h" - namespace chromecast { namespace media { CastDecryptConfigImpl::CastDecryptConfigImpl( - const ::media::DecryptConfig& config) - : key_id_(config.key_id()), iv_(config.iv()) { - for (const auto& sample : config.subsamples()) { - subsamples_.push_back( - SubsampleEntry(sample.clear_bytes, sample.cypher_bytes)); - } -} - -CastDecryptConfigImpl::CastDecryptConfigImpl( - const std::string& key_id, - const std::string& iv, - const std::vector<SubsampleEntry>& subsamples) - : key_id_(key_id), iv_(iv), subsamples_(subsamples) {} + std::string key_id, + std::string iv, + std::vector<SubsampleEntry> subsamples) + : key_id_(std::move(key_id)), + iv_(std::move(iv)), + subsamples_(std::move(subsamples)) {} CastDecryptConfigImpl::~CastDecryptConfigImpl() {} diff --git a/chromium/chromecast/media/cma/base/cast_decrypt_config_impl.h b/chromium/chromecast/media/cma/base/cast_decrypt_config_impl.h index b78494bb4cc..19bc64e01fb 100644 --- a/chromium/chromecast/media/cma/base/cast_decrypt_config_impl.h +++ b/chromium/chromecast/media/cma/base/cast_decrypt_config_impl.h @@ -7,20 +7,15 @@ #include "chromecast/public/media/cast_decrypt_config.h" -namespace media { -class DecryptConfig; -} - namespace chromecast { namespace media { // Contains all information that a decryptor needs to decrypt a media sample. class CastDecryptConfigImpl : public CastDecryptConfig { public: - CastDecryptConfigImpl(const ::media::DecryptConfig& config); - CastDecryptConfigImpl(const std::string& key_id, - const std::string& iv, - const std::vector<SubsampleEntry>& subsamples); + CastDecryptConfigImpl(std::string key_id, + std::string iv, + std::vector<SubsampleEntry> subsamples); ~CastDecryptConfigImpl() override; const std::string& key_id() const override; diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc index e4019c851a1..282c0da52ba 100644 --- a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc +++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.cc @@ -20,6 +20,24 @@ DecoderBufferAdapter::DecoderBufferAdapter( StreamId stream_id, const scoped_refptr<::media::DecoderBuffer>& buffer) : stream_id_(stream_id), buffer_(buffer) { + DCHECK(buffer_); + + const ::media::DecryptConfig* decrypt_config = + buffer_->end_of_stream() ? nullptr : buffer_->decrypt_config(); + if (decrypt_config && decrypt_config->is_encrypted()) { + std::vector<SubsampleEntry> subsamples; + for (const auto& sample : decrypt_config->subsamples()) { + subsamples.emplace_back(sample.clear_bytes, sample.cypher_bytes); + } + if (subsamples.empty()) { + // DecryptConfig may contain 0 subsamples if all content is encrypted. + // Map this case to a single fully-encrypted "subsample" for more + // consistent backend handling. + subsamples.emplace_back(0, buffer_->data_size()); + } + decrypt_config_.reset(new CastDecryptConfigImpl( + decrypt_config->key_id(), decrypt_config->iv(), std::move(subsamples))); + } } DecoderBufferAdapter::~DecoderBufferAdapter() { @@ -50,11 +68,6 @@ size_t DecoderBufferAdapter::data_size() const { } const CastDecryptConfig* DecoderBufferAdapter::decrypt_config() const { - if (buffer_->decrypt_config() && !decrypt_config_) { - const ::media::DecryptConfig* config = buffer_->decrypt_config(); - decrypt_config_.reset(new CastDecryptConfigImpl(*config)); - } - return decrypt_config_.get(); } diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h index c42fc9184c6..b1ec721245a 100644 --- a/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h +++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter.h @@ -49,7 +49,7 @@ class DecoderBufferAdapter : public DecoderBufferBase { StreamId stream_id_; scoped_refptr<::media::DecoderBuffer> const buffer_; - mutable std::unique_ptr<CastDecryptConfig> decrypt_config_; + std::unique_ptr<CastDecryptConfig> decrypt_config_; DISALLOW_COPY_AND_ASSIGN(DecoderBufferAdapter); }; diff --git a/chromium/chromecast/media/cma/base/decoder_buffer_adapter_unittest.cc b/chromium/chromecast/media/cma/base/decoder_buffer_adapter_unittest.cc new file mode 100644 index 00000000000..71fdf31a3ee --- /dev/null +++ b/chromium/chromecast/media/cma/base/decoder_buffer_adapter_unittest.cc @@ -0,0 +1,161 @@ +// Copyright 2016 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 "chromecast/media/cma/base/decoder_buffer_adapter.h" + +#include "chromecast/public/media/cast_decrypt_config.h" +#include "media/base/decoder_buffer.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { +static const uint8_t kBufferData[] = "hello"; +static const size_t kBufferDataSize = arraysize(kBufferData); +static const int64_t kBufferTimestampUs = 31; + +scoped_refptr<media::DecoderBuffer> MakeDecoderBuffer() { + scoped_refptr<media::DecoderBuffer> buffer = + media::DecoderBuffer::CopyFrom(kBufferData, kBufferDataSize); + buffer->set_timestamp(base::TimeDelta::FromMicroseconds(kBufferTimestampUs)); + return buffer; +} +} // namespace + +namespace chromecast { +namespace media { + +TEST(DecoderBufferAdapterTest, Default) { + scoped_refptr<::media::DecoderBuffer> buffer = MakeDecoderBuffer(); + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(buffer)); + + EXPECT_EQ(kPrimary, buffer_adapter->stream_id()); + EXPECT_EQ(kBufferTimestampUs, buffer_adapter->timestamp()); + EXPECT_EQ(0, memcmp(buffer_adapter->data(), kBufferData, kBufferDataSize)); + EXPECT_EQ(kBufferDataSize, buffer_adapter->data_size()); + EXPECT_EQ(nullptr, buffer_adapter->decrypt_config()); + EXPECT_FALSE(buffer_adapter->end_of_stream()); + EXPECT_EQ(buffer, buffer_adapter->ToMediaBuffer()); +} + +TEST(DecoderBufferAdapterTest, Secondary) { + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(kSecondary, MakeDecoderBuffer())); + EXPECT_EQ(kSecondary, buffer_adapter->stream_id()); +} + +TEST(DecoderBufferAdapterTest, Timestamp) { + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(MakeDecoderBuffer())); + EXPECT_EQ(kBufferTimestampUs, buffer_adapter->timestamp()); + + const int64_t kTestTimestampUs = 62; + buffer_adapter->set_timestamp( + base::TimeDelta::FromMicroseconds(kTestTimestampUs)); + EXPECT_EQ(kTestTimestampUs, buffer_adapter->timestamp()); +} + +TEST(DecoderBufferAdapterTest, Data) { + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(MakeDecoderBuffer())); + EXPECT_EQ(0, memcmp(buffer_adapter->data(), kBufferData, kBufferDataSize)); + EXPECT_EQ(kBufferDataSize, buffer_adapter->data_size()); + + const uint8_t kTestBufferData[] = "world"; + const size_t kTestBufferDataSize = arraysize(kTestBufferData); + memcpy(buffer_adapter->writable_data(), kTestBufferData, kTestBufferDataSize); + EXPECT_EQ( + 0, memcmp(buffer_adapter->data(), kTestBufferData, kTestBufferDataSize)); + EXPECT_EQ(kTestBufferDataSize, buffer_adapter->data_size()); +} + +TEST(DecoderBufferAdapterTest, DecryptConfig) { + const std::string kKeyId("foo-key"); + const std::string kIV("0123456789abcdef"); + + // NULL DecryptConfig. + { + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(MakeDecoderBuffer())); + EXPECT_EQ(nullptr, buffer_adapter->decrypt_config()); + } + + // Empty initialization vector. + { + std::vector<::media::SubsampleEntry> subsamples; + std::unique_ptr<::media::DecryptConfig> decrypt_config( + new ::media::DecryptConfig(kKeyId, "", subsamples)); + EXPECT_FALSE(decrypt_config->is_encrypted()); + + scoped_refptr<::media::DecoderBuffer> buffer = MakeDecoderBuffer(); + buffer->set_decrypt_config(std::move(decrypt_config)); + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(buffer)); + // DecoderBufferAdapter ignores the decrypt config. + EXPECT_EQ(nullptr, buffer_adapter->decrypt_config()); + } + + // Empty subsamples. + { + std::vector<::media::SubsampleEntry> subsamples; + std::unique_ptr<::media::DecryptConfig> decrypt_config( + new ::media::DecryptConfig(kKeyId, kIV, subsamples)); + EXPECT_TRUE(decrypt_config->is_encrypted()); + + scoped_refptr<::media::DecoderBuffer> buffer = MakeDecoderBuffer(); + buffer->set_decrypt_config(std::move(decrypt_config)); + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(buffer)); + const CastDecryptConfig* cast_decrypt_config = + buffer_adapter->decrypt_config(); + EXPECT_NE(nullptr, cast_decrypt_config); + EXPECT_EQ(kKeyId, cast_decrypt_config->key_id()); + EXPECT_EQ(kIV, cast_decrypt_config->iv()); + // DecoderBufferAdapter creates a single fully-encrypted subsample. + EXPECT_EQ(1u, cast_decrypt_config->subsamples().size()); + EXPECT_EQ(0u, cast_decrypt_config->subsamples()[0].clear_bytes); + EXPECT_EQ(kBufferDataSize, + cast_decrypt_config->subsamples()[0].cypher_bytes); + } + + // Regular DecryptConfig with non-empty subsamples. + { + uint32_t kClearBytes[] = {10, 15}; + uint32_t kCypherBytes[] = {5, 7}; + std::vector<::media::SubsampleEntry> subsamples; + subsamples.emplace_back(kClearBytes[0], kCypherBytes[0]); + subsamples.emplace_back(kClearBytes[1], kCypherBytes[1]); + + std::unique_ptr<::media::DecryptConfig> decrypt_config( + new ::media::DecryptConfig(kKeyId, kIV, subsamples)); + EXPECT_TRUE(decrypt_config->is_encrypted()); + + scoped_refptr<::media::DecoderBuffer> buffer = MakeDecoderBuffer(); + buffer->set_decrypt_config(std::move(decrypt_config)); + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(buffer)); + const CastDecryptConfig* cast_decrypt_config = + buffer_adapter->decrypt_config(); + EXPECT_NE(nullptr, cast_decrypt_config); + EXPECT_EQ(kKeyId, cast_decrypt_config->key_id()); + EXPECT_EQ(kIV, cast_decrypt_config->iv()); + // DecoderBufferAdapter copies all subsamples. + EXPECT_EQ(2u, cast_decrypt_config->subsamples().size()); + EXPECT_EQ(kClearBytes[0], cast_decrypt_config->subsamples()[0].clear_bytes); + EXPECT_EQ(kCypherBytes[0], + cast_decrypt_config->subsamples()[0].cypher_bytes); + EXPECT_EQ(kClearBytes[1], cast_decrypt_config->subsamples()[1].clear_bytes); + EXPECT_EQ(kCypherBytes[1], + cast_decrypt_config->subsamples()[1].cypher_bytes); + } +} + +TEST(DecoderBufferAdapterTest, EndOfStream) { + scoped_refptr<DecoderBufferAdapter> buffer_adapter( + new DecoderBufferAdapter(::media::DecoderBuffer::CreateEOSBuffer())); + EXPECT_TRUE(buffer_adapter->end_of_stream()); + EXPECT_EQ(nullptr, buffer_adapter->decrypt_config()); +} + +} // namespace media +} // namespace chromecast diff --git a/chromium/chromecast/media/cma/base/demuxer_stream_adapter.cc b/chromium/chromecast/media/cma/base/demuxer_stream_adapter.cc index 751965cdbf3..b6be89fe4e4 100644 --- a/chromium/chromecast/media/cma/base/demuxer_stream_adapter.cc +++ b/chromium/chromecast/media/cma/base/demuxer_stream_adapter.cc @@ -127,12 +127,12 @@ void DemuxerStreamAdapter::OnNewBuffer( } if (status == ::media::DemuxerStream::kAborted) { - DCHECK(input.get() == NULL); + DCHECK(!input); return; } if (status == ::media::DemuxerStream::kConfigChanged) { - DCHECK(input.get() == NULL); + DCHECK(!input); if (demuxer_stream_->type() == ::media::DemuxerStream::VIDEO) video_config_ = demuxer_stream_->video_decoder_config(); if (demuxer_stream_->type() == ::media::DemuxerStream::AUDIO) diff --git a/chromium/chromecast/media/cma/base/demuxer_stream_adapter_unittest.cc b/chromium/chromecast/media/cma/base/demuxer_stream_adapter_unittest.cc index 681159fd9d4..5882262f1e8 100644 --- a/chromium/chromecast/media/cma/base/demuxer_stream_adapter_unittest.cc +++ b/chromium/chromecast/media/cma/base/demuxer_stream_adapter_unittest.cc @@ -8,8 +8,10 @@ #include <memory> #include "base/bind.h" +#include "base/location.h" #include "base/macros.h" #include "base/memory/ref_counted.h" +#include "base/single_thread_task_runner.h" #include "base/threading/thread.h" #include "base/threading/thread_task_runner_handle.h" #include "base/time/time.h" @@ -85,10 +87,9 @@ void DemuxerStreamAdapterTest::Start() { // TODO(damienv): currently, test assertions which fail do not trigger the // exit of the unit test, the message loop is still running. Find a different // way to exit the unit test. - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&DemuxerStreamAdapterTest::OnTestTimeout, - base::Unretained(this)), + base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( + FROM_HERE, base::Bind(&DemuxerStreamAdapterTest::OnTestTimeout, + base::Unretained(this)), base::TimeDelta::FromSeconds(5)); coded_frame_provider_->Read(base::Bind(&DemuxerStreamAdapterTest::OnNewFrame, @@ -130,11 +131,10 @@ void DemuxerStreamAdapterTest::OnNewFrame( base::Closure flush_cb = base::Bind( &DemuxerStreamAdapterTest::OnFlushCompleted, base::Unretained(this)); if (use_post_task_for_flush_) { - base::MessageLoop::current()->PostTask( + base::ThreadTaskRunnerHandle::Get()->PostTask( FROM_HERE, base::Bind(&CodedFrameProvider::Flush, - base::Unretained(coded_frame_provider_.get()), - flush_cb)); + base::Unretained(coded_frame_provider_.get()), flush_cb)); } else { coded_frame_provider_->Flush(flush_cb); } diff --git a/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc b/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc index 764e4b4b8b7..229e2375632 100644 --- a/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc +++ b/chromium/chromecast/media/cma/ipc/media_message_fifo_unittest.cc @@ -54,7 +54,7 @@ void MsgProducer(std::unique_ptr<MediaMessageFifo> fifo, if (msg1) break; base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); - } while(true); + } while (true); } fifo.reset(); @@ -78,7 +78,7 @@ void MsgConsumer(std::unique_ptr<MediaMessageFifo> fifo, break; } base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); - } while(true); + } while (true); } fifo.reset(); @@ -133,7 +133,8 @@ TEST(MediaMessageFifoTest, AlternateWriteRead) { new FifoMemoryChunk(&buffer[0], buffer_size)), false)); - base::WaitableEvent event(false, false); + base::WaitableEvent event(base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); thread->task_runner()->PostTask( FROM_HERE, base::Bind(&MsgProducerConsumer, base::Passed(&producer_fifo), base::Passed(&consumer_fifo), &event)); @@ -163,8 +164,12 @@ TEST(MediaMessageFifoTest, MultiThreaded) { new FifoMemoryChunk(&buffer[0], buffer_size)), false)); - base::WaitableEvent producer_event_done(false, false); - base::WaitableEvent consumer_event_done(false, false); + base::WaitableEvent producer_event_done( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); + base::WaitableEvent consumer_event_done( + base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED); const int msg_count = 2048; producer_thread->task_runner()->PostTask( diff --git a/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc b/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc index 56c01833513..8d6a9e03ced 100644 --- a/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc +++ b/chromium/chromecast/media/cma/ipc_streamer/decoder_buffer_base_marshaller.cc @@ -170,27 +170,11 @@ void DecoderBufferBaseMarshaller::Write( CHECK(msg->WritePod(buffer->stream_id())); CHECK(msg->WritePod(buffer->timestamp())); - bool has_decrypt_config = - (buffer->decrypt_config() != NULL && - buffer->decrypt_config()->iv().size() > 0); + bool has_decrypt_config = buffer->decrypt_config() != nullptr; CHECK(msg->WritePod(has_decrypt_config)); - if (has_decrypt_config) { - // DecryptConfig may contain 0 subsamples if all content is encrypted. - // Map this case to a single fully-encrypted "subsample" for more consistent - // backend handling. - if (buffer->decrypt_config()->subsamples().empty()) { - std::vector<SubsampleEntry> encrypted_subsample_list(1); - encrypted_subsample_list[0].clear_bytes = 0; - encrypted_subsample_list[0].cypher_bytes = buffer->data_size(); - CastDecryptConfigImpl full_sample_config( - buffer->decrypt_config()->key_id(), buffer->decrypt_config()->iv(), - encrypted_subsample_list); - DecryptConfigMarshaller::Write(full_sample_config, msg); - } else { - DecryptConfigMarshaller::Write(*buffer->decrypt_config(), msg); - } - } + if (has_decrypt_config) + DecryptConfigMarshaller::Write(*buffer->decrypt_config(), msg); CHECK(msg->WritePod(buffer->data_size())); CHECK(msg->WriteBuffer(buffer->data(), buffer->data_size())); diff --git a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc index 38c9ae362b7..c59b751d01a 100644 --- a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc +++ b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.cc @@ -13,7 +13,7 @@ #include "base/strings/string_number_conversions.h" #include "base/threading/thread_task_runner_handle.h" #include "chromecast/media/base/decrypt_context_impl.h" -#include "chromecast/media/cdm/browser_cdm_cast.h" +#include "chromecast/media/cdm/cast_cdm_context.h" #include "chromecast/media/cma/base/buffering_frame_provider.h" #include "chromecast/media/cma/base/buffering_state.h" #include "chromecast/media/cma/base/cma_logging.h" @@ -45,10 +45,8 @@ AvPipelineImpl::AvPipelineImpl(MediaPipelineBackend::Decoder* decoder, playable_buffered_time_(::media::kNoTimestamp()), enable_feeding_(false), pending_read_(false), - enable_time_update_(false), - pending_time_update_task_(false), - media_keys_(NULL), - media_keys_callback_id_(kNoCallbackId), + cast_cdm_context_(NULL), + player_tracker_callback_id_(kNoCallbackId), weak_factory_(this) { DCHECK(decoder_); decoder_->SetDelegate(this); @@ -59,8 +57,8 @@ AvPipelineImpl::AvPipelineImpl(MediaPipelineBackend::Decoder* decoder, AvPipelineImpl::~AvPipelineImpl() { DCHECK(thread_checker_.CalledOnValidThread()); - if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) - media_keys_->UnregisterPlayer(media_keys_callback_id_); + if (cast_cdm_context_ && player_tracker_callback_id_ != kNoCallbackId) + cast_cdm_context_->UnregisterPlayer(player_tracker_callback_id_); } void AvPipelineImpl::SetCodedFrameProvider( @@ -152,17 +150,20 @@ void AvPipelineImpl::Stop() { set_state(kStopped); } -void AvPipelineImpl::SetCdm(BrowserCdmCast* media_keys) { +void AvPipelineImpl::SetCdm(CastCdmContext* cast_cdm_context) { DCHECK(thread_checker_.CalledOnValidThread()); - DCHECK(media_keys); + DCHECK(cast_cdm_context); - if (media_keys_ && media_keys_callback_id_ != kNoCallbackId) - media_keys_->UnregisterPlayer(media_keys_callback_id_); + if (cast_cdm_context_ && player_tracker_callback_id_ != kNoCallbackId) + cast_cdm_context_->UnregisterPlayer(player_tracker_callback_id_); - media_keys_ = media_keys; - media_keys_callback_id_ = media_keys_->RegisterPlayer( + cast_cdm_context_ = cast_cdm_context; + player_tracker_callback_id_ = cast_cdm_context_->RegisterPlayer( base::Bind(&AvPipelineImpl::OnCdmStateChanged, weak_this_), base::Bind(&AvPipelineImpl::OnCdmDestroyed, weak_this_)); + + // We could be waiting for CDM to provide key (see b/29564232). + OnCdmStateChanged(); } void AvPipelineImpl::FetchBuffer() { @@ -206,19 +207,20 @@ void AvPipelineImpl::ProcessPendingBuffer() { enable_feeding_ = false; } - std::unique_ptr<DecryptContextImpl> decrypt_context; if (!pending_buffer_->end_of_stream() && pending_buffer_->decrypt_config()) { // Verify that CDM has the key ID. // Should not send the frame if the key ID is not available yet. std::string key_id(pending_buffer_->decrypt_config()->key_id()); - if (!media_keys_) { + if (!cast_cdm_context_) { CMALOG(kLogControl) << "No CDM for frame: pts=" << pending_buffer_->timestamp(); return; } - decrypt_context = media_keys_->GetDecryptContext(key_id); - if (!decrypt_context.get()) { + + std::unique_ptr<DecryptContextImpl> decrypt_context = + cast_cdm_context_->GetDecryptContext(key_id); + if (!decrypt_context) { CMALOG(kLogControl) << "frame(pts=" << pending_buffer_->timestamp() << "): waiting for key id " << base::HexEncode(&key_id[0], key_id.size()); @@ -227,14 +229,30 @@ void AvPipelineImpl::ProcessPendingBuffer() { return; } + DCHECK_NE(decrypt_context->GetKeySystem(), KEY_SYSTEM_NONE); + // If we can get the clear content, decrypt the pending buffer if (decrypt_context->CanDecryptToBuffer()) { - pending_buffer_ = - DecryptDecoderBuffer(pending_buffer_, decrypt_context.get()); - decrypt_context.reset(); + auto buffer = pending_buffer_; + pending_buffer_ = nullptr; + DecryptDecoderBuffer( + buffer, decrypt_context.get(), + base::Bind(&AvPipelineImpl::OnBufferDecrypted, weak_this_, + base::Passed(&decrypt_context))); + + return; } + + pending_buffer_->set_decrypt_context(std::move(decrypt_context)); } + PushPendingBuffer(); +} + +void AvPipelineImpl::PushPendingBuffer() { + DCHECK(pending_buffer_); + DCHECK(!pushed_buffer_); + if (!pending_buffer_->end_of_stream() && buffering_state_.get()) { base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(pending_buffer_->timestamp()); @@ -242,10 +260,7 @@ void AvPipelineImpl::ProcessPendingBuffer() { buffering_state_->SetMaxRenderingTime(timestamp); } - DCHECK(!pushed_buffer_); pushed_buffer_ = pending_buffer_; - if (decrypt_context && decrypt_context->GetKeySystem() != KEY_SYSTEM_NONE) - pushed_buffer_->set_decrypt_context(std::move(decrypt_context)); pending_buffer_ = nullptr; MediaPipelineBackend::BufferStatus status = decoder_->PushBuffer(pushed_buffer_.get()); @@ -254,6 +269,18 @@ void AvPipelineImpl::ProcessPendingBuffer() { OnPushBufferComplete(status); } +void AvPipelineImpl::OnBufferDecrypted( + std::unique_ptr<DecryptContextImpl> decrypt_context, + scoped_refptr<DecoderBufferBase> buffer, + bool success) { + if (!success) { + LOG(WARNING) << "Can't decrypt with decrypt_context"; + buffer->set_decrypt_context(std::move(decrypt_context)); + } + pending_buffer_ = buffer; + PushPendingBuffer(); +} + void AvPipelineImpl::OnPushBufferComplete(BufferStatus status) { DCHECK(thread_checker_.CalledOnValidThread()); pushed_buffer_ = nullptr; @@ -287,8 +314,8 @@ void AvPipelineImpl::OnKeyStatusChanged(const std::string& key_id, uint32_t system_code) { CMALOG(kLogControl) << __FUNCTION__ << " key_status= " << key_status << " system_code=" << system_code; - DCHECK(media_keys_); - media_keys_->SetKeyStatus(key_id, key_status, system_code); + DCHECK(cast_cdm_context_); + cast_cdm_context_->SetKeyStatus(key_id, key_status, system_code); } void AvPipelineImpl::OnVideoResolutionChanged(const Size& size) { @@ -309,7 +336,7 @@ void AvPipelineImpl::OnCdmStateChanged() { void AvPipelineImpl::OnCdmDestroyed() { DCHECK(thread_checker_.CalledOnValidThread()); - media_keys_ = NULL; + cast_cdm_context_ = NULL; } void AvPipelineImpl::OnDataBuffered( @@ -349,8 +376,9 @@ void AvPipelineImpl::UpdatePlayableFrames() { const CastDecryptConfig* decrypt_config = non_playable_frame->decrypt_config(); if (decrypt_config && - !(media_keys_ && - media_keys_->GetDecryptContext(decrypt_config->key_id()).get())) { + !(cast_cdm_context_ && + cast_cdm_context_->GetDecryptContext(decrypt_config->key_id()) + .get())) { // The frame is still not playable. All the following are thus not // playable. break; diff --git a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h index 1ceb0ebb8f1..d8f752263a8 100644 --- a/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h +++ b/chromium/chromecast/media/cma/pipeline/av_pipeline_impl.h @@ -29,11 +29,12 @@ class VideoDecoderConfig; namespace chromecast { namespace media { -class BrowserCdmCast; +class CastCdmContext; class BufferingFrameProvider; class BufferingState; class CodedFrameProvider; class DecoderBufferBase; +class DecryptContextImpl; class AvPipelineImpl : MediaPipelineBackend::Decoder::Delegate { public: @@ -41,7 +42,7 @@ class AvPipelineImpl : MediaPipelineBackend::Decoder::Delegate { const AvPipelineClient& client); ~AvPipelineImpl() override; - void SetCdm(BrowserCdmCast* media_keys); + void SetCdm(CastCdmContext* cast_cdm_context); // Setup the pipeline and ensure samples are available for the given media // time, then start rendering samples. @@ -98,10 +99,6 @@ class AvPipelineImpl : MediaPipelineBackend::Decoder::Delegate { uint32_t system_code) override; void OnVideoResolutionChanged(const Size& size) override; - // Callback invoked when the CDM state has changed in a way that might - // impact media playback. - void OnCdmStateChange(); - // Feed the pipeline, getting the frames from |frame_provider_|. void FetchBuffer(); @@ -112,12 +109,16 @@ class AvPipelineImpl : MediaPipelineBackend::Decoder::Delegate { // Process a pending buffer. void ProcessPendingBuffer(); + void PushPendingBuffer(); // Callbacks: // - when BrowserCdm updated its state. // - when BrowserCdm has been destroyed. void OnCdmStateChanged(); void OnCdmDestroyed(); + void OnBufferDecrypted(std::unique_ptr<DecryptContextImpl> decrypt_context, + scoped_refptr<DecoderBufferBase> buffer, + bool success); // Callback invoked when a media buffer has been buffered by |frame_provider_| // which is a BufferingFrameProvider. @@ -165,14 +166,9 @@ class AvPipelineImpl : MediaPipelineBackend::Decoder::Delegate { // Buffer that has been pushed to the device but not processed yet. scoped_refptr<DecoderBufferBase> pushed_buffer_; - // The media time is retrieved at regular intervals. - // Indicate whether time update is enabled. - bool enable_time_update_; - bool pending_time_update_task_; - - // Decryption keys, if available. - BrowserCdmCast* media_keys_; - int media_keys_callback_id_; + // CdmContext, if available. + CastCdmContext* cast_cdm_context_; + int player_tracker_callback_id_; base::WeakPtr<AvPipelineImpl> weak_this_; base::WeakPtrFactory<AvPipelineImpl> weak_factory_; diff --git a/chromium/chromecast/media/cma/pipeline/decrypt_util.cc b/chromium/chromecast/media/cma/pipeline/decrypt_util.cc index 93f1a33e7e1..e45d07ad8c4 100644 --- a/chromium/chromecast/media/cma/pipeline/decrypt_util.cc +++ b/chromium/chromecast/media/cma/pipeline/decrypt_util.cc @@ -8,6 +8,8 @@ #include <stdint.h> #include <string> +#include "base/bind.h" +#include "base/callback.h" #include "base/logging.h" #include "base/macros.h" #include "chromecast/media/base/decrypt_context_impl.h" @@ -22,7 +24,7 @@ namespace { class DecoderBufferClear : public DecoderBufferBase { public: - explicit DecoderBufferClear(const scoped_refptr<DecoderBufferBase>& buffer); + explicit DecoderBufferClear(scoped_refptr<DecoderBufferBase> buffer); // DecoderBufferBase implementation. StreamId stream_id() const override; @@ -43,10 +45,8 @@ class DecoderBufferClear : public DecoderBufferBase { DISALLOW_COPY_AND_ASSIGN(DecoderBufferClear); }; -DecoderBufferClear::DecoderBufferClear( - const scoped_refptr<DecoderBufferBase>& buffer) - : buffer_(buffer) { -} +DecoderBufferClear::DecoderBufferClear(scoped_refptr<DecoderBufferBase> buffer) + : buffer_(buffer) {} DecoderBufferClear::~DecoderBufferClear() { } @@ -89,16 +89,21 @@ DecoderBufferClear::ToMediaBuffer() const { return buffer_->ToMediaBuffer(); } +void OnBufferDecrypted(scoped_refptr<DecoderBufferBase> buffer, + const BufferDecryptedCB& buffer_decrypted_cb, + bool success) { + scoped_refptr<DecoderBufferBase> out_buffer = + success ? new DecoderBufferClear(buffer) : buffer; + buffer_decrypted_cb.Run(out_buffer, success); +} } // namespace -scoped_refptr<DecoderBufferBase> DecryptDecoderBuffer( - const scoped_refptr<DecoderBufferBase>& buffer, - DecryptContextImpl* decrypt_ctxt) { - if (decrypt_ctxt->Decrypt(buffer.get(), buffer->writable_data())) - return scoped_refptr<DecoderBufferBase>(new DecoderBufferClear(buffer)); - - NOTREACHED(); - return buffer; +void DecryptDecoderBuffer(scoped_refptr<DecoderBufferBase> buffer, + DecryptContextImpl* decrypt_ctxt, + const BufferDecryptedCB& buffer_decrypted_cb) { + decrypt_ctxt->DecryptAsync( + buffer.get(), buffer->writable_data(), 0, + base::Bind(&OnBufferDecrypted, buffer, buffer_decrypted_cb)); } } // namespace media diff --git a/chromium/chromecast/media/cma/pipeline/decrypt_util.h b/chromium/chromecast/media/cma/pipeline/decrypt_util.h index f0bc2c8e1da..627068e93fb 100644 --- a/chromium/chromecast/media/cma/pipeline/decrypt_util.h +++ b/chromium/chromecast/media/cma/pipeline/decrypt_util.h @@ -5,6 +5,7 @@ #ifndef CHROMECAST_MEDIA_CMA_PIPELINE_DECRYPT_UTIL_H_ #define CHROMECAST_MEDIA_CMA_PIPELINE_DECRYPT_UTIL_H_ +#include "base/callback.h" #include "base/memory/ref_counted.h" namespace crypto { @@ -17,14 +18,18 @@ namespace media { class DecoderBufferBase; class DecryptContextImpl; +using BufferDecryptedCB = + base::Callback<void(scoped_refptr<DecoderBufferBase>, bool)>; + // Create a new buffer which corresponds to the clear version of |buffer|. // Note: the memory area corresponding to the ES data of the new buffer // is the same as the ES data of |buffer| (for efficiency). -// After the function is called, |buffer| is left in a inconsistent state -// in the sense it has some decryption info but the ES data is now in clear. -scoped_refptr<DecoderBufferBase> DecryptDecoderBuffer( - const scoped_refptr<DecoderBufferBase>& buffer, - DecryptContextImpl* decrypt_ctxt); +// After the |buffer_decrypted_cb| is called, |buffer| is left in a inconsistent +// state in the sense it has some decryption info but the ES data is now in +// clear. +void DecryptDecoderBuffer(scoped_refptr<DecoderBufferBase> buffer, + DecryptContextImpl* decrypt_ctxt, + const BufferDecryptedCB& buffer_decrypted_cb); } // namespace media } // namespace chromecast diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc index b33305313ec..b6fc997de60 100644 --- a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.cc @@ -15,7 +15,7 @@ #include "base/single_thread_task_runner.h" #include "base/threading/thread_task_runner_handle.h" #include "chromecast/base/metrics/cast_metrics_helper.h" -#include "chromecast/media/cdm/browser_cdm_cast.h" +#include "chromecast/media/cdm/cast_cdm_context.h" #include "chromecast/media/cma/base/buffering_controller.h" #include "chromecast/media/cma/base/buffering_state.h" #include "chromecast/media/cma/base/cma_logging.h" @@ -51,6 +51,9 @@ const base::TimeDelta kTimeUpdateInterval( // kTimeUpdateInterval * kStatisticsUpdatePeriod. const int kStatisticsUpdatePeriod = 4; +// Stall duration threshold that triggers a playback stall event. +constexpr int kPlaybackStallEventThresholdMs = 2500; + void LogEstimatedBitrate(int decoded_bytes, base::TimeDelta elapsed_time, const char* tag, @@ -78,15 +81,18 @@ struct MediaPipelineImpl::FlushTask { }; MediaPipelineImpl::MediaPipelineImpl() - : cdm_(nullptr), + : cdm_context_(nullptr), backend_state_(BACKEND_STATE_UNINITIALIZED), playback_rate_(1.0f), audio_decoder_(nullptr), video_decoder_(nullptr), pending_time_update_task_(false), + last_media_time_(::media::kNoTimestamp()), statistics_rolling_counter_(0), audio_bytes_for_bitrate_estimation_(0), video_bytes_for_bitrate_estimation_(0), + playback_stalled_(false), + playback_stalled_notification_sent_(false), weak_factory_(this) { CMALOG(kLogControl) << __FUNCTION__; weak_this_ = weak_factory_.GetWeakPtr(); @@ -154,14 +160,14 @@ void MediaPipelineImpl::SetCdm(int cdm_id) { // One possibility would be a GetCdmByIdCB that's passed in. } -void MediaPipelineImpl::SetCdm(BrowserCdmCast* cdm) { +void MediaPipelineImpl::SetCdm(CastCdmContext* cdm_context) { CMALOG(kLogControl) << __FUNCTION__; DCHECK(thread_checker_.CalledOnValidThread()); - cdm_ = cdm; + cdm_context_ = cdm_context; if (audio_pipeline_) - audio_pipeline_->SetCdm(cdm); + audio_pipeline_->SetCdm(cdm_context); if (video_pipeline_) - video_pipeline_->SetCdm(cdm); + video_pipeline_->SetCdm(cdm_context); } ::media::PipelineStatus MediaPipelineImpl::InitializeAudio( @@ -178,8 +184,8 @@ void MediaPipelineImpl::SetCdm(BrowserCdmCast* cdm) { } audio_decoder_.reset(new AudioDecoderSoftwareWrapper(backend_audio_decoder)); audio_pipeline_.reset(new AudioPipelineImpl(audio_decoder_.get(), client)); - if (cdm_) - audio_pipeline_->SetCdm(cdm_); + if (cdm_context_) + audio_pipeline_->SetCdm(cdm_context_); return audio_pipeline_->Initialize(config, std::move(frame_provider)); } @@ -195,8 +201,8 @@ void MediaPipelineImpl::SetCdm(BrowserCdmCast* cdm) { return ::media::PIPELINE_ERROR_ABORT; } video_pipeline_.reset(new VideoPipelineImpl(video_decoder_, client)); - if (cdm_) - video_pipeline_->SetCdm(cdm_); + if (cdm_context_) + video_pipeline_->SetCdm(cdm_context_); return video_pipeline_->Initialize(configs, std::move(frame_provider)); } @@ -423,6 +429,55 @@ void MediaPipelineImpl::OnBufferingNotification(bool is_buffering) { } } +void MediaPipelineImpl::CheckForPlaybackStall(base::TimeDelta media_time, + base::TimeTicks current_stc) { + DCHECK(media_time != ::media::kNoTimestamp()); + + // A playback stall is defined as a scenario where the underlying media + // pipeline has unexpectedly stopped making forward progress. The pipeline is + // NOT stalled if: + // + // 1. Media time is progressing + // 2. The backend is paused + // 3. We are currently buffering (this is captured in a separate event) + if (media_time != last_media_time_ || + backend_state_ != BACKEND_STATE_PLAYING || + (buffering_controller_ && buffering_controller_->IsBuffering())) { + if (playback_stalled_) { + // Transition out of the stalled condition. + base::TimeDelta stall_duration = current_stc - playback_stalled_time_; + CMALOG(kLogControl) + << "Transitioning out of stalled state. Stall duration was " + << stall_duration.InMilliseconds() << " ms"; + playback_stalled_ = false; + playback_stalled_notification_sent_ = false; + } + return; + } + + // Check to see if this is a new stall condition. + if (!playback_stalled_) { + playback_stalled_ = true; + playback_stalled_time_ = current_stc; + return; + } + + // If we are in an existing stall, check to see if we've been stalled for more + // than 2.5 s. If so, send a single notification of the stall event. + if (!playback_stalled_notification_sent_) { + base::TimeDelta current_stall_duration = + current_stc - playback_stalled_time_; + if (current_stall_duration.InMilliseconds() >= + kPlaybackStallEventThresholdMs) { + CMALOG(kLogControl) << "Playback stalled"; + metrics::CastMetricsHelper::GetInstance()->RecordApplicationEvent( + "Cast.Platform.PlaybackStall"); + playback_stalled_notification_sent_ = true; + } + return; + } +} + void MediaPipelineImpl::UpdateMediaTime() { pending_time_update_task_ = false; if ((backend_state_ != BACKEND_STATE_PLAYING) && @@ -473,6 +528,8 @@ void MediaPipelineImpl::UpdateMediaTime() { } base::TimeTicks stc = base::TimeTicks::Now(); + CheckForPlaybackStall(media_time, stc); + base::TimeDelta max_rendering_time = media_time; if (buffering_controller_) { buffering_controller_->SetMediaTime(media_time); @@ -504,8 +561,8 @@ void MediaPipelineImpl::OnError(::media::PipelineStatus error) { DCHECK(thread_checker_.CalledOnValidThread()); DCHECK_NE(error, ::media::PIPELINE_OK) << "PIPELINE_OK is not an error!"; - metrics::CastMetricsHelper::GetInstance()->RecordApplicationEvent( - "Cast.Platform.Error"); + metrics::CastMetricsHelper::GetInstance()->RecordApplicationEventWithValue( + "Cast.Platform.Error", error); if (!client_.error_cb.is_null()) client_.error_cb.Run(error); diff --git a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h index 753c5ef31f2..87fec0e1bce 100644 --- a/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h +++ b/chromium/chromecast/media/cma/pipeline/media_pipeline_impl.h @@ -27,8 +27,8 @@ namespace chromecast { namespace media { class AudioDecoderSoftwareWrapper; class AudioPipelineImpl; -class BrowserCdmCast; class BufferingController; +class CastCdmContext; class CodedFrameProvider; class VideoPipelineImpl; struct AvPipelineClient; @@ -64,7 +64,7 @@ class MediaPipelineImpl { bool HasAudio() const; bool HasVideo() const; - void SetCdm(BrowserCdmCast* cdm); + void SetCdm(CastCdmContext* cdm); private: enum BackendState { @@ -74,6 +74,9 @@ class MediaPipelineImpl { BACKEND_STATE_PAUSED }; struct FlushTask; + void CheckForPlaybackStall(base::TimeDelta media_time, + base::TimeTicks current_stc); + void OnFlushDone(bool is_audio_stream); // Invoked to notify about a change of buffering state. @@ -88,7 +91,7 @@ class MediaPipelineImpl { base::ThreadChecker thread_checker_; MediaPipelineClient client_; std::unique_ptr<BufferingController> buffering_controller_; - BrowserCdmCast* cdm_; + CastCdmContext* cdm_context_; // Interface with the underlying hardware media pipeline. BackendState backend_state_; @@ -116,6 +119,11 @@ class MediaPipelineImpl { int audio_bytes_for_bitrate_estimation_; int video_bytes_for_bitrate_estimation_; + // Playback stalled handling. + bool playback_stalled_; + base::TimeTicks playback_stalled_time_; + bool playback_stalled_notification_sent_; + base::WeakPtr<MediaPipelineImpl> weak_this_; base::WeakPtrFactory<MediaPipelineImpl> weak_factory_; diff --git a/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc index 84f6f9430ee..8238f76ae8a 100644 --- a/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc +++ b/chromium/chromecast/media/cma/pipeline/video_pipeline_impl.cc @@ -9,7 +9,6 @@ #include "base/bind.h" #include "chromecast/base/metrics/cast_metrics_helper.h" -#include "chromecast/media/cdm/browser_cdm_cast.h" #include "chromecast/media/cma/base/buffering_defs.h" #include "chromecast/media/cma/base/cma_logging.h" #include "chromecast/media/cma/base/coded_frame_provider.h" |