OLD | NEW |
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 <stddef.h> | 7 #include <stddef.h> |
8 | 8 |
9 #include "base/android/build_info.h" | 9 #include "base/android/build_info.h" |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/bind_helpers.h" |
11 #include "base/command_line.h" | 12 #include "base/command_line.h" |
12 #include "base/logging.h" | 13 #include "base/logging.h" |
13 #include "base/message_loop/message_loop.h" | 14 #include "base/message_loop/message_loop.h" |
14 #include "base/metrics/histogram.h" | 15 #include "base/metrics/histogram.h" |
15 #include "base/trace_event/trace_event.h" | 16 #include "base/trace_event/trace_event.h" |
16 #include "content/common/gpu/gpu_channel.h" | 17 #include "content/common/gpu/gpu_channel.h" |
17 #include "content/common/gpu/media/android_copying_backing_strategy.h" | 18 #include "content/common/gpu/media/android_copying_backing_strategy.h" |
18 #include "content/common/gpu/media/android_deferred_rendering_backing_strategy.h
" | 19 #include "content/common/gpu/media/android_deferred_rendering_backing_strategy.h
" |
19 #include "content/common/gpu/media/avda_return_on_failure.h" | 20 #include "content/common/gpu/media/avda_return_on_failure.h" |
20 #include "gpu/command_buffer/service/gles2_cmd_decoder.h" | 21 #include "gpu/command_buffer/service/gles2_cmd_decoder.h" |
| 22 #include "media/base/bind_to_current_loop.h" |
21 #include "media/base/bitstream_buffer.h" | 23 #include "media/base/bitstream_buffer.h" |
22 #include "media/base/limits.h" | 24 #include "media/base/limits.h" |
23 #include "media/base/media_switches.h" | 25 #include "media/base/media_switches.h" |
24 #include "media/base/timestamp_constants.h" | 26 #include "media/base/timestamp_constants.h" |
25 #include "media/base/video_decoder_config.h" | 27 #include "media/base/video_decoder_config.h" |
26 #include "media/video/picture.h" | 28 #include "media/video/picture.h" |
27 #include "ui/gl/android/scoped_java_surface.h" | 29 #include "ui/gl/android/scoped_java_surface.h" |
28 #include "ui/gl/android/surface_texture.h" | 30 #include "ui/gl/android/surface_texture.h" |
29 #include "ui/gl/gl_bindings.h" | 31 #include "ui/gl/gl_bindings.h" |
30 | 32 |
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
141 DISALLOW_COPY_AND_ASSIGN(OnFrameAvailableHandler); | 143 DISALLOW_COPY_AND_ASSIGN(OnFrameAvailableHandler); |
142 }; | 144 }; |
143 | 145 |
144 AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( | 146 AndroidVideoDecodeAccelerator::AndroidVideoDecodeAccelerator( |
145 const base::WeakPtr<gpu::gles2::GLES2Decoder> decoder, | 147 const base::WeakPtr<gpu::gles2::GLES2Decoder> decoder, |
146 const base::Callback<bool(void)>& make_context_current) | 148 const base::Callback<bool(void)>& make_context_current) |
147 : client_(NULL), | 149 : client_(NULL), |
148 make_context_current_(make_context_current), | 150 make_context_current_(make_context_current), |
149 codec_(media::kCodecH264), | 151 codec_(media::kCodecH264), |
150 is_encrypted_(false), | 152 is_encrypted_(false), |
| 153 needs_protected_surface_(false), |
151 state_(NO_ERROR), | 154 state_(NO_ERROR), |
152 picturebuffers_requested_(false), | 155 picturebuffers_requested_(false), |
153 gl_decoder_(decoder), | 156 gl_decoder_(decoder), |
| 157 cdm_registration_id_(0), |
154 weak_this_factory_(this) { | 158 weak_this_factory_(this) { |
155 if (UseDeferredRenderingStrategy()) | 159 if (UseDeferredRenderingStrategy()) |
156 strategy_.reset(new AndroidDeferredRenderingBackingStrategy()); | 160 strategy_.reset(new AndroidDeferredRenderingBackingStrategy()); |
157 else | 161 else |
158 strategy_.reset(new AndroidCopyingBackingStrategy()); | 162 strategy_.reset(new AndroidCopyingBackingStrategy()); |
159 } | 163 } |
160 | 164 |
161 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { | 165 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { |
162 DCHECK(thread_checker_.CalledOnValidThread()); | 166 DCHECK(thread_checker_.CalledOnValidThread()); |
| 167 |
| 168 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
| 169 if (cdm_) { |
| 170 DCHECK(cdm_registration_id_); |
| 171 static_cast<media::MediaDrmBridge*>(cdm_.get()) |
| 172 ->UnregisterPlayer(cdm_registration_id_); |
| 173 } |
| 174 #endif // defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
163 } | 175 } |
164 | 176 |
165 bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, | 177 bool AndroidVideoDecodeAccelerator::Initialize(const Config& config, |
166 Client* client) { | 178 Client* client) { |
167 DCHECK(!media_codec_); | 179 DCHECK(!media_codec_); |
168 DCHECK(thread_checker_.CalledOnValidThread()); | 180 DCHECK(thread_checker_.CalledOnValidThread()); |
169 TRACE_EVENT0("media", "AVDA::Initialize"); | 181 TRACE_EVENT0("media", "AVDA::Initialize"); |
170 | 182 |
171 DVLOG(1) << __FUNCTION__ << ": profile:" << config.profile | 183 DVLOG(1) << __FUNCTION__ << ": " << config.AsHumanReadableString(); |
172 << " is_encrypted:" << config.is_encrypted; | |
173 | 184 |
| 185 DCHECK(client); |
174 client_ = client; | 186 client_ = client; |
175 codec_ = VideoCodecProfileToVideoCodec(config.profile); | 187 codec_ = VideoCodecProfileToVideoCodec(config.profile); |
176 is_encrypted_ = config.is_encrypted; | 188 is_encrypted_ = config.is_encrypted; |
177 | 189 |
178 bool profile_supported = codec_ == media::kCodecVP8 || | 190 bool profile_supported = codec_ == media::kCodecVP8 || |
179 codec_ == media::kCodecVP9 || | 191 codec_ == media::kCodecVP9 || |
180 codec_ == media::kCodecH264; | 192 codec_ == media::kCodecH264; |
181 | 193 |
182 if (!profile_supported) { | 194 if (!profile_supported) { |
183 LOG(ERROR) << "Unsupported profile: " << config.profile; | 195 LOG(ERROR) << "Unsupported profile: " << config.profile; |
(...skipping 19 matching lines...) Expand all Loading... |
203 LOG(ERROR) << "Failed to get gles2 decoder instance."; | 215 LOG(ERROR) << "Failed to get gles2 decoder instance."; |
204 return false; | 216 return false; |
205 } | 217 } |
206 | 218 |
207 strategy_->Initialize(this); | 219 strategy_->Initialize(this); |
208 | 220 |
209 surface_texture_ = strategy_->CreateSurfaceTexture(); | 221 surface_texture_ = strategy_->CreateSurfaceTexture(); |
210 on_frame_available_handler_ = | 222 on_frame_available_handler_ = |
211 new OnFrameAvailableHandler(this, surface_texture_); | 223 new OnFrameAvailableHandler(this, surface_texture_); |
212 | 224 |
213 if (!ConfigureMediaCodec()) { | 225 // For encrypted streams we postpone configuration until MediaCrypto is |
214 LOG(ERROR) << "Failed to create MediaCodec instance."; | 226 // available. |
215 return false; | 227 if (is_encrypted_) |
216 } | 228 return true; |
217 | 229 |
218 return true; | 230 return ConfigureMediaCodec(); |
219 } | 231 } |
220 | 232 |
221 void AndroidVideoDecodeAccelerator::SetCdm(int cdm_id) { | 233 void AndroidVideoDecodeAccelerator::SetCdm(int cdm_id) { |
222 DVLOG(2) << __FUNCTION__ << ": " << cdm_id; | 234 DVLOG(2) << __FUNCTION__ << ": " << cdm_id; |
223 | 235 |
224 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) | 236 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
225 // TODO(timav): Implement CDM setting here. See http://crbug.com/542417 | 237 using media::MediaDrmBridge; |
226 scoped_refptr<media::MediaKeys> cdm = media::MojoCdmService::GetCdm(cdm_id); | 238 |
227 DCHECK(cdm); | 239 DCHECK(client_) << "SetCdm() must be called after Initialize()."; |
228 #endif | 240 |
| 241 if (cdm_) { |
| 242 NOTREACHED() << "We do not support resetting CDM."; |
| 243 NotifyCdmAttached(false); |
| 244 return; |
| 245 } |
| 246 |
| 247 cdm_ = media::MojoCdmService::GetCdm(cdm_id); |
| 248 DCHECK(cdm_); |
| 249 |
| 250 // On Android platform the MediaKeys will be its subclass MediaDrmBridge. |
| 251 MediaDrmBridge* drm_bridge = static_cast<MediaDrmBridge*>(cdm_.get()); |
| 252 |
| 253 // Register CDM callbacks. The callbacks registered will be posted back to |
| 254 // this thread via BindToCurrentLoop. |
| 255 |
| 256 // Since |this| holds a reference to the |cdm_|, by the time the CDM is |
| 257 // destructed, UnregisterPlayer() must have been called and |this| has been |
| 258 // destructed as well. So the |cdm_unset_cb| will never have a chance to be |
| 259 // called. |
| 260 // TODO(xhwang): Remove |cdm_unset_cb| after it's not used on all platforms. |
| 261 cdm_registration_id_ = |
| 262 drm_bridge->RegisterPlayer(media::BindToCurrentLoop(base::Bind( |
| 263 &AndroidVideoDecodeAccelerator::OnKeyAdded, |
| 264 weak_this_factory_.GetWeakPtr())), |
| 265 base::Bind(&base::DoNothing)); |
| 266 |
| 267 drm_bridge->SetMediaCryptoReadyCB(media::BindToCurrentLoop( |
| 268 base::Bind(&AndroidVideoDecodeAccelerator::OnMediaCryptoReady, |
| 269 weak_this_factory_.GetWeakPtr()))); |
| 270 |
| 271 // Postpone NotifyCdmAttached() call till we create the MediaCodec after |
| 272 // OnMediaCryptoReady(). |
| 273 |
| 274 #else |
229 | 275 |
230 NOTIMPLEMENTED(); | 276 NOTIMPLEMENTED(); |
231 NotifyCdmAttached(false); | 277 NotifyCdmAttached(false); |
| 278 |
| 279 #endif // !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) |
232 } | 280 } |
233 | 281 |
234 void AndroidVideoDecodeAccelerator::DoIOTask() { | 282 void AndroidVideoDecodeAccelerator::DoIOTask() { |
235 DCHECK(thread_checker_.CalledOnValidThread()); | 283 DCHECK(thread_checker_.CalledOnValidThread()); |
236 TRACE_EVENT0("media", "AVDA::DoIOTask"); | 284 TRACE_EVENT0("media", "AVDA::DoIOTask"); |
237 if (state_ == ERROR) { | 285 if (state_ == ERROR) { |
238 return; | 286 return; |
239 } | 287 } |
240 | 288 |
241 bool did_work = QueueInput(); | 289 bool did_work = QueueInput(); |
(...skipping 18 matching lines...) Expand all Loading... |
260 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER || | 308 DCHECK(status == media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER || |
261 status == media::MEDIA_CODEC_ERROR); | 309 status == media::MEDIA_CODEC_ERROR); |
262 return false; | 310 return false; |
263 } | 311 } |
264 | 312 |
265 base::Time queued_time = pending_bitstream_buffers_.front().second; | 313 base::Time queued_time = pending_bitstream_buffers_.front().second; |
266 UMA_HISTOGRAM_TIMES("Media.AVDA.InputQueueTime", | 314 UMA_HISTOGRAM_TIMES("Media.AVDA.InputQueueTime", |
267 base::Time::Now() - queued_time); | 315 base::Time::Now() - queued_time); |
268 media::BitstreamBuffer bitstream_buffer = | 316 media::BitstreamBuffer bitstream_buffer = |
269 pending_bitstream_buffers_.front().first; | 317 pending_bitstream_buffers_.front().first; |
270 pending_bitstream_buffers_.pop(); | |
271 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", | |
272 pending_bitstream_buffers_.size()); | |
273 | 318 |
274 if (bitstream_buffer.id() == -1) { | 319 if (bitstream_buffer.id() == -1) { |
| 320 pending_bitstream_buffers_.pop(); |
| 321 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", |
| 322 pending_bitstream_buffers_.size()); |
| 323 |
275 media_codec_->QueueEOS(input_buf_index); | 324 media_codec_->QueueEOS(input_buf_index); |
276 return true; | 325 return true; |
277 } | 326 } |
278 | 327 |
279 scoped_ptr<base::SharedMemory> shm( | 328 scoped_ptr<base::SharedMemory> shm( |
280 new base::SharedMemory(bitstream_buffer.handle(), true)); | 329 new base::SharedMemory(bitstream_buffer.handle(), true)); |
281 RETURN_ON_FAILURE(this, shm->Map(bitstream_buffer.size()), | 330 RETURN_ON_FAILURE(this, shm->Map(bitstream_buffer.size()), |
282 "Failed to SharedMemory::Map()", UNREADABLE_INPUT, false); | 331 "Failed to SharedMemory::Map()", UNREADABLE_INPUT, false); |
283 | 332 |
284 const base::TimeDelta presentation_timestamp = | 333 const base::TimeDelta presentation_timestamp = |
(...skipping 19 matching lines...) Expand all Loading... |
304 status = media_codec_->QueueInputBuffer(input_buf_index, memory, | 353 status = media_codec_->QueueInputBuffer(input_buf_index, memory, |
305 bitstream_buffer.size(), | 354 bitstream_buffer.size(), |
306 presentation_timestamp); | 355 presentation_timestamp); |
307 } else { | 356 } else { |
308 status = media_codec_->QueueSecureInputBuffer( | 357 status = media_codec_->QueueSecureInputBuffer( |
309 input_buf_index, memory, bitstream_buffer.size(), key_id, iv, | 358 input_buf_index, memory, bitstream_buffer.size(), key_id, iv, |
310 subsamples, presentation_timestamp); | 359 subsamples, presentation_timestamp); |
311 } | 360 } |
312 | 361 |
313 DVLOG(2) << __FUNCTION__ | 362 DVLOG(2) << __FUNCTION__ |
314 << ": QueueInputBuffer: pts:" << presentation_timestamp | 363 << ": Queue(Secure)InputBuffer: pts:" << presentation_timestamp |
315 << " status:" << status; | 364 << " status:" << status; |
316 | 365 |
| 366 if (status == media::MEDIA_CODEC_NO_KEY) { |
| 367 // Keep trying to enqueue the front pending buffer. |
| 368 // |
| 369 // TODO(timav): Figure out whether stopping the pipeline in response to |
| 370 // this error and restarting it in OnKeyAdded() has significant benefits |
| 371 // (e.g. saving power). |
| 372 DVLOG(1) << "QueueSecureInputBuffer failed: NO_KEY"; |
| 373 return true; |
| 374 } |
| 375 |
| 376 pending_bitstream_buffers_.pop(); |
| 377 TRACE_COUNTER1("media", "AVDA::PendingBitstreamBufferCount", |
| 378 pending_bitstream_buffers_.size()); |
| 379 |
317 RETURN_ON_FAILURE(this, status == media::MEDIA_CODEC_OK, | 380 RETURN_ON_FAILURE(this, status == media::MEDIA_CODEC_OK, |
318 "Failed to QueueInputBuffer: " << status, PLATFORM_FAILURE, | 381 "Failed to QueueInputBuffer: " << status, PLATFORM_FAILURE, |
319 false); | 382 false); |
320 | 383 |
321 // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output | 384 // We should call NotifyEndOfBitstreamBuffer(), when no more decoded output |
322 // will be returned from the bitstream buffer. However, MediaCodec API is | 385 // will be returned from the bitstream buffer. However, MediaCodec API is |
323 // not enough to guarantee it. | 386 // not enough to guarantee it. |
324 // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to | 387 // So, here, we calls NotifyEndOfBitstreamBuffer() in advance in order to |
325 // keep getting more bitstreams from the client, and throttle them by using | 388 // keep getting more bitstreams from the client, and throttle them by using |
326 // |bitstreams_notified_in_advance_|. | 389 // |bitstreams_notified_in_advance_|. |
(...skipping 252 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
579 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0)); | 642 Decode(media::BitstreamBuffer(-1, base::SharedMemoryHandle(), 0)); |
580 } | 643 } |
581 | 644 |
582 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodec() { | 645 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodec() { |
583 DCHECK(thread_checker_.CalledOnValidThread()); | 646 DCHECK(thread_checker_.CalledOnValidThread()); |
584 DCHECK(surface_texture_.get()); | 647 DCHECK(surface_texture_.get()); |
585 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec"); | 648 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec"); |
586 | 649 |
587 gfx::ScopedJavaSurface surface(surface_texture_.get()); | 650 gfx::ScopedJavaSurface surface(surface_texture_.get()); |
588 | 651 |
| 652 jobject media_crypto = media_crypto_ ? media_crypto_->obj() : nullptr; |
| 653 |
| 654 // |needs_protected_surface_| implies encrypted stream. |
| 655 DCHECK(!needs_protected_surface_ || media_crypto); |
| 656 |
589 // Pass a dummy 320x240 canvas size and let the codec signal the real size | 657 // Pass a dummy 320x240 canvas size and let the codec signal the real size |
590 // when it's known from the bitstream. | 658 // when it's known from the bitstream. |
591 media_codec_.reset(media::VideoCodecBridge::CreateDecoder( | 659 media_codec_.reset(media::VideoCodecBridge::CreateDecoder( |
592 codec_, false, gfx::Size(320, 240), surface.j_surface().obj(), NULL)); | 660 codec_, needs_protected_surface_, gfx::Size(320, 240), |
| 661 surface.j_surface().obj(), media_crypto)); |
593 strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_); | 662 strategy_->CodecChanged(media_codec_.get(), output_picture_buffers_); |
594 if (!media_codec_) | 663 if (!media_codec_) { |
| 664 LOG(ERROR) << "Failed to create MediaCodec instance."; |
595 return false; | 665 return false; |
| 666 } |
596 | 667 |
597 ManageTimer(true); | 668 ManageTimer(true); |
598 return true; | 669 return true; |
599 } | 670 } |
600 | 671 |
601 void AndroidVideoDecodeAccelerator::ResetCodecState() { | 672 void AndroidVideoDecodeAccelerator::ResetCodecState() { |
602 // TODO(chcunningham): This will likely dismiss a handful of decoded frames | 673 // TODO(chcunningham): This will likely dismiss a handful of decoded frames |
603 // that have not yet been drawn and returned to us for re-use. Consider | 674 // that have not yet been drawn and returned to us for re-use. Consider |
604 // a more complicated design that would wait for these to be drawn before | 675 // a more complicated design that would wait for these to be drawn before |
605 // dismissing. | 676 // dismissing. |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
705 | 776 |
706 void AndroidVideoDecodeAccelerator::PostError( | 777 void AndroidVideoDecodeAccelerator::PostError( |
707 const ::tracked_objects::Location& from_here, | 778 const ::tracked_objects::Location& from_here, |
708 media::VideoDecodeAccelerator::Error error) { | 779 media::VideoDecodeAccelerator::Error error) { |
709 base::MessageLoop::current()->PostTask( | 780 base::MessageLoop::current()->PostTask( |
710 from_here, base::Bind(&AndroidVideoDecodeAccelerator::NotifyError, | 781 from_here, base::Bind(&AndroidVideoDecodeAccelerator::NotifyError, |
711 weak_this_factory_.GetWeakPtr(), error)); | 782 weak_this_factory_.GetWeakPtr(), error)); |
712 state_ = ERROR; | 783 state_ = ERROR; |
713 } | 784 } |
714 | 785 |
| 786 void AndroidVideoDecodeAccelerator::OnMediaCryptoReady( |
| 787 media::MediaDrmBridge::JavaObjectPtr media_crypto, |
| 788 bool needs_protected_surface) { |
| 789 DVLOG(1) << __FUNCTION__; |
| 790 |
| 791 if (!media_crypto) { |
| 792 LOG(ERROR) << "MediaCrypto is not available, can't play encrypted stream."; |
| 793 NotifyCdmAttached(false); |
| 794 return; |
| 795 } |
| 796 |
| 797 DCHECK(!media_crypto->is_null()); |
| 798 |
| 799 // We assume this is a part of the initialization process, thus MediaCodec |
| 800 // is not created yet. |
| 801 DCHECK(!media_codec_); |
| 802 |
| 803 media_crypto_ = std::move(media_crypto); |
| 804 needs_protected_surface_ = needs_protected_surface; |
| 805 |
| 806 // After receiving |media_crypto_| we can configure MediaCodec. |
| 807 const bool success = ConfigureMediaCodec(); |
| 808 NotifyCdmAttached(success); |
| 809 } |
| 810 |
| 811 void AndroidVideoDecodeAccelerator::OnKeyAdded() { |
| 812 DVLOG(1) << __FUNCTION__; |
| 813 // TODO(timav): Figure out whether stopping the pipeline in response to |
| 814 // NO_KEY error and restarting it here has significant benefits (e.g. saving |
| 815 // power). Right now do nothing here. |
| 816 } |
| 817 |
715 void AndroidVideoDecodeAccelerator::NotifyCdmAttached(bool success) { | 818 void AndroidVideoDecodeAccelerator::NotifyCdmAttached(bool success) { |
716 client_->NotifyCdmAttached(success); | 819 client_->NotifyCdmAttached(success); |
717 } | 820 } |
718 | 821 |
719 void AndroidVideoDecodeAccelerator::NotifyPictureReady( | 822 void AndroidVideoDecodeAccelerator::NotifyPictureReady( |
720 const media::Picture& picture) { | 823 const media::Picture& picture) { |
721 client_->PictureReady(picture); | 824 client_->PictureReady(picture); |
722 } | 825 } |
723 | 826 |
724 void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( | 827 void AndroidVideoDecodeAccelerator::NotifyEndOfBitstreamBuffer( |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
802 | 905 |
803 if (UseDeferredRenderingStrategy()) { | 906 if (UseDeferredRenderingStrategy()) { |
804 capabilities.flags = media::VideoDecodeAccelerator::Capabilities:: | 907 capabilities.flags = media::VideoDecodeAccelerator::Capabilities:: |
805 NEEDS_ALL_PICTURE_BUFFERS_TO_DECODE; | 908 NEEDS_ALL_PICTURE_BUFFERS_TO_DECODE; |
806 } | 909 } |
807 | 910 |
808 return capabilities; | 911 return capabilities; |
809 } | 912 } |
810 | 913 |
811 } // namespace content | 914 } // namespace content |
OLD | NEW |