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

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

Issue 1920093003: media: Handle output SurfaceView destruction in AVDA (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@svteardown
Patch Set: WIP drain decoder Created 4 years, 7 months 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
« no previous file with comments | « media/gpu/android_video_decode_accelerator.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "media/gpu/android_video_decode_accelerator.h" 5 #include "media/gpu/android_video_decode_accelerator.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 8
9 #include <memory> 9 #include <memory>
10 10
(...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after
311 is_encrypted_(false), 311 is_encrypted_(false),
312 state_(NO_ERROR), 312 state_(NO_ERROR),
313 picturebuffers_requested_(false), 313 picturebuffers_requested_(false),
314 drain_type_(DRAIN_TYPE_NONE), 314 drain_type_(DRAIN_TYPE_NONE),
315 media_drm_bridge_cdm_context_(nullptr), 315 media_drm_bridge_cdm_context_(nullptr),
316 cdm_registration_id_(0), 316 cdm_registration_id_(0),
317 pending_input_buf_index_(-1), 317 pending_input_buf_index_(-1),
318 error_sequence_token_(0), 318 error_sequence_token_(0),
319 defer_errors_(false), 319 defer_errors_(false),
320 deferred_initialization_pending_(false), 320 deferred_initialization_pending_(false),
321 surface_id_(media::VideoDecodeAccelerator::Config::kNoSurfaceID),
321 weak_this_factory_(this) {} 322 weak_this_factory_(this) {}
322 323
323 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() { 324 AndroidVideoDecodeAccelerator::~AndroidVideoDecodeAccelerator() {
324 DCHECK(thread_checker_.CalledOnValidThread()); 325 DCHECK(thread_checker_.CalledOnValidThread());
325 g_avda_timer.Pointer()->StopTimer(this); 326 g_avda_timer.Pointer()->StopTimer(this);
326 g_avda_timer.Pointer()->StopThread(this); 327 g_avda_timer.Pointer()->StopThread(this);
327 328
328 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) 329 #if defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
329 if (!media_drm_bridge_cdm_context_) 330 if (!media_drm_bridge_cdm_context_)
330 return; 331 return;
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
408 } else { 409 } else {
409 DVLOG(1) << __FUNCTION__ << ", using copy back strategy."; 410 DVLOG(1) << __FUNCTION__ << ", using copy back strategy.";
410 strategy_.reset(new AndroidCopyingBackingStrategy(this)); 411 strategy_.reset(new AndroidCopyingBackingStrategy(this));
411 } 412 }
412 413
413 if (!make_context_current_cb_.Run()) { 414 if (!make_context_current_cb_.Run()) {
414 LOG(ERROR) << "Failed to make this decoder's GL context current."; 415 LOG(ERROR) << "Failed to make this decoder's GL context current.";
415 return false; 416 return false;
416 } 417 }
417 418
418 codec_config_->surface_ = strategy_->Initialize(config.surface_id); 419 surface_id_ = config.surface_id;
420 codec_config_->surface_ = strategy_->Initialize(surface_id_);
419 if (codec_config_->surface_.IsEmpty()) { 421 if (codec_config_->surface_.IsEmpty()) {
420 LOG(ERROR) << "Failed to initialize the backing strategy. The returned " 422 LOG(ERROR) << "Failed to initialize the backing strategy. The returned "
421 "Java surface is empty."; 423 "Java surface is empty.";
422 return false; 424 return false;
423 } 425 }
424 426
427 on_destroying_surface_cb_ =
428 base::Bind(&AndroidVideoDecodeAccelerator::OnDestroyingSurface,
429 weak_this_factory_.GetWeakPtr());
430 AVDASurfaceTracker::GetInstance()->RegisterOnDestroyingSurfaceCallback(
431 on_destroying_surface_cb_);
432
425 // TODO(watk,liberato): move this into the strategy. 433 // TODO(watk,liberato): move this into the strategy.
426 scoped_refptr<gfx::SurfaceTexture> surface_texture = 434 scoped_refptr<gfx::SurfaceTexture> surface_texture =
427 strategy_->GetSurfaceTexture(); 435 strategy_->GetSurfaceTexture();
428 if (surface_texture) { 436 if (surface_texture) {
429 on_frame_available_handler_ = 437 on_frame_available_handler_ =
430 new OnFrameAvailableHandler(this, surface_texture); 438 new OnFrameAvailableHandler(this, surface_texture);
431 } 439 }
432 440
433 // Start the thread for async configuration, even if we don't need it now. 441 // Start the thread for async configuration, even if we don't need it now.
434 // ResetCodecState might rebuild the codec later, for example. 442 // ResetCodecState might rebuild the codec later, for example.
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
496 504
497 NOTIMPLEMENTED(); 505 NOTIMPLEMENTED();
498 NotifyInitializationComplete(false); 506 NotifyInitializationComplete(false);
499 507
500 #endif // !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS) 508 #endif // !defined(ENABLE_MOJO_MEDIA_IN_GPU_PROCESS)
501 } 509 }
502 510
503 void AndroidVideoDecodeAccelerator::DoIOTask(bool start_timer) { 511 void AndroidVideoDecodeAccelerator::DoIOTask(bool start_timer) {
504 DCHECK(thread_checker_.CalledOnValidThread()); 512 DCHECK(thread_checker_.CalledOnValidThread());
505 TRACE_EVENT0("media", "AVDA::DoIOTask"); 513 TRACE_EVENT0("media", "AVDA::DoIOTask");
506 if (state_ == ERROR || state_ == WAITING_FOR_CODEC) 514 if (state_ == ERROR || state_ == WAITING_FOR_CODEC ||
515 state_ == SURFACE_DESTROYED) {
507 return; 516 return;
517 }
508 518
509 strategy_->MaybeRenderEarly(); 519 strategy_->MaybeRenderEarly();
510 bool did_work = QueueInput(); 520 bool did_work = QueueInput();
511 while (DequeueOutput()) 521 while (DequeueOutput())
512 did_work = true; 522 did_work = true;
513 523
514 ManageTimer(did_work || start_timer); 524 ManageTimer(did_work || start_timer);
515 } 525 }
516 526
517 bool AndroidVideoDecodeAccelerator::QueueInput() { 527 bool AndroidVideoDecodeAccelerator::QueueInput() {
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
667 TRACE_EVENT_BEGIN0("media", "AVDA::DequeueOutput"); 677 TRACE_EVENT_BEGIN0("media", "AVDA::DequeueOutput");
668 media::MediaCodecStatus status = media_codec_->DequeueOutputBuffer( 678 media::MediaCodecStatus status = media_codec_->DequeueOutputBuffer(
669 NoWaitTimeOut(), &buf_index, &offset, &size, &presentation_timestamp, 679 NoWaitTimeOut(), &buf_index, &offset, &size, &presentation_timestamp,
670 &eos, NULL); 680 &eos, NULL);
671 TRACE_EVENT_END2("media", "AVDA::DequeueOutput", "status", status, 681 TRACE_EVENT_END2("media", "AVDA::DequeueOutput", "status", status,
672 "presentation_timestamp (ms)", 682 "presentation_timestamp (ms)",
673 presentation_timestamp.InMilliseconds()); 683 presentation_timestamp.InMilliseconds());
674 684
675 switch (status) { 685 switch (status) {
676 case media::MEDIA_CODEC_ERROR: 686 case media::MEDIA_CODEC_ERROR:
677 // Do not post an error if we are draining for reset and destroy. 687 // Do not post an error if we are draining for reset or destroy.
678 // Instead, run the drain completion task. 688 // Instead, run the drain completion task.
679 if (IsDrainingForResetOrDestroy()) { 689 if (IsDrainingForResetOrDestroy()) {
680 DVLOG(1) << __FUNCTION__ << ": error while codec draining"; 690 DVLOG(1) << __FUNCTION__ << ": error while codec draining";
681 state_ = ERROR; 691 state_ = ERROR;
682 OnDrainCompleted(); 692 OnDrainCompleted();
683 } else { 693 } else {
684 POST_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed."); 694 POST_ERROR(PLATFORM_FAILURE, "DequeueOutputBuffer failed.");
685 } 695 }
686 return false; 696 return false;
687 697
(...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after
933 } 943 }
934 944
935 strategy_->ReuseOnePictureBuffer(i->second); 945 strategy_->ReuseOnePictureBuffer(i->second);
936 DoIOTask(true); 946 DoIOTask(true);
937 } 947 }
938 948
939 void AndroidVideoDecodeAccelerator::Flush() { 949 void AndroidVideoDecodeAccelerator::Flush() {
940 DVLOG(1) << __FUNCTION__; 950 DVLOG(1) << __FUNCTION__;
941 DCHECK(thread_checker_.CalledOnValidThread()); 951 DCHECK(thread_checker_.CalledOnValidThread());
942 952
943 StartCodecDrain(DRAIN_FOR_FLUSH); 953 if (state_ == SURFACE_DESTROYED) {
954 NotifyFlushDone();
955 } else {
956 StartCodecDrain(DRAIN_FOR_FLUSH);
957 }
944 } 958 }
945 959
946 void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() { 960 void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() {
947 DCHECK(thread_checker_.CalledOnValidThread()); 961 DCHECK(thread_checker_.CalledOnValidThread());
948 962
949 // It's probably okay just to return here, since the codec will be configured 963 // It's probably okay just to return here, since the codec will be configured
950 // asynchronously. It's unclear that any state for the new request could 964 // asynchronously. It's unclear that any state for the new request could
951 // be different, unless somebody modifies |codec_config_| while we're already 965 // be different, unless somebody modifies |codec_config_| while we're already
952 // waiting for a codec. One shouldn't do that for thread safety. 966 // waiting for a codec. One shouldn't do that for thread safety.
953 DCHECK_NE(state_, WAITING_FOR_CODEC); 967 DCHECK_NE(state_, WAITING_FOR_CODEC);
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after
999 return std::unique_ptr<media::VideoCodecBridge>( 1013 return std::unique_ptr<media::VideoCodecBridge>(
1000 media::VideoCodecBridge::CreateDecoder( 1014 media::VideoCodecBridge::CreateDecoder(
1001 codec_config->codec_, codec_config->needs_protected_surface_, 1015 codec_config->codec_, codec_config->needs_protected_surface_,
1002 codec_config->initial_expected_coded_size_, 1016 codec_config->initial_expected_coded_size_,
1003 codec_config->surface_.j_surface().obj(), media_crypto, true)); 1017 codec_config->surface_.j_surface().obj(), media_crypto, true));
1004 } 1018 }
1005 1019
1006 void AndroidVideoDecodeAccelerator::OnCodecConfigured( 1020 void AndroidVideoDecodeAccelerator::OnCodecConfigured(
1007 std::unique_ptr<media::VideoCodecBridge> media_codec) { 1021 std::unique_ptr<media::VideoCodecBridge> media_codec) {
1008 DCHECK(thread_checker_.CalledOnValidThread()); 1022 DCHECK(thread_checker_.CalledOnValidThread());
1009 DCHECK_EQ(state_, WAITING_FOR_CODEC); 1023 DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED);
1010
1011 media_codec_ = std::move(media_codec);
1012 1024
1013 // Record one instance of the codec being initialized. 1025 // Record one instance of the codec being initialized.
1014 RecordFormatChangedMetric(FormatChangedValue::CodecInitialized); 1026 RecordFormatChangedMetric(FormatChangedValue::CodecInitialized);
1015 1027
1016 strategy_->CodecChanged(media_codec_.get());
1017
1018 // If we are supposed to notify that initialization is complete, then do so 1028 // If we are supposed to notify that initialization is complete, then do so
1019 // now. Otherwise, this is a reconfiguration. 1029 // now. Otherwise, this is a reconfiguration.
1020 if (deferred_initialization_pending_) { 1030 if (deferred_initialization_pending_) {
1021 NotifyInitializationComplete(!!media_codec_); 1031 // Losing the output surface is not considered an error state, so notify
1032 // success. The client will destroy this soon.
1033 NotifyInitializationComplete(state_ == SURFACE_DESTROYED ? true
1034 : !!media_codec);
1022 deferred_initialization_pending_ = false; 1035 deferred_initialization_pending_ = false;
1023 } 1036 }
1024 1037
1038 // If |state_| changed to SURFACE_DESTROYED while we were configuring a codec,
1039 // then the codec is already invalid so we return early and drop it.
1040 if (state_ == SURFACE_DESTROYED)
1041 return;
1042
1043 media_codec_ = std::move(media_codec);
1044 strategy_->CodecChanged(media_codec_.get());
1025 if (!media_codec_) { 1045 if (!media_codec_) {
1026 POST_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec."); 1046 POST_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec.");
1027 return; 1047 return;
1028 } 1048 }
1029 1049
1030 state_ = NO_ERROR; 1050 state_ = NO_ERROR;
1031 1051
1032 ManageTimer(true); 1052 ManageTimer(true);
1033 } 1053 }
1034 1054
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1090 } 1110 }
1091 drain_type_ = DRAIN_TYPE_NONE; 1111 drain_type_ = DRAIN_TYPE_NONE;
1092 } 1112 }
1093 1113
1094 void AndroidVideoDecodeAccelerator::ResetCodecState( 1114 void AndroidVideoDecodeAccelerator::ResetCodecState(
1095 const base::Closure& done_cb) { 1115 const base::Closure& done_cb) {
1096 DCHECK(thread_checker_.CalledOnValidThread()); 1116 DCHECK(thread_checker_.CalledOnValidThread());
1097 1117
1098 // If there is already a reset in flight, then that counts. This can really 1118 // If there is already a reset in flight, then that counts. This can really
1099 // only happen if somebody calls Reset. 1119 // only happen if somebody calls Reset.
1100 if (state_ == WAITING_FOR_CODEC) { 1120 // If the surface is destroyed there's nothing to do.
1121 if (state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED) {
1101 if (!done_cb.is_null()) 1122 if (!done_cb.is_null())
1102 done_cb.Run(); 1123 done_cb.Run();
1103 return; 1124 return;
1104 } 1125 }
1105 1126
1106 bitstream_buffers_in_decoder_.clear(); 1127 bitstream_buffers_in_decoder_.clear();
1107 1128
1108 if (pending_input_buf_index_ != -1) { 1129 if (pending_input_buf_index_ != -1) {
1109 // The data for that index exists in the input buffer, but corresponding 1130 // The data for that index exists in the input buffer, but corresponding
1110 // shm block been deleted. Check that it is safe to flush the coec, i.e. 1131 // shm block been deleted. Check that it is safe to flush the coec, i.e.
(...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after
1217 StartCodecDrain(DRAIN_FOR_DESTROY); 1238 StartCodecDrain(DRAIN_FOR_DESTROY);
1218 } else { 1239 } else {
1219 ActualDestroy(); 1240 ActualDestroy();
1220 } 1241 }
1221 } 1242 }
1222 1243
1223 void AndroidVideoDecodeAccelerator::ActualDestroy() { 1244 void AndroidVideoDecodeAccelerator::ActualDestroy() {
1224 DVLOG(1) << __FUNCTION__; 1245 DVLOG(1) << __FUNCTION__;
1225 DCHECK(thread_checker_.CalledOnValidThread()); 1246 DCHECK(thread_checker_.CalledOnValidThread());
1226 1247
1248 if (!on_destroying_surface_cb_.is_null()) {
1249 AVDASurfaceTracker::GetInstance()->UnregisterOnDestroyingSurfaceCallback(
1250 on_destroying_surface_cb_);
1251 }
1252
1227 // Note that async codec construction might still be in progress. In that 1253 // Note that async codec construction might still be in progress. In that
1228 // case, the codec will be deleted when it completes once we invalidate all 1254 // case, the codec will be deleted when it completes once we invalidate all
1229 // our weak refs. 1255 // our weak refs.
1230 weak_this_factory_.InvalidateWeakPtrs(); 1256 weak_this_factory_.InvalidateWeakPtrs();
1231 if (media_codec_) { 1257 if (media_codec_) {
1232 g_avda_timer.Pointer()->StopTimer(this); 1258 g_avda_timer.Pointer()->StopTimer(this);
1233 media_codec_.reset(); 1259 media_codec_.reset();
1234 } 1260 }
1235 delete this; 1261 delete this;
1236 } 1262 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
1270 1296
1271 DCHECK_LE(1u, picture_buffer.internal_texture_ids().size()); 1297 DCHECK_LE(1u, picture_buffer.internal_texture_ids().size());
1272 gpu::gles2::TextureRef* texture_ref = 1298 gpu::gles2::TextureRef* texture_ref =
1273 texture_manager->GetTexture(picture_buffer.internal_texture_ids()[0]); 1299 texture_manager->GetTexture(picture_buffer.internal_texture_ids()[0]);
1274 RETURN_ON_FAILURE(this, texture_manager, "Null texture_ref", ILLEGAL_STATE, 1300 RETURN_ON_FAILURE(this, texture_manager, "Null texture_ref", ILLEGAL_STATE,
1275 nullptr); 1301 nullptr);
1276 1302
1277 return texture_ref; 1303 return texture_ref;
1278 } 1304 }
1279 1305
1306 void SynchronouslyDrainCodec() {
1307 // The timeout for dequeueing input/output buffers picked mostly arbitrarily.
1308 // It's about as long as a working MediaCodec might conceivably delay before
1309 // returning something. If the timeout is exceeded the MediaCodec might hang
1310 // when we try to delete it later, but that's effectively the same thing as
1311 // waiting longer here anyway.
1312 const base::TimeDelta kTimeout = base::TimeDelta::FromSeconds(3);
1313 strategy_->ReleaseCodecBuffers(output_picture_buffers_);
1314
1315 // If we're in the middle of a drain already, don't queue another EOS; just
1316 // drain until we get the one already queued.
1317 if (drain_type_ != DRAIN_TYPE_NONE) {
1318 int input_buf_index = -1;
1319 media::MediaCodecStatus status =
1320 media_codec_->DequeueInputBuffer(kTimeout, &input_buf_index);
liberato (no reviews please) 2016/05/04 14:19:56 input buffers might become available after we deco
watk 2016/05/04 18:18:46 Do we care about input buffers? We only need to qu
liberato (no reviews please) 2016/05/04 18:30:00 my concern is how does one know that an input buff
1321 if (status != media::MEDIA_CODEC_OK)
1322 return;
1323 DCHECK_NE(input_buf_index, -1);
1324
1325 media_codec_->QueueEOS(input_buf_index);
1326 }
1327
1328 // Drain all of the output buffers.
1329 for (;;) {
1330 int32_t buf_index = -1;
1331 size_t offset = 0;
1332 size_t size = 0;
1333 base::TimeDelta presentation_timestamp;
1334 bool eos = false;
1335 media::MediaCodecStatus status =
1336 media_codec_->DequeueOutputBuffer(kTimeout, &buf_index, &offset, &size,
1337 &presentation_timestamp, &eos, NULL);
1338 switch (status) {
1339 case media::MEDIA_CODEC_ERROR:
1340 case media::MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
1341 return;
1342 case media::MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
1343 case media::MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
1344 continue;
1345 case media::MEDIA_CODEC_OK:
1346 DCHECK_GE(buf_index, 0);
1347 if (eos)
1348 return;
1349 media_codec_->ReleaseOutputBuffer(buf_index, false);
1350 }
1351 }
1352 }
1353
1354 void AndroidVideoDecodeAccelerator::OnDestroyingSurface(int surface_id) {
1355 DCHECK(thread_checker_.CalledOnValidThread());
1356 TRACE_EVENT0("media", "AVDA::OnDestroyingSurface");
1357 DVLOG(1) << __FUNCTION__ << " surface_id: " << surface_id;
1358
1359 if (surface_id != surface_id_)
1360 return;
1361
1362 // Some VP8 files require a complete MediaCodec drain before we can call
1363 // MediaCodec.flush() or MediaCodec.reset() so we must synchronously drain
1364 // the MediaCodec here. http://crbug.com/598963.
1365 if (media_codec_ && codec_config_->codec_ == media::kCodecVP8)
1366 SynchronouslyDrainCodec();
1367
1368 // If we're currently asynchronously configuring a codec, it will be destroyed
1369 // when configuration completes and it notices that |state_| has changed to
1370 // SURFACE_DESTROYED.
1371 state_ = SURFACE_DESTROYED;
1372 if (media_codec_) {
1373 media_codec_.reset();
1374 strategy_->CodecChanged(media_codec_.get());
1375 }
1376
1377 // If we're draining, signal completion now because either the drain was
1378 // finished above, or the drain can no longer proceed anyway.
1379 if (drain_type_ != DRAIN_TYPE_NONE)
1380 OnDrainCompleted();
1381 }
1382
1280 void AndroidVideoDecodeAccelerator::OnFrameAvailable() { 1383 void AndroidVideoDecodeAccelerator::OnFrameAvailable() {
1281 // Remember: this may be on any thread. 1384 // Remember: this may be on any thread.
1282 DCHECK(strategy_); 1385 DCHECK(strategy_);
1283 strategy_->OnFrameAvailable(); 1386 strategy_->OnFrameAvailable();
1284 } 1387 }
1285 1388
1286 void AndroidVideoDecodeAccelerator::PostError( 1389 void AndroidVideoDecodeAccelerator::PostError(
1287 const ::tracked_objects::Location& from_here, 1390 const ::tracked_objects::Location& from_here,
1288 media::VideoDecodeAccelerator::Error error) { 1391 media::VideoDecodeAccelerator::Error error) {
1289 base::MessageLoop::current()->PostDelayedTask( 1392 base::MessageLoop::current()->PostDelayedTask(
(...skipping 168 matching lines...) Expand 10 before | Expand all | Expand 10 after
1458 if (media::MediaCodecUtil::IsSurfaceViewOutputSupported()) { 1561 if (media::MediaCodecUtil::IsSurfaceViewOutputSupported()) {
1459 capabilities.flags |= media::VideoDecodeAccelerator::Capabilities:: 1562 capabilities.flags |= media::VideoDecodeAccelerator::Capabilities::
1460 SUPPORTS_EXTERNAL_OUTPUT_SURFACE; 1563 SUPPORTS_EXTERNAL_OUTPUT_SURFACE;
1461 } 1564 }
1462 } 1565 }
1463 1566
1464 return capabilities; 1567 return capabilities;
1465 } 1568 }
1466 1569
1467 } // namespace media 1570 } // namespace media
OLDNEW
« no previous file with comments | « media/gpu/android_video_decode_accelerator.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698