Commit 54e165ed authored by halliwell's avatar halliwell Committed by Commit bot

[Chromecast] Limit number of concurrent audio and video decoders

Currently, an app can exceed the number of decoders that vendor backends
support (for example, by creating multiple video elements), leading to
unpredictable behaviour including crashes.  While most apps don't do
this, we have seen some problems crop up e.g. when ads are injected into
an existing page.

For now, restrict to 1 video decoder and 2 audio decoders.

BUG=internal b/26910920

Review-Url: https://codereview.chromium.org/2173593002
Cr-Commit-Position: refs/heads/master@{#407215}
parent 91f655b1
...@@ -16,6 +16,9 @@ namespace media { ...@@ -16,6 +16,9 @@ namespace media {
MediaPipelineBackendManager::MediaPipelineBackendManager( MediaPipelineBackendManager::MediaPipelineBackendManager(
scoped_refptr<base::SingleThreadTaskRunner> media_task_runner) scoped_refptr<base::SingleThreadTaskRunner> media_task_runner)
: media_task_runner_(std::move(media_task_runner)) { : media_task_runner_(std::move(media_task_runner)) {
DCHECK_EQ(2, NUM_DECODER_TYPES);
decoder_count_[AUDIO_DECODER] = 0;
decoder_count_[VIDEO_DECODER] = 0;
} }
MediaPipelineBackendManager::~MediaPipelineBackendManager() { MediaPipelineBackendManager::~MediaPipelineBackendManager() {
...@@ -42,6 +45,26 @@ MediaPipelineBackendManager::CreateMediaPipelineBackend( ...@@ -42,6 +45,26 @@ MediaPipelineBackendManager::CreateMediaPipelineBackend(
return backend_ptr; return backend_ptr;
} }
bool MediaPipelineBackendManager::IncrementDecoderCount(DecoderType type) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DCHECK(type < NUM_DECODER_TYPES);
const int limit = (type == AUDIO_DECODER) ? 2 : 1;
if (decoder_count_[type] >= limit) {
LOG(WARNING) << "Decoder limit reached for type " << type;
return false;
}
++decoder_count_[type];
return true;
}
void MediaPipelineBackendManager::DecrementDecoderCount(DecoderType type) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
DCHECK(type < NUM_DECODER_TYPES);
DCHECK(decoder_count_[type] > 0);
decoder_count_[type]--;
}
void MediaPipelineBackendManager::OnMediaPipelineBackendDestroyed( void MediaPipelineBackendManager::OnMediaPipelineBackendDestroyed(
const MediaPipelineBackend* backend) { const MediaPipelineBackend* backend) {
DCHECK(media_task_runner_->BelongsToCurrentThread()); DCHECK(media_task_runner_->BelongsToCurrentThread());
......
...@@ -23,6 +23,8 @@ namespace media { ...@@ -23,6 +23,8 @@ namespace media {
// All functions in this class should be called on the media thread. // All functions in this class should be called on the media thread.
class MediaPipelineBackendManager { class MediaPipelineBackendManager {
public: public:
enum DecoderType { AUDIO_DECODER, VIDEO_DECODER, NUM_DECODER_TYPES };
explicit MediaPipelineBackendManager( explicit MediaPipelineBackendManager(
scoped_refptr<base::SingleThreadTaskRunner> media_task_runner); scoped_refptr<base::SingleThreadTaskRunner> media_task_runner);
~MediaPipelineBackendManager(); ~MediaPipelineBackendManager();
...@@ -49,6 +51,11 @@ class MediaPipelineBackendManager { ...@@ -49,6 +51,11 @@ class MediaPipelineBackendManager {
private: private:
friend class MediaPipelineBackendWrapper; friend class MediaPipelineBackendWrapper;
// Backend wrapper instances must use these APIs when allocating and releasing
// decoder objects, so we can enforce global limit on #concurrent decoders.
bool IncrementDecoderCount(DecoderType type);
void DecrementDecoderCount(DecoderType type);
// Internal clean up when a new media pipeline backend is destroyed. // Internal clean up when a new media pipeline backend is destroyed.
void OnMediaPipelineBackendDestroyed(const MediaPipelineBackend* backend); void OnMediaPipelineBackendDestroyed(const MediaPipelineBackend* backend);
...@@ -59,6 +66,9 @@ class MediaPipelineBackendManager { ...@@ -59,6 +66,9 @@ class MediaPipelineBackendManager {
// A vector that stores all of the existing media_pipeline_backends_. // A vector that stores all of the existing media_pipeline_backends_.
std::vector<MediaPipelineBackend*> media_pipeline_backends_; std::vector<MediaPipelineBackend*> media_pipeline_backends_;
// Total count of decoders created
int decoder_count_[NUM_DECODER_TYPES];
// Volume multiplier for each type of audio streams. // Volume multiplier for each type of audio streams.
std::map<int, float> volume_by_stream_type_; std::map<int, float> volume_by_stream_type_;
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
namespace chromecast { namespace chromecast {
namespace media { namespace media {
using DecoderType = MediaPipelineBackendManager::DecoderType;
MediaPipelineBackendWrapper::MediaPipelineBackendWrapper( MediaPipelineBackendWrapper::MediaPipelineBackendWrapper(
std::unique_ptr<MediaPipelineBackend> backend, std::unique_ptr<MediaPipelineBackend> backend,
int stream_type, int stream_type,
...@@ -20,12 +22,18 @@ MediaPipelineBackendWrapper::MediaPipelineBackendWrapper( ...@@ -20,12 +22,18 @@ MediaPipelineBackendWrapper::MediaPipelineBackendWrapper(
audio_decoder_wrapper_(nullptr), audio_decoder_wrapper_(nullptr),
stream_type_volume_(stream_type_volume), stream_type_volume_(stream_type_volume),
is_initialized_(false), is_initialized_(false),
have_video_decoder_(false),
backend_manager_(backend_manager) { backend_manager_(backend_manager) {
DCHECK(backend_); DCHECK(backend_);
} }
MediaPipelineBackendWrapper::~MediaPipelineBackendWrapper() { MediaPipelineBackendWrapper::~MediaPipelineBackendWrapper() {
backend_manager_->OnMediaPipelineBackendDestroyed(this); backend_manager_->OnMediaPipelineBackendDestroyed(this);
if (audio_decoder_wrapper_)
backend_manager_->DecrementDecoderCount(DecoderType::AUDIO_DECODER);
if (have_video_decoder_)
backend_manager_->DecrementDecoderCount(DecoderType::VIDEO_DECODER);
} }
MediaPipelineBackend::AudioDecoder* MediaPipelineBackend::AudioDecoder*
...@@ -34,6 +42,9 @@ MediaPipelineBackendWrapper::CreateAudioDecoder() { ...@@ -34,6 +42,9 @@ MediaPipelineBackendWrapper::CreateAudioDecoder() {
if (audio_decoder_wrapper_) if (audio_decoder_wrapper_)
return nullptr; return nullptr;
if (!backend_manager_->IncrementDecoderCount(DecoderType::AUDIO_DECODER))
return nullptr;
audio_decoder_wrapper_.reset( audio_decoder_wrapper_.reset(
new AudioDecoderWrapper(backend_->CreateAudioDecoder())); new AudioDecoderWrapper(backend_->CreateAudioDecoder()));
return audio_decoder_wrapper_.get(); return audio_decoder_wrapper_.get();
...@@ -42,6 +53,12 @@ MediaPipelineBackendWrapper::CreateAudioDecoder() { ...@@ -42,6 +53,12 @@ MediaPipelineBackendWrapper::CreateAudioDecoder() {
MediaPipelineBackend::VideoDecoder* MediaPipelineBackend::VideoDecoder*
MediaPipelineBackendWrapper::CreateVideoDecoder() { MediaPipelineBackendWrapper::CreateVideoDecoder() {
DCHECK(!is_initialized_); DCHECK(!is_initialized_);
DCHECK(!have_video_decoder_);
if (!backend_manager_->IncrementDecoderCount(DecoderType::VIDEO_DECODER))
return nullptr;
have_video_decoder_ = true;
return backend_->CreateVideoDecoder(); return backend_->CreateVideoDecoder();
} }
......
...@@ -49,6 +49,7 @@ class MediaPipelineBackendWrapper : public MediaPipelineBackend { ...@@ -49,6 +49,7 @@ class MediaPipelineBackendWrapper : public MediaPipelineBackend {
std::unique_ptr<AudioDecoderWrapper> audio_decoder_wrapper_; std::unique_ptr<AudioDecoderWrapper> audio_decoder_wrapper_;
float stream_type_volume_; float stream_type_volume_;
bool is_initialized_; bool is_initialized_;
bool have_video_decoder_;
MediaPipelineBackendManager* const backend_manager_; MediaPipelineBackendManager* const backend_manager_;
DISALLOW_COPY_AND_ASSIGN(MediaPipelineBackendWrapper); DISALLOW_COPY_AND_ASSIGN(MediaPipelineBackendWrapper);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment