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 PREFETCH state when | |
18 // the queue size rached this limit. | |
19 const int PREFETCH_LIMIT = 8; | |
20 | |
21 // Request new data in the RUNNING state if the size of the queue | |
22 // is less than this. | |
23 const int PLAYBACK_LOW_LIMIT = 4; | |
24 | |
25 // Posting delay of the next frame processing, in milliseconds | |
26 const int NEXT_FRAME_DELAY_MS = 2; | |
27 } | |
28 | |
29 MediaCodecDecoder::MediaCodecDecoder( | |
30 const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, | |
31 const scoped_refptr<base::SingleThreadTaskRunner>& ui_task_runner, | |
32 const base::Closure& request_data_cb, | |
33 const base::Closure& starvation_cb, | |
34 const base::Closure& stop_done_cb, | |
35 const base::Closure& error_cb, | |
36 const char* decoder_thread_name) | |
37 : media_task_runner_(media_task_runner), | |
38 ui_task_runner_(ui_task_runner), | |
39 decoder_thread_(decoder_thread_name), | |
40 request_data_cb_(request_data_cb), | |
41 starvation_cb_(starvation_cb), | |
42 stop_done_cb_(stop_done_cb), | |
43 error_cb_(error_cb), | |
44 state_(STOPPED), | |
45 eos_enqueued_(false), | |
46 completed_(false), | |
47 last_frame_posted_(false), | |
48 weak_factory_(this) { | |
49 // Media thread | |
50 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
51 | |
52 DVLOG(1) << "Decoder::Decoder() " << decoder_thread_name; | |
53 | |
54 weak_this_ = weak_factory_.GetWeakPtr(); | |
55 } | |
56 | |
57 MediaCodecDecoder::~MediaCodecDecoder() { | |
58 // Media thread | |
59 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
60 | |
61 DVLOG(1) << "Decoder::~Decoder()"; | |
62 | |
63 // NB: ReleaseDecoderResources() is virtual | |
64 ReleaseDecoderResources(); | |
65 } | |
66 | |
67 void MediaCodecDecoder::SetDemuxerConfigs(const DemuxerConfigs& configs) { | |
68 // Media thread | |
69 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
70 | |
71 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
72 | |
73 base::AutoLock lock(configs_lock_); | |
74 configs_ = configs; | |
75 } | |
76 | |
77 base::android::ScopedJavaLocalRef<jobject> | |
78 MediaCodecDecoder::GetMediaCrypto() { | |
79 base::android::ScopedJavaLocalRef<jobject> media_crypto; | |
80 | |
81 // drm_bridge_ is not implemented | |
82 // if (drm_bridge_) | |
83 // media_crypto = drm_bridge_->GetMediaCrypto(); | |
84 return media_crypto; | |
85 } | |
86 | |
87 void MediaCodecDecoder::Prefetch(const base::Closure& prefetch_done_cb) { | |
88 // Media thread | |
89 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
90 | |
91 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
92 | |
93 DCHECK(GetState() == STOPPED); | |
94 | |
95 prefetch_done_cb_ = prefetch_done_cb; | |
96 | |
97 SetState(PREFETCHING); | |
98 PrefetchNextChunk(); | |
99 } | |
100 | |
101 MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure() { | |
102 // Media thread | |
103 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
104 | |
105 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
106 | |
107 // Here I assume that OnDemuxerConfigsAvailable won't come | |
108 // in the middle of demuxer data. | |
109 | |
110 bool is_reconfigure_needed = !media_codec_bridge_; | |
111 // || IsCodecReconfigureNeeded(configs_, configs_); | |
qinmin
2015/05/19 19:26:25
remove this line if it is not used, and it seems t
Tima Vaisburd
2015/05/20 05:19:58
Removed for now, we will need it in the next versi
| |
112 | |
113 if (!is_reconfigure_needed) { | |
114 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
115 << ": reconfiguration is not required, ignoring"; | |
116 return CONFIG_OK; | |
117 } | |
118 | |
119 return ConfigureInternal(); | |
120 } | |
121 | |
122 bool MediaCodecDecoder::Start(base::TimeDelta current_time) { | |
123 // Media thread | |
124 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
125 | |
126 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
127 << " current_time:" << current_time; | |
128 | |
129 if (state_ == RUNNING) { | |
130 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": already started"; | |
131 return true; // already started | |
132 } | |
133 | |
134 if (state_ == STOPPING) { | |
135 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
136 << ": wrong state STOPPING, ignoring"; | |
137 return false; | |
138 } | |
139 | |
140 DCHECK(!decoder_thread_.IsRunning()); | |
141 | |
142 // We only synchronize video stream. | |
143 // When audio is present, the |current_time| is audio time. | |
144 SynchronizePTSWithTime(current_time); | |
145 | |
146 last_frame_posted_ = false; | |
147 | |
148 // Start the decoder thread | |
149 if (!decoder_thread_.Start()) { | |
150 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
151 << ": cannot start decoder thread"; | |
152 return false; | |
153 } | |
154 | |
155 SetState(RUNNING); | |
156 | |
157 decoder_thread_.task_runner()->PostTask( | |
158 FROM_HERE, | |
159 base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this))); | |
160 | |
161 return true; | |
162 } | |
163 | |
164 void MediaCodecDecoder::SyncStop() { | |
165 // Media thread | |
166 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
167 | |
168 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
169 | |
170 // After this method returns, decoder thread will not be running. | |
171 | |
172 decoder_thread_.Stop(); // synchronous | |
173 state_ = STOPPED; | |
174 | |
175 // Shall we move |delayed_buffers_| from VideoDecoder to Decoder class? | |
176 ReleaseDelayedBuffers(); | |
177 } | |
178 | |
179 void MediaCodecDecoder::RequestToStop() { | |
180 // Media thread | |
181 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
182 | |
183 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
184 | |
185 DCHECK(GetState() == RUNNING); | |
186 SetState(STOPPING); | |
187 } | |
188 | |
189 void MediaCodecDecoder::OnLastFrameRendered(bool completed) { | |
190 // Media thread | |
191 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
192 | |
193 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
194 << " completed:" << completed; | |
195 | |
196 decoder_thread_.Stop(); // synchronous | |
197 state_ = STOPPED; | |
198 completed_ = completed; | |
199 | |
200 media_task_runner_->PostTask(FROM_HERE, stop_done_cb_); | |
201 } | |
202 | |
203 void MediaCodecDecoder::Flush() { | |
204 // Media thread | |
205 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
206 | |
207 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
208 | |
209 DCHECK_EQ(GetState(), STOPPED); | |
210 | |
211 eos_enqueued_ = false; | |
212 completed_ = false; | |
213 au_queue_.Flush(); | |
214 | |
215 if (media_codec_bridge_) { | |
216 // MediaCodecBridge::Reset() performs MediaCodecBridge.flush() | |
217 MediaCodecStatus flush_status = media_codec_bridge_->Reset(); | |
218 if (flush_status != MEDIA_CODEC_OK) | |
219 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
220 } | |
221 } | |
222 | |
223 void MediaCodecDecoder::OnDemuxerDataAvailable(const DemuxerData& data) { | |
224 // Media thread | |
225 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
226 | |
227 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
228 << " #AUs:" << data.access_units.size() | |
229 << " #Configs:" << data.demuxer_configs.size(); | |
230 | |
231 if (!data.access_units.empty()) | |
232 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
233 << " AU[0]: " << data.access_units[0]; | |
234 | |
235 au_queue_.PushBack(data); | |
236 | |
237 if (state_ == PREFETCHING) | |
238 PrefetchNextChunk(); | |
239 } | |
240 | |
241 void MediaCodecDecoder::ReleaseDecoderResources() { | |
242 // Media thread | |
243 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
244 | |
245 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
246 | |
247 decoder_thread_.Stop(); // synchronous | |
248 state_ = STOPPED; | |
249 media_codec_bridge_.reset(); | |
250 } | |
251 | |
252 void MediaCodecDecoder::ReleaseMediaCodec() { | |
253 // Media thread | |
254 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
255 | |
256 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
257 | |
258 media_codec_bridge_.reset(); | |
259 } | |
260 | |
261 bool MediaCodecDecoder::IsPrefetchingOrPlaying() const { | |
262 // Media thread | |
263 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
264 | |
265 base::AutoLock lock(state_lock_); | |
266 return state_ == PREFETCHING || state_ == RUNNING; | |
267 } | |
268 | |
269 bool MediaCodecDecoder::IsStopped() const { | |
270 // Media thread | |
271 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
272 | |
273 return GetState() == STOPPED; | |
274 } | |
275 | |
276 bool MediaCodecDecoder::IsCompleted() const { | |
277 // Media thread | |
278 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
279 | |
280 return completed_; | |
281 } | |
282 | |
283 MediaCodecDecoder::DecoderState MediaCodecDecoder::GetState() const { | |
284 base::AutoLock lock(state_lock_); | |
285 return state_; | |
286 } | |
287 | |
288 void MediaCodecDecoder::SetState(DecoderState state) { | |
289 DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << state; | |
290 | |
291 base::AutoLock lock(state_lock_); | |
292 state_ = state; | |
293 } | |
294 | |
295 void MediaCodecDecoder::PrefetchNextChunk() { | |
296 // Media thread | |
297 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
298 | |
299 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
300 | |
301 AccessUnitQueue::Info au_info; | |
302 au_queue_.GetInfo(&au_info); | |
303 | |
304 if (eos_enqueued_ || au_info.length >= PREFETCH_LIMIT || au_info.has_eos) { | |
305 // We are done prefetching | |
306 DVLOG(1) << class_name() << "::" << __FUNCTION__ << " posting PrefetchDone"; | |
307 media_task_runner_->PostTask( | |
308 FROM_HERE, base::ResetAndReturn(&prefetch_done_cb_)); | |
309 return; | |
310 } | |
311 | |
312 request_data_cb_.Run(); | |
313 } | |
314 | |
315 void MediaCodecDecoder::ProcessNextFrame() { | |
316 // Decoder thread | |
317 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
318 | |
319 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
320 | |
321 DecoderState state = GetState(); | |
322 | |
323 if (state != RUNNING && state != STOPPING) { | |
324 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": not running"; | |
325 return; | |
326 } | |
327 | |
328 if (state == STOPPING) { | |
329 if (NumDelayedRenderTasks() == 0 && !last_frame_posted_) { | |
330 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
331 << ": STOPPING, posting OnLastFrameRendered"; | |
332 media_task_runner_->PostTask( | |
333 FROM_HERE, | |
334 base::Bind(&MediaCodecDecoder::OnLastFrameRendered, | |
335 weak_this_, false)); | |
336 last_frame_posted_ = true; | |
337 } | |
338 | |
339 // We can stop processing, the |au_queue_| and MediaCodec queues can freeze | |
340 return; | |
341 } | |
342 | |
343 DCHECK(state == RUNNING); | |
344 | |
345 // Keep the number pending video frames low, ideally maintaining | |
346 // the same audio and video duration after stop request | |
347 | |
348 if (NumDelayedRenderTasks() <= 1) { | |
349 bool success= EnqueueInputBuffer(); | |
qinmin
2015/05/19 19:26:25
space after success.
Tima Vaisburd
2015/05/20 05:19:58
removed |bool success| altogether
| |
350 if (!success) | |
351 return; | |
352 } | |
353 | |
354 bool eos_encountered = false; | |
355 bool success = DepleteOutputBufferQueue(&eos_encountered); | |
356 if (!success) | |
357 return; | |
358 | |
359 if (eos_encountered) { | |
360 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
361 << " EOS dequeued, stopping frame processing"; | |
362 return; | |
363 } | |
364 | |
365 // We need a small delay if we want to stop this thread by | |
366 // decoder_thread_.Stop() reliably. | |
367 // The decoder thread message loop processes all pending | |
368 // (but not delayed) tasks before it can quit; without a delay | |
369 // the message loop might be forever processing the pendng tasks. | |
370 decoder_thread_.task_runner()->PostDelayedTask( | |
371 FROM_HERE, | |
372 base::Bind(&MediaCodecDecoder::ProcessNextFrame, | |
373 base::Unretained(this)), | |
374 base::TimeDelta::FromMilliseconds(NEXT_FRAME_DELAY_MS)); | |
375 } | |
376 | |
377 // Returns false if there was MediaCodec error. | |
378 bool MediaCodecDecoder::EnqueueInputBuffer() { | |
379 // Decoder thread | |
380 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
381 | |
382 //DVLOG(2) << class_name() << "::" << __FUNCTION__; | |
qinmin
2015/05/19 19:26:25
Remove unused lines
Tima Vaisburd
2015/05/20 05:19:58
Uncommented log instead of removing
| |
383 | |
384 if (eos_enqueued_) { | |
385 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
386 << ": eos_enqueued, returning"; | |
387 return true; // Nothing to do | |
388 } | |
389 | |
390 // Get the next frame from the queue and the queue info | |
391 | |
392 AccessUnitQueue::Info au_info; | |
393 au_queue_.GetInfo(&au_info); | |
394 | |
395 // Request the data from Demuxer | |
396 if (au_info.length <= PLAYBACK_LOW_LIMIT && !au_info.has_eos) | |
397 media_task_runner_->PostTask(FROM_HERE, request_data_cb_); | |
398 | |
399 // Get the next frame from the queue | |
400 | |
401 if (!au_info.length) { | |
402 // Report starvation and return, Start() will be called again later. | |
403 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
404 << ": starvation detected"; | |
405 media_task_runner_->PostTask(FROM_HERE, starvation_cb_); | |
406 return true; | |
407 } | |
408 | |
409 if (au_info.configs) { | |
410 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
411 << ": received new configs, not implemented"; | |
412 // post an error for now? | |
413 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
414 return true; | |
415 } | |
416 | |
417 // Dequeue input buffer | |
418 | |
419 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(20); | |
420 int index = -1; | |
421 MediaCodecStatus status = | |
422 media_codec_bridge_->DequeueInputBuffer(timeout, &index); | |
423 | |
424 DVLOG(2) << class_name() << ":: DequeueInputBuffer index:" << index; | |
425 | |
426 switch (status) { | |
427 case MEDIA_CODEC_ERROR: | |
428 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
429 << ": MEDIA_CODEC_ERROR DequeueInputBuffer failed"; | |
430 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
431 return false; | |
432 break; | |
433 | |
434 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | |
435 //DVLOG(2) << class_name() << "::" << __FUNCTION__ | |
436 // << " DequeueInputBuffer AGAIN"; | |
437 return true; | |
438 break; | |
439 | |
440 default: | |
441 break; | |
442 } | |
443 | |
444 // We got the buffer | |
445 DCHECK_EQ(status, MEDIA_CODEC_OK); | |
446 DCHECK_GE(index, 0); | |
447 | |
448 const AccessUnit* unit = au_info.front_unit; | |
449 DCHECK(unit); | |
450 | |
451 if (unit->is_end_of_stream) { | |
452 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": QueueEOS"; | |
453 media_codec_bridge_->QueueEOS(index); | |
454 eos_enqueued_ = true; | |
455 return true; | |
456 } | |
457 | |
458 status = media_codec_bridge_->QueueInputBuffer( | |
459 index, &unit->data[0], unit->data.size(), unit->timestamp); | |
460 | |
461 if (status == MEDIA_CODEC_ERROR) { | |
462 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
463 << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed"; | |
464 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
465 return false; | |
466 } | |
467 | |
468 // Have successfully queued input buffer | |
469 au_queue_.PopFront(); | |
470 return true; | |
471 } | |
472 | |
473 // Returns false if there was MediaCodec error. | |
474 bool MediaCodecDecoder::DepleteOutputBufferQueue(bool* eos_encountered) { | |
475 // Decoder thread | |
476 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
477 | |
478 //DVLOG(2) << class_name() << "::" << __FUNCTION__; | |
qinmin
2015/05/19 19:26:25
ditto
Tima Vaisburd
2015/05/20 05:19:58
Uncommented
| |
479 | |
480 int buffer_index = 0; | |
481 size_t offset = 0; | |
482 size_t size = 0; | |
483 base::TimeDelta pts; | |
484 MediaCodecStatus status; | |
485 | |
486 base::TimeDelta timeout[2]; | |
487 timeout[0] = base::TimeDelta::FromMilliseconds(20); | |
488 timeout[1] = base::TimeDelta::FromMilliseconds(0); | |
489 | |
490 int timeout_index = 0; | |
491 | |
492 do { | |
493 status = media_codec_bridge_->DequeueOutputBuffer( | |
494 timeout[timeout_index], | |
495 &buffer_index, | |
496 &offset, | |
497 &size, | |
498 &pts, | |
499 eos_encountered, | |
500 nullptr); | |
501 | |
502 timeout_index = 1; | |
503 | |
504 //DVLOG(2) << class_name() | |
qinmin
2015/05/19 19:26:25
ditto
Tima Vaisburd
2015/05/20 05:19:58
Done.
| |
505 // << ":: DequeueOutputBuffer index:" << buffer_index; | |
506 | |
507 switch (status) { | |
508 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | |
509 // TODO: | |
510 // Audio: set new rate. If it changed, | |
511 // the MediaSourcePlayer code calls ResetTimestampHelper(). | |
512 // Video: report new frame size to manager() | |
513 DVLOG(2) << class_name() << "::" << __FUNCTION__ | |
514 << " MEDIA_CODEC_OUTPUT_FORMAT_CHANGED"; | |
515 break; | |
516 | |
517 case MEDIA_CODEC_OK: | |
518 // We got the decoded frame | |
519 Render(buffer_index, size, true, pts, *eos_encountered); | |
520 break; | |
521 | |
522 case MEDIA_CODEC_ERROR: | |
523 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
524 << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer"; | |
525 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
526 break; | |
527 | |
528 default: | |
529 break; | |
530 } | |
531 | |
532 } while (status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER && | |
533 status != MEDIA_CODEC_ERROR && | |
534 !*eos_encountered); | |
535 | |
536 return status != MEDIA_CODEC_ERROR; | |
537 } | |
538 | |
539 void MediaCodecDecoder::CheckLastFrame(bool eos_encountered, | |
540 bool has_delayed_tasks) { | |
541 // Decoder thread | |
542 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
543 | |
544 bool last_frame_when_stopping = | |
545 GetState() == STOPPING && !has_delayed_tasks; | |
546 | |
547 if (last_frame_when_stopping || eos_encountered) { | |
548 media_task_runner_->PostTask( | |
549 FROM_HERE, | |
550 base::Bind(&MediaCodecDecoder::OnLastFrameRendered, | |
551 weak_this_, eos_encountered)); | |
552 last_frame_posted_ = true; | |
553 } | |
554 } | |
555 | |
556 } // namespace media | |
OLD | NEW |