| 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..9b9d016fee67bb886f2c7e2029e66851a776e1d7 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,11 @@ MediaCodecPlayer::MediaCodecPlayer(
|
| interpolator_(&default_tick_clock_),
|
| pending_start_(false),
|
| pending_seek_(kNoTimestamp()),
|
| + drm_bridge_(nullptr),
|
| + cdm_registration_id_(0),
|
| + is_protected_surface_required_(false),
|
| + key_is_required_(false),
|
| + key_is_added_(false),
|
| media_weak_factory_(this) {
|
| DCHECK(ui_task_runner_->BelongsToCurrentThread());
|
|
|
| @@ -71,6 +77,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 +112,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() {
|
| @@ -151,6 +164,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 && is_protected_surface_required_ &&
|
| + !surface.is_protected()) {
|
| + DVLOG(0) << __FUNCTION__ << ": surface is not protected, ignoring";
|
| + return;
|
| + }
|
| +
|
| video_decoder_->SetVideoSurface(surface.Pass());
|
|
|
| if (surface_is_empty) {
|
| @@ -223,6 +244,8 @@ void MediaCodecPlayer::Start() {
|
| case kStatePrefetching:
|
| case kStatePlaying:
|
| case kStateWaitingForSurface:
|
| + case kStateWaitingForKey:
|
| + case kStateWaitingForCrypto:
|
| case kStateError:
|
| break; // Ignore
|
| default:
|
| @@ -242,6 +265,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 +298,8 @@ void MediaCodecPlayer::SeekTo(base::TimeDelta timestamp) {
|
| case kStateWaitingForConfig:
|
| case kStatePrefetching:
|
| case kStateWaitingForSurface:
|
| + case kStateWaitingForKey:
|
| + case kStateWaitingForCrypto:
|
| SetState(kStateWaitingForSeek);
|
| StopDecoders();
|
| SetPendingStart(true);
|
| @@ -313,6 +340,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 +413,49 @@ 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_));
|
| +
|
| + // We assume this property never changes so we can ask it here and use later.
|
| + // TODO(timav): This value can be passed with MediaCryptoReadyCB.
|
| + is_protected_surface_required_ = drm_bridge_->IsProtectedSurfaceRequired();
|
| +
|
| + // If the crypto is ready by this time, OnMediaCryptoReady will be posted
|
| + // right away.
|
| + // drm_bridge_->SetMediaCryptoReadyCB(
|
| + // base::Bind(&MediaCodecPlayer::OnMediaCryptoReady, media_weak_this_));
|
| +
|
| + MediaDrmBridge::MediaCryptoReadyCB cb =
|
| + base::Bind(&MediaCodecPlayer::OnMediaCryptoReady, media_weak_this_);
|
| +
|
| + // Post back to UI thread?
|
| + // TODO(timav): We need weak ptr for UI thread.
|
| + ui_task_runner_->PostTask(FROM_HERE,
|
| + base::Bind(&MediaDrmBridge::SetMediaCryptoReadyCB,
|
| + base::Unretained(drm_bridge_), cb));
|
| }
|
|
|
| // Callbacks from Demuxer.
|
| @@ -604,6 +676,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 +795,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 +892,78 @@ 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());
|
| +
|
| + media_crypto_ = media_crypto.Pass();
|
| +
|
| + if (audio_decoder_) {
|
| + audio_decoder_->SetNeedsReconfigure();
|
| + }
|
| +
|
| + if (video_decoder_) {
|
| + video_decoder_->SetNeedsReconfigure();
|
| + video_decoder_->SetProtectedSurfaceRequired(is_protected_surface_required_);
|
| + }
|
| +
|
| + 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;
|
| + is_protected_surface_required_ = false;
|
| + drm_bridge_ = nullptr;
|
| + media_crypto_.reset();
|
| +}
|
| +
|
| // State machine operations, called on Media thread
|
|
|
| void MediaCodecPlayer::SetState(PlayerState new_state) {
|
| @@ -899,6 +1063,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 +1077,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 +1117,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 +1311,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 +1326,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 +1363,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);
|
| }
|
|
|