summaryrefslogtreecommitdiff
path: root/chromium/media/base/android/media_source_player.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/media/base/android/media_source_player.cc')
-rw-r--r--chromium/media/base/android/media_source_player.cc897
1 files changed, 351 insertions, 546 deletions
diff --git a/chromium/media/base/android/media_source_player.cc b/chromium/media/base/android/media_source_player.cc
index 3af8a2b0d3c..223515e9992 100644
--- a/chromium/media/base/android/media_source_player.cc
+++ b/chromium/media/base/android/media_source_player.cc
@@ -4,330 +4,66 @@
#include "media/base/android/media_source_player.h"
+#include <limits>
+
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
+#include "base/barrier_closure.h"
#include "base/basictypes.h"
#include "base/bind.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
-#include "base/message_loop/message_loop.h"
-#include "base/threading/thread.h"
-#include "media/base/android/media_codec_bridge.h"
+#include "media/base/android/audio_decoder_job.h"
#include "media/base/android/media_drm_bridge.h"
#include "media/base/android/media_player_manager.h"
+#include "media/base/android/video_decoder_job.h"
#include "media/base/audio_timestamp_helper.h"
namespace {
-// Timeout value for media codec operations. Because the first
-// DequeInputBuffer() can take about 150 milliseconds, use 250 milliseconds
-// here. See b/9357571.
-const int kMediaCodecTimeoutInMilliseconds = 250;
-
// Use 16bit PCM for audio output. Keep this value in sync with the output
// format we passed to AudioTrack in MediaCodecBridge.
const int kBytesPerAudioOutputSample = 2;
-
-class DecoderThread : public base::Thread {
- public:
- virtual ~DecoderThread() {}
- protected:
- DecoderThread(const char* name) : base::Thread(name) { Start(); }
-};
-
-class AudioDecoderThread : public DecoderThread {
- public:
- AudioDecoderThread() : DecoderThread("MediaSource_AudioDecoderThread") {}
-};
-
-class VideoDecoderThread : public DecoderThread {
- public:
- VideoDecoderThread() : DecoderThread("MediaSource_VideoDecoderThread") {}
-};
-
-// TODO(qinmin): Check if it is tolerable to use worker pool to handle all the
-// decoding tasks so that we don't need the global threads here.
-// http://crbug.com/245750
-base::LazyInstance<AudioDecoderThread>::Leaky
- g_audio_decoder_thread = LAZY_INSTANCE_INITIALIZER;
-
-base::LazyInstance<VideoDecoderThread>::Leaky
- g_video_decoder_thread = LAZY_INSTANCE_INITIALIZER;
-
}
namespace media {
-MediaDecoderJob::MediaDecoderJob(
- const scoped_refptr<base::MessageLoopProxy>& decoder_loop,
- MediaCodecBridge* media_codec_bridge,
- bool is_audio)
- : ui_loop_(base::MessageLoopProxy::current()),
- decoder_loop_(decoder_loop),
- media_codec_bridge_(media_codec_bridge),
- needs_flush_(false),
- is_audio_(is_audio),
- input_eos_encountered_(false),
- weak_this_(this),
- is_decoding_(false) {
-}
-
-MediaDecoderJob::~MediaDecoderJob() {}
-
-// Class for managing audio decoding jobs.
-class AudioDecoderJob : public MediaDecoderJob {
- public:
- virtual ~AudioDecoderJob() {}
-
- static AudioDecoderJob* Create(
- const AudioCodec audio_codec, int sample_rate, int channel_count,
- const uint8* extra_data, size_t extra_data_size, jobject media_crypto);
-
- void SetVolume(double volume);
-
- private:
- AudioDecoderJob(MediaCodecBridge* media_codec_bridge);
-};
-
-// Class for managing video decoding jobs.
-class VideoDecoderJob : public MediaDecoderJob {
- public:
- virtual ~VideoDecoderJob() {}
-
- static VideoDecoderJob* Create(
- const VideoCodec video_codec, const gfx::Size& size, jobject surface,
- jobject media_crypto);
-
- private:
- VideoDecoderJob(MediaCodecBridge* media_codec_bridge);
-};
-
-void MediaDecoderJob::Decode(
- const AccessUnit& unit,
- const base::TimeTicks& start_time_ticks,
- const base::TimeDelta& start_presentation_timestamp,
- const MediaDecoderJob::DecoderCallback& callback) {
- DCHECK(!is_decoding_);
- DCHECK(ui_loop_->BelongsToCurrentThread());
- is_decoding_ = true;
- decoder_loop_->PostTask(FROM_HERE, base::Bind(
- &MediaDecoderJob::DecodeInternal, base::Unretained(this), unit,
- start_time_ticks, start_presentation_timestamp, needs_flush_,
- callback));
- needs_flush_ = false;
-}
-
-MediaDecoderJob::DecodeStatus MediaDecoderJob::QueueInputBuffer(
- const AccessUnit& unit) {
- base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
- kMediaCodecTimeoutInMilliseconds);
- int input_buf_index = media_codec_bridge_->DequeueInputBuffer(timeout);
- if (input_buf_index == MediaCodecBridge::INFO_MEDIA_CODEC_ERROR)
- return DECODE_FAILED;
- if (input_buf_index == MediaCodecBridge::INFO_TRY_AGAIN_LATER)
- return DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER;
-
- // TODO(qinmin): skip frames if video is falling far behind.
- DCHECK(input_buf_index >= 0);
- if (unit.end_of_stream || unit.data.empty()) {
- media_codec_bridge_->QueueEOS(input_buf_index);
- return DECODE_INPUT_END_OF_STREAM;
- }
- if (unit.key_id.empty()) {
- media_codec_bridge_->QueueInputBuffer(
- input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp);
- } else {
- if (unit.iv.empty() || unit.subsamples.empty()) {
- LOG(ERROR) << "The access unit doesn't have iv or subsamples while it "
- << "has key IDs!";
- return DECODE_FAILED;
- }
- media_codec_bridge_->QueueSecureInputBuffer(
- input_buf_index, &unit.data[0], unit.data.size(),
- reinterpret_cast<const uint8*>(&unit.key_id[0]), unit.key_id.size(),
- reinterpret_cast<const uint8*>(&unit.iv[0]), unit.iv.size(),
- &unit.subsamples[0], unit.subsamples.size(), unit.timestamp);
- }
-
- return DECODE_SUCCEEDED;
-}
-
-void MediaDecoderJob::DecodeInternal(
- const AccessUnit& unit,
- const base::TimeTicks& start_time_ticks,
- const base::TimeDelta& start_presentation_timestamp,
- bool needs_flush,
- const MediaDecoderJob::DecoderCallback& callback) {
- if (needs_flush) {
- DVLOG(1) << "DecodeInternal needs flush.";
- input_eos_encountered_ = false;
- int reset_status = media_codec_bridge_->Reset();
- if (0 != reset_status) {
- ui_loop_->PostTask(FROM_HERE, base::Bind(
- callback, DECODE_FAILED, start_presentation_timestamp, 0));
- return;
+// static
+bool MediaSourcePlayer::IsTypeSupported(
+ const std::vector<uint8>& scheme_uuid,
+ const std::string& security_level,
+ const std::string& container,
+ const std::vector<std::string>& codecs) {
+ if (!MediaDrmBridge::IsCryptoSchemeSupported(scheme_uuid, container)) {
+ DVLOG(1) << "UUID and container '" << container << "' not supported.";
+ return false;
+ }
+
+ if (!MediaDrmBridge::IsSecurityLevelSupported(scheme_uuid, security_level)) {
+ DVLOG(1) << "UUID and security level '" << security_level
+ << "' not supported.";
+ return false;
+ }
+
+ bool is_secure = MediaDrmBridge::IsSecureDecoderRequired(security_level);
+ for (size_t i = 0; i < codecs.size(); ++i) {
+ if (!MediaCodecBridge::CanDecode(codecs[i], is_secure)) {
+ DVLOG(1) << "Codec '" << codecs[i] << "' "
+ << (is_secure ? "in secure mode " : "") << "not supported.";
+ return false;
}
}
- DecodeStatus decode_status = DECODE_INPUT_END_OF_STREAM;
- if (!input_eos_encountered_) {
- decode_status = QueueInputBuffer(unit);
- if (decode_status == DECODE_INPUT_END_OF_STREAM) {
- input_eos_encountered_ = true;
- } else if (decode_status != DECODE_SUCCEEDED) {
- ui_loop_->PostTask(FROM_HERE,
- base::Bind(callback, decode_status,
- start_presentation_timestamp, 0));
- return;
- }
- }
-
- size_t offset = 0;
- size_t size = 0;
- base::TimeDelta presentation_timestamp;
- bool end_of_stream = false;
-
- base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
- kMediaCodecTimeoutInMilliseconds);
- int outputBufferIndex = media_codec_bridge_->DequeueOutputBuffer(
- timeout, &offset, &size, &presentation_timestamp, &end_of_stream);
-
- if (end_of_stream)
- decode_status = DECODE_OUTPUT_END_OF_STREAM;
- switch (outputBufferIndex) {
- case MediaCodecBridge::INFO_OUTPUT_BUFFERS_CHANGED:
- DCHECK(decode_status != DECODE_INPUT_END_OF_STREAM);
- media_codec_bridge_->GetOutputBuffers();
- break;
- case MediaCodecBridge::INFO_OUTPUT_FORMAT_CHANGED:
- DCHECK(decode_status != DECODE_INPUT_END_OF_STREAM);
- // TODO(qinmin): figure out what we should do if format changes.
- decode_status = DECODE_FORMAT_CHANGED;
- break;
- case MediaCodecBridge::INFO_TRY_AGAIN_LATER:
- decode_status = DECODE_TRY_DEQUEUE_OUTPUT_AGAIN_LATER;
- break;
- case MediaCodecBridge::INFO_MEDIA_CODEC_ERROR:
- decode_status = DECODE_FAILED;
- break;
- default:
- DCHECK_LE(0, outputBufferIndex);
- base::TimeDelta time_to_render;
- DCHECK(!start_time_ticks.is_null());
- if (!is_audio_) {
- time_to_render = presentation_timestamp - (base::TimeTicks::Now() -
- start_time_ticks + start_presentation_timestamp);
- }
- if (time_to_render >= base::TimeDelta()) {
- base::MessageLoop::current()->PostDelayedTask(
- FROM_HERE,
- base::Bind(&MediaDecoderJob::ReleaseOutputBuffer,
- weak_this_.GetWeakPtr(), outputBufferIndex, size,
- presentation_timestamp, callback, decode_status),
- time_to_render);
- } else {
- // TODO(qinmin): The codec is lagging behind, need to recalculate the
- // |start_presentation_timestamp_| and |start_time_ticks_|.
- DVLOG(1) << (is_audio_ ? "audio " : "video ")
- << "codec is lagging behind :" << time_to_render.InMicroseconds();
- ReleaseOutputBuffer(outputBufferIndex, size, presentation_timestamp,
- callback, decode_status);
- }
- return;
- }
- ui_loop_->PostTask(FROM_HERE, base::Bind(
- callback, decode_status, start_presentation_timestamp, 0));
-}
-
-void MediaDecoderJob::ReleaseOutputBuffer(
- int outputBufferIndex, size_t size,
- const base::TimeDelta& presentation_timestamp,
- const MediaDecoderJob::DecoderCallback& callback, DecodeStatus status) {
- // TODO(qinmin): Refactor this function. Maybe AudioDecoderJob should provide
- // its own ReleaseOutputBuffer().
- if (is_audio_) {
- static_cast<AudioCodecBridge*>(media_codec_bridge_.get())->PlayOutputBuffer(
- outputBufferIndex, size);
- }
- if (status != DECODE_OUTPUT_END_OF_STREAM || size != 0u)
- media_codec_bridge_->ReleaseOutputBuffer(outputBufferIndex, !is_audio_);
- ui_loop_->PostTask(FROM_HERE, base::Bind(
- callback, status, presentation_timestamp, is_audio_ ? size : 0));
-}
-
-void MediaDecoderJob::OnDecodeCompleted() {
- DCHECK(ui_loop_->BelongsToCurrentThread());
- is_decoding_ = false;
-}
-
-void MediaDecoderJob::Flush() {
- // Do nothing, flush when the next Decode() happens.
- needs_flush_ = true;
-}
-
-void MediaDecoderJob::Release() {
- // If |decoding_| is false, there is nothing running on the decoder thread.
- // So it is safe to delete the MediaDecoderJob on the UI thread. However,
- // if we post a task to the decoder thread to delete object, then we cannot
- // immediately pass the surface to a new MediaDecoderJob instance because
- // the java surface is still owned by the old object. New decoder creation
- // will be blocked on the UI thread until the previous decoder gets deleted.
- // This introduces extra latency during config changes, and makes the logic in
- // MediaSourcePlayer more complicated.
- //
- // TODO(qinmin): Figure out the logic to passing the surface to a new
- // MediaDecoderJob instance after the previous one gets deleted on the decoder
- // thread.
- if (is_decoding_ && !decoder_loop_->BelongsToCurrentThread()) {
- DCHECK(ui_loop_->BelongsToCurrentThread());
- decoder_loop_->DeleteSoon(FROM_HERE, this);
- } else {
- delete this;
- }
-}
-
-VideoDecoderJob* VideoDecoderJob::Create(
- const VideoCodec video_codec, const gfx::Size& size, jobject surface,
- jobject media_crypto) {
- scoped_ptr<VideoCodecBridge> codec(VideoCodecBridge::Create(video_codec));
- if (codec && codec->Start(video_codec, size, surface, media_crypto))
- return new VideoDecoderJob(codec.release());
- return NULL;
-}
-
-VideoDecoderJob::VideoDecoderJob(MediaCodecBridge* media_codec_bridge)
- : MediaDecoderJob(g_video_decoder_thread.Pointer()->message_loop_proxy(),
- media_codec_bridge,
- false) {}
-
-AudioDecoderJob* AudioDecoderJob::Create(
- const AudioCodec audio_codec,
- int sample_rate,
- int channel_count,
- const uint8* extra_data,
- size_t extra_data_size,
- jobject media_crypto) {
- scoped_ptr<AudioCodecBridge> codec(AudioCodecBridge::Create(audio_codec));
- if (codec && codec->Start(audio_codec, sample_rate, channel_count, extra_data,
- extra_data_size, true, media_crypto)) {
- return new AudioDecoderJob(codec.release());
- }
- return NULL;
-}
-
-AudioDecoderJob::AudioDecoderJob(MediaCodecBridge* media_codec_bridge)
- : MediaDecoderJob(g_audio_decoder_thread.Pointer()->message_loop_proxy(),
- media_codec_bridge,
- true) {}
-
-void AudioDecoderJob::SetVolume(double volume) {
- static_cast<AudioCodecBridge*>(media_codec_bridge_.get())->SetVolume(volume);
+ return true;
}
MediaSourcePlayer::MediaSourcePlayer(
int player_id,
- MediaPlayerManager* manager)
+ MediaPlayerManager* manager,
+ int demuxer_client_id,
+ DemuxerAndroid* demuxer)
: MediaPlayerAndroid(player_id, manager),
+ demuxer_client_id_(demuxer_client_id),
+ demuxer_(demuxer),
pending_event_(NO_EVENT_PENDING),
seek_request_id_(0),
width_(0),
@@ -345,35 +81,54 @@ MediaSourcePlayer::MediaSourcePlayer(
clock_(&default_tick_clock_),
reconfig_audio_decoder_(false),
reconfig_video_decoder_(false),
- audio_access_unit_index_(0),
- video_access_unit_index_(0),
- waiting_for_audio_data_(false),
- waiting_for_video_data_(false),
- sync_decoder_jobs_(true),
weak_this_(this),
- drm_bridge_(NULL) {
+ drm_bridge_(NULL),
+ is_waiting_for_key_(false) {
+ demuxer_->AddDemuxerClient(demuxer_client_id_, this);
}
MediaSourcePlayer::~MediaSourcePlayer() {
+ demuxer_->RemoveDemuxerClient(demuxer_client_id_);
Release();
}
void MediaSourcePlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) {
- // Ignore non-empty surface that is unprotected if |is_video_encrypted_| is
- // true.
- if (is_video_encrypted_ && !surface.IsEmpty() && !surface.is_protected())
+ // For an empty surface, always pass it to the decoder job so that it
+ // can detach from the current one. Otherwise, don't pass an unprotected
+ // surface if the video content requires a protected one.
+ if (!surface.IsEmpty() &&
+ IsProtectedSurfaceRequired() && !surface.is_protected()) {
return;
+ }
surface_ = surface.Pass();
- pending_event_ |= SURFACE_CHANGE_EVENT_PENDING;
- if (pending_event_ & SEEK_EVENT_PENDING) {
+
+ // If there is a pending surface change event, just wait for it to be
+ // processed.
+ if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING))
+ return;
+ SetPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
+ if (IsEventPending(SEEK_EVENT_PENDING)) {
// Waiting for the seek to finish.
return;
}
+
// Setting a new surface will require a new MediaCodec to be created.
// Request a seek so that the new decoder will decode an I-frame first.
// Or otherwise, the new MediaCodec might crash. See b/8950387.
- pending_event_ |= SEEK_EVENT_PENDING;
+ ScheduleSeekEventAndStopDecoding();
+}
+
+void MediaSourcePlayer::ScheduleSeekEventAndStopDecoding() {
+ if (audio_decoder_job_ && audio_decoder_job_->is_decoding())
+ audio_decoder_job_->StopDecode();
+ if (video_decoder_job_ && video_decoder_job_->is_decoding())
+ video_decoder_job_->StopDecode();
+
+ if (IsEventPending(SEEK_EVENT_PENDING))
+ return;
+
+ SetPendingEvent(SEEK_EVENT_PENDING);
ProcessPendingEvents();
}
@@ -387,15 +142,19 @@ bool MediaSourcePlayer::Seekable() {
}
void MediaSourcePlayer::Start() {
+ DVLOG(1) << __FUNCTION__;
+
playing_ = true;
- if (is_video_encrypted_)
+ if (IsProtectedSurfaceRequired())
manager()->OnProtectedSurfaceRequested(player_id());
StartInternal();
}
-void MediaSourcePlayer::Pause() {
+void MediaSourcePlayer::Pause(bool is_media_related_action) {
+ DVLOG(1) << __FUNCTION__;
+
// Since decoder jobs have their own thread, decoding is not fully paused
// until all the decoder jobs call MediaDecoderCallback(). It is possible
// that Start() is called while the player is waiting for
@@ -418,11 +177,12 @@ int MediaSourcePlayer::GetVideoHeight() {
}
void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) {
+ DVLOG(1) << __FUNCTION__ << "(" << timestamp.InSecondsF() << ")";
+
clock_.SetTime(timestamp, timestamp);
if (audio_timestamp_helper_)
audio_timestamp_helper_->SetBaseTimestamp(timestamp);
- pending_event_ |= SEEK_EVENT_PENDING;
- ProcessPendingEvents();
+ ScheduleSeekEventAndStopDecoding();
}
base::TimeDelta MediaSourcePlayer::GetCurrentTime() {
@@ -434,13 +194,14 @@ base::TimeDelta MediaSourcePlayer::GetDuration() {
}
void MediaSourcePlayer::Release() {
- ClearDecodingData();
+ DVLOG(1) << __FUNCTION__;
audio_decoder_job_.reset();
video_decoder_job_.reset();
reconfig_audio_decoder_ = false;
reconfig_video_decoder_ = false;
playing_ = false;
pending_event_ = NO_EVENT_PENDING;
+ decoder_starvation_callback_.Cancel();
surface_ = gfx::ScopedJavaSurface();
ReleaseMediaResourcesFromManager();
}
@@ -450,6 +211,16 @@ void MediaSourcePlayer::SetVolume(double volume) {
SetVolumeInternal();
}
+void MediaSourcePlayer::OnKeyAdded() {
+ DVLOG(1) << __FUNCTION__;
+ if (!is_waiting_for_key_)
+ return;
+
+ is_waiting_for_key_ = false;
+ if (playing_)
+ StartInternal();
+}
+
bool MediaSourcePlayer::CanPause() {
return Seekable();
}
@@ -467,15 +238,20 @@ bool MediaSourcePlayer::IsPlayerReady() {
}
void MediaSourcePlayer::StartInternal() {
+ DVLOG(1) << __FUNCTION__;
// If there are pending events, wait for them finish.
if (pending_event_ != NO_EVENT_PENDING)
return;
+ // When we start, we'll have new demuxed data coming in. This new data could
+ // be clear (not encrypted) or encrypted with different keys. So
+ // |is_waiting_for_key_| condition may not be true anymore.
+ is_waiting_for_key_ = false;
+
// Create decoder jobs if they are not created
ConfigureAudioDecoderJob();
ConfigureVideoDecoderJob();
-
// If one of the decoder job is not ready, do nothing.
if ((HasAudio() && !audio_decoder_job_) ||
(HasVideo() && !video_decoder_job_)) {
@@ -484,20 +260,21 @@ void MediaSourcePlayer::StartInternal() {
audio_finished_ = false;
video_finished_ = false;
- sync_decoder_jobs_ = true;
- SyncAndStartDecoderJobs();
+ SetPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
+ ProcessPendingEvents();
}
-void MediaSourcePlayer::DemuxerReady(
- const MediaPlayerHostMsg_DemuxerReady_Params& params) {
- duration_ = base::TimeDelta::FromMilliseconds(params.duration_ms);
+void MediaSourcePlayer::OnDemuxerConfigsAvailable(
+ const DemuxerConfigs& configs) {
+ DVLOG(1) << __FUNCTION__;
+ duration_ = base::TimeDelta::FromMilliseconds(configs.duration_ms);
clock_.SetDuration(duration_);
- audio_codec_ = params.audio_codec;
- num_channels_ = params.audio_channels;
- sampling_rate_ = params.audio_sampling_rate;
- is_audio_encrypted_ = params.is_audio_encrypted;
- audio_extra_data_ = params.audio_extra_data;
+ audio_codec_ = configs.audio_codec;
+ num_channels_ = configs.audio_channels;
+ sampling_rate_ = configs.audio_sampling_rate;
+ is_audio_encrypted_ = configs.is_audio_encrypted;
+ audio_extra_data_ = configs.audio_extra_data;
if (HasAudio()) {
DCHECK_GT(num_channels_, 0);
audio_timestamp_helper_.reset(new AudioTimestampHelper(sampling_rate_));
@@ -506,269 +283,274 @@ void MediaSourcePlayer::DemuxerReady(
audio_timestamp_helper_.reset();
}
- video_codec_ = params.video_codec;
- width_ = params.video_size.width();
- height_ = params.video_size.height();
- is_video_encrypted_ = params.is_video_encrypted;
+ video_codec_ = configs.video_codec;
+ width_ = configs.video_size.width();
+ height_ = configs.video_size.height();
+ is_video_encrypted_ = configs.is_video_encrypted;
OnMediaMetadataChanged(duration_, width_, height_, true);
- if (pending_event_ & CONFIG_CHANGE_EVENT_PENDING) {
+ if (IsEventPending(CONFIG_CHANGE_EVENT_PENDING)) {
if (reconfig_audio_decoder_)
ConfigureAudioDecoderJob();
// If there is a pending surface change, we can merge it with the config
// change.
if (reconfig_video_decoder_) {
- pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING;
+ if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING))
+ ClearPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
ConfigureVideoDecoderJob();
}
- pending_event_ &= ~CONFIG_CHANGE_EVENT_PENDING;
+
+ ClearPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
+
+ // Resume decoding after the config change if we are still playing.
if (playing_)
StartInternal();
}
}
-void MediaSourcePlayer::ReadFromDemuxerAck(
- const MediaPlayerHostMsg_ReadFromDemuxerAck_Params& params) {
- DCHECK_LT(0u, params.access_units.size());
- if (params.type == DemuxerStream::AUDIO)
- waiting_for_audio_data_ = false;
- else
- waiting_for_video_data_ = false;
-
- // If there is a pending seek request, ignore the data from the chunk demuxer.
- // The data will be requested later when OnSeekRequestAck() is called.
- if (pending_event_ & SEEK_EVENT_PENDING)
- return;
-
- if (params.type == DemuxerStream::AUDIO) {
- DCHECK_EQ(0u, audio_access_unit_index_);
- received_audio_ = params;
- } else {
- DCHECK_EQ(0u, video_access_unit_index_);
- received_video_ = params;
- }
-
- if (pending_event_ != NO_EVENT_PENDING || !playing_)
- return;
-
- if (sync_decoder_jobs_) {
- SyncAndStartDecoderJobs();
- return;
- }
-
- if (params.type == DemuxerStream::AUDIO)
- DecodeMoreAudio();
- else
- DecodeMoreVideo();
+void MediaSourcePlayer::OnDemuxerDataAvailable(const DemuxerData& data) {
+ DVLOG(1) << __FUNCTION__ << "(" << data.type << ")";
+ DCHECK_LT(0u, data.access_units.size());
+ if (data.type == DemuxerStream::AUDIO && audio_decoder_job_)
+ audio_decoder_job_->OnDataReceived(data);
+ else if (data.type == DemuxerStream::VIDEO && video_decoder_job_)
+ video_decoder_job_->OnDataReceived(data);
}
-void MediaSourcePlayer::DurationChanged(const base::TimeDelta& duration) {
+void MediaSourcePlayer::OnDemuxerDurationChanged(base::TimeDelta duration) {
duration_ = duration;
clock_.SetDuration(duration_);
}
+base::android::ScopedJavaLocalRef<jobject> MediaSourcePlayer::GetMediaCrypto() {
+ base::android::ScopedJavaLocalRef<jobject> media_crypto;
+ if (drm_bridge_)
+ media_crypto = drm_bridge_->GetMediaCrypto();
+ return media_crypto;
+}
+
+void MediaSourcePlayer::OnMediaCryptoReady() {
+ DCHECK(!drm_bridge_->GetMediaCrypto().is_null());
+ drm_bridge_->SetMediaCryptoReadyCB(base::Closure());
+
+ if (playing_)
+ StartInternal();
+}
+
void MediaSourcePlayer::SetDrmBridge(MediaDrmBridge* drm_bridge) {
// Currently we don't support DRM change during the middle of playback, even
// if the player is paused.
// TODO(qinmin): support DRM change after playback has started.
// http://crbug.com/253792.
if (GetCurrentTime() > base::TimeDelta()) {
- LOG(INFO) << "Setting DRM bridge after play back has started. "
+ LOG(INFO) << "Setting DRM bridge after playback has started. "
<< "This is not well supported!";
}
drm_bridge_ = drm_bridge;
+ if (drm_bridge_->GetMediaCrypto().is_null()) {
+ drm_bridge_->SetMediaCryptoReadyCB(base::Bind(
+ &MediaSourcePlayer::OnMediaCryptoReady, weak_this_.GetWeakPtr()));
+ return;
+ }
+
if (playing_)
StartInternal();
}
-void MediaSourcePlayer::OnSeekRequestAck(unsigned seek_request_id) {
- DVLOG(1) << "OnSeekRequestAck(" << seek_request_id << ")";
+void MediaSourcePlayer::OnDemuxerSeeked(unsigned seek_request_id) {
+ DVLOG(1) << __FUNCTION__ << "(" << seek_request_id << ")";
// Do nothing until the most recent seek request is processed.
if (seek_request_id_ != seek_request_id)
return;
- pending_event_ &= ~SEEK_EVENT_PENDING;
+
+ ClearPendingEvent(SEEK_EVENT_PENDING);
+
OnSeekComplete();
ProcessPendingEvents();
}
void MediaSourcePlayer::UpdateTimestamps(
const base::TimeDelta& presentation_timestamp, size_t audio_output_bytes) {
+ base::TimeDelta new_max_time = presentation_timestamp;
+
if (audio_output_bytes > 0) {
audio_timestamp_helper_->AddFrames(
audio_output_bytes / (kBytesPerAudioOutputSample * num_channels_));
- clock_.SetMaxTime(audio_timestamp_helper_->GetTimestamp());
- } else {
- clock_.SetMaxTime(presentation_timestamp);
+ new_max_time = audio_timestamp_helper_->GetTimestamp();
}
+ clock_.SetMaxTime(new_max_time);
OnTimeUpdated();
}
void MediaSourcePlayer::ProcessPendingEvents() {
+ DVLOG(1) << __FUNCTION__ << " : 0x" << std::hex << pending_event_;
// Wait for all the decoding jobs to finish before processing pending tasks.
- if ((audio_decoder_job_ && audio_decoder_job_->is_decoding()) ||
- (video_decoder_job_ && video_decoder_job_->is_decoding())) {
+ if (video_decoder_job_ && video_decoder_job_->is_decoding()) {
+ DVLOG(1) << __FUNCTION__ << " : A video job is still decoding.";
+ return;
+ }
+
+ if (audio_decoder_job_ && audio_decoder_job_->is_decoding()) {
+ DVLOG(1) << __FUNCTION__ << " : An audio job is still decoding.";
+ return;
+ }
+
+ if (IsEventPending(PREFETCH_DONE_EVENT_PENDING)) {
+ DVLOG(1) << __FUNCTION__ << " : PREFETCH_DONE still pending.";
return;
}
- if (pending_event_ & SEEK_EVENT_PENDING) {
+ if (IsEventPending(SEEK_EVENT_PENDING)) {
+ int seek_request_id = ++seek_request_id_;
+ DVLOG(1) << __FUNCTION__ << " : Handling SEEK_EVENT: " << seek_request_id;
ClearDecodingData();
- manager()->OnMediaSeekRequest(
- player_id(), GetCurrentTime(), ++seek_request_id_);
+ demuxer_->RequestDemuxerSeek(
+ demuxer_client_id_, GetCurrentTime(), seek_request_id);
return;
}
start_time_ticks_ = base::TimeTicks();
- if (pending_event_ & CONFIG_CHANGE_EVENT_PENDING) {
+ if (IsEventPending(CONFIG_CHANGE_EVENT_PENDING)) {
+ DVLOG(1) << __FUNCTION__ << " : Handling CONFIG_CHANGE_EVENT.";
DCHECK(reconfig_audio_decoder_ || reconfig_video_decoder_);
- manager()->OnMediaConfigRequest(player_id());
+ demuxer_->RequestDemuxerConfigs(demuxer_client_id_);
return;
}
- if (pending_event_ & SURFACE_CHANGE_EVENT_PENDING) {
+ if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING)) {
+ DVLOG(1) << __FUNCTION__ << " : Handling SURFACE_CHANGE_EVENT.";
video_decoder_job_.reset();
ConfigureVideoDecoderJob();
- pending_event_ &= ~SURFACE_CHANGE_EVENT_PENDING;
+ ClearPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
}
+ if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING)) {
+ DVLOG(1) << __FUNCTION__ << " : Handling PREFETCH_REQUEST_EVENT.";
+ int count = (audio_decoder_job_ ? 1 : 0) + (video_decoder_job_ ? 1 : 0);
+
+ base::Closure barrier = BarrierClosure(count, base::Bind(
+ &MediaSourcePlayer::OnPrefetchDone, weak_this_.GetWeakPtr()));
+
+ if (audio_decoder_job_)
+ audio_decoder_job_->Prefetch(barrier);
+
+ if (video_decoder_job_)
+ video_decoder_job_->Prefetch(barrier);
+
+ SetPendingEvent(PREFETCH_DONE_EVENT_PENDING);
+ ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
+ return;
+ }
+
+ DCHECK_EQ(pending_event_, NO_EVENT_PENDING);
+
+ // Now that all pending events have been handled, resume decoding if we are
+ // still playing.
if (playing_)
StartInternal();
}
void MediaSourcePlayer::MediaDecoderCallback(
- bool is_audio, MediaDecoderJob::DecodeStatus decode_status,
+ bool is_audio, MediaCodecStatus status,
const base::TimeDelta& presentation_timestamp, size_t audio_output_bytes) {
- if (is_audio && audio_decoder_job_)
- audio_decoder_job_->OnDecodeCompleted();
- if (!is_audio && video_decoder_job_)
- video_decoder_job_->OnDecodeCompleted();
+ DVLOG(1) << __FUNCTION__ << ": " << is_audio << ", " << status;
+ DCHECK(!is_waiting_for_key_);
- if (is_audio)
+ bool is_clock_manager = is_audio || !HasAudio();
+
+ if (is_clock_manager)
decoder_starvation_callback_.Cancel();
- if (decode_status == MediaDecoderJob::DECODE_FAILED) {
+ if (status == MEDIA_CODEC_ERROR) {
Release();
OnMediaError(MEDIA_ERROR_DECODE);
return;
}
- // If the input reaches input EOS, there is no need to request new data.
- if (decode_status != MediaDecoderJob::DECODE_TRY_ENQUEUE_INPUT_AGAIN_LATER &&
- decode_status != MediaDecoderJob::DECODE_INPUT_END_OF_STREAM) {
- if (is_audio)
- audio_access_unit_index_++;
- else
- video_access_unit_index_++;
- }
-
if (pending_event_ != NO_EVENT_PENDING) {
ProcessPendingEvents();
return;
}
- if (decode_status == MediaDecoderJob::DECODE_SUCCEEDED &&
- (is_audio || !HasAudio())) {
- UpdateTimestamps(presentation_timestamp, audio_output_bytes);
- }
-
- if (decode_status == MediaDecoderJob::DECODE_OUTPUT_END_OF_STREAM) {
+ if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM) {
PlaybackCompleted(is_audio);
return;
}
+ if (status == MEDIA_CODEC_OK && is_clock_manager)
+ UpdateTimestamps(presentation_timestamp, audio_output_bytes);
+
if (!playing_) {
- if (is_audio || !HasAudio())
+ if (is_clock_manager)
clock_.Pause();
return;
}
- if (sync_decoder_jobs_) {
- SyncAndStartDecoderJobs();
+ if (status == MEDIA_CODEC_NO_KEY) {
+ is_waiting_for_key_ = true;
return;
}
- base::TimeDelta current_timestamp = GetCurrentTime();
+ // If the status is MEDIA_CODEC_STOPPED, stop decoding new data. The player is
+ // in the middle of a seek or stop event and needs to wait for the IPCs to
+ // come.
+ if (status == MEDIA_CODEC_STOPPED)
+ return;
+
+ if (status == MEDIA_CODEC_OK && is_clock_manager)
+ StartStarvationCallback(presentation_timestamp);
+
if (is_audio) {
- if (decode_status == MediaDecoderJob::DECODE_SUCCEEDED) {
- base::TimeDelta timeout =
- audio_timestamp_helper_->GetTimestamp() - current_timestamp;
- StartStarvationCallback(timeout);
- }
- if (!HasAudioData())
- RequestAudioData();
- else
- DecodeMoreAudio();
+ DecodeMoreAudio();
return;
}
- if (!HasAudio() && decode_status == MediaDecoderJob::DECODE_SUCCEEDED) {
- DCHECK(current_timestamp <= presentation_timestamp);
- // For video only streams, fps can be estimated from the difference
- // between the previous and current presentation timestamps. The
- // previous presentation timestamp is equal to current_timestamp.
- // TODO(qinmin): determine whether 2 is a good coefficient for estimating
- // video frame timeout.
- StartStarvationCallback(2 * (presentation_timestamp - current_timestamp));
- }
- if (!HasVideoData())
- RequestVideoData();
- else
- DecodeMoreVideo();
+ DecodeMoreVideo();
}
void MediaSourcePlayer::DecodeMoreAudio() {
+ DVLOG(1) << __FUNCTION__;
DCHECK(!audio_decoder_job_->is_decoding());
- DCHECK(HasAudioData());
-
- if (DemuxerStream::kConfigChanged ==
- received_audio_.access_units[audio_access_unit_index_].status) {
- // Wait for demuxer ready message.
- reconfig_audio_decoder_ = true;
- pending_event_ |= CONFIG_CHANGE_EVENT_PENDING;
- received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
- audio_access_unit_index_ = 0;
- ProcessPendingEvents();
+
+ if (audio_decoder_job_->Decode(
+ start_time_ticks_, start_presentation_timestamp_, base::Bind(
+ &MediaSourcePlayer::MediaDecoderCallback,
+ weak_this_.GetWeakPtr(), true))) {
return;
}
- audio_decoder_job_->Decode(
- received_audio_.access_units[audio_access_unit_index_],
- start_time_ticks_, start_presentation_timestamp_,
- base::Bind(&MediaSourcePlayer::MediaDecoderCallback,
- weak_this_.GetWeakPtr(), true));
+ // Failed to start the next decode.
+ // Wait for demuxer ready message.
+ reconfig_audio_decoder_ = true;
+ SetPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
+ ProcessPendingEvents();
}
void MediaSourcePlayer::DecodeMoreVideo() {
- DVLOG(1) << "DecodeMoreVideo()";
+ DVLOG(1) << __FUNCTION__;
DCHECK(!video_decoder_job_->is_decoding());
- DCHECK(HasVideoData());
-
- if (DemuxerStream::kConfigChanged ==
- received_video_.access_units[video_access_unit_index_].status) {
- // Wait for demuxer ready message.
- reconfig_video_decoder_ = true;
- pending_event_ |= CONFIG_CHANGE_EVENT_PENDING;
- received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
- video_access_unit_index_ = 0;
- ProcessPendingEvents();
+
+ if (video_decoder_job_->Decode(
+ start_time_ticks_, start_presentation_timestamp_, base::Bind(
+ &MediaSourcePlayer::MediaDecoderCallback,
+ weak_this_.GetWeakPtr(), false))) {
return;
}
- DVLOG(3) << "VideoDecoderJob::Decode(" << video_access_unit_index_ << ", "
- << start_time_ticks_.ToInternalValue() << ", "
- << start_presentation_timestamp_.InMilliseconds() << ")";
- video_decoder_job_->Decode(
- received_video_.access_units[video_access_unit_index_],
- start_time_ticks_, start_presentation_timestamp_,
- base::Bind(&MediaSourcePlayer::MediaDecoderCallback,
- weak_this_.GetWeakPtr(), false));
+ // Failed to start the next decode.
+ // Wait for demuxer ready message.
+ reconfig_video_decoder_ = true;
+ SetPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
+ ProcessPendingEvents();
}
void MediaSourcePlayer::PlaybackCompleted(bool is_audio) {
+ DVLOG(1) << __FUNCTION__ << "(" << is_audio << ")";
if (is_audio)
audio_finished_ = true;
else
@@ -783,18 +565,12 @@ void MediaSourcePlayer::PlaybackCompleted(bool is_audio) {
}
void MediaSourcePlayer::ClearDecodingData() {
- DVLOG(1) << "ClearDecodingData()";
+ DVLOG(1) << __FUNCTION__;
if (audio_decoder_job_)
audio_decoder_job_->Flush();
if (video_decoder_job_)
video_decoder_job_->Flush();
start_time_ticks_ = base::TimeTicks();
- received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
- received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
- audio_access_unit_index_ = 0;
- video_access_unit_index_ = 0;
- waiting_for_audio_data_ = false;
- waiting_for_video_data_ = false;
}
bool MediaSourcePlayer::HasVideo() {
@@ -815,26 +591,18 @@ void MediaSourcePlayer::ConfigureAudioDecoderJob() {
if (audio_decoder_job_ && !reconfig_audio_decoder_)
return;
- base::android::ScopedJavaLocalRef<jobject> media_codec;
- if (is_audio_encrypted_) {
- if (drm_bridge_) {
- media_codec = drm_bridge_->GetMediaCrypto();
- // TODO(qinmin): currently we assume MediaCrypto is available whenever
- // MediaDrmBridge is constructed. This will change if we want to support
- // more general uses cases of EME.
- DCHECK(!media_codec.is_null());
- } else {
- // Don't create the decoder job if |drm_bridge_| is not set,
- // so StartInternal() will not proceed.
- LOG(INFO) << "MediaDrmBridge is not available when creating decoder "
- << "for encrypted audio stream.";
- return;
- }
- }
+ base::android::ScopedJavaLocalRef<jobject> media_crypto = GetMediaCrypto();
+ if (is_audio_encrypted_ && media_crypto.is_null())
+ return;
+
+ DCHECK(!audio_decoder_job_ || !audio_decoder_job_->is_decoding());
audio_decoder_job_.reset(AudioDecoderJob::Create(
audio_codec_, sampling_rate_, num_channels_, &audio_extra_data_[0],
- audio_extra_data_.size(), media_codec.obj()));
+ audio_extra_data_.size(), media_crypto.obj(),
+ base::Bind(&DemuxerAndroid::RequestDemuxerData,
+ base::Unretained(demuxer_), demuxer_client_id_,
+ DemuxerStream::AUDIO)));
if (audio_decoder_job_) {
SetVolumeInternal();
@@ -852,25 +620,27 @@ void MediaSourcePlayer::ConfigureVideoDecoderJob() {
if (video_decoder_job_ && !reconfig_video_decoder_)
return;
- base::android::ScopedJavaLocalRef<jobject> media_codec;
- if (is_video_encrypted_) {
- if (drm_bridge_) {
- media_codec = drm_bridge_->GetMediaCrypto();
- DCHECK(!media_codec.is_null());
- } else {
- LOG(INFO) << "MediaDrmBridge is not available when creating decoder "
- << "for encrypted video stream.";
- return;
- }
- }
+ base::android::ScopedJavaLocalRef<jobject> media_crypto = GetMediaCrypto();
+ if (is_video_encrypted_ && media_crypto.is_null())
+ return;
+
+ DCHECK(!video_decoder_job_ || !video_decoder_job_->is_decoding());
// Release the old VideoDecoderJob first so the surface can get released.
// Android does not allow 2 MediaCodec instances use the same surface.
video_decoder_job_.reset();
// Create the new VideoDecoderJob.
- video_decoder_job_.reset(VideoDecoderJob::Create(
- video_codec_, gfx::Size(width_, height_), surface_.j_surface().obj(),
- media_codec.obj()));
+ bool is_secure = IsProtectedSurfaceRequired();
+ video_decoder_job_.reset(
+ VideoDecoderJob::Create(video_codec_,
+ is_secure,
+ gfx::Size(width_, height_),
+ surface_.j_surface().obj(),
+ media_crypto.obj(),
+ base::Bind(&DemuxerAndroid::RequestDemuxerData,
+ base::Unretained(demuxer_),
+ demuxer_client_id_,
+ DemuxerStream::VIDEO)));
if (video_decoder_job_)
reconfig_video_decoder_ = false;
@@ -881,11 +651,36 @@ void MediaSourcePlayer::ConfigureVideoDecoderJob() {
}
void MediaSourcePlayer::OnDecoderStarved() {
- sync_decoder_jobs_ = true;
+ DVLOG(1) << __FUNCTION__;
+ SetPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
+ ProcessPendingEvents();
}
void MediaSourcePlayer::StartStarvationCallback(
- const base::TimeDelta& timeout) {
+ const base::TimeDelta& presentation_timestamp) {
+ // 20ms was chosen because it is the typical size of a compressed audio frame.
+ // Anything smaller than this would likely cause unnecessary cycling in and
+ // out of the prefetch state.
+ const base::TimeDelta kMinStarvationTimeout =
+ base::TimeDelta::FromMilliseconds(20);
+
+ base::TimeDelta current_timestamp = GetCurrentTime();
+ base::TimeDelta timeout;
+ if (HasAudio()) {
+ timeout = audio_timestamp_helper_->GetTimestamp() - current_timestamp;
+ } else {
+ DCHECK(current_timestamp <= presentation_timestamp);
+
+ // For video only streams, fps can be estimated from the difference
+ // between the previous and current presentation timestamps. The
+ // previous presentation timestamp is equal to current_timestamp.
+ // TODO(qinmin): determine whether 2 is a good coefficient for estimating
+ // video frame timeout.
+ timeout = 2 * (presentation_timestamp - current_timestamp);
+ }
+
+ timeout = std::max(timeout, kMinStarvationTimeout);
+
decoder_starvation_callback_.Reset(
base::Bind(&MediaSourcePlayer::OnDecoderStarved,
weak_this_.GetWeakPtr()));
@@ -893,66 +688,76 @@ void MediaSourcePlayer::StartStarvationCallback(
FROM_HERE, decoder_starvation_callback_.callback(), timeout);
}
-void MediaSourcePlayer::SyncAndStartDecoderJobs() {
- // For streams with both audio and video, send the request for video too.
- // However, don't wait for the response so that we won't have lots of
- // noticeable pauses in the audio. Video will sync with audio by itself.
- if (HasVideo() && !HasVideoData()) {
- RequestVideoData();
- if (!HasAudio())
- return;
- }
- if (HasAudio() && !HasAudioData()) {
- RequestAudioData();
+void MediaSourcePlayer::SetVolumeInternal() {
+ if (audio_decoder_job_ && volume_ >= 0)
+ audio_decoder_job_->SetVolume(volume_);
+}
+
+bool MediaSourcePlayer::IsProtectedSurfaceRequired() {
+ return is_video_encrypted_ &&
+ drm_bridge_ && drm_bridge_->IsProtectedSurfaceRequired();
+}
+
+void MediaSourcePlayer::OnPrefetchDone() {
+ DVLOG(1) << __FUNCTION__;
+ DCHECK(!audio_decoder_job_ || !audio_decoder_job_->is_decoding());
+ DCHECK(!video_decoder_job_ || !video_decoder_job_->is_decoding());
+ DCHECK(IsEventPending(PREFETCH_DONE_EVENT_PENDING));
+
+ ClearPendingEvent(PREFETCH_DONE_EVENT_PENDING);
+
+ if (pending_event_ != NO_EVENT_PENDING) {
+ ProcessPendingEvents();
return;
}
+
start_time_ticks_ = base::TimeTicks::Now();
start_presentation_timestamp_ = GetCurrentTime();
if (!clock_.IsPlaying())
clock_.Play();
- if (HasAudioData() && !audio_decoder_job_->is_decoding())
+
+ if (audio_decoder_job_)
DecodeMoreAudio();
- if (HasVideoData() && !video_decoder_job_->is_decoding())
+ if (video_decoder_job_)
DecodeMoreVideo();
- sync_decoder_jobs_ = false;
}
-void MediaSourcePlayer::RequestAudioData() {
- DVLOG(2) << "RequestAudioData()";
- DCHECK(HasAudio());
+const char* MediaSourcePlayer::GetEventName(PendingEventFlags event) {
+ static const char* kPendingEventNames[] = {
+ "SEEK",
+ "SURFACE_CHANGE",
+ "CONFIG_CHANGE",
+ "PREFETCH_REQUEST",
+ "PREFETCH_DONE",
+ };
- if (waiting_for_audio_data_)
- return;
+ int mask = 1;
+ for (size_t i = 0; i < arraysize(kPendingEventNames); ++i, mask <<= 1) {
+ if (event & mask)
+ return kPendingEventNames[i];
+ }
- manager()->OnReadFromDemuxer(player_id(), DemuxerStream::AUDIO);
- received_audio_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
- audio_access_unit_index_ = 0;
- waiting_for_audio_data_ = true;
+ return "UNKNOWN";
}
-void MediaSourcePlayer::RequestVideoData() {
- DVLOG(2) << "RequestVideoData()";
- DCHECK(HasVideo());
- if (waiting_for_video_data_)
- return;
-
- manager()->OnReadFromDemuxer(player_id(), DemuxerStream::VIDEO);
- received_video_ = MediaPlayerHostMsg_ReadFromDemuxerAck_Params();
- video_access_unit_index_ = 0;
- waiting_for_video_data_ = true;
+bool MediaSourcePlayer::IsEventPending(PendingEventFlags event) const {
+ return pending_event_ & event;
}
-bool MediaSourcePlayer::HasAudioData() const {
- return audio_access_unit_index_ < received_audio_.access_units.size();
-}
+void MediaSourcePlayer::SetPendingEvent(PendingEventFlags event) {
+ DVLOG(1) << __FUNCTION__ << "(" << GetEventName(event) << ")";
+ DCHECK_NE(event, NO_EVENT_PENDING);
+ DCHECK(!IsEventPending(event));
-bool MediaSourcePlayer::HasVideoData() const {
- return video_access_unit_index_ < received_video_.access_units.size();
+ pending_event_ |= event;
}
-void MediaSourcePlayer::SetVolumeInternal() {
- if (audio_decoder_job_ && volume_ >= 0)
- audio_decoder_job_.get()->SetVolume(volume_);
+void MediaSourcePlayer::ClearPendingEvent(PendingEventFlags event) {
+ DVLOG(1) << __FUNCTION__ << "(" << GetEventName(event) << ")";
+ DCHECK_NE(event, NO_EVENT_PENDING);
+ DCHECK(IsEventPending(event)) << GetEventName(event);
+
+ pending_event_ &= ~event;
}
} // namespace media