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 85f2a1df189ad36257785fa5b550ef7b301dc1f2..5c4608bf64d683559af59658ed8cdc55db1cdda8 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/android/media_task_runner.h" |
| #include "media/base/timestamp_constants.h" |
| @@ -47,6 +48,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()); |
| @@ -56,6 +61,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); |
| @@ -90,6 +97,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() { |
| @@ -137,6 +149,14 @@ void MediaCodecPlayer::SetVideoSurface(gfx::ScopedJavaSurface surface) { |
| return; |
| } |
| + // Do not set unprotected surface if we know that we need a protected one. |
| + // Empty surface means the surface removal and we always allow for it. |
| + if (!surface_is_empty && video_decoder_->IsProtectedSurfaceRequired() && |
| + !surface.is_protected()) { |
| + DVLOG(0) << __FUNCTION__ << ": surface is not protected, ignoring"; |
| + return; |
| + } |
| + |
| video_decoder_->SetVideoSurface(surface.Pass()); |
| if (surface_is_empty) { |
| @@ -210,6 +230,8 @@ void MediaCodecPlayer::Start() { |
| case kStatePrefetching: |
| case kStatePlaying: |
| case kStateWaitingForSurface: |
| + case kStateWaitingForKey: |
| + case kStateWaitingForCrypto: |
| case kStateError: |
| break; // Ignore |
| default: |
| @@ -230,6 +252,8 @@ void MediaCodecPlayer::Pause(bool is_media_related_action) { |
| case kStateWaitingForPermission: |
| case kStatePrefetching: |
| case kStateWaitingForSurface: |
| + case kStateWaitingForKey: |
| + case kStateWaitingForCrypto: |
| SetState(kStatePaused); |
| StopDecoders(); |
| break; |
| @@ -262,6 +286,8 @@ void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) { |
| case kStateWaitingForPermission: |
| case kStatePrefetching: |
| case kStateWaitingForSurface: |
| + case kStateWaitingForKey: |
| + case kStateWaitingForCrypto: |
| SetState(kStateWaitingForSeek); |
| StopDecoders(); |
| SetPendingStart(true); |
| @@ -302,6 +328,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()); |
| @@ -371,8 +401,39 @@ 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_); |
| + |
| + cdm_registration_id_ = drm_bridge_->RegisterPlayer( |
| + base::Bind(&MediaCodecPlayer::OnKeyAdded, media_weak_this_), |
| + base::Bind(&MediaCodecPlayer::OnCdmUnset, media_weak_this_)); |
| + |
| + MediaDrmBridge::MediaCryptoReadyCB cb = |
| + base::Bind(&MediaCodecPlayer::OnMediaCryptoReady, media_weak_this_); |
| + |
| + // Post back to UI thread? |
| + ui_task_runner_->PostTask(FROM_HERE, |
| + base::Bind(&MediaDrmBridge::SetMediaCryptoReadyCB, |
| + drm_bridge_->WeakPtrForUIThread(), cb)); |
| } |
| // Callbacks from Demuxer. |
| @@ -633,6 +694,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(); |
| } |
| @@ -746,6 +813,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); |
| + } |
|
xhwang
2015/09/30 18:07:53
It seems to me whenever a key is required we'll st
Tima Vaisburd
2015/09/30 21:24:51
Yes.
xhwang
2015/10/01 00:04:39
In that case I think your impl is good.
Tima Vaisburd
2015/10/01 00:36:06
It seems it would be not that hard to try again se
xhwang
2015/10/01 00:50:05
Try until there's no new key available. New key is
|
| +} |
| + |
| void MediaCodecPlayer::OnError() { |
| DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); |
| DVLOG(1) << __FUNCTION__; |
| @@ -829,6 +910,79 @@ void MediaCodecPlayer::OnVideoResolutionChanged(const gfx::Size& size) { |
| FROM_HERE, base::Bind(metadata_changed_cb_, kNoTimestamp(), size)); |
| } |
| +// Callbacks from DRM |
|
xhwang
2015/09/30 18:07:53
s/DRM/MediaDrmBridge.
Tima Vaisburd
2015/09/30 23:00:50
Done.
|
| + |
| +void MediaCodecPlayer::OnMediaCryptoReady( |
| + MediaDrmBridge::JavaObjectPtr media_crypto, |
| + bool needs_protected_surface) { |
| + DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); |
| + DVLOG(1) << __FUNCTION__ << " protected surface is " |
| + << (needs_protected_surface ? "required" : "not required"); |
| + |
| + // We use the parameters that come with this callback every time we call |
| + // Configure(). This is possible only if the MediaCrypto object remains valid |
| + // and the surface requirement does not change until new SetCdm() is called. |
| + |
| + DCHECK(media_crypto); |
| + DCHECK(!media_crypto->is_null()); |
| + |
| + media_crypto_ = media_crypto.Pass(); |
| + |
| + if (audio_decoder_) { |
| + audio_decoder_->SetNeedsReconfigure(); |
| + } |
| + |
| + if (video_decoder_) { |
| + video_decoder_->SetNeedsReconfigure(); |
| + video_decoder_->SetProtectedSurfaceRequired(needs_protected_surface); |
| + } |
| + |
| + 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_->SetNeedsReconfigure(); |
| + } |
| + |
| + if (video_decoder_) { |
| + video_decoder_->SetProtectedSurfaceRequired(false); |
| + video_decoder_->SetNeedsReconfigure(); |
| + } |
| + |
| + cdm_registration_id_ = 0; |
| + drm_bridge_ = nullptr; |
| + media_crypto_.reset(); |
| +} |
| + |
| // State machine operations, called on Media thread |
| void MediaCodecPlayer::SetState(PlayerState new_state) { |
| @@ -940,6 +1094,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) { |
| @@ -950,6 +1108,9 @@ void MediaCodecPlayer::StartPlaybackOrBrowserSeek() { |
| StopDecoders(); |
| RequestDemuxerSeek(GetInterpolatedTime(), true); |
| break; |
| + case kStartCryptoRequired: |
| + SetState(kStateWaitingForCrypto); |
| + break; |
| case kStartFailed: |
| GetMediaTaskRunner()->PostTask(FROM_HERE, internal_error_cb_); |
| break; |
| @@ -987,29 +1148,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; |
| } |
| @@ -1168,6 +1342,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))); |
| @@ -1181,6 +1357,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), |
| @@ -1217,6 +1395,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); |
| } |