Chromium Code Reviews| Index: media/base/android/media_codec_player.cc |
| diff --git a/media/base/android/media_codec_player.cc b/media/base/android/media_codec_player.cc |
| index 4e1e8ff4fb9a52269e0eb4037f2d5bba5938883d..f402191f47f2146b658a1495e92f9dc3b926e7b8 100644 |
| --- a/media/base/android/media_codec_player.cc |
| +++ b/media/base/android/media_codec_player.cc |
| @@ -12,6 +12,7 @@ |
| #include "base/threading/thread.h" |
| #include "media/base/android/media_codec_audio_decoder.h" |
| #include "media/base/android/media_codec_video_decoder.h" |
| +#include "media/base/android/media_drm_bridge.h" |
| #include "media/base/android/media_player_manager.h" |
| #include "media/base/timestamp_constants.h" |
| @@ -62,6 +63,10 @@ MediaCodecPlayer::MediaCodecPlayer( |
| interpolator_(&default_tick_clock_), |
| pending_start_(false), |
| pending_seek_(kNoTimestamp()), |
| + drm_bridge_(nullptr), |
| + cdm_registration_id_(0), |
| + key_is_required_(false), |
| + key_is_added_(false), |
| media_weak_factory_(this) { |
| DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
| @@ -71,6 +76,8 @@ MediaCodecPlayer::MediaCodecPlayer( |
| completion_cb_ = |
| base::Bind(&MediaPlayerManager::OnPlaybackComplete, manager, player_id); |
| + key_required_cb_ = base::Bind(&MediaPlayerManager::OnWaitingForDecryptionKey, |
| + manager, player_id); |
| seek_done_cb_ = |
| base::Bind(&MediaPlayerManager::OnSeekComplete, manager, player_id); |
| error_cb_ = base::Bind(&MediaPlayerManager::OnError, manager, player_id); |
| @@ -104,6 +111,11 @@ MediaCodecPlayer::~MediaCodecPlayer() |
| video_decoder_->ReleaseDecoderResources(); |
| if (audio_decoder_) |
| audio_decoder_->ReleaseDecoderResources(); |
| + |
| + if (drm_bridge_) { |
| + DCHECK(cdm_registration_id_); |
| + drm_bridge_->UnregisterPlayer(cdm_registration_id_); |
| + } |
| } |
| void MediaCodecPlayer::Initialize() { |
| @@ -223,6 +235,8 @@ void MediaCodecPlayer::Start() { |
| case kStatePrefetching: |
| case kStatePlaying: |
| case kStateWaitingForSurface: |
| + case kStateWaitingForKey: |
| + case kStateWaitingForCrypto: |
| case kStateError: |
| break; // Ignore |
| default: |
| @@ -242,6 +256,8 @@ void MediaCodecPlayer::Pause(bool is_media_related_action) { |
| case kStateWaitingForConfig: |
| case kStatePrefetching: |
| case kStateWaitingForSurface: |
| + case kStateWaitingForKey: |
| + case kStateWaitingForCrypto: |
| SetState(kStatePaused); |
| StopDecoders(); |
| break; |
| @@ -273,6 +289,8 @@ void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) { |
| case kStateWaitingForConfig: |
| case kStatePrefetching: |
| case kStateWaitingForSurface: |
| + case kStateWaitingForKey: |
| + case kStateWaitingForCrypto: |
| SetState(kStateWaitingForSeek); |
| StopDecoders(); |
| SetPendingStart(true); |
| @@ -313,6 +331,10 @@ void MediaCodecPlayer::Release() { |
| if (state_ != kStateWaitingForSeek) |
| SetState(kStatePaused); |
| + // Crear encryption key related flags. |
| + key_is_required_ = false; |
| + key_is_added_ = false; |
| + |
| base::TimeDelta pending_seek_time = GetPendingSeek(); |
| if (pending_seek_time != kNoTimestamp()) { |
| SetPendingSeek(kNoTimestamp()); |
| @@ -382,8 +404,41 @@ bool MediaCodecPlayer::IsPlayerReady() { |
| } |
| void MediaCodecPlayer::SetCdm(BrowserCdm* cdm) { |
| - DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
| - NOTIMPLEMENTED(); |
| + RUN_ON_MEDIA_THREAD(SetCdm, cdm); |
| + |
| + DVLOG(1) << __FUNCTION__; |
| + |
| + // Currently we don't support DRM change during the middle of playback, even |
| + // if the player is paused. There is no current plan to support it, see |
| + // http://crbug.com/253792. |
| + if (state_ != kStatePaused || GetInterpolatedTime() > base::TimeDelta()) { |
| + VLOG(0) << "Setting DRM bridge after playback has started is not supported"; |
| + return; |
| + } |
| + |
| + if (drm_bridge_) { |
| + NOTREACHED() << "Currently we do not support resetting CDM."; |
| + return; |
| + } |
| + |
| + DCHECK(cdm); |
| + drm_bridge_ = static_cast<MediaDrmBridge*>(cdm); |
| + |
| + DCHECK(drm_bridge_); |
| + |
| + DCHECK(audio_decoder_); |
| + DCHECK(video_decoder_); |
| + audio_decoder_->SetDrmBridge(drm_bridge_); |
| + video_decoder_->SetDrmBridge(drm_bridge_); |
| + |
| + cdm_registration_id_ = drm_bridge_->RegisterPlayer( |
| + base::Bind(&MediaCodecPlayer::OnKeyAdded, media_weak_this_), |
| + base::Bind(&MediaCodecPlayer::OnCdmUnset, media_weak_this_)); |
| + |
| + // If the crypto is ready by this time, OnMediaCryptoReady will be posted |
| + // immediately, otherwise it will be posted when the crypro is ready. |
| + drm_bridge_->SetMediaCryptoReadyCB( |
| + base::Bind(&MediaCodecPlayer::OnMediaCryptoReady, media_weak_this_)); |
| } |
| // Callbacks from Demuxer. |
| @@ -604,6 +659,12 @@ void MediaCodecPlayer::OnPrefetchDone() { |
| return; |
| } |
| + if (key_is_required_ && !key_is_added_) { |
| + SetState(kStateWaitingForKey); |
| + ui_task_runner_->PostTask(FROM_HERE, key_required_cb_); |
| + return; |
| + } |
| + |
| SetState(kStatePlaying); |
| StartPlaybackOrBrowserSeek(); |
| } |
| @@ -717,6 +778,20 @@ void MediaCodecPlayer::OnStopDone(DemuxerStream::Type type) { |
| ui_task_runner_->PostTask(FROM_HERE, completion_cb_); |
| } |
| +void MediaCodecPlayer::OnKeyRequired(DemuxerStream::Type type) { |
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); |
| + DVLOG(1) << __FUNCTION__ << " " << type; |
| + |
| + // Request stop and restart to pick up the key. |
| + key_is_required_ = true; |
| + |
| + if (state_ == kStatePlaying) { |
| + SetState(kStateStopping); |
| + RequestToStopDecoders(); |
| + SetPendingStart(true); |
| + } |
| +} |
| + |
| void MediaCodecPlayer::OnError() { |
| DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); |
| DVLOG(1) << __FUNCTION__; |
| @@ -800,6 +875,67 @@ void MediaCodecPlayer::OnVideoResolutionChanged(const gfx::Size& size) { |
| FROM_HERE, base::Bind(metadata_changed_cb_, kNoTimestamp(), size)); |
| } |
| +// Callbacks from DRM |
| + |
| +void MediaCodecPlayer::OnMediaCryptoReady( |
| + MediaDrmBridge::JavaObjectPtr media_crypto) { |
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); |
| + DVLOG(1) << __FUNCTION__; |
| + |
| + // We receive the MediaCrypto global reference with this callback and use it |
| + // later for all subsequent configurations. This is possible only if the |
| + // MediaCrypto object remains valid until new SetCdm() is called. |
| + |
| + DCHECK(media_crypto); |
| + DCHECK(!media_crypto->is_null()); |
| + |
| + DCHECK(drm_bridge_); |
| + drm_bridge_->SetMediaCryptoReadyCB(MediaDrmBridge::MediaCryptoReadyCB()); |
|
Tima Vaisburd
2015/09/22 19:41:23
MediaDrmBridge does ResetAndReturn before sending
|
| + |
| + media_crypto_ = media_crypto.Pass(); |
| + |
| + if (state_ == kStateWaitingForCrypto) { |
| + // Resume start sequence (configure, etc.) |
| + SetState(kStatePlaying); |
| + StartPlaybackOrBrowserSeek(); |
| + } |
| + |
| + DVLOG(1) << __FUNCTION__ << " end"; |
| +} |
| + |
| +void MediaCodecPlayer::OnKeyAdded() { |
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); |
| + DVLOG(1) << __FUNCTION__; |
| + |
| + key_is_added_ = true; |
| + |
| + if (state_ == kStateWaitingForKey) { |
| + SetState(kStatePlaying); |
| + StartPlaybackOrBrowserSeek(); |
| + } |
| +} |
| + |
| +void MediaCodecPlayer::OnCdmUnset() { |
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); |
| + DVLOG(1) << __FUNCTION__; |
| + |
| + // This comment is copied from MediaSourcePlayer::OnCdmUnset(). |
| + // TODO(xhwang): Currently this is only called during teardown. Support full |
| + // detachment of CDM during playback. 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). |
| + |
| + if (audio_decoder_) |
| + audio_decoder_->SetDrmBridge(nullptr); |
| + if (video_decoder_) |
| + video_decoder_->SetDrmBridge(nullptr); |
| + |
| + cdm_registration_id_ = 0; |
| + drm_bridge_ = nullptr; |
| + media_crypto_.reset(); |
| +} |
| + |
| // State machine operations, called on Media thread |
| void MediaCodecPlayer::SetState(PlayerState new_state) { |
| @@ -899,6 +1035,10 @@ void MediaCodecPlayer::StartPlaybackOrBrowserSeek() { |
| // TODO(timav): consider replacing this method with posting a |
| // browser seek task (i.e. generate an event) from StartPlaybackDecoders(). |
| + // Clear encryption key related flags. |
| + key_is_required_ = false; |
| + key_is_added_ = false; |
| + |
| StartStatus status = StartPlaybackDecoders(); |
| switch (status) { |
| @@ -909,6 +1049,9 @@ void MediaCodecPlayer::StartPlaybackOrBrowserSeek() { |
| StopDecoders(); |
| RequestDemuxerSeek(GetInterpolatedTime(), true); |
| break; |
| + case kStartCryptoRequired: |
| + SetState(kStateWaitingForCrypto); |
| + break; |
| case kStartFailed: |
| GetMediaTaskRunner()->PostTask(FROM_HERE, internal_error_cb_); |
| break; |
| @@ -946,29 +1089,42 @@ MediaCodecPlayer::StartStatus MediaCodecPlayer::ConfigureDecoders() { |
| // prefetch state and never call this method. |
| DCHECK(do_audio || do_video); |
| - // Start with video: if browser seek is required it would |
| - // not make sense to configure audio. |
| + bool need_crypto = (do_audio && audio_decoder_->IsContentEncrypted()) || |
| + (do_video && video_decoder_->IsContentEncrypted()); |
| - if (do_video) { |
| - MediaCodecDecoder::ConfigStatus status = video_decoder_->Configure(); |
| - switch (status) { |
| - case MediaCodecDecoder::kConfigOk: |
| - break; |
| - case MediaCodecDecoder::kConfigKeyFrameRequired: |
| - // TODO(timav): post a task or return the status? |
| - return kStartBrowserSeekRequired; |
| - case MediaCodecDecoder::kConfigFailure: |
| - return kStartFailed; |
| + // Do we need to create a local ref from the global ref? |
| + jobject media_crypto = media_crypto_ ? media_crypto_->obj() : nullptr; |
| + |
| + if (need_crypto) { |
| + DVLOG(1) << (audio_decoder_->IsContentEncrypted() ? " audio" : "") |
| + << (video_decoder_->IsContentEncrypted() ? " video" : "") |
| + << " need(s) encryption"; |
| + if (!media_crypto) { |
| + DVLOG(1) << __FUNCTION__ << ": MediaCrypto is not found, returning"; |
| + return kStartCryptoRequired; |
| } |
| } |
| - if (do_audio) { |
| - MediaCodecDecoder::ConfigStatus status = audio_decoder_->Configure(); |
| - if (status != MediaCodecDecoder::kConfigOk) { |
| + // Start with video: if browser seek is required it would not make sense to |
| + // configure audio. |
| + |
| + MediaCodecDecoder::ConfigStatus status = MediaCodecDecoder::kConfigOk; |
| + if (do_video) |
| + status = video_decoder_->Configure(media_crypto); |
| + |
| + if (status == MediaCodecDecoder::kConfigOk && do_audio) |
| + status = audio_decoder_->Configure(media_crypto); |
| + |
| + switch (status) { |
| + case MediaCodecDecoder::kConfigOk: |
| + break; |
| + case MediaCodecDecoder::kConfigKeyFrameRequired: |
| + return kStartBrowserSeekRequired; |
| + case MediaCodecDecoder::kConfigNoCrypto: // TODO: delete this |
| + return kStartCryptoRequired; |
| + case MediaCodecDecoder::kConfigFailure: |
| return kStartFailed; |
| - } |
| } |
| - |
| return kStartOk; |
| } |
| @@ -1127,6 +1283,8 @@ void MediaCodecPlayer::CreateDecoders() { |
| DemuxerStream::AUDIO), |
| base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_, |
| DemuxerStream::AUDIO), |
| + base::Bind(&MediaCodecPlayer::OnKeyRequired, media_weak_this_, |
| + DemuxerStream::AUDIO), |
| internal_error_cb_, |
| base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_, |
| DemuxerStream::AUDIO))); |
| @@ -1140,6 +1298,8 @@ void MediaCodecPlayer::CreateDecoders() { |
| DemuxerStream::VIDEO), |
| base::Bind(&MediaCodecPlayer::OnStopDone, media_weak_this_, |
| DemuxerStream::VIDEO), |
| + base::Bind(&MediaCodecPlayer::OnKeyRequired, media_weak_this_, |
| + DemuxerStream::VIDEO), |
| internal_error_cb_, |
| base::Bind(&MediaCodecPlayer::OnTimeIntervalUpdate, media_weak_this_, |
| DemuxerStream::VIDEO), |
| @@ -1175,6 +1335,8 @@ const char* MediaCodecPlayer::AsString(PlayerState state) { |
| RETURN_STRING(kStatePlaying); |
| RETURN_STRING(kStateStopping); |
| RETURN_STRING(kStateWaitingForSurface); |
| + RETURN_STRING(kStateWaitingForKey); |
| + RETURN_STRING(kStateWaitingForCrypto); |
| RETURN_STRING(kStateWaitingForSeek); |
| RETURN_STRING(kStateError); |
| } |