summaryrefslogtreecommitdiffstats
path: root/chromecast
diff options
context:
space:
mode:
authorkmackay <kmackay@chromium.org>2015-12-11 10:51:58 -0800
committerCommit bot <commit-bot@chromium.org>2015-12-11 18:53:53 +0000
commit6f83710186cfe18681873478e4206a37a339ed80 (patch)
treea4c5b5f0085df6c56b1b0269f66a0f89dddc3c6a /chromecast
parent77a19d11a6c5040fc18a46637e966861fcfbf92f (diff)
downloadchromium_src-6f83710186cfe18681873478e4206a37a339ed80.zip
chromium_src-6f83710186cfe18681873478e4206a37a339ed80.tar.gz
chromium_src-6f83710186cfe18681873478e4206a37a339ed80.tar.bz2
[Chromecast] Use a software decoding wrapper for the backend audio decoder
If the backend audio decoder does not support the stream's audio config, automatically decode it in software and pass the decoded PCM to the backend. This will allow us to remove SwitchingMediaRenderer in a later CL, and allows Opus/FLAC playback through multiroom on all platforms. BUG= internal b/25701982 Review URL: https://codereview.chromium.org/1496353002 Cr-Commit-Position: refs/heads/master@{#364754}
Diffstat (limited to 'chromecast')
-rw-r--r--chromecast/media/cma/pipeline/BUILD.gn3
-rw-r--r--chromecast/media/cma/pipeline/audio_decoder_software_wrapper.cc144
-rw-r--r--chromecast/media/cma/pipeline/audio_decoder_software_wrapper.h68
-rw-r--r--chromecast/media/cma/pipeline/audio_pipeline_impl.cc8
-rw-r--r--chromecast/media/cma/pipeline/audio_pipeline_impl.h7
-rw-r--r--chromecast/media/cma/pipeline/media_pipeline_impl.cc36
-rw-r--r--chromecast/media/cma/pipeline/media_pipeline_impl.h12
-rw-r--r--chromecast/media/media.gyp3
8 files changed, 254 insertions, 27 deletions
diff --git a/chromecast/media/cma/pipeline/BUILD.gn b/chromecast/media/cma/pipeline/BUILD.gn
index a5809da..6b784a5 100644
--- a/chromecast/media/cma/pipeline/BUILD.gn
+++ b/chromecast/media/cma/pipeline/BUILD.gn
@@ -4,6 +4,8 @@
source_set("pipeline") {
sources = [
+ "audio_decoder_software_wrapper.cc",
+ "audio_decoder_software_wrapper.h",
"audio_pipeline_impl.cc",
"audio_pipeline_impl.h",
"av_pipeline_client.cc",
@@ -34,6 +36,7 @@ source_set("pipeline") {
"//chromecast/media/cdm",
"//chromecast/media/cma/backend",
"//chromecast/media/cma/base",
+ "//chromecast/media/cma/decoder",
"//crypto",
"//crypto:platform",
"//media",
diff --git a/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.cc b/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.cc
new file mode 100644
index 0000000..4b15bcc
--- /dev/null
+++ b/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.cc
@@ -0,0 +1,144 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromecast/media/cma/pipeline/audio_decoder_software_wrapper.h"
+
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/thread_task_runner_handle.h"
+#include "chromecast/media/cma/base/decoder_buffer_base.h"
+
+namespace chromecast {
+namespace media {
+
+AudioDecoderSoftwareWrapper::AudioDecoderSoftwareWrapper(
+ MediaPipelineBackend::AudioDecoder* backend_decoder)
+ : backend_decoder_(backend_decoder),
+ delegate_(nullptr),
+ weak_factory_(this) {
+ DCHECK(backend_decoder_);
+ backend_decoder_->SetDelegate(this);
+}
+
+AudioDecoderSoftwareWrapper::~AudioDecoderSoftwareWrapper() {}
+
+void AudioDecoderSoftwareWrapper::SetDelegate(DecoderDelegate* delegate) {
+ DCHECK(delegate);
+ delegate_ = delegate;
+}
+
+MediaPipelineBackend::BufferStatus AudioDecoderSoftwareWrapper::PushBuffer(
+ CastDecoderBuffer* buffer) {
+ DCHECK(buffer);
+ if (!software_decoder_)
+ return backend_decoder_->PushBuffer(buffer);
+
+ DecoderBufferBase* buffer_base = static_cast<DecoderBufferBase*>(buffer);
+ if (!software_decoder_->Decode(
+ make_scoped_refptr(buffer_base),
+ base::Bind(&AudioDecoderSoftwareWrapper::OnDecodedBuffer,
+ weak_factory_.GetWeakPtr()))) {
+ return MediaPipelineBackend::kBufferFailed;
+ }
+ return MediaPipelineBackend::kBufferPending;
+}
+
+void AudioDecoderSoftwareWrapper::GetStatistics(Statistics* statistics) {
+ DCHECK(statistics);
+ return backend_decoder_->GetStatistics(statistics);
+}
+
+bool AudioDecoderSoftwareWrapper::SetConfig(const AudioConfig& config) {
+ DCHECK(delegate_);
+ DCHECK(IsValidConfig(config));
+
+ if (backend_decoder_->SetConfig(config)) {
+ software_decoder_.reset();
+ output_config_ = config;
+ return true;
+ }
+
+ if (config.is_encrypted || !CreateSoftwareDecoder(config))
+ return false;
+
+ output_config_.codec = media::kCodecPCM;
+ output_config_.sample_format = media::kSampleFormatS16;
+ output_config_.channel_number = 2;
+ output_config_.bytes_per_channel = 2;
+ output_config_.samples_per_second = config.samples_per_second;
+ output_config_.is_encrypted = false;
+ return backend_decoder_->SetConfig(output_config_);
+}
+
+bool AudioDecoderSoftwareWrapper::SetVolume(float multiplier) {
+ return backend_decoder_->SetVolume(multiplier);
+}
+
+AudioDecoderSoftwareWrapper::RenderingDelay
+AudioDecoderSoftwareWrapper::GetRenderingDelay() {
+ return backend_decoder_->GetRenderingDelay();
+}
+
+bool AudioDecoderSoftwareWrapper::CreateSoftwareDecoder(
+ const AudioConfig& config) {
+ // TODO(kmackay) Consider using planar float instead.
+ software_decoder_ = media::CastAudioDecoder::Create(
+ base::ThreadTaskRunnerHandle::Get(), config,
+ media::CastAudioDecoder::kOutputSigned16,
+ base::Bind(&AudioDecoderSoftwareWrapper::OnDecoderInitialized,
+ weak_factory_.GetWeakPtr()));
+ return (software_decoder_.get() != nullptr);
+}
+
+void AudioDecoderSoftwareWrapper::OnDecoderInitialized(bool success) {
+ if (!success) {
+ LOG(ERROR) << "Failed to initialize software decoder";
+ delegate_->OnDecoderError();
+ }
+}
+
+void AudioDecoderSoftwareWrapper::OnDecodedBuffer(
+ CastAudioDecoder::Status status,
+ const scoped_refptr<DecoderBufferBase>& decoded) {
+ if (status != CastAudioDecoder::kDecodeOk) {
+ delegate_->OnPushBufferComplete(MediaPipelineBackend::kBufferFailed);
+ return;
+ }
+
+ pending_pushed_buffer_ = decoded;
+ MediaPipelineBackend::BufferStatus buffer_status =
+ backend_decoder_->PushBuffer(pending_pushed_buffer_.get());
+ if (buffer_status != MediaPipelineBackend::kBufferPending)
+ delegate_->OnPushBufferComplete(buffer_status);
+}
+
+void AudioDecoderSoftwareWrapper::OnPushBufferComplete(
+ MediaPipelineBackend::BufferStatus status) {
+ DCHECK(delegate_);
+ delegate_->OnPushBufferComplete(status);
+}
+
+void AudioDecoderSoftwareWrapper::OnEndOfStream() {
+ DCHECK(delegate_);
+ delegate_->OnEndOfStream();
+}
+
+void AudioDecoderSoftwareWrapper::OnDecoderError() {
+ DCHECK(delegate_);
+ delegate_->OnDecoderError();
+}
+
+void AudioDecoderSoftwareWrapper::OnKeyStatusChanged(const std::string& key_id,
+ CastKeyStatus key_status,
+ uint32_t system_code) {
+ DCHECK(delegate_);
+ delegate_->OnKeyStatusChanged(key_id, key_status, system_code);
+}
+
+void AudioDecoderSoftwareWrapper::OnVideoResolutionChanged(const Size& size) {
+ NOTREACHED();
+}
+
+} // namespace media
+} // namespace chromecast
diff --git a/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.h b/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.h
new file mode 100644
index 0000000..c186147
--- /dev/null
+++ b/chromecast/media/cma/pipeline/audio_decoder_software_wrapper.h
@@ -0,0 +1,68 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_DECODER_SOFTWARE_WRAPPER_H_
+#define CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_DECODER_SOFTWARE_WRAPPER_H_
+
+#include "base/macros.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "chromecast/media/cma/decoder/cast_audio_decoder.h"
+#include "chromecast/public/media/media_pipeline_backend.h"
+
+namespace chromecast {
+namespace media {
+
+// Provides transparent software decoding if the backend does not support the
+// incoming audio config.
+class AudioDecoderSoftwareWrapper
+ : public MediaPipelineBackend::AudioDecoder,
+ public MediaPipelineBackend::Decoder::Delegate {
+ public:
+ using DecoderDelegate = MediaPipelineBackend::Decoder::Delegate;
+
+ AudioDecoderSoftwareWrapper(
+ MediaPipelineBackend::AudioDecoder* backend_decoder);
+ ~AudioDecoderSoftwareWrapper() override;
+
+ // MediaPipelineBackend::AudioDecoder implementation:
+ void SetDelegate(DecoderDelegate* delegate) override;
+ MediaPipelineBackend::BufferStatus PushBuffer(
+ CastDecoderBuffer* buffer) override;
+ void GetStatistics(Statistics* statistics) override;
+ bool SetConfig(const AudioConfig& config) override;
+ bool SetVolume(float multiplier) override;
+ RenderingDelay GetRenderingDelay() override;
+
+ private:
+ bool CreateSoftwareDecoder(const AudioConfig& config);
+ void OnDecoderInitialized(bool success);
+ void OnDecodedBuffer(CastAudioDecoder::Status status,
+ const scoped_refptr<DecoderBufferBase>& decoded);
+
+ // MediaPipelineBackend::Decoder::Delegate implementation:
+ void OnPushBufferComplete(MediaPipelineBackend::BufferStatus status) override;
+ void OnEndOfStream() override;
+ void OnDecoderError() override;
+ void OnKeyStatusChanged(const std::string& key_id,
+ CastKeyStatus key_status,
+ uint32_t system_code) override;
+ void OnVideoResolutionChanged(const Size& size) override;
+
+ MediaPipelineBackend::AudioDecoder* const backend_decoder_;
+ DecoderDelegate* delegate_;
+ scoped_ptr<CastAudioDecoder> software_decoder_;
+ AudioConfig output_config_;
+ scoped_refptr<DecoderBufferBase> pending_pushed_buffer_;
+
+ base::WeakPtrFactory<AudioDecoderSoftwareWrapper> weak_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioDecoderSoftwareWrapper);
+};
+
+} // namespace media
+} // namespace chromecast
+
+#endif // CHROMECAST_MEDIA_CMA_PIPELINE_AUDIO_DECODER_SOFTWARE_WRAPPER_H_
diff --git a/chromecast/media/cma/pipeline/audio_pipeline_impl.cc b/chromecast/media/cma/pipeline/audio_pipeline_impl.cc
index 5aa3c1a06..2f051eb 100644
--- a/chromecast/media/cma/pipeline/audio_pipeline_impl.cc
+++ b/chromecast/media/cma/pipeline/audio_pipeline_impl.cc
@@ -26,8 +26,7 @@ AudioPipelineImpl::AudioPipelineImpl(
DCHECK(audio_decoder_);
}
-AudioPipelineImpl::~AudioPipelineImpl() {
-}
+AudioPipelineImpl::~AudioPipelineImpl() {}
void AudioPipelineImpl::Initialize(
const ::media::AudioDecoderConfig& audio_config,
@@ -41,8 +40,9 @@ void AudioPipelineImpl::Initialize(
}
DCHECK(audio_config.IsValidConfig());
- if (!audio_decoder_->SetConfig(
- DecoderConfigAdapter::ToCastAudioConfig(kPrimary, audio_config))) {
+ AudioConfig cast_audio_config =
+ DecoderConfigAdapter::ToCastAudioConfig(kPrimary, audio_config);
+ if (!audio_decoder_->SetConfig(cast_audio_config)) {
status_cb.Run(::media::PIPELINE_ERROR_INITIALIZATION_FAILED);
return;
}
diff --git a/chromecast/media/cma/pipeline/audio_pipeline_impl.h b/chromecast/media/cma/pipeline/audio_pipeline_impl.h
index b7e5a765..92cc10f 100644
--- a/chromecast/media/cma/pipeline/audio_pipeline_impl.h
+++ b/chromecast/media/cma/pipeline/audio_pipeline_impl.h
@@ -30,10 +30,9 @@ class AudioPipelineImpl : public AvPipelineImpl {
const AvPipelineClient& client);
~AudioPipelineImpl() override;
- void Initialize(
- const ::media::AudioDecoderConfig& config,
- scoped_ptr<CodedFrameProvider> frame_provider,
- const ::media::PipelineStatusCB& status_cb);
+ void Initialize(const ::media::AudioDecoderConfig& config,
+ scoped_ptr<CodedFrameProvider> frame_provider,
+ const ::media::PipelineStatusCB& status_cb);
void SetVolume(float volume);
diff --git a/chromecast/media/cma/pipeline/media_pipeline_impl.cc b/chromecast/media/cma/pipeline/media_pipeline_impl.cc
index a74ea2f..05fffcd 100644
--- a/chromecast/media/cma/pipeline/media_pipeline_impl.cc
+++ b/chromecast/media/cma/pipeline/media_pipeline_impl.cc
@@ -4,6 +4,8 @@
#include "chromecast/media/cma/pipeline/media_pipeline_impl.h"
+#include <algorithm>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
@@ -17,6 +19,7 @@
#include "chromecast/media/cma/base/buffering_state.h"
#include "chromecast/media/cma/base/cma_logging.h"
#include "chromecast/media/cma/base/coded_frame_provider.h"
+#include "chromecast/media/cma/pipeline/audio_decoder_software_wrapper.h"
#include "chromecast/media/cma/pipeline/audio_pipeline_impl.h"
#include "chromecast/media/cma/pipeline/video_pipeline_impl.h"
#include "chromecast/public/media/media_pipeline_backend.h"
@@ -75,6 +78,7 @@ MediaPipelineImpl::~MediaPipelineImpl() {
// destructor, it's important to delete them first.
video_pipeline_.reset();
audio_pipeline_.reset();
+ audio_decoder_.reset();
media_pipeline_backend_.reset();
if (!client_.pipeline_backend_destroyed_cb.is_null())
client_.pipeline_backend_destroyed_cb.Run();
@@ -85,6 +89,7 @@ void MediaPipelineImpl::Initialize(
scoped_ptr<MediaPipelineBackend> media_pipeline_backend) {
CMALOG(kLogControl) << __FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
+ audio_decoder_.reset();
media_pipeline_backend_.reset(media_pipeline_backend.release());
if (!client_.pipeline_backend_created_cb.is_null())
client_.pipeline_backend_created_cb.Run();
@@ -140,19 +145,21 @@ void MediaPipelineImpl::InitializeAudio(
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!audio_decoder_);
- audio_decoder_ = media_pipeline_backend_->CreateAudioDecoder();
- if (!audio_decoder_) {
+ MediaPipelineBackend::AudioDecoder* backend_audio_decoder =
+ media_pipeline_backend_->CreateAudioDecoder();
+ if (!backend_audio_decoder) {
status_cb.Run(::media::PIPELINE_ERROR_ABORT);
return;
}
- audio_pipeline_.reset(new AudioPipelineImpl(audio_decoder_, client));
+ audio_decoder_.reset(new AudioDecoderSoftwareWrapper(backend_audio_decoder));
+ audio_pipeline_.reset(new AudioPipelineImpl(audio_decoder_.get(), client));
if (cdm_)
audio_pipeline_->SetCdm(cdm_);
audio_pipeline_->Initialize(config, frame_provider.Pass(), status_cb);
}
void MediaPipelineImpl::InitializeVideo(
- const std::vector< ::media::VideoDecoderConfig>& configs,
+ const std::vector<::media::VideoDecoderConfig>& configs,
const VideoPipelineClient& client,
scoped_ptr<CodedFrameProvider> frame_provider,
const ::media::PipelineStatusCB& status_cb) {
@@ -257,14 +264,12 @@ void MediaPipelineImpl::Flush(const ::media::PipelineStatusCB& status_cb) {
// provider and invalidate all the unreleased buffers.
::media::SerialRunner::Queue bound_fns;
if (audio_pipeline_) {
- bound_fns.Push(base::Bind(
- &AudioPipelineImpl::Flush,
- base::Unretained(audio_pipeline_.get())));
+ bound_fns.Push(base::Bind(&AudioPipelineImpl::Flush,
+ base::Unretained(audio_pipeline_.get())));
}
if (video_pipeline_) {
- bound_fns.Push(base::Bind(
- &VideoPipelineImpl::Flush,
- base::Unretained(video_pipeline_.get())));
+ bound_fns.Push(base::Bind(&VideoPipelineImpl::Flush,
+ base::Unretained(video_pipeline_.get())));
}
::media::PipelineStatusCB transition_cb =
base::Bind(&MediaPipelineImpl::OnFlushDone, weak_this_, status_cb);
@@ -294,6 +299,7 @@ void MediaPipelineImpl::Stop() {
// Release hardware resources on Stop.
audio_pipeline_ = nullptr;
video_pipeline_ = nullptr;
+ audio_decoder_.reset();
media_pipeline_backend_.reset();
}
@@ -323,9 +329,8 @@ void MediaPipelineImpl::SetVolume(float volume) {
audio_pipeline_->SetVolume(volume);
}
-void MediaPipelineImpl::OnFlushDone(
- const ::media::PipelineStatusCB& status_cb,
- ::media::PipelineStatus status) {
+void MediaPipelineImpl::OnFlushDone(const ::media::PipelineStatusCB& status_cb,
+ ::media::PipelineStatus status) {
// Clear pending buffers.
if (audio_pipeline_)
audio_pipeline_->BackendStopped();
@@ -342,8 +347,9 @@ void MediaPipelineImpl::OnBufferingNotification(bool is_buffering) {
DCHECK(buffering_controller_);
if (!client_.buffering_state_cb.is_null()) {
- ::media::BufferingState buffering_state = is_buffering ?
- ::media::BUFFERING_HAVE_NOTHING : ::media::BUFFERING_HAVE_ENOUGH;
+ ::media::BufferingState buffering_state =
+ is_buffering ? ::media::BUFFERING_HAVE_NOTHING
+ : ::media::BUFFERING_HAVE_ENOUGH;
client_.buffering_state_cb.Run(buffering_state);
}
diff --git a/chromecast/media/cma/pipeline/media_pipeline_impl.h b/chromecast/media/cma/pipeline/media_pipeline_impl.h
index 34a687d..5ca8ea4 100644
--- a/chromecast/media/cma/pipeline/media_pipeline_impl.h
+++ b/chromecast/media/cma/pipeline/media_pipeline_impl.h
@@ -5,6 +5,9 @@
#ifndef CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_IMPL_H_
#define CHROMECAST_MEDIA_CMA_PIPELINE_MEDIA_PIPELINE_IMPL_H_
+#include <string>
+#include <vector>
+
#include "base/basictypes.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
@@ -23,13 +26,14 @@ class VideoDecoderConfig;
namespace chromecast {
namespace media {
+class AudioDecoderSoftwareWrapper;
class AudioPipelineImpl;
-struct AvPipelineClient;
-struct VideoPipelineClient;
class BrowserCdmCast;
class BufferingController;
class CodedFrameProvider;
class VideoPipelineImpl;
+struct AvPipelineClient;
+struct VideoPipelineClient;
class MediaPipelineImpl {
public:
@@ -48,7 +52,7 @@ class MediaPipelineImpl {
const AvPipelineClient& client,
scoped_ptr<CodedFrameProvider> frame_provider,
const ::media::PipelineStatusCB& status_cb);
- void InitializeVideo(const std::vector< ::media::VideoDecoderConfig>& configs,
+ void InitializeVideo(const std::vector<::media::VideoDecoderConfig>& configs,
const VideoPipelineClient& client,
scoped_ptr<CodedFrameProvider> frame_provider,
const ::media::PipelineStatusCB& status_cb);
@@ -78,7 +82,7 @@ class MediaPipelineImpl {
// Interface with the underlying hardware media pipeline.
scoped_ptr<MediaPipelineBackend> media_pipeline_backend_;
- MediaPipelineBackend::AudioDecoder* audio_decoder_;
+ scoped_ptr<AudioDecoderSoftwareWrapper> audio_decoder_;
MediaPipelineBackend::VideoDecoder* video_decoder_;
bool backend_initialized_;
diff --git a/chromecast/media/media.gyp b/chromecast/media/media.gyp
index 726a861..01b79c0 100644
--- a/chromecast/media/media.gyp
+++ b/chromecast/media/media.gyp
@@ -203,6 +203,7 @@
'type': '<(component)',
'dependencies': [
'cma_base',
+ 'cma_decoder',
'media_base',
'media_cdm',
'../../base/base.gyp:base',
@@ -211,6 +212,8 @@
'../../third_party/boringssl/boringssl.gyp:boringssl',
],
'sources': [
+ 'cma/pipeline/audio_decoder_software_wrapper.cc',
+ 'cma/pipeline/audio_decoder_software_wrapper.h',
'cma/pipeline/audio_pipeline_impl.cc',
'cma/pipeline/audio_pipeline_impl.h',
'cma/pipeline/av_pipeline_client.cc',