Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(116)

Side by Side Diff: content/common/gpu/media/android_video_decode_accelerator.cc

Issue 1469353010: Configure MediaCodec with CDM in ADVA (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Added the handling of NO_KEY error Created 5 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "content/common/gpu/media/android_video_decode_accelerator.h" 5 #include "content/common/gpu/media/android_video_decode_accelerator.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/message_loop/message_loop.h" 9 #include "base/message_loop/message_loop.h"
10 #include "base/metrics/histogram.h" 10 #include "base/metrics/histogram.h"
11 #include "base/trace_event/trace_event.h" 11 #include "base/trace_event/trace_event.h"
12 #include "content/common/gpu/gpu_channel.h" 12 #include "content/common/gpu/gpu_channel.h"
13 #include "content/common/gpu/media/avda_return_on_failure.h" 13 #include "content/common/gpu/media/avda_return_on_failure.h"
14 #include "gpu/command_buffer/service/gles2_cmd_decoder.h" 14 #include "gpu/command_buffer/service/gles2_cmd_decoder.h"
15 #include "media/base/bind_to_current_loop.h"
15 #include "media/base/bitstream_buffer.h" 16 #include "media/base/bitstream_buffer.h"
16 #include "media/base/limits.h" 17 #include "media/base/limits.h"
17 #include "media/base/timestamp_constants.h" 18 #include "media/base/timestamp_constants.h"
18 #include "media/base/video_decoder_config.h" 19 #include "media/base/video_decoder_config.h"
19 #include "media/video/picture.h" 20 #include "media/video/picture.h"
20 #include "ui/gl/android/scoped_java_surface.h" 21 #include "ui/gl/android/scoped_java_surface.h"
21 #include "ui/gl/android/surface_texture.h" 22 #include "ui/gl/android/surface_texture.h"
22 #include "ui/gl/gl_bindings.h" 23 #include "ui/gl/gl_bindings.h"
23 24
24 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) 25 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
79 const base::Callback<bool(void)>& make_context_current, 80 const base::Callback<bool(void)>& make_context_current,
80 scoped_ptr<BackingStrategy> strategy) 81 scoped_ptr<BackingStrategy> strategy)
81 : client_(NULL), 82 : client_(NULL),
82 make_context_current_(make_context_current), 83 make_context_current_(make_context_current),
83 codec_(media::kCodecH264), 84 codec_(media::kCodecH264),
84 is_encrypted_(false), 85 is_encrypted_(false),
85 state_(NO_ERROR), 86 state_(NO_ERROR),
86 picturebuffers_requested_(false), 87 picturebuffers_requested_(false),
87 gl_decoder_(decoder), 88 gl_decoder_(decoder),
88 strategy_(strategy.Pass()), 89 strategy_(strategy.Pass()),
90 cdm_registration_id_(0),
89 weak_this_factory_(this) {} 91 weak_this_factory_(this) {}
90 92
91 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { 93 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() {
92 DCHECK(thread_checker_.CalledOnValidThread()); 94 DCHECK(thread_checker_.CalledOnValidThread());
95
96 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
97 if (cdm_) {
98 DCHECK(cdm_registration_id_);
99 static_cast<media::MediaDrmBridge*>(cdm_.get())
100 ->UnregisterPlayer(cdm_registration_id_);
101 }
102 #endif // defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
93 } 103 }
94 104
95 bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, 105 bool AndroidVideoDecodeAccelerator::Initialize(const Config& config,
96 Client* client) { 106 Client* client) {
97 DCHECK(!media_codec_); 107 DCHECK(!media_codec_);
98 DCHECK(thread_checker_.CalledOnValidThread()); 108 DCHECK(thread_checker_.CalledOnValidThread());
99 TRACE_EVENT0("media", "AVDA::Initialize"); 109 TRACE_EVENT0("media", "AVDA::Initialize");
100 110
101 DVLOG(1) << __FUNCTION__ << ": profile:" << config.profile 111 DVLOG(1) << __FUNCTION__ << ": profile:" << config.profile
102 << " is_encrypted:" << config.is_encrypted; 112 << " is_encrypted:" << config.is_encrypted;
(...skipping 28 matching lines...) Expand all
131 return false; 141 return false;
132 } 142 }
133 143
134 if (!gl_decoder_) { 144 if (!gl_decoder_) {
135 LOG(ERROR) << "Failed to get gles2 decoder instance."; 145 LOG(ERROR) << "Failed to get gles2 decoder instance.";
136 return false; 146 return false;
137 } 147 }
138 148
139 strategy_->Initialize(this); 149 strategy_->Initialize(this);
140 150
141 surface_texture_ = strategy_->CreateSurfaceTexture(); 151 surface_texture_ = strategy_->CreateSurfaceTexture();
liberato (no reviews please) 2015/12/04 16:20:11 how does this fit with |needs_protected_surface|?
Tima Vaisburd 2015/12/04 20:12:01 Can the protected surface be deferred in the subse
liberato (no reviews please) 2015/12/04 21:40:13 sure, i didn't mean that protected surfaces have t
142 152
143 if (!ConfigureMediaCodec()) { 153 // For encrypted streams we postpone configuration until MediaCrypto is
liberato (no reviews please) 2015/12/04 16:20:11 all of this would become a call to FinishInitializ
144 LOG(ERROR) << "Failed to create MediaCodec instance."; 154 // available.
145 return false; 155 if (is_encrypted_)
146 } 156 return true;
147 157
148 return true; 158 return ConfigureMediaCodec();
149 } 159 }
150 160
151 void AndroidVideoDecodeAccelerator::SetCdm(int cdm_id) { 161 void AndroidVideoDecodeAccelerator::SetCdm(int cdm_id) {
xhwang 2015/12/04 20:22:03 This should only be called after Initialize(), oth
liberato (no reviews please) 2015/12/04 21:40:13 maybe additional values for |state_| are useful he
Tima Vaisburd 2015/12/04 22:54:51 Yes, I agree. For now I DCHECKed client_, I'll try
152 DVLOG(2) << __FUNCTION__ << ": " << cdm_id; 162 DVLOG(2) << __FUNCTION__ << ": " << cdm_id;
153 163
154 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) 164 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
155 // TODO(timav): Implement CDM setting here. See http://crbug.com/542417 165 using media::MediaDrmBridge;
156 scoped_refptr<media::MediaKeys> cdm = media::MojoCdmService::GetCdm(cdm_id); 166
157 DCHECK(cdm); 167 if (cdm_) {
158 #endif 168 NOTREACHED() << "We do not support resetting CDM.";
169 NotifyCdmAttached(false);
170 return;
171 }
172
173 cdm_ = media::MojoCdmService::GetCdm(cdm_id);
174 DCHECK(cdm_);
175
176 // On Android platform the MediaKeys fill be its subclass MediaDrmBridge.
liberato (no reviews please) 2015/12/04 16:20:11 s/fill/will
Tima Vaisburd 2015/12/04 20:12:01 Done.
177 MediaDrmBridge* drm_bridge = static_cast<MediaDrmBridge*>(cdm_.get());
178
179 // Register CDM callbacks. The callbacks registered will be posted back to
180 // this thread via BindToCurrentLoop.
181
182 // Since |this| holds a reference to the |cdm_|, by the time the CDM is
183 // destructed, UnregisterPlayer() must have been called and |this| has been
184 // destructed as well. So the |cdm_unset_cb| will never have a chance to be
185 // called.
186 // TODO(xhwang): Remove |cdm_unset_cb| after it's not used on all platforms.
187 cdm_registration_id_ = drm_bridge->RegisterPlayer(
188 media::BindToCurrentLoop(
189 base::Bind(&AndroidVideoDecodeAccelerator::OnCdmKeyAdded,
190 weak_this_factory_.GetWeakPtr())),
191 base::Bind(&base::DoNothing));
192
193 drm_bridge->SetMediaCryptoReadyCB(media::BindToCurrentLoop(
194 base::Bind(&AndroidVideoDecodeAccelerator::OnCdmMediaCryptoReady,
195 weak_this_factory_.GetWeakPtr())));
196
197 // Postpone NotifyCdmAttached() call till we create the MediaCodec after
198 // OnCdmMediaCryptoReady().
199
200 #else
159 201
160 NOTIMPLEMENTED(); 202 NOTIMPLEMENTED();
161 NotifyCdmAttached(false); 203 NotifyCdmAttached(false);
204
205 #endif // !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
162 } 206 }
163 207
164 void AndroidVideoDecodeAccelerator::DoIOTask() { 208 void AndroidVideoDecodeAccelerator::DoIOTask() {
165 DCHECK(thread_checker_.CalledOnValidThread()); 209 DCHECK(thread_checker_.CalledOnValidThread());
166 TRACE_EVENT0("media", "AVDA::DoIOTask"); 210 TRACE_EVENT0("media", "AVDA::DoIOTask");
167 if (state_ == ERROR) { 211 if (state_ == ERROR) {
168 return; 212 return;
169 } 213 }
170 214
liberato (no reviews please) 2015/12/04 16:20:11 might want to check for |media_codec_| here, since
Tima Vaisburd 2015/12/04 20:12:01 Please explain how. I've assumed that initializati
liberato (no reviews please) 2015/12/04 21:40:13 yeah, i suppose you're right. |init_cb_| doesn't
171 QueueInput(); 215 QueueInput();
172 while (DequeueOutput()) 216 while (DequeueOutput())
173 ; 217 ;
174 } 218 }
175 219
176 void AndroidVideoDecodeAccelerator::QueueInput() { 220 void AndroidVideoDecodeAccelerator::QueueInput() {
177 DCHECK(thread_checker_.CalledOnValidThread()); 221 DCHECK(thread_checker_.CalledOnValidThread());
178 TRACE_EVENT0("media", "AVDA::QueueInput"); 222 TRACE_EVENT0("media", "AVDA::QueueInput");
179 if (bitstreams_notified_in_advance_.size() > kMaxBitstreamsNotifiedInAdvance) 223 if (bitstreams_notified_in_advance_.size() > kMaxBitstreamsNotifiedInAdvance)
180 return; 224 return;
181 if (pending_bitstream_buffers_.empty()) 225 if (pending_bitstream_buffers_.empty())
182 return; 226 return;
183 227
184 int input_buf_index = 0; 228 int input_buf_index = 0;
185 media::MediaCodecStatus status = 229 media::MediaCodecStatus status =
186 media_codec_->DequeueInputBuffer(NoWaitTimeOut(), &input_buf_index); 230 media_codec_->DequeueInputBuffer(NoWaitTimeOut(), &input_buf_index);
187 if (status != media::MEDIA_CODEC_OK) { 231 if (status != media::MEDIA_CODEC_OK) {
188 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER || 232 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER ||
189 status == media::MEDIA_CODEC_ERROR); 233 status == media::MEDIA_CODEC_ERROR);
190 return; 234 return;
191 } 235 }
192 236
193 base::Time queued_time = pending_bitstream_buffers_.front().second; 237 base::Time queued_time = pending_bitstream_buffers_.front().second;
194 UMA_HISTOGRAM_TIMES("Media.AVDA.InputQueueTime", 238 UMA_HISTOGRAM_TIMES("Media.AVDA.InputQueueTime",
195 base::Time::Now() - queued_time); 239 base::Time::Now() - queued_time);
196 media::BitstreamBuffer bitstream_buffer = 240 media::BitstreamBuffer bitstream_buffer =
197 pending_bitstream_buffers_.front().first; 241 pending_bitstream_buffers_.front().first;
198 pending_bitstream_buffers_.pop();
199 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount",
200 pending_bitstream_buffers_.size());
201 242
202 if (bitstream_buffer.id() == -1) { 243 if (bitstream_buffer.id() == -1) {
244 pending_bitstream_buffers_.pop();
245 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount",
246 pending_bitstream_buffers_.size());
247
203 media_codec_->QueueEOS(input_buf_index); 248 media_codec_->QueueEOS(input_buf_index);
204 return; 249 return;
205 } 250 }
206 251
207 scoped_ptr<base::SharedMemory> shm( 252 scoped_ptr<base::SharedMemory> shm(
208 new base::SharedMemory(bitstream_buffer.handle(), true)); 253 new base::SharedMemory(bitstream_buffer.handle(), true));
209 RETURN_ON_FAILURE(this, shm->Map(bitstream_buffer.size()), 254 RETURN_ON_FAILURE(this, shm->Map(bitstream_buffer.size()),
210 "Failed to SharedMemory::Map()", UNREADABLE_INPUT); 255 "Failed to SharedMemory::Map()", UNREADABLE_INPUT);
211 256
212 const base::TimeDelta presentation_timestamp = 257 const base::TimeDelta presentation_timestamp =
(...skipping 22 matching lines...) Expand all
235 } else { 280 } else {
236 status = media_codec_->QueueSecureInputBuffer( 281 status = media_codec_->QueueSecureInputBuffer(
237 input_buf_index, memory, bitstream_buffer.size(), key_id, iv, 282 input_buf_index, memory, bitstream_buffer.size(), key_id, iv,
238 subsamples, presentation_timestamp); 283 subsamples, presentation_timestamp);
239 } 284 }
240 285
241 DVLOG(2) << __FUNCTION__ 286 DVLOG(2) << __FUNCTION__
242 << ": QueueInputBuffer: pts:" << presentation_timestamp 287 << ": QueueInputBuffer: pts:" << presentation_timestamp
243 << " status:" << status; 288 << " status:" << status;
244 289
290 if (status == media::MEDIA_CODEC_NO_KEY)
291 return; // keep trying to enqueue the front pending buffer
xhwang 2015/12/04 20:22:03 This is an important event. Add a log here.
Tima Vaisburd 2015/12/04 22:54:51 The log line above already prints the status, unle
xhwang 2015/12/04 23:24:09 We need a log with higher level for unexpected eve
Tima Vaisburd 2015/12/05 01:35:25 Done, but it would be better to have states and ha
292
293 pending_bitstream_buffers_.pop();
294 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount",
295 pending_bitstream_buffers_.size());
296
245 RETURN_ON_FAILURE(this, status == media::MEDIA_CODEC_OK, 297 RETURN_ON_FAILURE(this, status == media::MEDIA_CODEC_OK,
246 "Failed to QueueInputBuffer: " << status, PLATFORM_FAILURE); 298 "Failed to QueueInputBuffer: " << status, PLATFORM_FAILURE);
247 299
248 // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output 300 // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output
249 // will be returned from the bitstream buffer. However, MediaCodec API is 301 // will be returned from the bitstream buffer. However, MediaCodec API is
250 // not enough to guarantee it. 302 // not enough to guarantee it.
251 // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to 303 // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to
252 // keep getting more bitstreams from the client, and throttle them by using 304 // keep getting more bitstreams from the client, and throttle them by using
253 // |bitstreams_notified_in_advance_|. 305 // |bitstreams_notified_in_advance_|.
254 // TODO(dwkang): check if there is a way to remove this workaround. 306 // TODO(dwkang): check if there is a way to remove this workaround.
(...skipping 243 matching lines...) Expand 10 before | Expand all | Expand 10 after
498 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0)); 550 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0));
499 } 551 }
500 552
501 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodec() { 553 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodec() {
502 DCHECK(thread_checker_.CalledOnValidThread()); 554 DCHECK(thread_checker_.CalledOnValidThread());
503 DCHECK(surface_texture_.get()); 555 DCHECK(surface_texture_.get());
504 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec"); 556 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec");
505 557
506 gfx::ScopedJavaSurface surface(surface_texture_.get()); 558 gfx::ScopedJavaSurface surface(surface_texture_.get());
507 559
560 jobject media_crypto = media_crypto_ ? media_crypto_->obj() : nullptr;
561
508 // Pass a dummy 320x240 canvas size and let the codec signal the real size 562 // Pass a dummy 320x240 canvas size and let the codec signal the real size
509 // when it's known from the bitstream. 563 // when it's known from the bitstream.
510 media_codec_.reset(media::VideoCodecBridge::CreateDecoder( 564 media_codec_.reset(media::VideoCodecBridge::CreateDecoder(
511 codec_, false, gfx::Size(320, 240), surface.j_surface().obj(), NULL)); 565 codec_, false, gfx::Size(320, 240), surface.j_surface().obj(),
566 media_crypto));
512 strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_); 567 strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_);
513 if (!media_codec_) 568 if (!media_codec_) {
569 LOG(ERROR) << "Failed to create MediaCodec instance.";
514 return false; 570 return false;
571 }
515 572
516 io_timer_.Start(FROM_HERE, 573 io_timer_.Start(FROM_HERE,
517 DecodePollDelay(), 574 DecodePollDelay(),
518 this, 575 this,
519 &AndroidVideoDecodeAccelerator::DoIOTask); 576 &AndroidVideoDecodeAccelerator::DoIOTask);
520 return true; 577 return true;
521 } 578 }
522 579
523 void AndroidVideoDecodeAccelerator::Reset() { 580 void AndroidVideoDecodeAccelerator::Reset() {
524 DCHECK(thread_checker_.CalledOnValidThread()); 581 DCHECK(thread_checker_.CalledOnValidThread());
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after
599 656
600 void AndroidVideoDecodeAccelerator::PostError( 657 void AndroidVideoDecodeAccelerator::PostError(
601 const ::tracked_objects::Location& from_here, 658 const ::tracked_objects::Location& from_here,
602 media::VideoDecodeAccelerator::Error error) { 659 media::VideoDecodeAccelerator::Error error) {
603 base::MessageLoop::current()->PostTask( 660 base::MessageLoop::current()->PostTask(
604 from_here, base::Bind(&AndroidVideoDecodeAccelerator::NotifyError, 661 from_here, base::Bind(&AndroidVideoDecodeAccelerator::NotifyError,
605 weak_this_factory_.GetWeakPtr(), error)); 662 weak_this_factory_.GetWeakPtr(), error));
606 state_ = ERROR; 663 state_ = ERROR;
607 } 664 }
608 665
666 void AndroidVideoDecodeAccelerator::OnCdmMediaCryptoReady(
667 media::MediaDrmBridge::JavaObjectPtr media_crypto,
668 bool needs_protected_surface) {
669 DVLOG(1) << __FUNCTION__;
670
671 DCHECK(media_crypto);
xhwang 2015/12/04 20:22:03 With my CL, |media_crypto| can actually be null if
Tima Vaisburd 2015/12/04 22:54:51 Done.
672 DCHECK(!media_crypto->is_null());
liberato (no reviews please) 2015/12/04 16:20:11 DCHECK(!media_codec)
Tima Vaisburd 2015/12/04 20:12:01 I added this DCHECK, but ConfigureMediaCodec() doe
liberato (no reviews please) 2015/12/04 21:40:13 it depends if we've sent in any buffers.
673
674 media_crypto_ = media_crypto.Pass();
675
676 // Configure here instead of in Initialize()
677 const bool success = ConfigureMediaCodec();
678 NotifyCdmAttached(success);
liberato (no reviews please) 2015/12/04 16:20:11 outside the scope of this CL, but NotifyInitializa
Tima Vaisburd 2015/12/04 20:12:01 I heard that there is a work on Version 2 which wi
liberato (no reviews please) 2015/12/04 21:40:13 yes, that's why i think it's outside the scope of
679 }
680
681 void AndroidVideoDecodeAccelerator::OnCdmKeyAdded() {
682 DVLOG(1) << __FUNCTION__;
xhwang 2015/12/04 20:22:03 NOTIMPLEMENTED() Also add a TODO to finish this p
Tima Vaisburd 2015/12/04 22:54:51 Since in this CL we do not try to stop the decodin
xhwang 2015/12/04 23:24:09 I don't fully understand how this works. What's th
Tima Vaisburd 2015/12/05 01:35:25 Added some comments, please take a look.
683 }
684
609 void AndroidVideoDecodeAccelerator::NotifyCdmAttached(bool success) { 685 void AndroidVideoDecodeAccelerator::NotifyCdmAttached(bool success) {
610 client_->NotifyCdmAttached(success); 686 client_->NotifyCdmAttached(success);
611 } 687 }
612 688
613 void AndroidVideoDecodeAccelerator::NotifyPictureReady( 689 void AndroidVideoDecodeAccelerator::NotifyPictureReady(
614 const media::Picture& picture) { 690 const media::Picture& picture) {
615 client_->PictureReady(picture); 691 client_->PictureReady(picture);
616 } 692 }
617 693
618 void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( 694 void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer(
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
666 // software fallback for H264 on Android anyway. 742 // software fallback for H264 on Android anyway.
667 profile.max_resolution.SetSize(3840, 2160); 743 profile.max_resolution.SetSize(3840, 2160);
668 profiles.push_back(profile); 744 profiles.push_back(profile);
669 } 745 }
670 #endif 746 #endif
671 747
672 return profiles; 748 return profiles;
673 } 749 }
674 750
675 } // namespace content 751 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698