| 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 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 108 static inline const base::TimeDelta ErrorPostingDelay() { | 108 static inline const base::TimeDelta ErrorPostingDelay() { |
| 109 return base::TimeDelta::FromSeconds(2); | 109 return base::TimeDelta::FromSeconds(2); |
| 110 } | 110 } |
| 111 | 111 |
| 112 // For RecordFormatChangedMetric. | 112 // For RecordFormatChangedMetric. |
| 113 enum FormatChangedValue { | 113 enum FormatChangedValue { |
| 114 CodecInitialized = false, | 114 CodecInitialized = false, |
| 115 MissingFormatChanged = true | 115 MissingFormatChanged = true |
| 116 }; | 116 }; |
| 117 | 117 |
| 118 // Maximum number of concurrent, incomplete codec creations that we'll allow |
| 119 // before turning off autodection of codec type. |
| 120 enum { kMaxConcurrentCodecAutodetections = 4 }; |
| 121 |
| 118 static inline void RecordFormatChangedMetric(FormatChangedValue value) { | 122 static inline void RecordFormatChangedMetric(FormatChangedValue value) { |
| 119 UMA_HISTOGRAM_BOOLEAN("Media.AVDA.MissingFormatChanged", !!value); | 123 UMA_HISTOGRAM_BOOLEAN("Media.AVDA.MissingFormatChanged", !!value); |
| 120 } | 124 } |
| 121 | 125 |
| 122 // Handle OnFrameAvailable callbacks safely. Since they occur asynchronously, | 126 // Handle OnFrameAvailable callbacks safely. Since they occur asynchronously, |
| 123 // we take care that the AVDA that wants them still exists. A WeakPtr to | 127 // we take care that the AVDA that wants them still exists. A WeakPtr to |
| 124 // the AVDA would be preferable, except that OnFrameAvailable callbacks can | 128 // the AVDA would be preferable, except that OnFrameAvailable callbacks can |
| 125 // occur off the gpu main thread. We also can't guarantee when the | 129 // occur off the gpu main thread. We also can't guarantee when the |
| 126 // SurfaceTexture will quit sending callbacks to coordinate with the | 130 // SurfaceTexture will quit sending callbacks to coordinate with the |
| 127 // destruction of the AVDA, so we have a separate object that the cb can own. | 131 // destruction of the AVDA, so we have a separate object that the cb can own. |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 178 // buffer to MediaCodec). This is inherently a race, since we don't know if | 182 // buffer to MediaCodec). This is inherently a race, since we don't know if |
| 179 // MediaCodec is broken or just slow. Since the MediaCodec API doesn't let | 183 // MediaCodec is broken or just slow. Since the MediaCodec API doesn't let |
| 180 // us wait on MediaCodec state changes prior to L, we more or less have to | 184 // us wait on MediaCodec state changes prior to L, we more or less have to |
| 181 // time out or keep polling forever in some common cases. | 185 // time out or keep polling forever in some common cases. |
| 182 class AVDATimerManager { | 186 class AVDATimerManager { |
| 183 public: | 187 public: |
| 184 // Make sure that the construction thread is started for |avda_instance|. | 188 // Make sure that the construction thread is started for |avda_instance|. |
| 185 bool StartThread(AndroidVideoDecodeAccelerator* avda_instance) { | 189 bool StartThread(AndroidVideoDecodeAccelerator* avda_instance) { |
| 186 DCHECK(thread_checker_.CalledOnValidThread()); | 190 DCHECK(thread_checker_.CalledOnValidThread()); |
| 187 | 191 |
| 188 if (thread_avda_instances_.empty()) { | 192 // If we chose not to shut it down due to pending codec constructions, then |
| 193 // the thread might already be started even if there are no avda instances. |
| 194 // Plus, sometimes we just fail to start the thread. |
| 195 if (!construction_thread_.IsRunning()) { |
| 189 if (!construction_thread_.Start()) { | 196 if (!construction_thread_.Start()) { |
| 190 LOG(ERROR) << "Failed to start construction thread."; | 197 LOG(ERROR) << "Failed to start construction thread."; |
| 191 return false; | 198 return false; |
| 192 } | 199 } |
| 193 } | 200 } |
| 194 | 201 |
| 195 thread_avda_instances_.insert(avda_instance); | 202 thread_avda_instances_.insert(avda_instance); |
| 196 return true; | 203 return true; |
| 197 } | 204 } |
| 198 | 205 |
| 199 // |avda_instance| will no longer need the construction thread. Stop the | 206 // |avda_instance| will no longer need the construction thread. Stop the |
| 200 // thread if this is the last instance. | 207 // thread if this is the last instance. |
| 201 void StopThread(AndroidVideoDecodeAccelerator* avda_instance) { | 208 void StopThread(AndroidVideoDecodeAccelerator* avda_instance) { |
| 202 DCHECK(thread_checker_.CalledOnValidThread()); | 209 DCHECK(thread_checker_.CalledOnValidThread()); |
| 203 | 210 |
| 204 thread_avda_instances_.erase(avda_instance); | 211 thread_avda_instances_.erase(avda_instance); |
| 205 if (thread_avda_instances_.empty()) | 212 if (!thread_avda_instances_.empty()) |
| 206 construction_thread_.Stop(); | 213 return; |
| 214 |
| 215 // Don't stop the thread if there are outstanding requests, since they |
| 216 // might be hung. They also might simply be incomplete, and the thread |
| 217 // will stay running until we try to shut it down again. |
| 218 base::AutoLock auto_lock(autodetection_info_.lock_); |
| 219 if (autodetection_info_.outstanding_) |
| 220 return; |
| 221 |
| 222 construction_thread_.Stop(); |
| 207 } | 223 } |
| 208 | 224 |
| 209 // Request periodic callback of |avda_instance|->DoIOTask(). Does nothing if | 225 // Request periodic callback of |avda_instance|->DoIOTask(). Does nothing if |
| 210 // the instance is already registered and the timer started. The first request | 226 // the instance is already registered and the timer started. The first request |
| 211 // will start the repeating timer on an interval of DecodePollDelay(). | 227 // will start the repeating timer on an interval of DecodePollDelay(). |
| 212 void StartTimer(AndroidVideoDecodeAccelerator* avda_instance) { | 228 void StartTimer(AndroidVideoDecodeAccelerator* avda_instance) { |
| 213 DCHECK(thread_checker_.CalledOnValidThread()); | 229 DCHECK(thread_checker_.CalledOnValidThread()); |
| 214 | 230 |
| 215 timer_avda_instances_.insert(avda_instance); | 231 timer_avda_instances_.insert(avda_instance); |
| 216 | 232 |
| (...skipping 25 matching lines...) Expand all Loading... |
| 242 io_timer_.Stop(); | 258 io_timer_.Stop(); |
| 243 } | 259 } |
| 244 | 260 |
| 245 // Eventually, we should run the timer on this thread. For now, we just keep | 261 // Eventually, we should run the timer on this thread. For now, we just keep |
| 246 // it as a convenience for construction. | 262 // it as a convenience for construction. |
| 247 scoped_refptr<base::SingleThreadTaskRunner> ConstructionTaskRunner() { | 263 scoped_refptr<base::SingleThreadTaskRunner> ConstructionTaskRunner() { |
| 248 DCHECK(thread_checker_.CalledOnValidThread()); | 264 DCHECK(thread_checker_.CalledOnValidThread()); |
| 249 return construction_thread_.task_runner(); | 265 return construction_thread_.task_runner(); |
| 250 } | 266 } |
| 251 | 267 |
| 268 // Called on the main thread when codec autodetection starts. There may be |
| 269 // several calls to this before any call to OnAsyncCodecAutodetectionComplete. |
| 270 void OnAsyncCodecAutodetectionStarted() { |
| 271 base::AutoLock auto_lock(autodetection_info_.lock_); |
| 272 ++autodetection_info_.outstanding_; |
| 273 } |
| 274 |
| 275 // Called on any thread when a codec is constructed with autodetection. This |
| 276 // assumes that requests are ordered, so please don't mix sync and async codec |
| 277 // construction here. This may be called on any thread. |
| 278 void OnAsyncCodecAutodetectionComplete() { |
| 279 base::AutoLock auto_lock_l(autodetection_info_.lock_); |
| 280 DCHECK_GT(autodetection_info_.outstanding_, 0); |
| 281 --autodetection_info_.outstanding_; |
| 282 } |
| 283 |
| 284 // Return a hint about whether autodetecting the codec type is safe or not. |
| 285 bool IsCodecAutodetectionProbablySafe() { |
| 286 base::AutoLock auto_lock_l(autodetection_info_.lock_); |
| 287 |
| 288 return autodetection_info_.outstanding_ < kMaxConcurrentCodecAutodetections; |
| 289 } |
| 290 |
| 252 // |avda| would like to use |surface_id|. If it is not busy, then mark it | 291 // |avda| would like to use |surface_id|. If it is not busy, then mark it |
| 253 // as busy and return true. If it is busy, then replace any existing waiter, | 292 // as busy and return true. If it is busy, then replace any existing waiter, |
| 254 // make |avda| the current waiter, and return false. Any existing waiter | 293 // make |avda| the current waiter, and return false. Any existing waiter |
| 255 // is assumed to be on the way out, so we fail its allocation request. | 294 // is assumed to be on the way out, so we fail its allocation request. |
| 256 bool AllocateSurface(int surface_id, AndroidVideoDecodeAccelerator* avda) { | 295 bool AllocateSurface(int surface_id, AndroidVideoDecodeAccelerator* avda) { |
| 257 // Nobody has to wait for no surface. | 296 // Nobody has to wait for no surface. |
| 258 if (surface_id == AndroidVideoDecodeAccelerator::Config::kNoSurfaceID) | 297 if (surface_id == AndroidVideoDecodeAccelerator::Config::kNoSurfaceID) |
| 259 return true; | 298 return true; |
| 260 | 299 |
| 261 auto iter = surface_waiter_map_.find(surface_id); | 300 auto iter = surface_waiter_map_.find(surface_id); |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 347 SurfaceWaiterMap surface_waiter_map_; | 386 SurfaceWaiterMap surface_waiter_map_; |
| 348 | 387 |
| 349 // Since we can't delete while iterating when using a set, defer erasure until | 388 // Since we can't delete while iterating when using a set, defer erasure until |
| 350 // after iteration complete. | 389 // after iteration complete. |
| 351 bool timer_running_ = false; | 390 bool timer_running_ = false; |
| 352 std::set<AndroidVideoDecodeAccelerator*> pending_erase_; | 391 std::set<AndroidVideoDecodeAccelerator*> pending_erase_; |
| 353 | 392 |
| 354 // Repeating timer responsible for draining pending IO to the codecs. | 393 // Repeating timer responsible for draining pending IO to the codecs. |
| 355 base::RepeatingTimer io_timer_; | 394 base::RepeatingTimer io_timer_; |
| 356 | 395 |
| 396 // Data for determining if codec creation is hanging. |
| 397 struct { |
| 398 // Lock that protects other members of this struct. |
| 399 base::Lock lock_; |
| 400 |
| 401 // Number of currently pending autodetection requests. |
| 402 int outstanding_ = 0; |
| 403 } autodetection_info_; |
| 404 |
| 357 base::Thread construction_thread_; | 405 base::Thread construction_thread_; |
| 358 | 406 |
| 359 base::ThreadChecker thread_checker_; | 407 base::ThreadChecker thread_checker_; |
| 360 | 408 |
| 361 DISALLOW_COPY_AND_ASSIGN(AVDATimerManager); | 409 DISALLOW_COPY_AND_ASSIGN(AVDATimerManager); |
| 362 }; | 410 }; |
| 363 | 411 |
| 364 static base::LazyInstance<AVDATimerManager>::Leaky g_avda_timer = | 412 static base::LazyInstance<AVDATimerManager>::Leaky g_avda_timer = |
| 365 LAZY_INSTANCE_INITIALIZER; | 413 LAZY_INSTANCE_INITIALIZER; |
| 366 | 414 |
| (...skipping 673 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1040 // Tell the strategy that we're changing codecs. The codec itself could be | 1088 // Tell the strategy that we're changing codecs. The codec itself could be |
| 1041 // used normally, since we don't replace it until we're back on the main | 1089 // used normally, since we don't replace it until we're back on the main |
| 1042 // thread. However, if we're using an output surface, then the incoming codec | 1090 // thread. However, if we're using an output surface, then the incoming codec |
| 1043 // might access that surface while the main thread is drawing. Telling the | 1091 // might access that surface while the main thread is drawing. Telling the |
| 1044 // strategy to forget the codec avoids this. | 1092 // strategy to forget the codec avoids this. |
| 1045 if (media_codec_) { | 1093 if (media_codec_) { |
| 1046 media_codec_.reset(); | 1094 media_codec_.reset(); |
| 1047 strategy_->CodecChanged(nullptr); | 1095 strategy_->CodecChanged(nullptr); |
| 1048 } | 1096 } |
| 1049 | 1097 |
| 1098 // Choose whether to autodetect the codec type. |
| 1099 codec_config_->allow_autodetection_ = |
| 1100 g_avda_timer.Pointer()->IsCodecAutodetectionProbablySafe(); |
| 1101 codec_config_->notify_completion_ = codec_config_->allow_autodetection_; |
| 1102 if (codec_config_->allow_autodetection_) |
| 1103 g_avda_timer.Pointer()->OnAsyncCodecAutodetectionStarted(); |
| 1104 |
| 1105 // If we're not trying autodetection, then use the main thread. The original |
| 1106 // might be blocked. |
| 1050 scoped_refptr<base::SingleThreadTaskRunner> task_runner = | 1107 scoped_refptr<base::SingleThreadTaskRunner> task_runner = |
| 1051 g_avda_timer.Pointer()->ConstructionTaskRunner(); | 1108 codec_config_->allow_autodetection_ |
| 1109 ? g_avda_timer.Pointer()->ConstructionTaskRunner() |
| 1110 : base::ThreadTaskRunnerHandle::Get(); |
| 1052 CHECK(task_runner); | 1111 CHECK(task_runner); |
| 1053 | 1112 |
| 1054 base::PostTaskAndReplyWithResult( | 1113 base::PostTaskAndReplyWithResult( |
| 1055 task_runner.get(), FROM_HERE, | 1114 task_runner.get(), FROM_HERE, |
| 1056 base::Bind(&AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread, | 1115 base::Bind(&AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread, |
| 1057 codec_config_), | 1116 codec_config_), |
| 1058 base::Bind(&AndroidVideoDecodeAccelerator::OnCodecConfigured, | 1117 base::Bind(&AndroidVideoDecodeAccelerator::OnCodecConfigured, |
| 1059 weak_this_factory_.GetWeakPtr())); | 1118 weak_this_factory_.GetWeakPtr())); |
| 1060 } | 1119 } |
| 1061 | 1120 |
| 1062 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() { | 1121 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() { |
| 1063 state_ = WAITING_FOR_CODEC; | 1122 state_ = WAITING_FOR_CODEC; |
| 1123 |
| 1124 // Decide whether to allow autodetection or not. Since we're on the main |
| 1125 // thread, and this request is unordered with respect to pending async config |
| 1126 // attempts, don't record it. It may break book-keeping, and there's not |
| 1127 // much we can do anyway. |
| 1128 codec_config_->allow_autodetection_ = |
| 1129 g_avda_timer.Pointer()->IsCodecAutodetectionProbablySafe(); |
| 1130 codec_config_->notify_completion_ = false; |
| 1131 |
| 1064 std::unique_ptr<VideoCodecBridge> media_codec = | 1132 std::unique_ptr<VideoCodecBridge> media_codec = |
| 1065 ConfigureMediaCodecOnAnyThread(codec_config_); | 1133 ConfigureMediaCodecOnAnyThread(codec_config_); |
| 1066 OnCodecConfigured(std::move(media_codec)); | 1134 OnCodecConfigured(std::move(media_codec)); |
| 1067 return !!media_codec_; | 1135 return !!media_codec_; |
| 1068 } | 1136 } |
| 1069 | 1137 |
| 1070 std::unique_ptr<VideoCodecBridge> | 1138 std::unique_ptr<VideoCodecBridge> |
| 1071 AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread( | 1139 AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread( |
| 1072 scoped_refptr<CodecConfig> codec_config) { | 1140 scoped_refptr<CodecConfig> codec_config) { |
| 1073 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec"); | 1141 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec"); |
| 1074 | 1142 |
| 1075 jobject media_crypto = codec_config->media_crypto_ | 1143 jobject media_crypto = codec_config->media_crypto_ |
| 1076 ? codec_config->media_crypto_->obj() | 1144 ? codec_config->media_crypto_->obj() |
| 1077 : nullptr; | 1145 : nullptr; |
| 1078 | 1146 |
| 1079 // |needs_protected_surface_| implies encrypted stream. | 1147 // |needs_protected_surface_| implies encrypted stream. |
| 1080 DCHECK(!codec_config->needs_protected_surface_ || media_crypto); | 1148 DCHECK(!codec_config->needs_protected_surface_ || media_crypto); |
| 1081 | 1149 |
| 1082 return std::unique_ptr<VideoCodecBridge>(VideoCodecBridge::CreateDecoder( | 1150 const bool require_software_codec = !codec_config->allow_autodetection_; |
| 1151 |
| 1152 std::unique_ptr<VideoCodecBridge> codec(VideoCodecBridge::CreateDecoder( |
| 1083 codec_config->codec_, codec_config->needs_protected_surface_, | 1153 codec_config->codec_, codec_config->needs_protected_surface_, |
| 1084 codec_config->initial_expected_coded_size_, | 1154 codec_config->initial_expected_coded_size_, |
| 1085 codec_config->surface_.j_surface().obj(), media_crypto, true)); | 1155 codec_config->surface_.j_surface().obj(), media_crypto, true, |
| 1156 require_software_codec)); |
| 1157 |
| 1158 // If we successfully completed after an autodetect, then let the other |
| 1159 // instances know that we didn't get stuck. |
| 1160 if (codec_config->notify_completion_) |
| 1161 g_avda_timer.Pointer()->OnAsyncCodecAutodetectionComplete(); |
| 1162 |
| 1163 return codec; |
| 1086 } | 1164 } |
| 1087 | 1165 |
| 1088 void AndroidVideoDecodeAccelerator::OnCodecConfigured( | 1166 void AndroidVideoDecodeAccelerator::OnCodecConfigured( |
| 1089 std::unique_ptr<VideoCodecBridge> media_codec) { | 1167 std::unique_ptr<VideoCodecBridge> media_codec) { |
| 1090 DCHECK(thread_checker_.CalledOnValidThread()); | 1168 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1091 DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED); | 1169 DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED); |
| 1092 | 1170 |
| 1093 // Record one instance of the codec being initialized. | 1171 // Record one instance of the codec being initialized. |
| 1094 RecordFormatChangedMetric(FormatChangedValue::CodecInitialized); | 1172 RecordFormatChangedMetric(FormatChangedValue::CodecInitialized); |
| 1095 | 1173 |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1315 | 1393 |
| 1316 void AndroidVideoDecodeAccelerator::ActualDestroy() { | 1394 void AndroidVideoDecodeAccelerator::ActualDestroy() { |
| 1317 DVLOG(1) << __FUNCTION__; | 1395 DVLOG(1) << __FUNCTION__; |
| 1318 DCHECK(thread_checker_.CalledOnValidThread()); | 1396 DCHECK(thread_checker_.CalledOnValidThread()); |
| 1319 | 1397 |
| 1320 if (!on_destroying_surface_cb_.is_null()) { | 1398 if (!on_destroying_surface_cb_.is_null()) { |
| 1321 AVDASurfaceTracker::GetInstance()->UnregisterOnDestroyingSurfaceCallback( | 1399 AVDASurfaceTracker::GetInstance()->UnregisterOnDestroyingSurfaceCallback( |
| 1322 on_destroying_surface_cb_); | 1400 on_destroying_surface_cb_); |
| 1323 } | 1401 } |
| 1324 | 1402 |
| 1403 AVDATimerManager* manager = g_avda_timer.Pointer(); |
| 1404 |
| 1325 // We no longer care about |surface_id|, in case we did before. It's okay | 1405 // We no longer care about |surface_id|, in case we did before. It's okay |
| 1326 // if we have no surface and/or weren't the owner or a waiter. | 1406 // if we have no surface and/or weren't the owner or a waiter. |
| 1327 g_avda_timer.Pointer()->DeallocateSurface(config_.surface_id, this); | 1407 manager->DeallocateSurface(config_.surface_id, this); |
| 1328 | 1408 |
| 1329 // Note that async codec construction might still be in progress. In that | 1409 // Note that async codec construction might still be in progress. In that |
| 1330 // case, the codec will be deleted when it completes once we invalidate all | 1410 // case, the codec will be deleted when it completes once we invalidate all |
| 1331 // our weak refs. | 1411 // our weak refs. |
| 1332 weak_this_factory_.InvalidateWeakPtrs(); | 1412 weak_this_factory_.InvalidateWeakPtrs(); |
| 1333 if (media_codec_) { | 1413 if (media_codec_) { |
| 1334 g_avda_timer.Pointer()->StopTimer(this); | 1414 manager->StopTimer(this); |
| 1335 media_codec_.reset(); | 1415 // If codec construction is broken, then we can't release this codec if it's |
| 1416 // backed by hardware, else it may hang too. Post it to the construction |
| 1417 // thread, and it'll get freed if things start working. If things are |
| 1418 // already working, then it'll be freed soon. |
| 1419 if (media_codec_->IsSoftwareCodec()) { |
| 1420 media_codec_.reset(); |
| 1421 } else { |
| 1422 manager->ConstructionTaskRunner()->DeleteSoon(FROM_HERE, |
| 1423 media_codec_.release()); |
| 1424 } |
| 1336 } | 1425 } |
| 1337 delete this; | 1426 delete this; |
| 1338 } | 1427 } |
| 1339 | 1428 |
| 1340 bool AndroidVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( | 1429 bool AndroidVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( |
| 1341 const base::WeakPtr<Client>& decode_client, | 1430 const base::WeakPtr<Client>& decode_client, |
| 1342 const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) { | 1431 const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) { |
| 1343 return false; | 1432 return false; |
| 1344 } | 1433 } |
| 1345 | 1434 |
| (...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1651 if (MediaCodecUtil::IsSurfaceViewOutputSupported()) { | 1740 if (MediaCodecUtil::IsSurfaceViewOutputSupported()) { |
| 1652 capabilities.flags |= VideoDecodeAccelerator::Capabilities:: | 1741 capabilities.flags |= VideoDecodeAccelerator::Capabilities:: |
| 1653 SUPPORTS_EXTERNAL_OUTPUT_SURFACE; | 1742 SUPPORTS_EXTERNAL_OUTPUT_SURFACE; |
| 1654 } | 1743 } |
| 1655 } | 1744 } |
| 1656 | 1745 |
| 1657 return capabilities; | 1746 return capabilities; |
| 1658 } | 1747 } |
| 1659 | 1748 |
| 1660 } // namespace media | 1749 } // namespace media |
| OLD | NEW |