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

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

Issue 2084143002: Make AVDA fall back to software decoding if needed. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: changed _l to auto_lock Created 4 years, 6 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
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 97 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
126 static void delete_codec_helper(std::unique_ptr<VideoCodecBridge> codec) {
127 // Do nothing with codec. It will be deleted / released on destruction.
128 }
129
122 // Handle OnFrameAvailable callbacks safely. Since they occur asynchronously, 130 // Handle OnFrameAvailable callbacks safely. Since they occur asynchronously,
123 // we take care that the AVDA that wants them still exists. A WeakPtr to 131 // 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 132 // the AVDA would be preferable, except that OnFrameAvailable callbacks can
125 // occur off the gpu main thread. We also can't guarantee when the 133 // occur off the gpu main thread. We also can't guarantee when the
126 // SurfaceTexture will quit sending callbacks to coordinate with the 134 // 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. 135 // destruction of the AVDA, so we have a separate object that the cb can own.
128 class AndroidVideoDecodeAccelerator::OnFrameAvailableHandler 136 class AndroidVideoDecodeAccelerator::OnFrameAvailableHandler
129 : public base::RefCountedThreadSafe<OnFrameAvailableHandler> { 137 : public base::RefCountedThreadSafe<OnFrameAvailableHandler> {
130 public: 138 public:
131 // We do not retain ownership of |owner|. It must remain valid until 139 // We do not retain ownership of |owner|. It must remain valid until
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
178 // buffer to MediaCodec). This is inherently a race, since we don't know if 186 // 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 187 // 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 188 // 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. 189 // time out or keep polling forever in some common cases.
182 class AVDATimerManager { 190 class AVDATimerManager {
183 public: 191 public:
184 // Make sure that the construction thread is started for |avda_instance|. 192 // Make sure that the construction thread is started for |avda_instance|.
185 bool StartThread(AndroidVideoDecodeAccelerator* avda_instance) { 193 bool StartThread(AndroidVideoDecodeAccelerator* avda_instance) {
186 DCHECK(thread_checker_.CalledOnValidThread()); 194 DCHECK(thread_checker_.CalledOnValidThread());
187 195
188 if (thread_avda_instances_.empty()) { 196 // If we chose not to shut it down due to pending codec constructions, then
197 // the thread might already be started even if there are no avda instances.
198 // Plus, sometimes we just fail to start the thread.
199 if (!construction_thread_.IsRunning()) {
189 if (!construction_thread_.Start()) { 200 if (!construction_thread_.Start()) {
190 LOG(ERROR) << "Failed to start construction thread."; 201 LOG(ERROR) << "Failed to start construction thread.";
191 return false; 202 return false;
192 } 203 }
193 } 204 }
194 205
195 thread_avda_instances_.insert(avda_instance); 206 thread_avda_instances_.insert(avda_instance);
196 return true; 207 return true;
197 } 208 }
198 209
199 // |avda_instance| will no longer need the construction thread. Stop the 210 // |avda_instance| will no longer need the construction thread. Stop the
200 // thread if this is the last instance. 211 // thread if this is the last instance.
201 void StopThread(AndroidVideoDecodeAccelerator* avda_instance) { 212 void StopThread(AndroidVideoDecodeAccelerator* avda_instance) {
202 DCHECK(thread_checker_.CalledOnValidThread()); 213 DCHECK(thread_checker_.CalledOnValidThread());
203 214
204 thread_avda_instances_.erase(avda_instance); 215 thread_avda_instances_.erase(avda_instance);
205 if (thread_avda_instances_.empty()) 216 if (!thread_avda_instances_.empty())
206 construction_thread_.Stop(); 217 return;
218
219 // Don't stop the thread if there are outstanding requests, since they
220 // might be hung. They also might simply be incomplete, and the thread
221 // will stay running until we try to shut it down again.
222 base::AutoLock auto_lock(autodetection_info_.lock_);
223 if (autodetection_info_.outstanding_)
224 return;
225
226 construction_thread_.Stop();
207 } 227 }
208 228
209 // Request periodic callback of |avda_instance|->DoIOTask(). Does nothing if 229 // Request periodic callback of |avda_instance|->DoIOTask(). Does nothing if
210 // the instance is already registered and the timer started. The first request 230 // the instance is already registered and the timer started. The first request
211 // will start the repeating timer on an interval of DecodePollDelay(). 231 // will start the repeating timer on an interval of DecodePollDelay().
212 void StartTimer(AndroidVideoDecodeAccelerator* avda_instance) { 232 void StartTimer(AndroidVideoDecodeAccelerator* avda_instance) {
213 DCHECK(thread_checker_.CalledOnValidThread()); 233 DCHECK(thread_checker_.CalledOnValidThread());
214 234
215 timer_avda_instances_.insert(avda_instance); 235 timer_avda_instances_.insert(avda_instance);
216 236
(...skipping 25 matching lines...) Expand all
242 io_timer_.Stop(); 262 io_timer_.Stop();
243 } 263 }
244 264
245 // Eventually, we should run the timer on this thread. For now, we just keep 265 // Eventually, we should run the timer on this thread. For now, we just keep
246 // it as a convenience for construction. 266 // it as a convenience for construction.
247 scoped_refptr<base::SingleThreadTaskRunner> ConstructionTaskRunner() { 267 scoped_refptr<base::SingleThreadTaskRunner> ConstructionTaskRunner() {
248 DCHECK(thread_checker_.CalledOnValidThread()); 268 DCHECK(thread_checker_.CalledOnValidThread());
249 return construction_thread_.task_runner(); 269 return construction_thread_.task_runner();
250 } 270 }
251 271
272 // Called on the main thread when codec autodetection starts. There may be
273 // several calls to this before any call to OnAnyThread.
watk 2016/06/22 22:39:46 Can you clarify what OnAnyThread is?
liberato (no reviews please) 2016/06/23 15:18:20 meant "OnAsync...Complete", thanks.
274 void OnAsyncCodecAutodetectionStarted() {
275 base::AutoLock auto_lock(autodetection_info_.lock_);
276 ++autodetection_info_.outstanding_;
277 }
278
279 // Called on any thread when a codec is constructed with autodetection. This
280 // assumes that requests are ordered, so please don't mix sync and async codec
281 // construction here. This may be called on any thread.
282 void OnAsyncCodecAutodetectionComplete() {
283 base::AutoLock auto_lock_l(autodetection_info_.lock_);
284 DCHECK_GT(autodetection_info_.outstanding_, 0);
285 --autodetection_info_.outstanding_;
286 }
287
288 // Return a hint about whether autodetecting the codec type is safe or not.
watk 2016/06/22 22:39:46 I feel like we're too focused on autodetection as
liberato (no reviews please) 2016/06/23 15:18:19 yeah, i can see that. i chose 'autodetection' bec
289 bool IsCodecAutodetectionProbablySafe() {
290 base::AutoLock auto_lock_l(autodetection_info_.lock_);
291
292 return autodetection_info_.outstanding_ < kMaxConcurrentCodecAutodetections;
293 }
294
252 // |avda| would like to use |surface_id|. If it is not busy, then mark it 295 // |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, 296 // 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 297 // 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. 298 // is assumed to be on the way out, so we fail its allocation request.
256 bool AllocateSurface(int surface_id, AndroidVideoDecodeAccelerator* avda) { 299 bool AllocateSurface(int surface_id, AndroidVideoDecodeAccelerator* avda) {
257 // Nobody has to wait for no surface. 300 // Nobody has to wait for no surface.
258 if (surface_id == AndroidVideoDecodeAccelerator::Config::kNoSurfaceID) 301 if (surface_id == AndroidVideoDecodeAccelerator::Config::kNoSurfaceID)
259 return true; 302 return true;
260 303
261 auto iter = surface_waiter_map_.find(surface_id); 304 auto iter = surface_waiter_map_.find(surface_id);
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after
347 SurfaceWaiterMap surface_waiter_map_; 390 SurfaceWaiterMap surface_waiter_map_;
348 391
349 // Since we can't delete while iterating when using a set, defer erasure until 392 // Since we can't delete while iterating when using a set, defer erasure until
350 // after iteration complete. 393 // after iteration complete.
351 bool timer_running_ = false; 394 bool timer_running_ = false;
352 std::set<AndroidVideoDecodeAccelerator*> pending_erase_; 395 std::set<AndroidVideoDecodeAccelerator*> pending_erase_;
353 396
354 // Repeating timer responsible for draining pending IO to the codecs. 397 // Repeating timer responsible for draining pending IO to the codecs.
355 base::RepeatingTimer io_timer_; 398 base::RepeatingTimer io_timer_;
356 399
400 // Data for determining if codec creation is hanging.
401 struct {
402 // Lock that protects other members of this struct.
403 base::Lock lock_;
404
405 // Number of currently pending autodetection requests.
406 int outstanding_ = 0;
407 } autodetection_info_;
408
357 base::Thread construction_thread_; 409 base::Thread construction_thread_;
358 410
359 base::ThreadChecker thread_checker_; 411 base::ThreadChecker thread_checker_;
360 412
361 DISALLOW_COPY_AND_ASSIGN(AVDATimerManager); 413 DISALLOW_COPY_AND_ASSIGN(AVDATimerManager);
362 }; 414 };
363 415
364 static base::LazyInstance<AVDATimerManager>::Leaky g_avda_timer = 416 static base::LazyInstance<AVDATimerManager>::Leaky g_avda_timer =
365 LAZY_INSTANCE_INITIALIZER; 417 LAZY_INSTANCE_INITIALIZER;
366 418
(...skipping 673 matching lines...) Expand 10 before | Expand all | Expand 10 after
1040 // Tell the strategy that we're changing codecs. The codec itself could be 1092 // 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 1093 // 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 1094 // 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 1095 // might access that surface while the main thread is drawing. Telling the
1044 // strategy to forget the codec avoids this. 1096 // strategy to forget the codec avoids this.
1045 if (media_codec_) { 1097 if (media_codec_) {
1046 media_codec_.reset(); 1098 media_codec_.reset();
1047 strategy_->CodecChanged(nullptr); 1099 strategy_->CodecChanged(nullptr);
1048 } 1100 }
1049 1101
1102 // Choose whether to autodetect the codec type.
1103 codec_config_->allow_autodetection_ =
1104 g_avda_timer.Pointer()->IsCodecAutodetectionProbablySafe();
1105 codec_config_->notify_completion_ = codec_config_->allow_autodetection_;
1106 if (codec_config_->allow_autodetection_)
1107 g_avda_timer.Pointer()->OnAsyncCodecAutodetectionStarted();
1108
1109 // If we're not trying autodetection, then use the main thread. The original
1110 // might be blocked.
1050 scoped_refptr<base::SingleThreadTaskRunner> task_runner = 1111 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
1051 g_avda_timer.Pointer()->ConstructionTaskRunner(); 1112 codec_config_->allow_autodetection_
1113 ? g_avda_timer.Pointer()->ConstructionTaskRunner()
1114 : base::ThreadTaskRunnerHandle::Get();
1052 CHECK(task_runner); 1115 CHECK(task_runner);
1053 1116
1054 base::PostTaskAndReplyWithResult( 1117 base::PostTaskAndReplyWithResult(
1055 task_runner.get(), FROM_HERE, 1118 task_runner.get(), FROM_HERE,
1056 base::Bind(&AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread, 1119 base::Bind(&AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread,
1057 codec_config_), 1120 codec_config_),
1058 base::Bind(&AndroidVideoDecodeAccelerator::OnCodecConfigured, 1121 base::Bind(&AndroidVideoDecodeAccelerator::OnCodecConfigured,
1059 weak_this_factory_.GetWeakPtr())); 1122 weak_this_factory_.GetWeakPtr()));
1060 } 1123 }
1061 1124
1062 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() { 1125 bool AndroidVideoDecodeAccelerator::ConfigureMediaCodecSynchronously() {
1063 state_ = WAITING_FOR_CODEC; 1126 state_ = WAITING_FOR_CODEC;
1127
1128 // Decide whether to allow autodetection or not. Since we're on the main
1129 // thread, and this request is unordered with respect to pending async config
1130 // attempts, don't record it. It may break book-keeping, and there's not
1131 // much we can do anyway.
1132 codec_config_->allow_autodetection_ =
1133 g_avda_timer.Pointer()->IsCodecAutodetectionProbablySafe();
1134 codec_config_->notify_completion_ = false;
1135
1064 std::unique_ptr<VideoCodecBridge> media_codec = 1136 std::unique_ptr<VideoCodecBridge> media_codec =
1065 ConfigureMediaCodecOnAnyThread(codec_config_); 1137 ConfigureMediaCodecOnAnyThread(codec_config_);
1066 OnCodecConfigured(std::move(media_codec)); 1138 OnCodecConfigured(std::move(media_codec));
1067 return !!media_codec_; 1139 return !!media_codec_;
1068 } 1140 }
1069 1141
1070 std::unique_ptr<VideoCodecBridge> 1142 std::unique_ptr<VideoCodecBridge>
1071 AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread( 1143 AndroidVideoDecodeAccelerator::ConfigureMediaCodecOnAnyThread(
1072 scoped_refptr<CodecConfig> codec_config) { 1144 scoped_refptr<CodecConfig> codec_config) {
1073 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec"); 1145 TRACE_EVENT0("media", "AVDA::ConfigureMediaCodec");
1074 1146
1075 jobject media_crypto = codec_config->media_crypto_ 1147 jobject media_crypto = codec_config->media_crypto_
1076 ? codec_config->media_crypto_->obj() 1148 ? codec_config->media_crypto_->obj()
1077 : nullptr; 1149 : nullptr;
1078 1150
1079 // |needs_protected_surface_| implies encrypted stream. 1151 // |needs_protected_surface_| implies encrypted stream.
1080 DCHECK(!codec_config->needs_protected_surface_ || media_crypto); 1152 DCHECK(!codec_config->needs_protected_surface_ || media_crypto);
1081 1153
1082 return std::unique_ptr<VideoCodecBridge>(VideoCodecBridge::CreateDecoder( 1154 const bool require_software = !codec_config->allow_autodetection_;
1155
1156 std::unique_ptr<VideoCodecBridge> codec(VideoCodecBridge::CreateDecoder(
1083 codec_config->codec_, codec_config->needs_protected_surface_, 1157 codec_config->codec_, codec_config->needs_protected_surface_,
1084 codec_config->initial_expected_coded_size_, 1158 codec_config->initial_expected_coded_size_,
1085 codec_config->surface_.j_surface().obj(), media_crypto, true)); 1159 codec_config->surface_.j_surface().obj(), media_crypto, true,
1160 require_software));
1161
1162 // If we successfully completed after an autodetect, then reset the timer so
watk 2016/06/22 22:39:46 "reset the timer" feels like a misleading way to s
liberato (no reviews please) 2016/06/23 15:18:20 Done.
1163 // that other instances know that autodetection is okay.
1164 if (codec_config->notify_completion_)
1165 g_avda_timer.Pointer()->OnAsyncCodecAutodetectionComplete();
1166
1167 return codec;
1086 } 1168 }
1087 1169
1088 void AndroidVideoDecodeAccelerator::OnCodecConfigured( 1170 void AndroidVideoDecodeAccelerator::OnCodecConfigured(
1089 std::unique_ptr<VideoCodecBridge> media_codec) { 1171 std::unique_ptr<VideoCodecBridge> media_codec) {
1090 DCHECK(thread_checker_.CalledOnValidThread()); 1172 DCHECK(thread_checker_.CalledOnValidThread());
1091 DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED); 1173 DCHECK(state_ == WAITING_FOR_CODEC || state_ == SURFACE_DESTROYED);
1092 1174
1093 // Record one instance of the codec being initialized. 1175 // Record one instance of the codec being initialized.
1094 RecordFormatChangedMetric(FormatChangedValue::CodecInitialized); 1176 RecordFormatChangedMetric(FormatChangedValue::CodecInitialized);
1095 1177
(...skipping 214 matching lines...) Expand 10 before | Expand all | Expand 10 after
1310 StartCodecDrain(DRAIN_FOR_DESTROY); 1392 StartCodecDrain(DRAIN_FOR_DESTROY);
1311 } else { 1393 } else {
1312 ActualDestroy(); 1394 ActualDestroy();
1313 } 1395 }
1314 } 1396 }
1315 1397
1316 void AndroidVideoDecodeAccelerator::ActualDestroy() { 1398 void AndroidVideoDecodeAccelerator::ActualDestroy() {
1317 DVLOG(1) << __FUNCTION__; 1399 DVLOG(1) << __FUNCTION__;
1318 DCHECK(thread_checker_.CalledOnValidThread()); 1400 DCHECK(thread_checker_.CalledOnValidThread());
1319 1401
1402 AVDATimerManager* manager = g_avda_timer.Pointer();
watk 2016/06/22 22:39:46 Move variable closer to usage.
liberato (no reviews please) 2016/06/23 15:18:19 Done.
1403
1320 if (!on_destroying_surface_cb_.is_null()) { 1404 if (!on_destroying_surface_cb_.is_null()) {
1321 AVDASurfaceTracker::GetInstance()->UnregisterOnDestroyingSurfaceCallback( 1405 AVDASurfaceTracker::GetInstance()->UnregisterOnDestroyingSurfaceCallback(
1322 on_destroying_surface_cb_); 1406 on_destroying_surface_cb_);
1323 } 1407 }
1324 1408
1325 // We no longer care about |surface_id|, in case we did before. It's okay 1409 // 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. 1410 // if we have no surface and/or weren't the owner or a waiter.
1327 g_avda_timer.Pointer()->DeallocateSurface(config_.surface_id, this); 1411 manager->DeallocateSurface(config_.surface_id, this);
1328 1412
1329 // Note that async codec construction might still be in progress. In that 1413 // 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 1414 // case, the codec will be deleted when it completes once we invalidate all
1331 // our weak refs. 1415 // our weak refs.
1332 weak_this_factory_.InvalidateWeakPtrs(); 1416 weak_this_factory_.InvalidateWeakPtrs();
1333 if (media_codec_) { 1417 if (media_codec_) {
1334 g_avda_timer.Pointer()->StopTimer(this); 1418 manager->StopTimer(this);
1335 media_codec_.reset(); 1419 // If codec construction is broken, then we can't release this codec if it's
1420 // backed by hardware, else it may hang too. Post it to the construction
1421 // thread, and it'll get freed if things start working. If things are
1422 // already working, then it'll be freed soon.
1423 if (media_codec_->IsSoftwareCodec()) {
1424 media_codec_.reset();
1425 } else {
1426 manager->ConstructionTaskRunner()->PostTask(
DaleCurtis 2016/06/22 21:20:11 Can you just use DeleteSoon() ?
liberato (no reviews please) 2016/06/22 21:43:55 ooh, shiny.
1427 FROM_HERE,
1428 base::Bind(delete_codec_helper, base::Passed(&media_codec_)));
watk 2016/06/22 22:39:46 We might consider incrementing outstanding_ while
1429 }
1336 } 1430 }
1337 delete this; 1431 delete this;
1338 } 1432 }
1339 1433
1340 bool AndroidVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread( 1434 bool AndroidVideoDecodeAccelerator::TryToSetupDecodeOnSeparateThread(
1341 const base::WeakPtr<Client>& decode_client, 1435 const base::WeakPtr<Client>& decode_client,
1342 const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) { 1436 const scoped_refptr<base::SingleThreadTaskRunner>& decode_task_runner) {
1343 return false; 1437 return false;
1344 } 1438 }
1345 1439
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after
1651 if (MediaCodecUtil::IsSurfaceViewOutputSupported()) { 1745 if (MediaCodecUtil::IsSurfaceViewOutputSupported()) {
1652 capabilities.flags |= VideoDecodeAccelerator::Capabilities:: 1746 capabilities.flags |= VideoDecodeAccelerator::Capabilities::
1653 SUPPORTS_EXTERNAL_OUTPUT_SURFACE; 1747 SUPPORTS_EXTERNAL_OUTPUT_SURFACE;
1654 } 1748 }
1655 } 1749 }
1656 1750
1657 return capabilities; 1751 return capabilities;
1658 } 1752 }
1659 1753
1660 } // namespace media 1754 } // namespace media
OLDNEW
« media/base/android/ndk_media_codec_bridge.cc ('K') | « 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