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..b7538875b31933fa188c3a6c52622dec15a7e085 100644 |
--- a/media/base/android/media_codec_player.cc |
+++ b/media/base/android/media_codec_player.cc |
@@ -12,6 +12,8 @@ |
#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_drm_proxy.h" |
#include "media/base/android/media_player_manager.h" |
#include "media/base/timestamp_constants.h" |
@@ -62,6 +64,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()); |
@@ -104,6 +110,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 +234,8 @@ void MediaCodecPlayer::Start() { |
case kStatePrefetching: |
case kStatePlaying: |
case kStateWaitingForSurface: |
+ case kStateWaitingForKey: |
+ case kStateWaitingForCrypto: |
case kStateError: |
break; // Ignore |
default: |
@@ -242,6 +255,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 +288,8 @@ void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) { |
case kStateWaitingForConfig: |
case kStatePrefetching: |
case kStateWaitingForSurface: |
+ case kStateWaitingForKey: |
+ case kStateWaitingForCrypto: |
SetState(kStateWaitingForSeek); |
StopDecoders(); |
SetPendingStart(true); |
@@ -313,6 +330,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 +403,42 @@ 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); |
+ MediaDrmProxy* drm_proxy = static_cast<MediaDrmProxy*>(cdm); |
+ drm_bridge_ = drm_proxy->GetDrmBridge(); |
+ |
+ 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,11 @@ void MediaCodecPlayer::OnPrefetchDone() { |
return; |
} |
+ if (key_is_required_ && !key_is_added_) { |
+ SetState(kStateWaitingForKey); |
+ return; |
+ } |
+ |
SetState(kStatePlaying); |
StartPlaybackOrBrowserSeek(); |
} |
@@ -717,6 +777,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 +874,57 @@ void MediaCodecPlayer::OnVideoResolutionChanged(const gfx::Size& size) { |
FROM_HERE, base::Bind(metadata_changed_cb_, kNoTimestamp(), size)); |
} |
+// Callbacks from DRM |
+ |
+void MediaCodecPlayer::OnMediaCryptoReady() { |
+ DCHECK(GetMediaTaskRunner()->BelongsToCurrentThread()); |
+ DVLOG(1) << __FUNCTION__; |
+ |
+ DCHECK(drm_bridge_); |
+ DCHECK(!drm_bridge_->GetMediaCrypto().is_null()); |
+ drm_bridge_->SetMediaCryptoReadyCB(base::Closure()); |
+ |
+ if (state_ == kStateWaitingForCrypto) { |
+ // Resume decoders creation. |
+ 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; |
+} |
+ |
// State machine operations, called on Media thread |
void MediaCodecPlayer::SetState(PlayerState new_state) { |
@@ -899,6 +1024,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 +1038,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 +1078,26 @@ 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. |
+ // Start with video: if browser seek is required it would not make sense to |
+ // configure audio. |
- 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; |
- } |
- } |
+ MediaCodecDecoder::ConfigStatus status = MediaCodecDecoder::kConfigOk; |
+ if (do_video) |
+ status = video_decoder_->Configure(); |
+ |
+ if (status == MediaCodecDecoder::kConfigOk && do_audio) |
+ status = audio_decoder_->Configure(); |
- if (do_audio) { |
- MediaCodecDecoder::ConfigStatus status = audio_decoder_->Configure(); |
- if (status != MediaCodecDecoder::kConfigOk) { |
+ switch (status) { |
+ case MediaCodecDecoder::kConfigOk: |
+ break; |
+ case MediaCodecDecoder::kConfigKeyFrameRequired: |
+ return kStartBrowserSeekRequired; |
+ case MediaCodecDecoder::kConfigNoCrypto: |
+ return kStartCryptoRequired; |
+ case MediaCodecDecoder::kConfigFailure: |
return kStartFailed; |
- } |
} |
- |
return kStartOk; |
} |
@@ -1127,6 +1256,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 +1271,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 +1308,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); |
} |