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 {
MediaPipelineBackendManager::MediaPipelineBackendManager(
scoped_refptr<base::SingleThreadTaskRunner> 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() {
......@@ -42,6 +45,26 @@ MediaPipelineBackendManager::CreateMediaPipelineBackend(
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(
const MediaPipelineBackend* backend) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
......
......@@ -23,6 +23,8 @@ namespace media {
// All functions in this class should be called on the media thread.
class MediaPipelineBackendManager {
public:
enum DecoderType { AUDIO_DECODER, VIDEO_DECODER, NUM_DECODER_TYPES };
explicit MediaPipelineBackendManager(
scoped_refptr<base::SingleThreadTaskRunner> media_task_runner);
~MediaPipelineBackendManager();
......@@ -49,6 +51,11 @@ class MediaPipelineBackendManager {
private:
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.
void OnMediaPipelineBackendDestroyed(const MediaPipelineBackend* backend);
......@@ -59,6 +66,9 @@ class MediaPipelineBackendManager {
// A vector that stores all of the existing 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.
std::map<int, float> volume_by_stream_type_;
......
......@@ -10,6 +10,8 @@
namespace chromecast {
namespace media {
using DecoderType = MediaPipelineBackendManager::DecoderType;
MediaPipelineBackendWrapper::MediaPipelineBackendWrapper(
std::unique_ptr<MediaPipelineBackend> backend,
int stream_type,
......@@ -20,12 +22,18 @@ MediaPipelineBackendWrapper::MediaPipelineBackendWrapper(
audio_decoder_wrapper_(nullptr),
stream_type_volume_(stream_type_volume),
is_initialized_(false),
have_video_decoder_(false),
backend_manager_(backend_manager) {
DCHECK(backend_);
}
MediaPipelineBackendWrapper::~MediaPipelineBackendWrapper() {
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*
......@@ -34,6 +42,9 @@ MediaPipelineBackendWrapper::CreateAudioDecoder() {
if (audio_decoder_wrapper_)
return nullptr;
if (!backend_manager_->IncrementDecoderCount(DecoderType::AUDIO_DECODER))
return nullptr;
audio_decoder_wrapper_.reset(
new AudioDecoderWrapper(backend_->CreateAudioDecoder()));
return audio_decoder_wrapper_.get();
......@@ -42,6 +53,12 @@ MediaPipelineBackendWrapper::CreateAudioDecoder() {
MediaPipelineBackend::VideoDecoder*
MediaPipelineBackendWrapper::CreateVideoDecoder() {
DCHECK(!is_initialized_);
DCHECK(!have_video_decoder_);
if (!backend_manager_->IncrementDecoderCount(DecoderType::VIDEO_DECODER))
return nullptr;
have_video_decoder_ = true;
return backend_->CreateVideoDecoder();
}
......
......@@ -49,6 +49,7 @@ class MediaPipelineBackendWrapper : public MediaPipelineBackend {
std::unique_ptr<AudioDecoderWrapper> audio_decoder_wrapper_;
float stream_type_volume_;
bool is_initialized_;
bool have_video_decoder_;
MediaPipelineBackendManager* const backend_manager_;
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