summaryrefslogtreecommitdiffstats
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.cc629
1 files changed, 199 insertions, 430 deletions
diff --git a/chromium/media/base/android/media_source_player.cc b/chromium/media/base/android/media_source_player.cc
index ee84528a87c..f065a5c9347 100644
--- a/chromium/media/base/android/media_source_player.cc
+++ b/chromium/media/base/android/media_source_player.cc
@@ -19,131 +19,82 @@
#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"
-#include "media/base/buffers.h"
-namespace {
-
-// 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;
-}
namespace media {
-// 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;
- }
- }
-
- return true;
-}
-
MediaSourcePlayer::MediaSourcePlayer(
int player_id,
MediaPlayerManager* manager,
- scoped_ptr<DemuxerAndroid> demuxer)
- : MediaPlayerAndroid(player_id, manager),
+ const RequestMediaResourcesCB& request_media_resources_cb,
+ const ReleaseMediaResourcesCB& release_media_resources_cb,
+ scoped_ptr<DemuxerAndroid> demuxer,
+ const GURL& frame_url)
+ : MediaPlayerAndroid(player_id,
+ manager,
+ request_media_resources_cb,
+ release_media_resources_cb,
+ frame_url),
demuxer_(demuxer.Pass()),
pending_event_(NO_EVENT_PENDING),
- width_(0),
- height_(0),
- audio_codec_(kUnknownAudioCodec),
- video_codec_(kUnknownVideoCodec),
- num_channels_(0),
- sampling_rate_(0),
- reached_audio_eos_(false),
- reached_video_eos_(false),
playing_(false),
- is_audio_encrypted_(false),
- is_video_encrypted_(false),
- volume_(-1.0),
clock_(&default_tick_clock_),
- next_video_data_is_iframe_(true),
doing_browser_seek_(false),
pending_seek_(false),
- reconfig_audio_decoder_(false),
- reconfig_video_decoder_(false),
- weak_this_(this),
drm_bridge_(NULL),
- is_waiting_for_key_(false) {
+ cdm_registration_id_(0),
+ is_waiting_for_key_(false),
+ is_waiting_for_audio_decoder_(false),
+ is_waiting_for_video_decoder_(false),
+ weak_factory_(this) {
+ 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(request_media_resources_cb_, player_id),
+ base::Bind(release_media_resources_cb_, player_id),
+ base::Bind(&MediaSourcePlayer::OnDemuxerConfigsChanged,
+ weak_factory_.GetWeakPtr())));
demuxer_->Initialize(this);
clock_.SetMaxTime(base::TimeDelta());
+ weak_this_ = weak_factory_.GetWeakPtr();
}
MediaSourcePlayer::~MediaSourcePlayer() {
Release();
+ DCHECK_EQ(!drm_bridge_, !cdm_registration_id_);
+ if (drm_bridge_) {
+ drm_bridge_->UnregisterPlayer(cdm_registration_id_);
+ cdm_registration_id_ = 0;
+ }
}
void MediaSourcePlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) {
- // 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();
-
- // If there is a pending surface change event, just wait for it to be
- // processed.
- if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING))
- return;
-
- // Eventual processing of surface change will take care of feeding the new
- // video decoder initially with I-frame. See b/8950387.
- SetPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
-
- // If seek is already pending, processing of the pending surface change
- // event will occur in OnDemuxerSeekDone().
- if (IsEventPending(SEEK_EVENT_PENDING))
- return;
-
- // If video config change is already pending, processing of the pending
- // surface change event will occur in OnDemuxerConfigsAvailable().
- if (reconfig_video_decoder_ && IsEventPending(CONFIG_CHANGE_EVENT_PENDING))
+ DVLOG(1) << __FUNCTION__;
+ if (!video_decoder_job_->SetVideoSurface(surface.Pass()))
return;
-
- // Otherwise we need to trigger pending event processing now.
- ProcessPendingEvents();
+ // Retry video decoder creation.
+ RetryDecoderCreation(false, true);
}
void MediaSourcePlayer::ScheduleSeekEventAndStopDecoding(
- const base::TimeDelta& seek_time) {
+ base::TimeDelta seek_time) {
DVLOG(1) << __FUNCTION__ << "(" << seek_time.InSecondsF() << ")";
DCHECK(!IsEventPending(SEEK_EVENT_PENDING));
pending_seek_ = false;
clock_.SetTime(seek_time, seek_time);
- if (audio_timestamp_helper_)
- audio_timestamp_helper_->SetBaseTimestamp(seek_time);
- if (audio_decoder_job_ && audio_decoder_job_->is_decoding())
+ if (audio_decoder_job_->is_decoding())
audio_decoder_job_->StopDecode();
- if (video_decoder_job_ && video_decoder_job_->is_decoding())
+ if (video_decoder_job_->is_decoding())
video_decoder_job_->StopDecode();
SetPendingEvent(SEEK_EVENT_PENDING);
@@ -172,8 +123,14 @@ void MediaSourcePlayer::Start() {
playing_ = true;
- if (IsProtectedSurfaceRequired())
- manager()->OnProtectedSurfaceRequested(player_id());
+ bool request_fullscreen = IsProtectedSurfaceRequired();
+#if defined(VIDEO_HOLE)
+ // Skip to request fullscreen when hole-punching is used.
+ request_fullscreen = request_fullscreen &&
+ !manager()->ShouldUseVideoOverlayForEmbeddedEncryptedVideo();
+#endif // defined(VIDEO_HOLE)
+ if (request_fullscreen)
+ manager()->RequestFullScreen(player_id());
StartInternal();
}
@@ -195,14 +152,14 @@ bool MediaSourcePlayer::IsPlaying() {
}
int MediaSourcePlayer::GetVideoWidth() {
- return width_;
+ return video_decoder_job_->width();
}
int MediaSourcePlayer::GetVideoHeight() {
- return height_;
+ return video_decoder_job_->height();
}
-void MediaSourcePlayer::SeekTo(const base::TimeDelta& timestamp) {
+void MediaSourcePlayer::SeekTo(base::TimeDelta timestamp) {
DVLOG(1) << __FUNCTION__ << "(" << timestamp.InSecondsF() << ")";
if (IsEventPending(SEEK_EVENT_PENDING)) {
@@ -232,53 +189,22 @@ base::TimeDelta MediaSourcePlayer::GetDuration() {
void MediaSourcePlayer::Release() {
DVLOG(1) << __FUNCTION__;
- // Allow pending seeks and config changes to survive this Release().
- // If previously pending a prefetch done event, or a job was still decoding,
- // then at end of Release() we need to ProcessPendingEvents() to process any
- // seek or config change that was blocked by the prefetch or decode.
- // 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.
- bool process_pending_events = false;
- process_pending_events = IsEventPending(PREFETCH_DONE_EVENT_PENDING) ||
- (audio_decoder_job_ && audio_decoder_job_->is_decoding()) ||
- (video_decoder_job_ && video_decoder_job_->is_decoding());
-
- // Clear all the pending events except seeks and config changes.
- pending_event_ &= (SEEK_EVENT_PENDING | CONFIG_CHANGE_EVENT_PENDING);
-
- audio_decoder_job_.reset();
- ResetVideoDecoderJob();
-
- // Prevent job re-creation attempts in OnDemuxerConfigsAvailable()
- reconfig_audio_decoder_ = false;
- reconfig_video_decoder_ = false;
+ is_surface_in_use_ = false;
+ audio_decoder_job_->ReleaseDecoderResources();
+ video_decoder_job_->ReleaseDecoderResources();
// Prevent player restart, including job re-creation attempts.
playing_ = false;
decoder_starvation_callback_.Cancel();
- surface_ = gfx::ScopedJavaSurface();
- manager()->ReleaseMediaResources(player_id());
- if (process_pending_events) {
- DVLOG(1) << __FUNCTION__ << " : Resuming seek or config change processing";
- ProcessPendingEvents();
- }
}
void MediaSourcePlayer::SetVolume(double volume) {
- volume_ = volume;
- SetVolumeInternal();
+ audio_decoder_job_->SetVolume(volume);
}
-void MediaSourcePlayer::OnKeyAdded() {
- DVLOG(1) << __FUNCTION__;
- if (!is_waiting_for_key_)
- return;
-
- is_waiting_for_key_ = false;
- if (playing_)
- StartInternal();
+bool MediaSourcePlayer::IsSurfaceInUse() const {
+ return is_surface_in_use_;
}
bool MediaSourcePlayer::CanPause() {
@@ -308,16 +234,6 @@ void MediaSourcePlayer::StartInternal() {
// |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_)) {
- return;
- }
-
SetPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
ProcessPendingEvents();
}
@@ -325,55 +241,24 @@ void MediaSourcePlayer::StartInternal() {
void MediaSourcePlayer::OnDemuxerConfigsAvailable(
const DemuxerConfigs& configs) {
DVLOG(1) << __FUNCTION__;
- duration_ = base::TimeDelta::FromMilliseconds(configs.duration_ms);
+ DCHECK(!HasAudio() && !HasVideo());
+ duration_ = configs.duration;
clock_.SetDuration(duration_);
- 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_));
- audio_timestamp_helper_->SetBaseTimestamp(GetCurrentTime());
- } else {
- audio_timestamp_helper_.reset();
- }
-
- video_codec_ = configs.video_codec;
- width_ = configs.video_size.width();
- height_ = configs.video_size.height();
- is_video_encrypted_ = configs.is_video_encrypted;
-
- manager()->OnMediaMetadataChanged(
- player_id(), duration_, width_, height_, true);
-
- if (IsEventPending(CONFIG_CHANGE_EVENT_PENDING)) {
- if (reconfig_audio_decoder_)
- ConfigureAudioDecoderJob();
-
- if (reconfig_video_decoder_)
- ConfigureVideoDecoderJob();
-
- ClearPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
-
- // Resume decoding after the config change if we are still playing.
- if (playing_)
- StartInternal();
- }
+ 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());
- if (data.type == DemuxerStream::AUDIO && audio_decoder_job_) {
+ CHECK_GE(1u, data.demuxer_configs.size());
+
+ if (data.type == DemuxerStream::AUDIO)
audio_decoder_job_->OnDataReceived(data);
- } else if (data.type == DemuxerStream::VIDEO) {
- next_video_data_is_iframe_ = false;
- if (video_decoder_job_)
- video_decoder_job_->OnDataReceived(data);
- }
+ else if (data.type == DemuxerStream::VIDEO)
+ video_decoder_job_->OnDataReceived(data);
}
void MediaSourcePlayer::OnDemuxerDurationChanged(base::TimeDelta duration) {
@@ -381,22 +266,15 @@ void MediaSourcePlayer::OnDemuxerDurationChanged(base::TimeDelta 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();
+ // Retry decoder creation if the decoders are waiting for MediaCrypto.
+ RetryDecoderCreation(true, true);
}
-void MediaSourcePlayer::SetDrmBridge(MediaDrmBridge* drm_bridge) {
+void MediaSourcePlayer::SetCdm(BrowserCdm* 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.
@@ -406,28 +284,39 @@ void MediaSourcePlayer::SetDrmBridge(MediaDrmBridge* drm_bridge) {
<< "This is not well supported!";
}
- drm_bridge_ = drm_bridge;
+ if (drm_bridge_) {
+ NOTREACHED() << "Currently we do not support resetting CDM.";
+ return;
+ }
+
+ // Only MediaDrmBridge will be set on MediaSourcePlayer.
+ drm_bridge_ = static_cast<MediaDrmBridge*>(cdm);
+
+ cdm_registration_id_ = drm_bridge_->RegisterPlayer(
+ base::Bind(&MediaSourcePlayer::OnKeyAdded, weak_this_),
+ base::Bind(&MediaSourcePlayer::OnCdmUnset, weak_this_));
+
+ audio_decoder_job_->SetDrmBridge(drm_bridge_);
+ video_decoder_job_->SetDrmBridge(drm_bridge_);
if (drm_bridge_->GetMediaCrypto().is_null()) {
- drm_bridge_->SetMediaCryptoReadyCB(base::Bind(
- &MediaSourcePlayer::OnMediaCryptoReady, weak_this_.GetWeakPtr()));
+ drm_bridge_->SetMediaCryptoReadyCB(
+ base::Bind(&MediaSourcePlayer::OnMediaCryptoReady, weak_this_));
return;
}
- if (playing_)
- StartInternal();
+ // If the player is previously waiting for CDM, retry decoder creation.
+ RetryDecoderCreation(true, true);
}
void MediaSourcePlayer::OnDemuxerSeekDone(
- const base::TimeDelta& actual_browser_seek_time) {
+ base::TimeDelta actual_browser_seek_time) {
DVLOG(1) << __FUNCTION__;
ClearPendingEvent(SEEK_EVENT_PENDING);
if (IsEventPending(PREFETCH_REQUEST_EVENT_PENDING))
ClearPendingEvent(PREFETCH_REQUEST_EVENT_PENDING);
- next_video_data_is_iframe_ = true;
-
if (pending_seek_) {
DVLOG(1) << __FUNCTION__ << "processing pending seek";
DCHECK(doing_browser_seek_);
@@ -441,28 +330,27 @@ void MediaSourcePlayer::OnDemuxerSeekDone(
// 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(actual_browser_seek_time >= GetCurrentTime());
+ DCHECK(seek_time >= GetCurrentTime());
DVLOG(1) << __FUNCTION__ << " : setting clock to actual browser seek time: "
- << actual_browser_seek_time.InSecondsF();
- clock_.SetTime(actual_browser_seek_time, actual_browser_seek_time);
- if (audio_timestamp_helper_)
- audio_timestamp_helper_->SetBaseTimestamp(actual_browser_seek_time);
+ << seek_time.InSecondsF();
+ clock_.SetTime(seek_time, seek_time);
+ audio_decoder_job_->SetBaseTimestamp(seek_time);
+ } else {
+ DCHECK(actual_browser_seek_time == kNoTimestamp());
}
- reached_audio_eos_ = false;
- reached_video_eos_ = false;
-
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 (audio_decoder_job_)
+ if (HasAudio())
audio_decoder_job_->BeginPrerolling(preroll_timestamp_);
- if (video_decoder_job_)
+ if (HasVideo())
video_decoder_job_->BeginPrerolling(preroll_timestamp_);
if (!doing_browser_seek_)
@@ -472,28 +360,21 @@ void MediaSourcePlayer::OnDemuxerSeekDone(
}
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_));
- new_max_time = audio_timestamp_helper_->GetTimestamp();
- }
-
- clock_.SetMaxTime(new_max_time);
+ base::TimeDelta current_presentation_timestamp,
+ base::TimeDelta max_presentation_timestamp) {
+ clock_.SetTime(current_presentation_timestamp, max_presentation_timestamp);
manager()->OnTimeUpdate(player_id(), GetCurrentTime());
}
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_ && video_decoder_job_->is_decoding()) {
+ if (video_decoder_job_->is_decoding()) {
DVLOG(1) << __FUNCTION__ << " : A video job is still decoding.";
return;
}
- if (audio_decoder_job_ && audio_decoder_job_->is_decoding()) {
+ if (audio_decoder_job_->is_decoding()) {
DVLOG(1) << __FUNCTION__ << " : An audio job is still decoding.";
return;
}
@@ -506,36 +387,20 @@ void MediaSourcePlayer::ProcessPendingEvents() {
if (IsEventPending(SEEK_EVENT_PENDING)) {
DVLOG(1) << __FUNCTION__ << " : Handling SEEK_EVENT";
ClearDecodingData();
+ audio_decoder_job_->SetBaseTimestamp(GetCurrentTime());
demuxer_->RequestDemuxerSeek(GetCurrentTime(), doing_browser_seek_);
return;
}
- start_time_ticks_ = base::TimeTicks();
- if (IsEventPending(CONFIG_CHANGE_EVENT_PENDING)) {
- DVLOG(1) << __FUNCTION__ << " : Handling CONFIG_CHANGE_EVENT.";
- DCHECK(reconfig_audio_decoder_ || reconfig_video_decoder_);
- demuxer_->RequestDemuxerConfigs();
- return;
- }
-
- if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING)) {
- DVLOG(1) << __FUNCTION__ << " : Handling SURFACE_CHANGE_EVENT.";
- // Setting a new surface will require a new MediaCodec to be created.
- ResetVideoDecoderJob();
- ConfigureVideoDecoderJob();
-
- // Return early if we can't successfully configure a new video decoder job
- // yet, except continue processing other pending events if |surface_| is
- // empty.
- if (HasVideo() && !video_decoder_job_ && !surface_.IsEmpty())
+ 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.";
- DCHECK(audio_decoder_job_ || AudioFinished());
- DCHECK(video_decoder_job_ || VideoFinished());
-
int count = (AudioFinished() ? 0 : 1) + (VideoFinished() ? 0 : 1);
// It is possible that all streams have finished decode, yet starvation
@@ -546,8 +411,8 @@ void MediaSourcePlayer::ProcessPendingEvents() {
return;
SetPendingEvent(PREFETCH_DONE_EVENT_PENDING);
- base::Closure barrier = BarrierClosure(count, base::Bind(
- &MediaSourcePlayer::OnPrefetchDone, weak_this_.GetWeakPtr()));
+ base::Closure barrier = BarrierClosure(
+ count, base::Bind(&MediaSourcePlayer::OnPrefetchDone, weak_this_));
if (!AudioFinished())
audio_decoder_job_->Prefetch(barrier);
@@ -568,7 +433,8 @@ void MediaSourcePlayer::ProcessPendingEvents() {
void MediaSourcePlayer::MediaDecoderCallback(
bool is_audio, MediaCodecStatus status,
- const base::TimeDelta& presentation_timestamp, size_t audio_output_bytes) {
+ 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.
@@ -612,6 +478,12 @@ void MediaSourcePlayer::MediaDecoderCallback(
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);
@@ -623,11 +495,6 @@ void MediaSourcePlayer::MediaDecoderCallback(
if (status == MEDIA_CODEC_OUTPUT_END_OF_STREAM)
return;
- if (status == MEDIA_CODEC_OK && is_clock_manager &&
- presentation_timestamp != kNoTimestamp()) {
- UpdateTimestamps(presentation_timestamp, audio_output_bytes);
- }
-
if (!playing_) {
if (is_clock_manager)
clock_.Pause();
@@ -649,18 +516,17 @@ void MediaSourcePlayer::MediaDecoderCallback(
// 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 (presentation_timestamp != kNoTimestamp())
- StartStarvationCallback(presentation_timestamp);
+ if (current_presentation_timestamp != kNoTimestamp())
+ StartStarvationCallback(current_presentation_timestamp,
+ max_presentation_timestamp);
else
start_time_ticks_ = base::TimeTicks::Now();
}
- if (is_audio) {
+ if (is_audio)
DecodeMoreAudio();
- return;
- }
-
- DecodeMoreVideo();
+ else
+ DecodeMoreVideo();
}
void MediaSourcePlayer::DecodeMoreAudio() {
@@ -669,28 +535,17 @@ void MediaSourcePlayer::DecodeMoreAudio() {
DCHECK(!AudioFinished());
if (audio_decoder_job_->Decode(
- start_time_ticks_, start_presentation_timestamp_, base::Bind(
- &MediaSourcePlayer::MediaDecoderCallback,
- weak_this_.GetWeakPtr(), true))) {
+ start_time_ticks_,
+ start_presentation_timestamp_,
+ base::Bind(&MediaSourcePlayer::MediaDecoderCallback, weak_this_, true))) {
TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSourcePlayer::DecodeMoreAudio",
audio_decoder_job_.get());
return;
}
- // Failed to start the next decode.
- // Wait for demuxer ready message.
- DCHECK(!reconfig_audio_decoder_);
- reconfig_audio_decoder_ = true;
-
- // Config change may have just been detected on the other stream. If so,
- // don't send a duplicate demuxer config request.
- if (IsEventPending(CONFIG_CHANGE_EVENT_PENDING)) {
- DCHECK(reconfig_video_decoder_);
- return;
- }
-
- SetPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
- ProcessPendingEvents();
+ is_waiting_for_audio_decoder_ = true;
+ if (!IsEventPending(DECODER_CREATION_EVENT_PENDING))
+ SetPendingEvent(DECODER_CREATION_EVENT_PENDING);
}
void MediaSourcePlayer::DecodeMoreVideo() {
@@ -699,41 +554,28 @@ void MediaSourcePlayer::DecodeMoreVideo() {
DCHECK(!VideoFinished());
if (video_decoder_job_->Decode(
- start_time_ticks_, start_presentation_timestamp_, base::Bind(
- &MediaSourcePlayer::MediaDecoderCallback,
- weak_this_.GetWeakPtr(), false))) {
+ start_time_ticks_,
+ start_presentation_timestamp_,
+ base::Bind(&MediaSourcePlayer::MediaDecoderCallback, weak_this_,
+ false))) {
TRACE_EVENT_ASYNC_BEGIN0("media", "MediaSourcePlayer::DecodeMoreVideo",
video_decoder_job_.get());
return;
}
- // Failed to start the next decode.
- // Wait for demuxer ready message.
-
- // After this detection of video config change, next video data received
- // will begin with I-frame.
- next_video_data_is_iframe_ = true;
-
- DCHECK(!reconfig_video_decoder_);
- reconfig_video_decoder_ = true;
-
- // Config change may have just been detected on the other stream. If so,
- // don't send a duplicate demuxer config request.
- if (IsEventPending(CONFIG_CHANGE_EVENT_PENDING)) {
- DCHECK(reconfig_audio_decoder_);
+ // If the decoder is waiting for iframe, trigger a browser seek.
+ if (!video_decoder_job_->next_video_data_is_iframe()) {
+ BrowserSeekToCurrentTime();
return;
}
- SetPendingEvent(CONFIG_CHANGE_EVENT_PENDING);
- ProcessPendingEvents();
+ is_waiting_for_video_decoder_ = true;
+ if (!IsEventPending(DECODER_CREATION_EVENT_PENDING))
+ SetPendingEvent(DECODER_CREATION_EVENT_PENDING);
}
void MediaSourcePlayer::PlaybackCompleted(bool is_audio) {
DVLOG(1) << __FUNCTION__ << "(" << is_audio << ")";
- if (is_audio)
- reached_audio_eos_ = true;
- else
- reached_video_eos_ = true;
if (AudioFinished() && VideoFinished()) {
playing_ = false;
@@ -745,132 +587,25 @@ void MediaSourcePlayer::PlaybackCompleted(bool is_audio) {
void MediaSourcePlayer::ClearDecodingData() {
DVLOG(1) << __FUNCTION__;
- if (audio_decoder_job_)
- audio_decoder_job_->Flush();
- if (video_decoder_job_)
- video_decoder_job_->Flush();
+ audio_decoder_job_->Flush();
+ video_decoder_job_->Flush();
start_time_ticks_ = base::TimeTicks();
}
bool MediaSourcePlayer::HasVideo() {
- return kUnknownVideoCodec != video_codec_;
+ return video_decoder_job_->HasStream();
}
bool MediaSourcePlayer::HasAudio() {
- return kUnknownAudioCodec != audio_codec_;
+ return audio_decoder_job_->HasStream();
}
bool MediaSourcePlayer::AudioFinished() {
- return reached_audio_eos_ || !HasAudio();
+ return audio_decoder_job_->OutputEOSReached() || !HasAudio();
}
bool MediaSourcePlayer::VideoFinished() {
- return reached_video_eos_ || !HasVideo();
-}
-
-void MediaSourcePlayer::ConfigureAudioDecoderJob() {
- if (!HasAudio()) {
- audio_decoder_job_.reset();
- return;
- }
-
- // Create audio decoder job only if config changes.
- if (audio_decoder_job_ && !reconfig_audio_decoder_)
- 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());
-
- DVLOG(1) << __FUNCTION__ << " : creating new audio decoder job";
-
- audio_decoder_job_.reset(AudioDecoderJob::Create(
- audio_codec_, sampling_rate_, num_channels_, &audio_extra_data_[0],
- audio_extra_data_.size(), media_crypto.obj(),
- base::Bind(&DemuxerAndroid::RequestDemuxerData,
- base::Unretained(demuxer_.get()), DemuxerStream::AUDIO)));
-
- if (audio_decoder_job_) {
- SetVolumeInternal();
- audio_decoder_job_->BeginPrerolling(preroll_timestamp_);
- reconfig_audio_decoder_ = false;
- }
-}
-
-void MediaSourcePlayer::ResetVideoDecoderJob() {
- video_decoder_job_.reset();
-
- // Any eventual video decoder job re-creation will use the current |surface_|.
- if (IsEventPending(SURFACE_CHANGE_EVENT_PENDING))
- ClearPendingEvent(SURFACE_CHANGE_EVENT_PENDING);
-}
-
-void MediaSourcePlayer::ConfigureVideoDecoderJob() {
- if (!HasVideo() || surface_.IsEmpty()) {
- ResetVideoDecoderJob();
- return;
- }
-
- // Create video decoder job only if config changes or we don't have a job.
- if (video_decoder_job_ && !reconfig_video_decoder_) {
- DCHECK(!IsEventPending(SURFACE_CHANGE_EVENT_PENDING));
- return;
- }
-
- DCHECK(!video_decoder_job_ || !video_decoder_job_->is_decoding());
-
- if (reconfig_video_decoder_) {
- // No hack browser seek should be required. I-Frame must be next.
- DCHECK(next_video_data_is_iframe_) << "Received video data between "
- << "detecting video config change and reconfiguring video decoder";
- }
-
- // If uncertain that video I-frame data is next and there is no seek already
- // in process, request browser demuxer seek so the new decoder will decode
- // an I-frame first. Otherwise, the new MediaCodec might crash. See b/8950387.
- // Eventual OnDemuxerSeekDone() will trigger ProcessPendingEvents() and
- // continue from here.
- // TODO(wolenetz): Instead of doing hack browser seek, replay cached data
- // since last keyframe. See http://crbug.com/304234.
- if (!next_video_data_is_iframe_ && !IsEventPending(SEEK_EVENT_PENDING)) {
- BrowserSeekToCurrentTime();
- return;
- }
-
- // Release the old VideoDecoderJob first so the surface can get released.
- // Android does not allow 2 MediaCodec instances use the same surface.
- ResetVideoDecoderJob();
-
- base::android::ScopedJavaLocalRef<jobject> media_crypto = GetMediaCrypto();
- if (is_video_encrypted_ && media_crypto.is_null())
- return;
-
- DVLOG(1) << __FUNCTION__ << " : creating new video decoder job";
-
- // Create the new VideoDecoderJob.
- 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_.get()),
- DemuxerStream::VIDEO)));
- if (!video_decoder_job_)
- return;
-
- video_decoder_job_->BeginPrerolling(preroll_timestamp_);
- reconfig_video_decoder_ = false;
-
- // Inform the fullscreen view the player is ready.
- // TODO(qinmin): refactor MediaPlayerBridge so that we have a better way
- // to inform ContentVideoView.
- manager()->OnMediaMetadataChanged(
- player_id(), duration_, width_, height_, true);
+ return video_decoder_job_->OutputEOSReached() || !HasVideo();
}
void MediaSourcePlayer::OnDecoderStarved() {
@@ -880,7 +615,8 @@ void MediaSourcePlayer::OnDecoderStarved() {
}
void MediaSourcePlayer::StartStarvationCallback(
- const base::TimeDelta& presentation_timestamp) {
+ 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.
@@ -890,41 +626,35 @@ void MediaSourcePlayer::StartStarvationCallback(
base::TimeDelta current_timestamp = GetCurrentTime();
base::TimeDelta timeout;
if (HasAudio()) {
- timeout = audio_timestamp_helper_->GetTimestamp() - current_timestamp;
+ timeout = max_presentation_timestamp - current_timestamp;
} else {
- DCHECK(current_timestamp <= presentation_timestamp);
+ 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 * (presentation_timestamp - current_timestamp);
+ timeout = 2 * (current_presentation_timestamp - current_timestamp);
}
timeout = std::max(timeout, kMinStarvationTimeout);
decoder_starvation_callback_.Reset(
- base::Bind(&MediaSourcePlayer::OnDecoderStarved,
- weak_this_.GetWeakPtr()));
+ base::Bind(&MediaSourcePlayer::OnDecoderStarved, weak_this_));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE, decoder_starvation_callback_.callback(), timeout);
}
-void MediaSourcePlayer::SetVolumeInternal() {
- if (audio_decoder_job_ && volume_ >= 0)
- audio_decoder_job_->SetVolume(volume_);
-}
-
bool MediaSourcePlayer::IsProtectedSurfaceRequired() {
- return is_video_encrypted_ &&
+ return video_decoder_job_->is_content_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(!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.
@@ -933,7 +663,6 @@ void MediaSourcePlayer::OnPrefetchDone() {
// and http://crbug.com/304234.
if (!IsEventPending(PREFETCH_DONE_EVENT_PENDING)) {
DVLOG(1) << __FUNCTION__ << " : aborting";
- DCHECK(!audio_decoder_job_ && !video_decoder_job_);
return;
}
@@ -944,6 +673,9 @@ void MediaSourcePlayer::OnPrefetchDone() {
return;
}
+ if (!playing_)
+ return;
+
start_time_ticks_ = base::TimeTicks::Now();
start_presentation_timestamp_ = GetCurrentTime();
if (!clock_.IsPlaying())
@@ -956,13 +688,18 @@ void MediaSourcePlayer::OnPrefetchDone() {
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",
- "SURFACE_CHANGE",
- "CONFIG_CHANGE",
+ "DECODER_CREATION",
"PREFETCH_REQUEST",
- "PREFETCH_DONE",
};
int mask = 1;
@@ -994,4 +731,36 @@ void MediaSourcePlayer::ClearPendingEvent(PendingEventFlags 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_)
+ return;
+
+ is_waiting_for_key_ = false;
+ if (playing_)
+ StartInternal();
+}
+
+void MediaSourcePlayer::OnCdmUnset() {
+ DVLOG(1) << __FUNCTION__;
+ // TODO(xhwang): Support detachment of CDM. This will be needed when we start
+ // to support setMediaKeys(0) (see http://crbug.com/330324), or when we
+ // release MediaDrm when the video is paused, or when the device goes to
+ // sleep (see http://crbug.com/272421).
+ NOTREACHED() << "CDM detachment not supported.";
+ DCHECK(drm_bridge_);
+ audio_decoder_job_->SetDrmBridge(NULL);
+ video_decoder_job_->SetDrmBridge(NULL);
+ drm_bridge_ = NULL;
+}
+
} // namespace media