Index: media/base/android/media_codec_decoder.cc |
diff --git a/media/base/android/media_codec_decoder.cc b/media/base/android/media_codec_decoder.cc |
index fd8d84ecc60787c1eced4114010f086dd985e1e3..1f13114850a2261fa0a94ae87ac4a4b8a55149c0 100644 |
--- a/media/base/android/media_codec_decoder.cc |
+++ b/media/base/android/media_codec_decoder.cc |
@@ -9,6 +9,7 @@ |
#include "base/callback_helpers.h" |
#include "base/logging.h" |
#include "media/base/android/media_codec_bridge.h" |
+#include "media/base/android/media_drm_bridge.h" |
namespace media { |
@@ -37,10 +38,12 @@ MediaCodecDecoder::MediaCodecDecoder( |
const base::Closure& starvation_cb, |
const base::Closure& decoder_drained_cb, |
const base::Closure& stop_done_cb, |
+ const base::Closure& key_required_cb, |
const base::Closure& error_cb, |
const char* decoder_thread_name) |
: media_task_runner_(media_task_runner), |
decoder_thread_(decoder_thread_name), |
+ drm_bridge_(nullptr), |
needs_reconfigure_(false), |
drain_decoder_(false), |
always_reconfigure_for_tests_(false), |
@@ -48,10 +51,12 @@ MediaCodecDecoder::MediaCodecDecoder( |
starvation_cb_(starvation_cb), |
decoder_drained_cb_(decoder_drained_cb), |
stop_done_cb_(stop_done_cb), |
+ key_required_cb_(key_required_cb), |
error_cb_(error_cb), |
state_(kStopped), |
is_prepared_(false), |
eos_enqueued_(false), |
+ key_request_posted_(false), |
completed_(false), |
last_frame_posted_(false), |
is_data_request_in_progress_(false), |
@@ -179,13 +184,20 @@ void MediaCodecDecoder::SetPrerollTimestamp(base::TimeDelta preroll_timestamp) { |
preroll_timestamp_ = preroll_timestamp; |
} |
+void MediaCodecDecoder::SetDrmBridge(MediaDrmBridge* drm_bridge) { |
+ DCHECK(media_task_runner_->BelongsToCurrentThread()); |
+ |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__; |
+ |
+ drm_bridge_ = drm_bridge; |
+ |
+ needs_reconfigure_ = true; |
+} |
+ |
base::android::ScopedJavaLocalRef<jobject> MediaCodecDecoder::GetMediaCrypto() { |
base::android::ScopedJavaLocalRef<jobject> media_crypto; |
- |
- // TODO(timav): implement DRM. |
- // drm_bridge_ is not implemented |
- // if (drm_bridge_) |
- // media_crypto = drm_bridge_->GetMediaCrypto(); |
+ if (drm_bridge_) |
+ media_crypto = drm_bridge_->GetMediaCrypto(); |
return media_crypto; |
} |
@@ -225,6 +237,12 @@ MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure() { |
return kConfigOk; |
} |
+ if (IsContentEncrypted() && GetMediaCrypto().is_null()) { |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__ |
+ << ": MediaCrypto is missing"; |
+ return kConfigNoCrypto; |
+ } |
+ |
// Read all |kConfigChanged| units preceding the data one. |
AccessUnitQueue::Info au_info = au_queue_.GetInfo(); |
while (au_info.configs) { |
@@ -401,6 +419,8 @@ void MediaCodecDecoder::OnLastFrameRendered(bool eos_encountered) { |
SetState(kStopped); |
completed_ = (eos_encountered && !drain_decoder_); |
+ key_request_posted_ = false; |
+ |
// If the stream is completed during preroll we need to report it since |
// another stream might be running and the player waits for two callbacks. |
if (completed_ && !preroll_done_cb_.is_null()) { |
@@ -498,6 +518,8 @@ void MediaCodecDecoder::DoEmergencyStop() { |
decoder_thread_.Stop(); // synchronous |
SetState(kStopped); |
+ |
+ key_request_posted_ = false; |
} |
void MediaCodecDecoder::CheckLastFrame(bool eos_encountered, |
@@ -618,6 +640,12 @@ bool MediaCodecDecoder::EnqueueInputBuffer() { |
return true; // Nothing to do |
} |
+ if (key_request_posted_) { |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__ |
+ << ": key_request_posted, returning"; |
+ return true; // Nothing to do |
+ } |
+ |
// Keep the number pending video frames low, ideally maintaining |
// the same audio and video duration after stop request |
if (NumDelayedRenderTasks() > 1) { |
@@ -695,17 +723,54 @@ bool MediaCodecDecoder::EnqueueInputBuffer() { |
DCHECK(unit); |
- DVLOG(2) << class_name() << "::" << __FUNCTION__ |
- << ": QueueInputBuffer pts:" << unit->timestamp; |
+ if (unit->key_id.empty() || unit->iv.empty()) { |
+ DVLOG(2) << class_name() << "::" << __FUNCTION__ |
+ << ": QueueInputBuffer pts:" << unit->timestamp; |
- status = media_codec_bridge_->QueueInputBuffer( |
- index, &unit->data[0], unit->data.size(), unit->timestamp); |
+ status = media_codec_bridge_->QueueInputBuffer( |
+ index, &unit->data[0], unit->data.size(), unit->timestamp); |
+ } else { |
+ DVLOG(2) << class_name() << "::" << __FUNCTION__ |
+ << ": QueueSecureInputBuffer pts:" << unit->timestamp |
+ << " key_id size:" << unit->key_id.size() |
+ << " iv size:" << unit->iv.size() |
+ << " subsamples size:" << unit->subsamples.size(); |
- if (status == MEDIA_CODEC_ERROR) { |
- DVLOG(0) << class_name() << "::" << __FUNCTION__ |
- << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed"; |
- media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
- return false; |
+ status = media_codec_bridge_->QueueSecureInputBuffer( |
+ index, &unit->data[0], unit->data.size(), |
+ reinterpret_cast<const uint8_t*>(&unit->key_id[0]), unit->key_id.size(), |
+ reinterpret_cast<const uint8_t*>(&unit->iv[0]), unit->iv.size(), |
+ unit->subsamples.empty() ? nullptr : &unit->subsamples[0], |
+ unit->subsamples.size(), unit->timestamp); |
+ } |
+ |
+ switch (status) { |
+ case MEDIA_CODEC_OK: |
+ break; |
+ |
+ case MEDIA_CODEC_ERROR: |
+ DVLOG(0) << class_name() << "::" << __FUNCTION__ |
+ << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed"; |
+ media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
+ return false; |
+ |
+ case MEDIA_CODEC_NO_KEY: |
+ DVLOG(1) << class_name() << "::" << __FUNCTION__ |
+ << ": MEDIA_CODEC_NO_KEY"; |
+ media_task_runner_->PostTask(FROM_HERE, key_required_cb_); |
+ |
+ // In response to the |key_required_cb_| the player will request to stop |
+ // decoder. We need to keep running to properly perform the stop, but |
+ // prevent enqueuing the same frame over and over again so we won't |
+ // generate more |key_required_cb_|. |
+ key_request_posted_ = true; |
+ return true; |
+ |
+ default: |
+ NOTREACHED() << class_name() << "::" << __FUNCTION__ |
+ << ": unexpected error code " << status; |
+ media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); |
+ return false; |
} |
// Have successfully queued input buffer, go to next access unit. |