Chromium Code Reviews| 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 "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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 = false, did_input = false, did_output = false; | 520 bool did_work = false, did_input = false, did_output = false; |
| 511 do { | 521 do { |
| 512 did_input = QueueInput(); | 522 did_input = QueueInput(); |
| 513 did_output = DequeueOutput(); | 523 did_output = DequeueOutput(); |
| 514 if (did_input || did_output) | 524 if (did_input || did_output) |
| 515 did_work = true; | 525 did_work = true; |
| 516 } while (did_input || did_output); | 526 } while (did_input || did_output); |
| 517 | 527 |
| (...skipping 419 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 937 } | 947 } |
| 938 | 948 |
| 939 strategy_->ReuseOnePictureBuffer(i->second); | 949 strategy_->ReuseOnePictureBuffer(i->second); |
| 940 DoIOTask(true); | 950 DoIOTask(true); |
| 941 } | 951 } |
| 942 | 952 |
| 943 void AndroidVideoDecodeAccelerator::Flush() { | 953 void AndroidVideoDecodeAccelerator::Flush() { |
| 944 DVLOG(1) << __FUNCTION__; | 954 DVLOG(1) << __FUNCTION__; |
| 945 DCHECK(thread_checker_.CalledOnValidThread()); | 955 DCHECK(thread_checker_.CalledOnValidThread()); |
| 946 | 956 |
| 947 StartCodecDrain(DRAIN_FOR_FLUSH); | 957 if (state_ == SURFACE_DESTROYED) |
| 958 NotifyFlushDone(); | |
| 959 else | |
| 960 StartCodecDrain(DRAIN_FOR_FLUSH); | |
| 948 } | 961 } |
| 949 | 962 |
| 950 void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() { | 963 void AndroidVideoDecodeAccelerator::ConfigureMediaCodecAsynchronously() { |
| 951 DCHECK(thread_checker_.CalledOnValidThread()); | 964 DCHECK(thread_checker_.CalledOnValidThread()); |
| 952 | 965 |
| 953 // It's probably okay just to return here, since the codec will be configured | 966 // It's probably okay just to return here, since the codec will be configured |
| 954 // asynchronously. It's unclear that any state for the new request could | 967 // asynchronously. It's unclear that any state for the new request could |
| 955 // be different, unless somebody modifies |codec_config_| while we're already | 968 // be different, unless somebody modifies |codec_config_| while we're already |
| 956 // waiting for a codec. One shouldn't do that for thread safety. | 969 // waiting for a codec. One shouldn't do that for thread safety. |
| 957 DCHECK_NE(state_, WAITING_FOR_CODEC); | 970 DCHECK_NE(state_, WAITING_FOR_CODEC); |
| (...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1003 return std::unique_ptr<media::VideoCodecBridge>( | 1016 return std::unique_ptr<media::VideoCodecBridge>( |
| 1004 media::VideoCodecBridge::CreateDecoder( | 1017 media::VideoCodecBridge::CreateDecoder( |
| 1005 codec_config->codec_, codec_config->needs_protected_surface_, | 1018 codec_config->codec_, codec_config->needs_protected_surface_, |
| 1006 codec_config->initial_expected_coded_size_, | 1019 codec_config->initial_expected_coded_size_, |
| 1007 codec_config->surface_.j_surface().obj(), media_crypto, true)); | 1020 codec_config->surface_.j_surface().obj(), media_crypto, true)); |
| 1008 } | 1021 } |
| 1009 | 1022 |
| 1010 void AndroidVideoDecodeAccelerator::OnCodecConfigured( | 1023 void AndroidVideoDecodeAccelerator::OnCodecConfigured( |
| 1011 std::unique_ptr<media::VideoCodecBridge> media_codec) { | 1024 std::unique_ptr<media::VideoCodecBridge> media_codec) { |
| 1012 DCHECK(thread_checker_.CalledOnValidThread()); | 1025 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1013 DCHECK_EQ(state_, WAITING_FOR_CODEC); | 1026 DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED); |
| 1014 | |
| 1015 media_codec_ = std::move(media_codec); | |
| 1016 | 1027 |
| 1017 // Record one instance of the codec being initialized. | 1028 // Record one instance of the codec being initialized. |
| 1018 RecordFormatChangedMetric(FormatChangedValue::CodecInitialized); | 1029 RecordFormatChangedMetric(FormatChangedValue::CodecInitialized); |
| 1019 | 1030 |
| 1020 strategy_->CodecChanged(media_codec_.get()); | |
| 1021 | |
| 1022 // If we are supposed to notify that initialization is complete, then do so | 1031 // If we are supposed to notify that initialization is complete, then do so |
| 1023 // now. Otherwise, this is a reconfiguration. | 1032 // now. Otherwise, this is a reconfiguration. |
| 1024 if (deferred_initialization_pending_) { | 1033 if (deferred_initialization_pending_) { |
| 1025 NotifyInitializationComplete(!!media_codec_); | 1034 // Losing the output surface is not considered an error state, so notify |
| 1035 // success. The client will destroy this soon. | |
| 1036 NotifyInitializationComplete(state_ == SURFACE_DESTROYED ? true | |
| 1037 : !!media_codec); | |
| 1026 deferred_initialization_pending_ = false; | 1038 deferred_initialization_pending_ = false; |
| 1027 } | 1039 } |
| 1028 | 1040 |
| 1041 // If |state_| changed to SURFACE_DESTROYED while we were configuring a codec, | |
| 1042 // then the codec is already invalid so we return early and drop it. | |
| 1043 if (state_ == SURFACE_DESTROYED) | |
| 1044 return; | |
| 1045 | |
| 1046 media_codec_ = std::move(media_codec); | |
| 1047 strategy_->CodecChanged(media_codec_.get()); | |
| 1029 if (!media_codec_) { | 1048 if (!media_codec_) { |
| 1030 POST_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec."); | 1049 POST_ERROR(PLATFORM_FAILURE, "Failed to create MediaCodec."); |
| 1031 return; | 1050 return; |
| 1032 } | 1051 } |
| 1033 | 1052 |
| 1034 state_ = NO_ERROR; | 1053 state_ = NO_ERROR; |
| 1035 | 1054 |
| 1036 ManageTimer(true); | 1055 ManageTimer(true); |
| 1037 } | 1056 } |
| 1038 | 1057 |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1094 } | 1113 } |
| 1095 drain_type_ = DRAIN_TYPE_NONE; | 1114 drain_type_ = DRAIN_TYPE_NONE; |
| 1096 } | 1115 } |
| 1097 | 1116 |
| 1098 void AndroidVideoDecodeAccelerator::ResetCodecState( | 1117 void AndroidVideoDecodeAccelerator::ResetCodecState( |
| 1099 const base::Closure& done_cb) { | 1118 const base::Closure& done_cb) { |
| 1100 DCHECK(thread_checker_.CalledOnValidThread()); | 1119 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1101 | 1120 |
| 1102 // If there is already a reset in flight, then that counts. This can really | 1121 // If there is already a reset in flight, then that counts. This can really |
| 1103 // only happen if somebody calls Reset. | 1122 // only happen if somebody calls Reset. |
| 1104 if (state_ == WAITING_FOR_CODEC) { | 1123 // If the surface is destroyed there's nothing to do. |
| 1124 if (state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED) { | |
| 1105 if (!done_cb.is_null()) | 1125 if (!done_cb.is_null()) |
| 1106 done_cb.Run(); | 1126 done_cb.Run(); |
| 1107 return; | 1127 return; |
| 1108 } | 1128 } |
| 1109 | 1129 |
| 1110 bitstream_buffers_in_decoder_.clear(); | 1130 bitstream_buffers_in_decoder_.clear(); |
| 1111 | 1131 |
| 1112 if (pending_input_buf_index_ != -1) { | 1132 if (pending_input_buf_index_ != -1) { |
| 1113 // The data for that index exists in the input buffer, but corresponding | 1133 // The data for that index exists in the input buffer, but corresponding |
| 1114 // shm block been deleted. Check that it is safe to flush the coec, i.e. | 1134 // 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 Loading... | |
| 1221 StartCodecDrain(DRAIN_FOR_DESTROY); | 1241 StartCodecDrain(DRAIN_FOR_DESTROY); |
| 1222 } else { | 1242 } else { |
| 1223 ActualDestroy(); | 1243 ActualDestroy(); |
| 1224 } | 1244 } |
| 1225 } | 1245 } |
| 1226 | 1246 |
| 1227 void AndroidVideoDecodeAccelerator::ActualDestroy() { | 1247 void AndroidVideoDecodeAccelerator::ActualDestroy() { |
| 1228 DVLOG(1) << __FUNCTION__; | 1248 DVLOG(1) << __FUNCTION__; |
| 1229 DCHECK(thread_checker_.CalledOnValidThread()); | 1249 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1230 | 1250 |
| 1251 if (!on_destroying_surface_cb_.is_null()) { | |
| 1252 AVDASurfaceTracker::GetInstance()->UnregisterOnDestroyingSurfaceCallback( | |
| 1253 on_destroying_surface_cb_); | |
| 1254 } | |
| 1255 | |
| 1231 // Note that async codec construction might still be in progress. In that | 1256 // Note that async codec construction might still be in progress. In that |
| 1232 // case, the codec will be deleted when it completes once we invalidate all | 1257 // case, the codec will be deleted when it completes once we invalidate all |
| 1233 // our weak refs. | 1258 // our weak refs. |
| 1234 weak_this_factory_.InvalidateWeakPtrs(); | 1259 weak_this_factory_.InvalidateWeakPtrs(); |
| 1235 if (media_codec_) { | 1260 if (media_codec_) { |
| 1236 g_avda_timer.Pointer()->StopTimer(this); | 1261 g_avda_timer.Pointer()->StopTimer(this); |
| 1237 media_codec_.reset(); | 1262 media_codec_.reset(); |
| 1238 } | 1263 } |
| 1239 delete this; | 1264 delete this; |
| 1240 } | 1265 } |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1274 | 1299 |
| 1275 DCHECK_LE(1u, picture_buffer.internal_texture_ids().size()); | 1300 DCHECK_LE(1u, picture_buffer.internal_texture_ids().size()); |
| 1276 gpu::gles2::TextureRef* texture_ref = | 1301 gpu::gles2::TextureRef* texture_ref = |
| 1277 texture_manager->GetTexture(picture_buffer.internal_texture_ids()[0]); | 1302 texture_manager->GetTexture(picture_buffer.internal_texture_ids()[0]); |
| 1278 RETURN_ON_FAILURE(this, texture_manager, "Null texture_ref", ILLEGAL_STATE, | 1303 RETURN_ON_FAILURE(this, texture_manager, "Null texture_ref", ILLEGAL_STATE, |
| 1279 nullptr); | 1304 nullptr); |
| 1280 | 1305 |
| 1281 return texture_ref; | 1306 return texture_ref; |
| 1282 } | 1307 } |
| 1283 | 1308 |
| 1309 void AndroidVideoDecodeAccelerator::OnDestroyingSurface(int surface_id) { | |
| 1310 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1311 TRACE_EVENT0("media", "AVDA::OnDestroyingSurface"); | |
| 1312 DVLOG(1) << __FUNCTION__ << " surface_id: " << surface_id; | |
| 1313 | |
| 1314 if (surface_id != surface_id_) | |
| 1315 return; | |
| 1316 | |
| 1317 // If we're currently asynchronously configuring a codec, it will be destroyed | |
| 1318 // when configuration completes and it notices that |state_| has changed to | |
| 1319 // SURFACE_DESTROYED. | |
| 1320 state_ = SURFACE_DESTROYED; | |
| 1321 if (media_codec_) { | |
| 1322 media_codec_.reset(); | |
| 1323 strategy_->CodecChanged(media_codec_.get()); | |
| 1324 } | |
| 1325 // If we're draining, signal completion now because the drain can no longer | |
| 1326 // proceed. | |
| 1327 if (drain_type_ != DRAIN_TYPE_NONE) | |
| 1328 OnDrainCompleted(); | |
| 1329 } | |
| 1330 | |
| 1284 void AndroidVideoDecodeAccelerator::OnFrameAvailable() { | 1331 void AndroidVideoDecodeAccelerator::OnFrameAvailable() { |
| 1285 // Remember: this may be on any thread. | 1332 // Remember: this may be on any thread. |
| 1286 DCHECK(strategy_); | 1333 DCHECK(strategy_); |
| 1287 strategy_->OnFrameAvailable(); | 1334 strategy_->OnFrameAvailable(); |
| 1288 } | 1335 } |
| 1289 | 1336 |
| 1290 void AndroidVideoDecodeAccelerator::PostError( | 1337 void AndroidVideoDecodeAccelerator::PostError( |
| 1291 const ::tracked_objects::Location& from_here, | 1338 const ::tracked_objects::Location& from_here, |
| 1292 media::VideoDecodeAccelerator::Error error) { | 1339 media::VideoDecodeAccelerator::Error error) { |
| 1293 base::MessageLoop::current()->PostDelayedTask( | 1340 base::MessageLoop::current()->PostDelayedTask( |
| (...skipping 106 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1400 return !gpu_preferences.enable_threaded_texture_mailboxes; | 1447 return !gpu_preferences.enable_threaded_texture_mailboxes; |
| 1401 } | 1448 } |
| 1402 | 1449 |
| 1403 // static | 1450 // static |
| 1404 media::VideoDecodeAccelerator::Capabilities | 1451 media::VideoDecodeAccelerator::Capabilities |
| 1405 AndroidVideoDecodeAccelerator::GetCapabilities( | 1452 AndroidVideoDecodeAccelerator::GetCapabilities( |
| 1406 const gpu::GpuPreferences& gpu_preferences) { | 1453 const gpu::GpuPreferences& gpu_preferences) { |
| 1407 Capabilities capabilities; | 1454 Capabilities capabilities; |
| 1408 SupportedProfiles& profiles = capabilities.supported_profiles; | 1455 SupportedProfiles& profiles = capabilities.supported_profiles; |
| 1409 | 1456 |
| 1410 if (media::MediaCodecUtil::IsVp8DecoderAvailable()) { | 1457 // Only support VP8 on Android versions where we don't have to synchronously |
| 1458 // tear down the MediaCodec on surface destruction because VP8 requires | |
| 1459 // us to completely drain the decoder before releasing it, which is difficult | |
| 1460 // and time consuming to do while the surface is being destroyed. | |
| 1461 if (base::android::BuildInfo::GetInstance()->sdk_int() >= 18 && | |
|
liberato (no reviews please)
2016/05/11 22:09:00
should this be > rather than >= ? 18 is JB i thin
| |
| 1462 media::MediaCodecUtil::IsVp8DecoderAvailable()) { | |
| 1411 SupportedProfile profile; | 1463 SupportedProfile profile; |
| 1412 profile.profile = media::VP8PROFILE_ANY; | 1464 profile.profile = media::VP8PROFILE_ANY; |
| 1413 profile.min_resolution.SetSize(0, 0); | 1465 profile.min_resolution.SetSize(0, 0); |
| 1414 profile.max_resolution.SetSize(1920, 1088); | 1466 profile.max_resolution.SetSize(1920, 1088); |
| 1415 // If we know MediaCodec will just create a software codec, prefer our | 1467 // If we know MediaCodec will just create a software codec, prefer our |
| 1416 // internal software decoder instead. It's more up to date and secured | 1468 // internal software decoder instead. It's more up to date and secured |
| 1417 // within the renderer sandbox. However if the content is encrypted, we | 1469 // within the renderer sandbox. However if the content is encrypted, we |
| 1418 // must use MediaCodec anyways since MediaDrm offers no way to decrypt | 1470 // must use MediaCodec anyways since MediaDrm offers no way to decrypt |
| 1419 // the buffers and let us use our internal software decoders. | 1471 // the buffers and let us use our internal software decoders. |
| 1420 profile.encrypted_only = media::VideoCodecBridge::IsKnownUnaccelerated( | 1472 profile.encrypted_only = media::VideoCodecBridge::IsKnownUnaccelerated( |
| (...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1462 if (media::MediaCodecUtil::IsSurfaceViewOutputSupported()) { | 1514 if (media::MediaCodecUtil::IsSurfaceViewOutputSupported()) { |
| 1463 capabilities.flags |= media::VideoDecodeAccelerator::Capabilities:: | 1515 capabilities.flags |= media::VideoDecodeAccelerator::Capabilities:: |
| 1464 SUPPORTS_EXTERNAL_OUTPUT_SURFACE; | 1516 SUPPORTS_EXTERNAL_OUTPUT_SURFACE; |
| 1465 } | 1517 } |
| 1466 } | 1518 } |
| 1467 | 1519 |
| 1468 return capabilities; | 1520 return capabilities; |
| 1469 } | 1521 } |
| 1470 | 1522 |
| 1471 } // namespace media | 1523 } // namespace media |
| OLD | NEW |