Index: content/common/gpu/media/android_video_decode_accelerator.cc |
diff --git a/content/common/gpu/media/android_video_decode_accelerator.cc b/content/common/gpu/media/android_video_decode_accelerator.cc |
index 00e1bb67856e14163ea5da84a6d04a37607335de..d7c1bf6a7464ef5d166ee32dd4882c1ed621e796 100644 |
--- a/content/common/gpu/media/android_video_decode_accelerator.cc |
+++ b/content/common/gpu/media/android_video_decode_accelerator.cc |
@@ -5,6 +5,7 @@ |
#include "content/common/gpu/media/android_video_decode_accelerator.h" |
#include "base/bind.h" |
+#include "base/bind_helpers.h" |
#include "base/logging.h" |
#include "base/message_loop/message_loop.h" |
#include "base/metrics/histogram.h" |
@@ -12,6 +13,7 @@ |
#include "content/common/gpu/gpu_channel.h" |
#include "content/common/gpu/media/avda_return_on_failure.h" |
#include "gpu/command_buffer/service/gles2_cmd_decoder.h" |
+#include "media/base/bind_to_current_loop.h" |
#include "media/base/bitstream_buffer.h" |
#include "media/base/limits.h" |
#include "media/base/timestamp_constants.h" |
@@ -82,14 +84,24 @@ AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( |
make_context_current_(make_context_current), |
codec_(media::kCodecH264), |
is_encrypted_(false), |
+ needs_protected_surface_(false), |
state_(NO_ERROR), |
picturebuffers_requested_(false), |
gl_decoder_(decoder), |
strategy_(strategy.Pass()), |
+ cdm_registration_id_(0), |
weak_this_factory_(this) {} |
AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { |
DCHECK(thread_checker_.CalledOnValidThread()); |
+ |
+#if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
+ if (cdm_) { |
+ DCHECK(cdm_registration_id_); |
+ static_cast<media::MediaDrmBridge*>(cdm_.get()) |
+ ->UnregisterPlayer(cdm_registration_id_); |
+ } |
+#endif // defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
} |
bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
@@ -101,6 +113,7 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
DVLOG(1) << __FUNCTION__ << ": profile:" << config.profile |
<< " is_encrypted:" << config.is_encrypted; |
+ DCHECK(client); |
client_ = client; |
codec_ = VideoCodecProfileToVideoCodec(config.profile); |
is_encrypted_ = config.is_encrypted; |
@@ -140,25 +153,61 @@ bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
surface_texture_ = strategy_->CreateSurfaceTexture(); |
- if (!ConfigureMediaCodec()) { |
- LOG(ERROR) << "Failed to create MediaCodec instance."; |
- return false; |
- } |
+ // For encrypted streams we postpone configuration until MediaCrypto is |
+ // available. |
+ if (is_encrypted_) |
+ return true; |
- return true; |
+ return ConfigureMediaCodec(); |
} |
void AndroidVideoDecodeAccelerator::SetCdm(int cdm_id) { |
DVLOG(2) << __FUNCTION__ << ": " << cdm_id; |
#if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
- // TODO(timav): Implement CDM setting here. See http://crbug.com/542417 |
- scoped_refptr<media::MediaKeys> cdm = media::MojoCdmService::GetCdm(cdm_id); |
- DCHECK(cdm); |
-#endif |
+ using media::MediaDrmBridge; |
+ |
+ DCHECK(client_) << "SetCdm() must be called after Initialize()."; |
+ |
+ if (cdm_) { |
+ NOTREACHED() << "We do not support resetting CDM."; |
+ NotifyCdmAttached(false); |
+ return; |
+ } |
+ |
+ cdm_ = media::MojoCdmService::GetCdm(cdm_id); |
+ DCHECK(cdm_); |
+ |
+ // On Android platform the MediaKeys will be its subclass MediaDrmBridge. |
+ MediaDrmBridge* drm_bridge = static_cast<MediaDrmBridge*>(cdm_.get()); |
+ |
+ // Register CDM callbacks. The callbacks registered will be posted back to |
+ // this thread via BindToCurrentLoop. |
+ |
+ // Since |this| holds a reference to the |cdm_|, by the time the CDM is |
+ // destructed, UnregisterPlayer() must have been called and |this| has been |
+ // destructed as well. So the |cdm_unset_cb| will never have a chance to be |
+ // called. |
+ // TODO(xhwang): Remove |cdm_unset_cb| after it's not used on all platforms. |
+ cdm_registration_id_ = |
+ drm_bridge->RegisterPlayer(media::BindToCurrentLoop(base::Bind( |
+ &AndroidVideoDecodeAccelerator::OnKeyAdded, |
+ weak_this_factory_.GetWeakPtr())), |
+ base::Bind(&base::DoNothing)); |
+ |
+ drm_bridge->SetMediaCryptoReadyCB(media::BindToCurrentLoop( |
+ base::Bind(&AndroidVideoDecodeAccelerator::OnMediaCryptoReady, |
+ weak_this_factory_.GetWeakPtr()))); |
+ |
+ // Postpone NotifyCdmAttached() call till we create the MediaCodec after |
+ // OnMediaCryptoReady(). |
+ |
+#else |
NOTIMPLEMENTED(); |
NotifyCdmAttached(false); |
+ |
+#endif // !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
} |
void AndroidVideoDecodeAccelerator::DoIOTask() { |
@@ -195,11 +244,12 @@ void AndroidVideoDecodeAccelerator::QueueInput() { |
base::Time::Now() - queued_time); |
media::BitstreamBuffer bitstream_buffer = |
pending_bitstream_buffers_.front().first; |
- pending_bitstream_buffers_.pop(); |
- TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", |
- pending_bitstream_buffers_.size()); |
if (bitstream_buffer.id() == -1) { |
+ pending_bitstream_buffers_.pop(); |
+ TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", |
+ pending_bitstream_buffers_.size()); |
+ |
media_codec_->QueueEOS(input_buf_index); |
return; |
} |
@@ -239,9 +289,23 @@ void AndroidVideoDecodeAccelerator::QueueInput() { |
} |
DVLOG(2) << __FUNCTION__ |
- << ": QueueInputBuffer: pts:" << presentation_timestamp |
+ << ": Queue(Secure)InputBuffer: pts:" << presentation_timestamp |
<< " status:" << status; |
+ if (status == media::MEDIA_CODEC_NO_KEY) { |
+ // Keep trying to enqueue the front pending buffer. |
+ // |
+ // TODO(timav): Figure out whether stopping the pipeline in response to |
+ // this error and restarting it in OnKeyAdded() has significant benefits |
+ // (e.g. saving power). |
+ DVLOG(1) << "QueueSecureInputBuffer failed: NO_KEY"; |
+ return; |
+ } |
+ |
+ pending_bitstream_buffers_.pop(); |
+ TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", |
+ pending_bitstream_buffers_.size()); |
+ |
RETURN_ON_FAILURE(this, status == media::MEDIA_CODEC_OK, |
"Failed to QueueInputBuffer: " << status, PLATFORM_FAILURE); |
@@ -505,13 +569,21 @@ bool AndroidVideoDecodeAccelerator::ConfigureMediaCodec() { |
gfx::ScopedJavaSurface surface(surface_texture_.get()); |
+ jobject media_crypto = media_crypto_ ? media_crypto_->obj() : nullptr; |
+ |
+ // |needs_protected_surface_| implies encrypted stream. |
+ DCHECK(!needs_protected_surface_ || media_crypto); |
+ |
// Pass a dummy 320x240 canvas size and let the codec signal the real size |
// when it's known from the bitstream. |
media_codec_.reset(media::VideoCodecBridge::CreateDecoder( |
- codec_, false, gfx::Size(320, 240), surface.j_surface().obj(), NULL)); |
+ codec_, needs_protected_surface_, gfx::Size(320, 240), |
+ surface.j_surface().obj(), media_crypto)); |
strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_); |
- if (!media_codec_) |
+ if (!media_codec_) { |
+ LOG(ERROR) << "Failed to create MediaCodec instance."; |
return false; |
+ } |
io_timer_.Start(FROM_HERE, |
DecodePollDelay(), |
@@ -606,6 +678,38 @@ void AndroidVideoDecodeAccelerator::PostError( |
state_ = ERROR; |
} |
+void AndroidVideoDecodeAccelerator::OnMediaCryptoReady( |
+ media::MediaDrmBridge::JavaObjectPtr media_crypto, |
+ bool needs_protected_surface) { |
+ DVLOG(1) << __FUNCTION__; |
+ |
+ if (!media_crypto) { |
+ LOG(ERROR) << "MediaCrypto is not available, can't play encrypted stream."; |
+ NotifyCdmAttached(false); |
+ return; |
+ } |
+ |
+ DCHECK(!media_crypto->is_null()); |
+ |
+ // We assume this is a part of the initialization process, thus MediaCodec |
+ // is not created yet. |
+ DCHECK(!media_codec_); |
+ |
+ media_crypto_ = media_crypto.Pass(); |
xhwang
2015/12/05 06:22:26
nit: std::move
Tima Vaisburd
2015/12/07 21:28:26
Done.
|
+ needs_protected_surface_ = needs_protected_surface; |
+ |
+ // After receiving |media_crypto_| we can configure MediaCodec. |
+ const bool success = ConfigureMediaCodec(); |
+ NotifyCdmAttached(success); |
+} |
+ |
+void AndroidVideoDecodeAccelerator::OnKeyAdded() { |
+ DVLOG(1) << __FUNCTION__; |
+ // TODO(timav): Figure out whether stopping the pipeline in response to |
+ // NO_KEY error and restarting it here has significant benefits (e.g. saving |
+ // power). Right now do nothing here. |
+} |
+ |
void AndroidVideoDecodeAccelerator::NotifyCdmAttached(bool success) { |
client_->NotifyCdmAttached(success); |
} |