OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/base/android/media_codec_decoder.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/bind_helpers.h" | |
9 #include "base/callback_helpers.h" | |
10 #include "base/logging.h" | |
11 #include "media/base/android/media_codec_bridge.h" | |
12 | |
13 namespace media { | |
14 | |
15 namespace { | |
16 | |
17 // Stop requesting new data in the kPrefetching state when the queue size | |
18 // reaches this limit. | |
19 const int kPrefetchLimit = 8; | |
20 | |
21 // Request new data in the kRunning state if the queue size is less than this. | |
22 const int kPlaybackLowLimit = 4; | |
23 | |
24 // Posting delay of the next frame processing, in milliseconds | |
25 const int kNextFrameDelay = 2; | |
26 | |
27 // Timeout for dequeuing an input buffer from MediaCodec in milliseconds. | |
28 const int kInputBufferTimeout = 20; | |
29 | |
30 // Timeout for dequeuing an output buffer from MediaCodec in milliseconds. | |
31 const int kOutputBufferTimeout = 20; | |
32 } | |
33 | |
34 MediaCodecDecoder::MediaCodecDecoder( | |
35 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | |
36 const base::Closure& request_data_cb, | |
37 const base::Closure& starvation_cb, | |
38 const base::Closure& stop_done_cb, | |
39 const base::Closure& error_cb, | |
40 const char* decoder_thread_name) | |
41 : media_task_runner_(media_task_runner), | |
42 decoder_thread_(decoder_thread_name), | |
43 request_data_cb_(request_data_cb), | |
44 starvation_cb_(starvation_cb), | |
45 stop_done_cb_(stop_done_cb), | |
46 error_cb_(error_cb), | |
47 state_(kStopped), | |
48 eos_enqueued_(false), | |
49 completed_(false), | |
50 last_frame_posted_(false), | |
51 weak_factory_(this) { | |
52 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
53 | |
54 DVLOG(1) << "Decoder::Decoder() " << decoder_thread_name; | |
55 | |
56 weak_this_ = weak_factory_.GetWeakPtr(); | |
57 | |
58 internal_error_cb_ = base::Bind(&MediaCodecDecoder::OnCodecError, weak_this_); | |
59 } | |
60 | |
61 MediaCodecDecoder::~MediaCodecDecoder() { | |
62 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
63 | |
64 DVLOG(1) << "Decoder::~Decoder()"; | |
65 | |
66 // NB: ReleaseDecoderResources() is virtual | |
67 ReleaseDecoderResources(); | |
68 } | |
69 | |
70 void MediaCodecDecoder::ReleaseDecoderResources() { | |
71 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
72 | |
73 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
74 | |
75 decoder_thread_.Stop(); // synchronous | |
76 state_ = kStopped; | |
77 media_codec_bridge_.reset(); | |
78 } | |
79 | |
80 void MediaCodecDecoder::Flush() { | |
81 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
82 | |
83 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
84 | |
85 DCHECK_EQ(GetState(), kStopped); | |
wolenetz
2015/06/19 23:28:41
nit: (not strong feeling here) Why not just DCHECK
Tima Vaisburd
2015/06/20 02:32:30
When I was writing this I imagined that IsStopped(
wolenetz
2015/06/22 21:41:21
no strong feeling. as-is is fine.
| |
86 | |
87 eos_enqueued_ = false; | |
88 completed_ = false; | |
89 au_queue_.Flush(); | |
90 | |
91 if (media_codec_bridge_) { | |
92 // MediaCodecBridge::Reset() performs MediaCodecBridge.flush() | |
93 MediaCodecStatus flush_status = media_codec_bridge_->Reset(); | |
94 if (flush_status != MEDIA_CODEC_OK) { | |
95 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
96 << "MediaCodecBridge::Reset() failed"; | |
97 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); | |
98 } | |
99 } | |
100 } | |
101 | |
102 void MediaCodecDecoder::ReleaseMediaCodec() { | |
103 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
104 | |
105 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
106 | |
107 media_codec_bridge_.reset(); | |
108 } | |
109 | |
110 bool MediaCodecDecoder::IsPrefetchingOrPlaying() const { | |
111 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
112 | |
113 base::AutoLock lock(state_lock_); | |
114 return state_ == kPrefetching || state_ == kRunning; | |
115 } | |
116 | |
117 bool MediaCodecDecoder::IsStopped() const { | |
118 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
119 | |
120 return GetState() == kStopped; | |
121 } | |
122 | |
123 bool MediaCodecDecoder::IsCompleted() const { | |
124 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
125 | |
126 return completed_; | |
127 } | |
128 | |
129 base::android::ScopedJavaLocalRef<jobject> MediaCodecDecoder::GetMediaCrypto() { | |
130 base::android::ScopedJavaLocalRef<jobject> media_crypto; | |
131 | |
132 // TODO(timav): implement DRM. | |
133 // drm_bridge_ is not implemented | |
134 // if (drm_bridge_) | |
135 // media_crypto = drm_bridge_->GetMediaCrypto(); | |
136 return media_crypto; | |
137 } | |
138 | |
139 void MediaCodecDecoder::Prefetch(const base::Closure& prefetch_done_cb) { | |
140 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
141 | |
142 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
143 | |
144 DCHECK(GetState() == kStopped); | |
145 | |
146 prefetch_done_cb_ = prefetch_done_cb; | |
147 | |
148 SetState(kPrefetching); | |
149 PrefetchNextChunk(); | |
150 } | |
151 | |
152 MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure() { | |
153 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
154 | |
155 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
156 | |
157 if (GetState() == kError) { | |
158 DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state kError"; | |
159 return CONFIG_FAILURE; | |
160 } | |
161 | |
162 // Here I assume that OnDemuxerConfigsAvailable won't come | |
wolenetz
2015/06/19 23:28:41
aside: This is probably a valid assumption. (Confi
| |
163 // in the middle of demuxer data. | |
164 | |
165 if (media_codec_bridge_) { | |
166 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
167 << ": reconfiguration is not required, ignoring"; | |
168 return CONFIG_OK; | |
169 } | |
170 | |
171 return ConfigureInternal(); | |
172 } | |
173 | |
174 bool MediaCodecDecoder::Start(base::TimeDelta current_time) { | |
175 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
176 | |
177 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
178 << " current_time:" << current_time; | |
179 | |
180 DecoderState state = GetState(); | |
181 if (state == kRunning) { | |
182 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": already started"; | |
183 return true; // already started | |
184 } | |
185 | |
186 if (state != kPrefetched) { | |
187 DVLOG(0) << class_name() << "::" << __FUNCTION__ << ": wrong state " | |
188 << AsString(state) << " ignoring"; | |
189 return false; | |
190 } | |
191 | |
192 if (!media_codec_bridge_) { | |
193 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
194 << ": not configured, ignoring"; | |
195 return false; | |
196 } | |
197 | |
198 DCHECK(!decoder_thread_.IsRunning()); | |
199 | |
200 // We only synchronize video stream. | |
201 // When audio is present, the |current_time| is audio time. | |
202 SynchronizePTSWithTime(current_time); | |
203 | |
204 last_frame_posted_ = false; | |
205 | |
206 // Start the decoder thread | |
207 if (!decoder_thread_.Start()) { | |
208 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
209 << ": cannot start decoder thread"; | |
210 return false; | |
211 } | |
212 | |
213 SetState(kRunning); | |
214 | |
215 decoder_thread_.task_runner()->PostTask( | |
216 FROM_HERE, | |
217 base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this))); | |
218 | |
219 return true; | |
220 } | |
221 | |
222 void MediaCodecDecoder::SyncStop() { | |
223 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
224 | |
225 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
226 | |
227 if (GetState() == kError) { | |
228 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
229 << ": wrong state kError, ignoring"; | |
230 return; | |
231 } | |
232 | |
233 // After this method returns, decoder thread will not be running. | |
234 | |
235 decoder_thread_.Stop(); // synchronous | |
236 state_ = kStopped; | |
237 | |
238 // Shall we move |delayed_buffers_| from VideoDecoder to Decoder class? | |
239 ReleaseDelayedBuffers(); | |
240 } | |
241 | |
242 void MediaCodecDecoder::RequestToStop() { | |
243 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
244 | |
245 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
246 | |
247 DecoderState state = GetState(); | |
248 switch (state) { | |
249 case kError: | |
250 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
251 << ": wrong state kError, ignoring"; | |
252 break; | |
253 case kRunning: | |
254 SetState(kStopping); | |
255 break; | |
256 case kStopping: | |
257 break; // ignore | |
258 case kStopped: | |
259 case kPrefetching: | |
260 case kPrefetched: | |
261 // There is nothing to wait for, we can sent nofigication right away. | |
262 DCHECK(!decoder_thread_.IsRunning()); | |
263 SetState(kStopped); | |
264 media_task_runner_->PostTask(FROM_HERE, stop_done_cb_); | |
265 break; | |
266 default: | |
267 NOTREACHED(); | |
268 break; | |
269 } | |
270 } | |
271 | |
272 void MediaCodecDecoder::OnLastFrameRendered(bool completed) { | |
273 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
274 | |
275 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
276 << " completed:" << completed; | |
277 | |
278 decoder_thread_.Stop(); // synchronous | |
279 state_ = kStopped; | |
280 completed_ = completed; | |
281 | |
282 media_task_runner_->PostTask(FROM_HERE, stop_done_cb_); | |
283 } | |
284 | |
285 void MediaCodecDecoder::OnDemuxerDataAvailable(const DemuxerData& data) { | |
286 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
287 | |
288 DVLOG(2) << class_name() << "::" << __FUNCTION__ | |
289 << " #AUs:" << data.access_units.size() | |
290 << " #Configs:" << data.demuxer_configs.size(); | |
291 #if !defined(NDEBUG) | |
292 for (const auto& unit : data.access_units) | |
293 DVLOG(2) << class_name() << "::" << __FUNCTION__ << " au: " << unit; | |
294 #endif | |
295 | |
296 au_queue_.PushBack(data); | |
297 | |
298 if (state_ == kPrefetching) | |
299 PrefetchNextChunk(); | |
300 } | |
301 | |
302 void MediaCodecDecoder::CheckLastFrame(bool eos_encountered, | |
303 bool has_delayed_tasks) { | |
304 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
305 | |
306 bool last_frame_when_stopping = GetState() == kStopping && !has_delayed_tasks; | |
307 | |
308 if (last_frame_when_stopping || eos_encountered) { | |
309 media_task_runner_->PostTask( | |
310 FROM_HERE, base::Bind(&MediaCodecDecoder::OnLastFrameRendered, | |
311 weak_this_, eos_encountered)); | |
312 last_frame_posted_ = true; | |
313 } | |
314 } | |
315 | |
316 void MediaCodecDecoder::OnCodecError() { | |
317 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
318 | |
319 SetState(kError); | |
320 error_cb_.Run(); | |
321 } | |
322 | |
323 void MediaCodecDecoder::PrefetchNextChunk() { | |
324 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
325 | |
326 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
327 | |
328 AccessUnitQueue::Info au_info = au_queue_.GetInfo(); | |
329 | |
330 if (eos_enqueued_ || au_info.length >= kPrefetchLimit || au_info.has_eos) { | |
331 // We are done prefetching | |
332 SetState(kPrefetched); | |
333 DVLOG(1) << class_name() << "::" << __FUNCTION__ << " posting PrefetchDone"; | |
334 media_task_runner_->PostTask(FROM_HERE, | |
335 base::ResetAndReturn(&prefetch_done_cb_)); | |
336 return; | |
337 } | |
338 | |
339 request_data_cb_.Run(); | |
340 } | |
341 | |
342 void MediaCodecDecoder::ProcessNextFrame() { | |
343 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
344 | |
345 DVLOG(2) << class_name() << "::" << __FUNCTION__; | |
346 | |
347 DecoderState state = GetState(); | |
348 | |
349 if (state != kRunning && state != kStopping) { | |
350 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": not running"; | |
351 return; | |
352 } | |
353 | |
354 if (state == kStopping) { | |
355 if (NumDelayedRenderTasks() == 0 && !last_frame_posted_) { | |
356 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
357 << ": kStopping, posting OnLastFrameRendered"; | |
358 media_task_runner_->PostTask( | |
359 FROM_HERE, base::Bind(&MediaCodecDecoder::OnLastFrameRendered, | |
360 weak_this_, false)); | |
361 last_frame_posted_ = true; | |
362 } | |
363 | |
364 // We can stop processing, the |au_queue_| and MediaCodec queues can freeze. | |
365 // We only need to let finish the delayed rendering tasks. | |
366 return; | |
367 } | |
368 | |
369 DCHECK(state == kRunning); | |
370 | |
371 if (!EnqueueInputBuffer()) | |
372 return; | |
373 | |
374 bool eos_encountered = false; | |
375 if (!DepleteOutputBufferQueue(&eos_encountered)) | |
376 return; | |
377 | |
378 if (eos_encountered) { | |
379 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
380 << " EOS dequeued, stopping frame processing"; | |
381 return; | |
382 } | |
383 | |
384 // We need a small delay if we want to stop this thread by | |
385 // decoder_thread_.Stop() reliably. | |
386 // The decoder thread message loop processes all pending | |
387 // (but not delayed) tasks before it can quit; without a delay | |
388 // the message loop might be forever processing the pendng tasks. | |
389 decoder_thread_.task_runner()->PostDelayedTask( | |
390 FROM_HERE, | |
391 base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this)), | |
392 base::TimeDelta::FromMilliseconds(kNextFrameDelay)); | |
wolenetz
2015/06/19 23:28:41
aside: might this prevent (really high) high frame
Tima Vaisburd
2015/06/20 02:32:30
I haven't seen this so far. I also reduced kNextFr
wolenetz
2015/06/22 21:41:21
Acknowledged.
| |
393 } | |
394 | |
395 // Returns false if we should stop decoding process. Right now | |
396 // it happens if we got MediaCodec error or detected starvation. | |
397 bool MediaCodecDecoder::EnqueueInputBuffer() { | |
398 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
399 | |
400 DVLOG(2) << class_name() << "::" << __FUNCTION__; | |
401 | |
402 if (eos_enqueued_) { | |
403 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
404 << ": eos_enqueued, returning"; | |
405 return true; // Nothing to do | |
406 } | |
407 | |
408 // Keep the number pending video frames low, ideally maintaining | |
409 // the same audio and video duration after stop request | |
410 if (NumDelayedRenderTasks() > 1) { | |
411 DVLOG(2) << class_name() << "::" << __FUNCTION__ << ": # delayed buffers (" | |
412 << NumDelayedRenderTasks() << ") exceeds 1, returning"; | |
413 return true; // Nothing to do | |
414 } | |
415 | |
416 // Get the next frame from the queue and the queue info | |
417 | |
418 AccessUnitQueue::Info au_info = au_queue_.GetInfo(); | |
419 | |
420 // Request the data from Demuxer | |
421 if (au_info.length <= kPlaybackLowLimit && !au_info.has_eos) | |
422 media_task_runner_->PostTask(FROM_HERE, request_data_cb_); | |
423 | |
424 // Get the next frame from the queue | |
425 | |
426 if (!au_info.length) { | |
427 // Report starvation and return, Start() will be called again later. | |
428 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": starvation detected"; | |
429 media_task_runner_->PostTask(FROM_HERE, starvation_cb_); | |
430 return false; | |
431 } | |
432 | |
433 if (au_info.configs) { | |
434 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
435 << ": received new configs, not implemented"; | |
436 // post an error for now? | |
437 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); | |
438 return false; | |
439 } | |
440 | |
441 // Dequeue input buffer | |
442 | |
443 base::TimeDelta timeout = | |
444 base::TimeDelta::FromMilliseconds(kInputBufferTimeout); | |
445 int index = -1; | |
446 MediaCodecStatus status = | |
447 media_codec_bridge_->DequeueInputBuffer(timeout, &index); | |
448 | |
449 DVLOG(2) << class_name() << ":: DequeueInputBuffer index:" << index; | |
450 | |
451 switch (status) { | |
452 case MEDIA_CODEC_ERROR: | |
453 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
454 << ": MEDIA_CODEC_ERROR DequeueInputBuffer failed"; | |
455 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); | |
456 return false; | |
457 | |
458 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | |
459 return true; | |
460 | |
461 default: | |
462 break; | |
463 } | |
464 | |
465 // We got the buffer | |
466 DCHECK_EQ(status, MEDIA_CODEC_OK); | |
467 DCHECK_GE(index, 0); | |
468 | |
469 const AccessUnit* unit = au_info.front_unit; | |
470 DCHECK(unit); | |
471 | |
472 if (unit->is_end_of_stream) { | |
473 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": QueueEOS"; | |
474 media_codec_bridge_->QueueEOS(index); | |
475 eos_enqueued_ = true; | |
476 return true; | |
477 } | |
478 | |
479 DVLOG(2) << class_name() << ":: QueueInputBuffer pts:" << unit->timestamp; | |
480 | |
481 status = media_codec_bridge_->QueueInputBuffer( | |
482 index, &unit->data[0], unit->data.size(), unit->timestamp); | |
483 | |
484 if (status == MEDIA_CODEC_ERROR) { | |
485 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
486 << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed"; | |
487 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); | |
488 return false; | |
489 } | |
490 | |
491 // Have successfully queued input buffer, go to next access unit. | |
492 au_queue_.Advance(); | |
493 return true; | |
494 } | |
495 | |
496 // Returns false if there was MediaCodec error. | |
497 bool MediaCodecDecoder::DepleteOutputBufferQueue(bool* eos_encountered) { | |
wolenetz
2015/06/19 23:28:41
aside: I like this naming :)
Tima Vaisburd
2015/06/20 02:32:30
M-mm.. wrong choice? I meant "process till the que
wolenetz
2015/06/22 21:41:21
Hmm. Deplete[...] is better IMO than Process[...].
| |
498 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
499 | |
500 DVLOG(2) << class_name() << "::" << __FUNCTION__; | |
501 | |
502 int buffer_index = 0; | |
503 size_t offset = 0; | |
504 size_t size = 0; | |
505 base::TimeDelta pts; | |
506 MediaCodecStatus status; | |
507 | |
508 base::TimeDelta timeout = | |
509 base::TimeDelta::FromMilliseconds(kOutputBufferTimeout); | |
510 | |
511 // Extract all output buffers that are available. | |
512 // Usually there will be only one, but sometimes it is preceeded by | |
513 // MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED or MEDIA_CODEC_OUTPUT_FORMAT_CHANGED. | |
514 do { | |
515 status = media_codec_bridge_->DequeueOutputBuffer( | |
516 timeout, &buffer_index, &offset, &size, &pts, eos_encountered, nullptr); | |
517 | |
518 // Reset the timeout to 0 for the subsequent DequeueOutputBuffer() calls | |
519 // to quickly break the loop after we got all currently available buffers. | |
520 timeout = base::TimeDelta::FromMilliseconds(0); | |
521 | |
522 switch (status) { | |
523 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | |
524 // Output buffers are replaced in MediaCodecBridge, nothing to do. | |
525 break; | |
526 | |
527 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | |
528 DVLOG(2) << class_name() << "::" << __FUNCTION__ | |
529 << " MEDIA_CODEC_OUTPUT_FORMAT_CHANGED"; | |
530 OnOutputFormatChanged(); | |
531 break; | |
532 | |
533 case MEDIA_CODEC_OK: | |
534 // We got the decoded frame | |
535 Render(buffer_index, size, true, pts, *eos_encountered); | |
536 break; | |
537 | |
538 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | |
539 // Nothing to do. | |
540 break; | |
541 | |
542 case MEDIA_CODEC_ERROR: | |
543 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
544 << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer"; | |
545 media_task_runner_->PostTask(FROM_HERE, internal_error_cb_); | |
546 break; | |
547 | |
548 default: | |
549 NOTREACHED(); | |
550 break; | |
551 } | |
552 | |
553 } while (status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER && | |
554 status != MEDIA_CODEC_ERROR && !*eos_encountered); | |
555 | |
556 return status != MEDIA_CODEC_ERROR; | |
557 } | |
558 | |
559 MediaCodecDecoder::DecoderState MediaCodecDecoder::GetState() const { | |
560 base::AutoLock lock(state_lock_); | |
561 return state_; | |
562 } | |
563 | |
564 void MediaCodecDecoder::SetState(DecoderState state) { | |
565 DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << state; | |
566 | |
567 base::AutoLock lock(state_lock_); | |
568 state_ = state; | |
569 } | |
570 | |
571 #undef RETURN_STRING | |
572 #define RETURN_STRING(x) \ | |
573 case x: \ | |
574 return #x; | |
575 | |
576 const char* MediaCodecDecoder::AsString(DecoderState state) { | |
577 switch (state) { | |
578 RETURN_STRING(kStopped); | |
579 RETURN_STRING(kPrefetching); | |
580 RETURN_STRING(kPrefetched); | |
581 RETURN_STRING(kRunning); | |
582 RETURN_STRING(kStopping); | |
583 RETURN_STRING(kError); | |
584 default: | |
585 return "Unknown DecoderState"; | |
586 } | |
587 } | |
588 | |
589 #undef RETURN_STRING | |
590 | |
591 } // namespace media | |
OLD | NEW |