Index: media/base/android/media_source_player.cc |
diff --git a/media/base/android/media_source_player.cc b/media/base/android/media_source_player.cc |
deleted file mode 100644 |
index cf818faca03dd885d03e83bbe76147e837b3be8a..0000000000000000000000000000000000000000 |
--- a/media/base/android/media_source_player.cc |
+++ /dev/null |
@@ -1,865 +0,0 @@ |
-// Copyright (c) 2013 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 "media/base/android/media_source_player.h" |
- |
-#include <stddef.h> |
-#include <stdint.h> |
-#include <limits> |
-#include <utility> |
- |
-#include "base/android/jni_android.h" |
-#include "base/android/jni_string.h" |
-#include "base/barrier_closure.h" |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/callback_helpers.h" |
-#include "base/location.h" |
-#include "base/logging.h" |
-#include "base/macros.h" |
-#include "base/single_thread_task_runner.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/threading/thread_task_runner_handle.h" |
-#include "base/trace_event/trace_event.h" |
-#include "media/base/android/audio_decoder_job.h" |
-#include "media/base/android/media_player_manager.h" |
-#include "media/base/android/video_decoder_job.h" |
-#include "media/base/bind_to_current_loop.h" |
-#include "media/base/timestamp_constants.h" |
- |
-namespace media { |
- |
-MediaSourcePlayer::MediaSourcePlayer( |
- int player_id, |
- MediaPlayerManager* manager, |
- const OnDecoderResourcesReleasedCB& on_decoder_resources_released_cb, |
- std::unique_ptr<DemuxerAndroid> demuxer, |
- const GURL& frame_url) |
- : MediaPlayerAndroid(player_id, |
- manager, |
- on_decoder_resources_released_cb, |
- frame_url), |
- demuxer_(std::move(demuxer)), |
- pending_event_(NO_EVENT_PENDING), |
- playing_(false), |
- interpolator_(&default_tick_clock_), |
- doing_browser_seek_(false), |
- pending_seek_(false), |
- cdm_registration_id_(0), |
- is_waiting_for_key_(false), |
- key_added_while_decode_pending_(false), |
- is_waiting_for_audio_decoder_(false), |
- is_waiting_for_video_decoder_(false), |
- prerolling_(true), |
- weak_factory_(this) { |
- media_stat_.reset(new MediaStatistics()); |
- |
- audio_decoder_job_.reset(new AudioDecoderJob( |
- base::Bind(&DemuxerAndroid::RequestDemuxerData, |
- base::Unretained(demuxer_.get()), |
- DemuxerStream::AUDIO), |
- base::Bind(&MediaSourcePlayer::OnDemuxerConfigsChanged, |
- weak_factory_.GetWeakPtr()))); |
- video_decoder_job_.reset(new VideoDecoderJob( |
- base::Bind(&DemuxerAndroid::RequestDemuxerData, |
- base::Unretained(demuxer_.get()), |
- DemuxerStream::VIDEO), |
- base::Bind(&MediaSourcePlayer::OnDemuxerConfigsChanged, |
- weak_factory_.GetWeakPtr()))); |
- |
- demuxer_->Initialize(this); |
- interpolator_.SetPlaybackRate(1.0); |
- interpolator_.SetUpperBound(base::TimeDelta()); |
- weak_this_ = weak_factory_.GetWeakPtr(); |
-} |
- |
-MediaSourcePlayer::~MediaSourcePlayer() { |
- Release(); |
- DCHECK_EQ(!cdm_, !cdm_registration_id_); |
- if (cdm_) { |
- // Cancel previously registered callback (if any). |
- static_cast<MediaDrmBridge*>(cdm_.get()) |
- ->SetMediaCryptoReadyCB(MediaDrmBridge::MediaCryptoReadyCB()); |
- |
- static_cast<MediaDrmBridge*>(cdm_.get()) |
- ->UnregisterPlayer(cdm_registration_id_); |
- cdm_registration_id_ = 0; |
- } |
-} |
- |
-void MediaSourcePlayer::SetVideoSurface(gl::ScopedJavaSurface surface) { |
- DVLOG(1) << __FUNCTION__; |
- if (!video_decoder_job_->SetVideoSurface(std::move(surface))) |
- return; |
- // Retry video decoder creation. |
- RetryDecoderCreation(false, true); |
-} |
- |
-void MediaSourcePlayer::ScheduleSeekEventAndStopDecoding( |
- base::TimeDelta seek_time) { |
- DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ")"; |
- DCHECK(!IsEventPending(SEEK_EVENT_PENDING)); |
- |
- pending_seek_ = false; |
- |
- interpolator_.SetBounds(seek_time, seek_time, default_tick_clock_.NowTicks()); |
- |
- if (audio_decoder_job_->is_decoding()) |
- audio_decoder_job_->StopDecode(); |
- if (video_decoder_job_->is_decoding()) |
- video_decoder_job_->StopDecode(); |
- |
- SetPendingEvent(SEEK_EVENT_PENDING); |
- ProcessPendingEvents(); |
-} |
- |
-void MediaSourcePlayer::BrowserSeekToCurrentTime() { |
- DVLOG(1) << __FUNCTION__; |
- |
- DCHECK(!IsEventPending(SEEK_EVENT_PENDING)); |
- doing_browser_seek_ = true; |
- ScheduleSeekEventAndStopDecoding(GetCurrentTime()); |
-} |
- |
-bool MediaSourcePlayer::Seekable() { |
- // If the duration TimeDelta, converted to milliseconds from microseconds, |
- // is >= 2^31, then the media is assumed to be unbounded and unseekable. |
- // 2^31 is the bound due to java player using 32-bit integer for time |
- // values at millisecond resolution. |
- return duration_ < |
- base::TimeDelta::FromMilliseconds(std::numeric_limits<int32_t>::max()); |
-} |
- |
-void MediaSourcePlayer::Start() { |
- DVLOG(1) << __FUNCTION__; |
- |
- playing_ = true; |
- |
- StartInternal(); |
-} |
- |
-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 |
- // MediaDecoderCallback(). In that case, decoding will continue when |
- // MediaDecoderCallback() is called. |
- playing_ = false; |
- start_time_ticks_ = base::TimeTicks(); |
-} |
- |
-bool MediaSourcePlayer::IsPlaying() { |
- return playing_; |
-} |
- |
-bool MediaSourcePlayer::HasVideo() const { |
- return video_decoder_job_->HasStream(); |
-} |
- |
-bool MediaSourcePlayer::HasAudio() const { |
- return audio_decoder_job_->HasStream(); |
-} |
- |
-int MediaSourcePlayer::GetVideoWidth() { |
- return video_decoder_job_->output_width(); |
-} |
- |
-int MediaSourcePlayer::GetVideoHeight() { |
- return video_decoder_job_->output_height(); |
-} |
- |
-void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) { |
- DVLOG(1) << __FUNCTION__ << "(" << timestamp.InSecondsF() << ")"; |
- |
- if (IsEventPending(SEEK_EVENT_PENDING)) { |
- DCHECK(doing_browser_seek_) << "SeekTo while SeekTo in progress"; |
- DCHECK(!pending_seek_) << "SeekTo while SeekTo pending browser seek"; |
- |
- // There is a browser seek currently in progress to obtain I-frame to feed |
- // a newly constructed video decoder. Remember this real seek request so |
- // it can be initiated once OnDemuxerSeekDone() occurs for the browser seek. |
- pending_seek_ = true; |
- pending_seek_time_ = timestamp; |
- return; |
- } |
- |
- doing_browser_seek_ = false; |
- ScheduleSeekEventAndStopDecoding(timestamp); |
-} |
- |
-base::TimeDelta MediaSourcePlayer::GetCurrentTime() { |
- return std::min(interpolator_.GetInterpolatedTime(), duration_); |
-} |
- |
-base::TimeDelta MediaSourcePlayer::GetDuration() { |
- return duration_; |
-} |
- |
-void MediaSourcePlayer::Release() { |
- DVLOG(1) << __FUNCTION__; |
- |
- media_stat_->StopAndReport(GetCurrentTime()); |
- |
- audio_decoder_job_->ReleaseDecoderResources(); |
- video_decoder_job_->ReleaseDecoderResources(); |
- |
- // Prevent player restart, including job re-creation attempts. |
- playing_ = false; |
- |
- decoder_starvation_callback_.Cancel(); |
- |
- DetachListener(); |
- on_decoder_resources_released_cb_.Run(player_id()); |
-} |
- |
-void MediaSourcePlayer::UpdateEffectiveVolumeInternal(double effective_volume) { |
- audio_decoder_job_->SetVolume(effective_volume); |
-} |
- |
-bool MediaSourcePlayer::CanPause() { |
- return Seekable(); |
-} |
- |
-bool MediaSourcePlayer::CanSeekForward() { |
- return Seekable(); |
-} |
- |
-bool MediaSourcePlayer::CanSeekBackward() { |
- return Seekable(); |
-} |
- |
-bool MediaSourcePlayer::IsPlayerReady() { |
- return HasAudio() || HasVideo(); |
-} |
- |
-void MediaSourcePlayer::StartInternal() { |
- DVLOG(1) << __FUNCTION__; |
- // If there are pending events, wait for them finish. |
- if (pending_event_ != NO_EVENT_PENDING) |
- return; |
- |
- if (!manager()->RequestPlay(player_id(), duration_, HasAudio())) { |
- Pause(true); |
- return; |
- } |
- |
- // When we start, we could have new demuxed data coming in. This new data |
- // could be clear (not encrypted) or encrypted with different keys. So key |
- // related info should all be cleared. |
- is_waiting_for_key_ = false; |
- key_added_while_decode_pending_ = false; |
- AttachListener(nullptr); |
- |
- SetPendingEvent(PREFETCH_REQUEST_EVENT_PENDING); |
- ProcessPendingEvents(); |
-} |
- |
-void MediaSourcePlayer::OnDemuxerConfigsAvailable( |
- const DemuxerConfigs& configs) { |
- DVLOG(1) << __FUNCTION__; |
- DCHECK(!HasAudio() && !HasVideo()); |
- |
- duration_ = configs.duration; |
- |
- audio_decoder_job_->SetDemuxerConfigs(configs); |
- video_decoder_job_->SetDemuxerConfigs(configs); |
- OnDemuxerConfigsChanged(); |
-} |
- |
-void MediaSourcePlayer::OnDemuxerDataAvailable(const DemuxerData& data) { |
- DVLOG(1) << __FUNCTION__ << "(" << data.type << ")"; |
- DCHECK_LT(0u, data.access_units.size()); |
- CHECK_GE(1u, data.demuxer_configs.size()); |
- |
- if (data.type == DemuxerStream::AUDIO) |
- audio_decoder_job_->OnDataReceived(data); |
- else if (data.type == DemuxerStream::VIDEO) |
- video_decoder_job_->OnDataReceived(data); |
-} |
- |
-void MediaSourcePlayer::OnDemuxerDurationChanged(base::TimeDelta duration) { |
- duration_ = duration; |
-} |
- |
-void MediaSourcePlayer::OnMediaCryptoReady( |
- MediaDrmBridge::JavaObjectPtr media_crypto, |
- bool /* needs_protected_surface */) { |
- DCHECK(media_crypto); |
- DCHECK(static_cast<MediaDrmBridge*>(cdm_.get())->GetMediaCrypto()); |
- |
- if (media_crypto->is_null()) { |
- // TODO(xhwang): Fail playback nicely here if needed. Note that we could get |
- // here even though the stream to play is unencrypted and therefore |
- // MediaCrypto is not needed. In that case, we may ignore this error and |
- // continue playback, or fail the playback. |
- LOG(ERROR) << "MediaCrypto creation failed!"; |
- return; |
- } |
- |
- // Retry decoder creation if the decoders are waiting for MediaCrypto. |
- RetryDecoderCreation(true, true); |
-} |
- |
-void MediaSourcePlayer::SetCdm(const scoped_refptr<MediaKeys>& cdm) { |
- DCHECK(cdm); |
- |
- // 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()) { |
- VLOG(0) << "Setting DRM bridge after playback has started. " |
- << "This is not well supported!"; |
- } |
- |
- if (cdm_) { |
- NOTREACHED() << "Currently we do not support resetting CDM."; |
- return; |
- } |
- |
- cdm_ = cdm; |
- |
- // Only MediaDrmBridge will be set on MediaSourcePlayer. |
- MediaDrmBridge* drm_bridge = static_cast<MediaDrmBridge*>(cdm_.get()); |
- |
- // No need to set |cdm_unset_cb| since |this| holds a reference to the |cdm_|. |
- cdm_registration_id_ = drm_bridge->RegisterPlayer( |
- base::Bind(&MediaSourcePlayer::OnKeyAdded, weak_this_), |
- base::Bind(&base::DoNothing)); |
- |
- audio_decoder_job_->SetDrmBridge(drm_bridge); |
- video_decoder_job_->SetDrmBridge(drm_bridge); |
- |
- // Use BindToCurrentLoop to avoid reentrancy. |
- drm_bridge->SetMediaCryptoReadyCB(BindToCurrentLoop( |
- base::Bind(&MediaSourcePlayer::OnMediaCryptoReady, weak_this_))); |
-} |
- |
-void MediaSourcePlayer::OnDemuxerSeekDone( |
- base::TimeDelta actual_browser_seek_time) { |
- DVLOG(1) << __FUNCTION__; |
- |
- ClearPendingEvent(SEEK_EVENT_PENDING); |
- if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING)) |
- ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING); |
- |
- if (pending_seek_) { |
- DVLOG(1) << __FUNCTION__ << "processing pending seek"; |
- DCHECK(doing_browser_seek_); |
- pending_seek_ = false; |
- SeekTo(pending_seek_time_); |
- return; |
- } |
- |
- // It is possible that a browser seek to I-frame had to seek to a buffered |
- // I-frame later than the requested one due to data removal or GC. Update |
- // player clock to the actual seek target. |
- if (doing_browser_seek_) { |
- DCHECK(actual_browser_seek_time != kNoTimestamp); |
- base::TimeDelta seek_time = actual_browser_seek_time; |
- // A browser seek must not jump into the past. Ideally, it seeks to the |
- // requested time, but it might jump into the future. |
- DCHECK(seek_time >= GetCurrentTime()); |
- DVLOG(1) << __FUNCTION__ << " : setting clock to actual browser seek time: " |
- << seek_time.InSecondsF(); |
- interpolator_.SetBounds(seek_time, seek_time, |
- default_tick_clock_.NowTicks()); |
- audio_decoder_job_->SetBaseTimestamp(seek_time); |
- } else { |
- DCHECK(actual_browser_seek_time == kNoTimestamp); |
- } |
- |
- base::TimeDelta current_time = GetCurrentTime(); |
- // TODO(qinmin): Simplify the logic by using |start_presentation_timestamp_| |
- // to preroll media decoder jobs. Currently |start_presentation_timestamp_| |
- // is calculated from decoder output, while preroll relies on the access |
- // unit's timestamp. There are some differences between the two. |
- preroll_timestamp_ = current_time; |
- if (HasAudio()) |
- audio_decoder_job_->BeginPrerolling(preroll_timestamp_); |
- if (HasVideo()) |
- video_decoder_job_->BeginPrerolling(preroll_timestamp_); |
- prerolling_ = true; |
- |
- if (!doing_browser_seek_) |
- manager()->OnSeekComplete(player_id(), current_time); |
- |
- ProcessPendingEvents(); |
-} |
- |
-void MediaSourcePlayer::UpdateTimestamps( |
- base::TimeDelta current_presentation_timestamp, |
- base::TimeDelta max_presentation_timestamp) { |
- base::TimeTicks now_ticks = default_tick_clock_.NowTicks(); |
- interpolator_.SetBounds(current_presentation_timestamp, |
- max_presentation_timestamp, now_ticks); |
- manager()->OnTimeUpdate(player_id(), GetCurrentTime(), now_ticks); |
-} |
- |
-void MediaSourcePlayer::ProcessPendingEvents() { |
- DVLOG(1) << __FUNCTION__ << " : 0x" << std::hex << pending_event_; |
- // Wait for all the decoding jobs to finish before processing pending tasks. |
- if (video_decoder_job_->is_decoding()) { |
- DVLOG(1) << __FUNCTION__ << " : A video job is still decoding."; |
- return; |
- } |
- |
- if (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 (IsEventPending(SEEK_EVENT_PENDING)) { |
- DVLOG(1) << __FUNCTION__ << " : Handling SEEK_EVENT"; |
- ClearDecodingData(); |
- audio_decoder_job_->SetBaseTimestamp(GetCurrentTime()); |
- demuxer_->RequestDemuxerSeek(GetCurrentTime(), doing_browser_seek_); |
- return; |
- } |
- |
- if (IsEventPending(DECODER_CREATION_EVENT_PENDING)) { |
- // Don't continue if one of the decoder is not created. |
- if (is_waiting_for_audio_decoder_ || is_waiting_for_video_decoder_) |
- return; |
- ClearPendingEvent(DECODER_CREATION_EVENT_PENDING); |
- } |
- |
- if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING)) { |
- DVLOG(1) << __FUNCTION__ << " : Handling PREFETCH_REQUEST_EVENT."; |
- |
- int count = (AudioFinished() ? 0 : 1) + (VideoFinished() ? 0 : 1); |
- |
- // It is possible that all streams have finished decode, yet starvation |
- // occurred during the last stream's EOS decode. In this case, prefetch is a |
- // no-op. |
- ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING); |
- if (count == 0) |
- return; |
- |
- SetPendingEvent(PREFETCH_DONE_EVENT_PENDING); |
- base::Closure barrier = BarrierClosure( |
- count, base::Bind(&MediaSourcePlayer::OnPrefetchDone, weak_this_)); |
- |
- if (!AudioFinished()) |
- audio_decoder_job_->Prefetch(barrier); |
- |
- if (!VideoFinished()) |
- video_decoder_job_->Prefetch(barrier); |
- |
- 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, |
- MediaCodecStatus status, |
- bool is_late_frame, |
- base::TimeDelta current_presentation_timestamp, |
- base::TimeDelta max_presentation_timestamp) { |
- DVLOG(1) << __FUNCTION__ << ": " << is_audio << ", " << status; |
- |
- // TODO(xhwang): Drop IntToString() when http://crbug.com/303899 is fixed. |
- if (is_audio) { |
- TRACE_EVENT_ASYNC_END1("media", |
- "MediaSourcePlayer::DecodeMoreAudio", |
- audio_decoder_job_.get(), |
- "MediaCodecStatus", |
- base::IntToString(status)); |
- } else { |
- TRACE_EVENT_ASYNC_END1("media", |
- "MediaSourcePlayer::DecodeMoreVideo", |
- video_decoder_job_.get(), |
- "MediaCodecStatus", |
- base::IntToString(status)); |
- } |
- |
- // Let tests hook the completion of this decode cycle. |
- if (!decode_callback_for_testing_.is_null()) |
- base::ResetAndReturn(&decode_callback_for_testing_).Run(); |
- |
- bool is_clock_manager = is_audio || !HasAudio(); |
- |
- if (is_clock_manager) |
- decoder_starvation_callback_.Cancel(); |
- |
- if (status == MEDIA_CODEC_ERROR) { |
- DVLOG(1) << __FUNCTION__ << " : decode error"; |
- Release(); |
- manager()->OnError(player_id(), MEDIA_ERROR_DECODE); |
- if (is_clock_manager) |
- media_stat_->StopAndReport(GetCurrentTime()); |
- return; |
- } |
- |
- // Increment frame counts for UMA. |
- if (current_presentation_timestamp != kNoTimestamp) { |
- FrameStatistics& frame_stats = is_audio ? media_stat_->audio_frame_stats() |
- : media_stat_->video_frame_stats(); |
- frame_stats.IncrementFrameCount(); |
- if (is_late_frame) |
- frame_stats.IncrementLateFrameCount(); |
- } |
- |
- DCHECK(!IsEventPending(PREFETCH_DONE_EVENT_PENDING)); |
- |
- // Let |SEEK_EVENT_PENDING| (the highest priority event outside of |
- // |PREFETCH_DONE_EVENT_PENDING|) preempt output EOS detection here. Process |
- // any other pending events only after handling EOS detection. |
- if (IsEventPending(SEEK_EVENT_PENDING)) { |
- ProcessPendingEvents(); |
- // In case of Seek GetCurrentTime() already tells the time to seek to. |
- if (is_clock_manager && !doing_browser_seek_) |
- media_stat_->StopAndReport(current_presentation_timestamp); |
- return; |
- } |
- |
- if ((status == MEDIA_CODEC_OK || status == MEDIA_CODEC_INPUT_END_OF_STREAM) && |
- is_clock_manager && current_presentation_timestamp != kNoTimestamp) { |
- UpdateTimestamps(current_presentation_timestamp, |
- max_presentation_timestamp); |
- } |
- |
- if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM) { |
- PlaybackCompleted(is_audio); |
- if (is_clock_manager) |
- interpolator_.StopInterpolating(); |
- } |
- |
- if (pending_event_ != NO_EVENT_PENDING) { |
- ProcessPendingEvents(); |
- return; |
- } |
- |
- if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM) { |
- if (is_clock_manager) |
- media_stat_->StopAndReport(GetCurrentTime()); |
- return; |
- } |
- |
- if (!playing_) { |
- if (is_clock_manager) |
- interpolator_.StopInterpolating(); |
- |
- if (is_clock_manager) |
- media_stat_->StopAndReport(GetCurrentTime()); |
- return; |
- } |
- |
- if (status == MEDIA_CODEC_NO_KEY) { |
- if (key_added_while_decode_pending_) { |
- DVLOG(2) << __FUNCTION__ << ": Key was added during decoding."; |
- ResumePlaybackAfterKeyAdded(); |
- } else { |
- is_waiting_for_key_ = true; |
- manager()->OnWaitingForDecryptionKey(player_id()); |
- if (is_clock_manager) |
- media_stat_->StopAndReport(GetCurrentTime()); |
- } |
- return; |
- } |
- |
- // If |key_added_while_decode_pending_| is true and both audio and video |
- // decoding succeeded, we should clear |key_added_while_decode_pending_| here. |
- // But that would add more complexity into this function. If we don't clear it |
- // here, the worst case would be we call ResumePlaybackAfterKeyAdded() when |
- // we don't really have a new key. This should rarely happen and the |
- // performance impact should be pretty small. |
- // TODO(qinmin/xhwang): This class is complicated because we handle both audio |
- // and video in one file. If we separate them, we should be able to remove a |
- // lot of duplication. |
- |
- // If the status is MEDIA_CODEC_ABORT, 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_ABORT) { |
- return; |
- } |
- |
- if (prerolling_ && IsPrerollFinished(is_audio)) { |
- if (IsPrerollFinished(!is_audio)) { |
- prerolling_ = false; |
- StartInternal(); |
- } |
- return; |
- } |
- |
- if (is_clock_manager) { |
- // If we have a valid timestamp, start the starvation callback. Otherwise, |
- // reset the |start_time_ticks_| so that the next frame will not suffer |
- // from the decoding delay caused by the current frame. |
- if (current_presentation_timestamp != kNoTimestamp) |
- StartStarvationCallback(current_presentation_timestamp, |
- max_presentation_timestamp); |
- else |
- start_time_ticks_ = base::TimeTicks::Now(); |
- } |
- |
- if (is_audio) |
- DecodeMoreAudio(); |
- else |
- DecodeMoreVideo(); |
-} |
- |
-bool MediaSourcePlayer::IsPrerollFinished(bool is_audio) const { |
- if (is_audio) |
- return !HasAudio() || !audio_decoder_job_->prerolling(); |
- return !HasVideo() || !video_decoder_job_->prerolling(); |
-} |
- |
-void MediaSourcePlayer::DecodeMoreAudio() { |
- DVLOG(1) << __FUNCTION__; |
- DCHECK(!audio_decoder_job_->is_decoding()); |
- DCHECK(!AudioFinished()); |
- |
- MediaDecoderJob::MediaDecoderJobStatus status = audio_decoder_job_->Decode( |
- start_time_ticks_, |
- start_presentation_timestamp_, |
- base::Bind(&MediaSourcePlayer::MediaDecoderCallback, weak_this_, true)); |
- |
- switch (status) { |
- case MediaDecoderJob::STATUS_SUCCESS: |
- TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSourcePlayer::DecodeMoreAudio", |
- audio_decoder_job_.get()); |
- break; |
- case MediaDecoderJob::STATUS_KEY_FRAME_REQUIRED: |
- NOTREACHED(); |
- break; |
- case MediaDecoderJob::STATUS_FAILURE: |
- is_waiting_for_audio_decoder_ = true; |
- if (!IsEventPending(DECODER_CREATION_EVENT_PENDING)) |
- SetPendingEvent(DECODER_CREATION_EVENT_PENDING); |
- break; |
- } |
-} |
- |
-void MediaSourcePlayer::DecodeMoreVideo() { |
- DVLOG(1) << __FUNCTION__; |
- DCHECK(!video_decoder_job_->is_decoding()); |
- DCHECK(!VideoFinished()); |
- |
- MediaDecoderJob::MediaDecoderJobStatus status = video_decoder_job_->Decode( |
- start_time_ticks_, |
- start_presentation_timestamp_, |
- base::Bind(&MediaSourcePlayer::MediaDecoderCallback, weak_this_, |
- false)); |
- |
- switch (status) { |
- case MediaDecoderJob::STATUS_SUCCESS: |
- TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSourcePlayer::DecodeMoreVideo", |
- video_decoder_job_.get()); |
- break; |
- case MediaDecoderJob::STATUS_KEY_FRAME_REQUIRED: |
- BrowserSeekToCurrentTime(); |
- break; |
- case MediaDecoderJob::STATUS_FAILURE: |
- is_waiting_for_video_decoder_ = true; |
- if (!IsEventPending(DECODER_CREATION_EVENT_PENDING)) |
- SetPendingEvent(DECODER_CREATION_EVENT_PENDING); |
- break; |
- } |
-} |
- |
-void MediaSourcePlayer::PlaybackCompleted(bool is_audio) { |
- DVLOG(1) << __FUNCTION__ << "(" << is_audio << ")"; |
- |
- if (AudioFinished() && VideoFinished()) { |
- playing_ = false; |
- start_time_ticks_ = base::TimeTicks(); |
- manager()->OnPlaybackComplete(player_id()); |
- } |
-} |
- |
-void MediaSourcePlayer::ClearDecodingData() { |
- DVLOG(1) << __FUNCTION__; |
- audio_decoder_job_->Flush(); |
- video_decoder_job_->Flush(); |
- start_time_ticks_ = base::TimeTicks(); |
-} |
- |
-bool MediaSourcePlayer::AudioFinished() { |
- return audio_decoder_job_->OutputEOSReached() || !HasAudio(); |
-} |
- |
-bool MediaSourcePlayer::VideoFinished() { |
- return video_decoder_job_->OutputEOSReached() || !HasVideo(); |
-} |
- |
-void MediaSourcePlayer::OnDecoderStarved() { |
- DVLOG(1) << __FUNCTION__; |
- |
- media_stat_->AddStarvation(); |
- |
- SetPendingEvent(PREFETCH_REQUEST_EVENT_PENDING); |
- ProcessPendingEvents(); |
-} |
- |
-void MediaSourcePlayer::StartStarvationCallback( |
- base::TimeDelta current_presentation_timestamp, |
- base::TimeDelta max_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 = max_presentation_timestamp - current_timestamp; |
- } else { |
- DCHECK(current_timestamp <= current_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 * (current_presentation_timestamp - current_timestamp); |
- } |
- |
- timeout = std::max(timeout, kMinStarvationTimeout); |
- |
- decoder_starvation_callback_.Reset( |
- base::Bind(&MediaSourcePlayer::OnDecoderStarved, weak_this_)); |
- base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
- FROM_HERE, decoder_starvation_callback_.callback(), timeout); |
-} |
- |
-void MediaSourcePlayer::OnPrefetchDone() { |
- DVLOG(1) << __FUNCTION__; |
- DCHECK(!audio_decoder_job_->is_decoding()); |
- DCHECK(!video_decoder_job_->is_decoding()); |
- |
- // A previously posted OnPrefetchDone() could race against a Release(). If |
- // Release() won the race, we should no longer have decoder jobs. |
- // TODO(qinmin/wolenetz): Maintain channel state to not double-request data |
- // or drop data received across Release()+Start(). See http://crbug.com/306314 |
- // and http://crbug.com/304234. |
- if (!IsEventPending(PREFETCH_DONE_EVENT_PENDING)) { |
- DVLOG(1) << __FUNCTION__ << " : aborting"; |
- return; |
- } |
- |
- ClearPendingEvent(PREFETCH_DONE_EVENT_PENDING); |
- |
- if (pending_event_ != NO_EVENT_PENDING) { |
- ProcessPendingEvents(); |
- return; |
- } |
- |
- if (!playing_) |
- return; |
- |
- start_time_ticks_ = base::TimeTicks::Now(); |
- start_presentation_timestamp_ = GetCurrentTime(); |
- if (!interpolator_.interpolating()) |
- interpolator_.StartInterpolating(); |
- |
- if (!AudioFinished() || !VideoFinished()) |
- media_stat_->Start(start_presentation_timestamp_); |
- |
- if (!AudioFinished()) |
- DecodeMoreAudio(); |
- |
- if (!VideoFinished()) |
- DecodeMoreVideo(); |
-} |
- |
-void MediaSourcePlayer::OnDemuxerConfigsChanged() { |
- manager()->OnMediaMetadataChanged( |
- player_id(), duration_, GetVideoWidth(), GetVideoHeight(), true); |
-} |
- |
-const char* MediaSourcePlayer::GetEventName(PendingEventFlags event) { |
- // Please keep this in sync with PendingEventFlags. |
- static const char* kPendingEventNames[] = { |
- "PREFETCH_DONE", |
- "SEEK", |
- "DECODER_CREATION", |
- "PREFETCH_REQUEST", |
- }; |
- |
- int mask = 1; |
- for (size_t i = 0; i < arraysize(kPendingEventNames); ++i, mask <<= 1) { |
- if (event & mask) |
- return kPendingEventNames[i]; |
- } |
- |
- return "UNKNOWN"; |
-} |
- |
-bool MediaSourcePlayer::IsEventPending(PendingEventFlags event) const { |
- return pending_event_ & event; |
-} |
- |
-void MediaSourcePlayer::SetPendingEvent(PendingEventFlags event) { |
- DVLOG(1) << __FUNCTION__ << "(" << GetEventName(event) << ")"; |
- DCHECK_NE(event, NO_EVENT_PENDING); |
- DCHECK(!IsEventPending(event)); |
- |
- pending_event_ |= event; |
-} |
- |
-void MediaSourcePlayer::ClearPendingEvent(PendingEventFlags event) { |
- DVLOG(1) << __FUNCTION__ << "(" << GetEventName(event) << ")"; |
- DCHECK_NE(event, NO_EVENT_PENDING); |
- DCHECK(IsEventPending(event)) << GetEventName(event); |
- |
- pending_event_ &= ~event; |
-} |
- |
-void MediaSourcePlayer::RetryDecoderCreation(bool audio, bool video) { |
- if (audio) |
- is_waiting_for_audio_decoder_ = false; |
- if (video) |
- is_waiting_for_video_decoder_ = false; |
- if (IsEventPending(DECODER_CREATION_EVENT_PENDING)) |
- ProcessPendingEvents(); |
-} |
- |
-void MediaSourcePlayer::OnKeyAdded() { |
- DVLOG(1) << __FUNCTION__; |
- |
- if (is_waiting_for_key_) { |
- ResumePlaybackAfterKeyAdded(); |
- return; |
- } |
- |
- if ((audio_decoder_job_->is_content_encrypted() && |
- audio_decoder_job_->is_decoding()) || |
- (video_decoder_job_->is_content_encrypted() && |
- video_decoder_job_->is_decoding())) { |
- DVLOG(1) << __FUNCTION__ << ": " << "Key added during pending decode."; |
- key_added_while_decode_pending_ = true; |
- } |
-} |
- |
-void MediaSourcePlayer::ResumePlaybackAfterKeyAdded() { |
- DVLOG(1) << __FUNCTION__; |
- DCHECK(is_waiting_for_key_ || key_added_while_decode_pending_); |
- |
- is_waiting_for_key_ = false; |
- key_added_while_decode_pending_ = false; |
- |
- // StartInternal() will trigger a prefetch, where in most cases we'll just |
- // use previously received data. |
- if (playing_) |
- StartInternal(); |
-} |
- |
-} // namespace media |