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 the queue size reaches | |
18 // this limit. | |
19 const int kPrefetchLimit = 8; | |
20 | |
21 // Request new data in the RUNNING 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_(STOPPED), | |
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 | |
59 MediaCodecDecoder::~MediaCodecDecoder() { | |
60 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
61 | |
62 DVLOG(1) << "Decoder::~Decoder()"; | |
63 | |
64 // NB: ReleaseDecoderResources() is virtual | |
65 ReleaseDecoderResources(); | |
66 } | |
67 | |
68 void MediaCodecDecoder::ReleaseDecoderResources() { | |
69 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
70 | |
71 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
72 | |
73 decoder_thread_.Stop(); // synchronous | |
74 state_ = STOPPED; | |
75 media_codec_bridge_.reset(); | |
76 } | |
77 | |
78 void MediaCodecDecoder::Flush() { | |
79 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
80 | |
81 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
82 | |
83 DCHECK_EQ(GetState(), STOPPED); | |
84 | |
85 eos_enqueued_ = false; | |
86 completed_ = false; | |
87 au_queue_.Flush(); | |
88 | |
89 if (media_codec_bridge_) { | |
90 // MediaCodecBridge::Reset() performs MediaCodecBridge.flush() | |
91 MediaCodecStatus flush_status = media_codec_bridge_->Reset(); | |
92 if (flush_status != MEDIA_CODEC_OK) | |
93 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
94 } | |
95 } | |
96 | |
97 void MediaCodecDecoder::ReleaseMediaCodec() { | |
98 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
99 | |
100 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
101 | |
102 media_codec_bridge_.reset(); | |
103 } | |
104 | |
105 bool MediaCodecDecoder::IsPrefetchingOrPlaying() const { | |
106 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
107 | |
108 base::AutoLock lock(state_lock_); | |
109 return state_ == PREFETCHING || state_ == RUNNING; | |
110 } | |
111 | |
112 bool MediaCodecDecoder::IsStopped() const { | |
113 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
114 | |
115 return GetState() == STOPPED; | |
116 } | |
117 | |
118 bool MediaCodecDecoder::IsCompleted() const { | |
119 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
120 | |
121 return completed_; | |
122 } | |
123 | |
124 base::android::ScopedJavaLocalRef<jobject> MediaCodecDecoder::GetMediaCrypto() { | |
125 base::android::ScopedJavaLocalRef<jobject> media_crypto; | |
126 | |
127 // drm_bridge_ is not implemented | |
128 // if (drm_bridge_) | |
129 // media_crypto = drm_bridge_->GetMediaCrypto(); | |
130 return media_crypto; | |
131 } | |
132 | |
133 void MediaCodecDecoder::Prefetch(const base::Closure& prefetch_done_cb) { | |
134 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
135 | |
136 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
137 | |
138 DCHECK(GetState() == STOPPED); | |
139 | |
140 prefetch_done_cb_ = prefetch_done_cb; | |
141 | |
142 SetState(PREFETCHING); | |
143 PrefetchNextChunk(); | |
144 } | |
145 | |
146 MediaCodecDecoder::ConfigStatus MediaCodecDecoder::Configure() { | |
147 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
148 | |
149 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
150 | |
151 // Here I assume that OnDemuxerConfigsAvailable won't come | |
152 // in the middle of demuxer data. | |
153 | |
154 if (media_codec_bridge_) { | |
155 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
156 << ": reconfiguration is not required, ignoring"; | |
157 return CONFIG_OK; | |
158 } | |
159 | |
160 return ConfigureInternal(); | |
161 } | |
162 | |
163 bool MediaCodecDecoder::Start(base::TimeDelta current_time) { | |
164 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
165 | |
166 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
167 << " current_time:" << current_time; | |
168 | |
169 if (state_ == RUNNING) { | |
170 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": already started"; | |
171 return true; // already started | |
172 } | |
173 | |
174 if (state_ == STOPPING) { | |
175 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
176 << ": wrong state STOPPING, ignoring"; | |
177 return false; | |
178 } | |
179 | |
180 if (!media_codec_bridge_) { | |
181 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
182 << ": not configured, ignoring"; | |
183 return false; | |
184 } | |
185 | |
186 DCHECK(!decoder_thread_.IsRunning()); | |
187 | |
188 // We only synchronize video stream. | |
189 // When audio is present, the |current_time| is audio time. | |
190 SynchronizePTSWithTime(current_time); | |
191 | |
192 last_frame_posted_ = false; | |
193 | |
194 // Start the decoder thread | |
195 if (!decoder_thread_.Start()) { | |
196 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
197 << ": cannot start decoder thread"; | |
198 return false; | |
199 } | |
200 | |
201 SetState(RUNNING); | |
202 | |
203 decoder_thread_.task_runner()->PostTask( | |
204 FROM_HERE, | |
205 base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this))); | |
206 | |
207 return true; | |
208 } | |
209 | |
210 void MediaCodecDecoder::SyncStop() { | |
211 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
212 | |
213 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
214 | |
215 // After this method returns, decoder thread will not be running. | |
216 | |
217 decoder_thread_.Stop(); // synchronous | |
218 state_ = STOPPED; | |
219 | |
220 // Shall we move |delayed_buffers_| from VideoDecoder to Decoder class? | |
221 ReleaseDelayedBuffers(); | |
222 } | |
223 | |
224 void MediaCodecDecoder::RequestToStop() { | |
225 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
226 | |
227 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
228 | |
229 DCHECK(GetState() == RUNNING); | |
230 SetState(STOPPING); | |
231 } | |
232 | |
233 void MediaCodecDecoder::OnLastFrameRendered(bool completed) { | |
234 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
235 | |
236 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
237 << " completed:" << completed; | |
238 | |
239 decoder_thread_.Stop(); // synchronous | |
240 state_ = STOPPED; | |
241 completed_ = completed; | |
242 | |
243 media_task_runner_->PostTask(FROM_HERE, stop_done_cb_); | |
244 } | |
245 | |
246 void MediaCodecDecoder::OnDemuxerDataAvailable(const DemuxerData& data) { | |
247 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
248 | |
249 DVLOG(2) << class_name() << "::" << __FUNCTION__ | |
250 << " #AUs:" << data.access_units.size() | |
251 << " #Configs:" << data.demuxer_configs.size(); | |
252 #if !defined(NDEBUG) | |
253 for (const auto& unit : data.access_units) | |
254 DVLOG(2) << class_name() << "::" << __FUNCTION__ << " au: " << unit; | |
255 #endif | |
256 | |
257 au_queue_.PushBack(data); | |
258 | |
259 if (state_ == PREFETCHING) | |
260 PrefetchNextChunk(); | |
261 } | |
262 | |
263 void MediaCodecDecoder::CheckLastFrame(bool eos_encountered, | |
264 bool has_delayed_tasks) { | |
265 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
266 | |
267 bool last_frame_when_stopping = GetState() == STOPPING && !has_delayed_tasks; | |
268 | |
269 if (last_frame_when_stopping || eos_encountered) { | |
270 media_task_runner_->PostTask( | |
271 FROM_HERE, base::Bind(&MediaCodecDecoder::OnLastFrameRendered, | |
272 weak_this_, eos_encountered)); | |
273 last_frame_posted_ = true; | |
274 } | |
275 } | |
276 | |
277 void MediaCodecDecoder::PrefetchNextChunk() { | |
278 DCHECK(media_task_runner_->BelongsToCurrentThread()); | |
279 | |
280 DVLOG(1) << class_name() << "::" << __FUNCTION__; | |
281 | |
282 AccessUnitQueue::Info au_info = au_queue_.GetInfo(); | |
283 | |
284 if (eos_enqueued_ || au_info.length >= kPrefetchLimit || au_info.has_eos) { | |
285 // We are done prefetching | |
286 DVLOG(1) << class_name() << "::" << __FUNCTION__ << " posting PrefetchDone"; | |
287 media_task_runner_->PostTask(FROM_HERE, | |
288 base::ResetAndReturn(&prefetch_done_cb_)); | |
289 return; | |
290 } | |
291 | |
292 request_data_cb_.Run(); | |
293 } | |
294 | |
295 void MediaCodecDecoder::ProcessNextFrame() { | |
296 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
297 | |
298 DVLOG(2) << class_name() << "::" << __FUNCTION__; | |
299 | |
300 DecoderState state = GetState(); | |
301 | |
302 if (state != RUNNING && state != STOPPING) { | |
303 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": not running"; | |
304 return; | |
305 } | |
306 | |
307 if (state == STOPPING) { | |
308 if (NumDelayedRenderTasks() == 0 && !last_frame_posted_) { | |
309 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
310 << ": STOPPING, posting OnLastFrameRendered"; | |
311 media_task_runner_->PostTask( | |
312 FROM_HERE, base::Bind(&MediaCodecDecoder::OnLastFrameRendered, | |
313 weak_this_, false)); | |
314 last_frame_posted_ = true; | |
315 } | |
316 | |
317 // We can stop processing, the |au_queue_| and MediaCodec queues can freeze. | |
318 // We only need to let finish the delayed rendering tasks. | |
319 return; | |
320 } | |
321 | |
322 DCHECK(state == RUNNING); | |
323 | |
324 // Keep the number pending video frames low, ideally maintaining | |
325 // the same audio and video duration after stop request | |
326 | |
327 if (NumDelayedRenderTasks() <= 1) { | |
328 if (!EnqueueInputBuffer()) | |
329 return; | |
330 } | |
331 | |
332 bool eos_encountered = false; | |
333 if (!DepleteOutputBufferQueue(&eos_encountered)) | |
334 return; | |
335 | |
336 if (eos_encountered) { | |
337 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
338 << " EOS dequeued, stopping frame processing"; | |
339 return; | |
340 } | |
341 | |
342 // We need a small delay if we want to stop this thread by | |
343 // decoder_thread_.Stop() reliably. | |
344 // The decoder thread message loop processes all pending | |
345 // (but not delayed) tasks before it can quit; without a delay | |
346 // the message loop might be forever processing the pendng tasks. | |
347 decoder_thread_.task_runner()->PostDelayedTask( | |
348 FROM_HERE, | |
349 base::Bind(&MediaCodecDecoder::ProcessNextFrame, base::Unretained(this)), | |
350 base::TimeDelta::FromMilliseconds(kNextFrameDelay)); | |
351 } | |
352 | |
353 // Returns false if we should stop decoding process. Right now | |
354 // it happens if we got MediaCodec error or detected starvation. | |
355 bool MediaCodecDecoder::EnqueueInputBuffer() { | |
356 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
357 | |
358 DVLOG(2) << class_name() << "::" << __FUNCTION__; | |
359 | |
360 if (eos_enqueued_) { | |
361 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
362 << ": eos_enqueued, returning"; | |
363 return true; // Nothing to do | |
364 } | |
365 | |
366 // Get the next frame from the queue and the queue info | |
367 | |
368 AccessUnitQueue::Info au_info = au_queue_.GetInfo(); | |
369 | |
370 // Request the data from Demuxer | |
371 if (au_info.length <= kPlaybackLowLimit && !au_info.has_eos) | |
372 media_task_runner_->PostTask(FROM_HERE, request_data_cb_); | |
373 | |
374 // Get the next frame from the queue | |
375 | |
376 if (!au_info.length) { | |
377 // Report starvation and return, Start() will be called again later. | |
378 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": starvation detected"; | |
379 media_task_runner_->PostTask(FROM_HERE, starvation_cb_); | |
liberato (no reviews please)
2015/06/12 22:24:15
does |state_| need to be reset here, so that Start
Tima Vaisburd
2015/06/15 18:57:08
I'm not quite sure I completely understood your id
| |
380 return false; | |
381 } | |
382 | |
383 if (au_info.configs) { | |
384 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
385 << ": received new configs, not implemented"; | |
386 // post an error for now? | |
387 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
388 return false; | |
389 } | |
390 | |
391 // Dequeue input buffer | |
392 | |
393 base::TimeDelta timeout = | |
394 base::TimeDelta::FromMilliseconds(kInputBufferTimeout); | |
395 int index = -1; | |
396 MediaCodecStatus status = | |
397 media_codec_bridge_->DequeueInputBuffer(timeout, &index); | |
398 | |
399 DVLOG(2) << class_name() << ":: DequeueInputBuffer index:" << index; | |
400 | |
401 switch (status) { | |
402 case MEDIA_CODEC_ERROR: | |
403 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
404 << ": MEDIA_CODEC_ERROR DequeueInputBuffer failed"; | |
405 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
406 return false; | |
407 | |
408 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | |
409 return true; | |
410 | |
411 default: | |
412 break; | |
413 } | |
414 | |
415 // We got the buffer | |
416 DCHECK_EQ(status, MEDIA_CODEC_OK); | |
417 DCHECK_GE(index, 0); | |
418 | |
419 const AccessUnit* unit = au_info.front_unit; | |
420 DCHECK(unit); | |
421 | |
422 if (unit->is_end_of_stream) { | |
423 DVLOG(1) << class_name() << "::" << __FUNCTION__ << ": QueueEOS"; | |
424 media_codec_bridge_->QueueEOS(index); | |
425 eos_enqueued_ = true; | |
426 return true; | |
427 } | |
428 | |
429 DVLOG(2) << class_name() << ":: QueueInputBuffer pts:" << unit->timestamp; | |
430 | |
431 status = media_codec_bridge_->QueueInputBuffer( | |
432 index, &unit->data[0], unit->data.size(), unit->timestamp); | |
433 | |
434 if (status == MEDIA_CODEC_ERROR) { | |
435 DVLOG(1) << class_name() << "::" << __FUNCTION__ | |
436 << ": MEDIA_CODEC_ERROR: QueueInputBuffer failed"; | |
437 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
438 return false; | |
439 } | |
440 | |
441 // Have successfully queued input buffer, go to next access unit. | |
442 au_queue_.Advance(); | |
443 return true; | |
444 } | |
445 | |
446 // Returns false if there was MediaCodec error. | |
447 bool MediaCodecDecoder::DepleteOutputBufferQueue(bool* eos_encountered) { | |
448 DCHECK(decoder_thread_.task_runner()->BelongsToCurrentThread()); | |
449 | |
450 DVLOG(2) << class_name() << "::" << __FUNCTION__; | |
451 | |
452 int buffer_index = 0; | |
453 size_t offset = 0; | |
454 size_t size = 0; | |
455 base::TimeDelta pts; | |
456 MediaCodecStatus status; | |
457 | |
458 base::TimeDelta timeout = | |
459 base::TimeDelta::FromMilliseconds(kOutputBufferTimeout); | |
460 | |
461 // Extract all output buffers that are available. | |
462 // Usually there will be only one, but sometimes it is preceeded by | |
463 // MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED or MEDIA_CODEC_OUTPUT_FORMAT_CHANGED. | |
464 do { | |
465 status = media_codec_bridge_->DequeueOutputBuffer( | |
466 timeout, &buffer_index, &offset, &size, &pts, eos_encountered, nullptr); | |
467 | |
468 // Reset the timeout to 0 for the subsequent DequeueOutputBuffer() calls | |
469 // to quickly break the loop after we got all currently available buffers. | |
470 timeout = base::TimeDelta::FromMilliseconds(0); | |
471 | |
472 switch (status) { | |
473 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | |
474 // Output buffers are replaced in MediaCodecBridge, nothing to do. | |
475 break; | |
476 | |
477 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | |
478 DVLOG(2) << class_name() << "::" << __FUNCTION__ | |
479 << " MEDIA_CODEC_OUTPUT_FORMAT_CHANGED"; | |
480 OnOutputFormatChanged(); | |
481 break; | |
482 | |
483 case MEDIA_CODEC_OK: | |
484 // We got the decoded frame | |
485 Render(buffer_index, size, true, pts, *eos_encountered); | |
486 break; | |
487 | |
488 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | |
489 // Nothing to do. | |
490 break; | |
491 | |
492 case MEDIA_CODEC_ERROR: | |
493 DVLOG(0) << class_name() << "::" << __FUNCTION__ | |
494 << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer"; | |
495 media_task_runner_->PostTask(FROM_HERE, error_cb_); | |
496 break; | |
497 | |
498 default: | |
499 NOTREACHED(); | |
500 break; | |
501 } | |
502 | |
503 } while (status != MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER && | |
504 status != MEDIA_CODEC_ERROR && !*eos_encountered); | |
505 | |
506 return status != MEDIA_CODEC_ERROR; | |
507 } | |
508 | |
509 MediaCodecDecoder::DecoderState MediaCodecDecoder::GetState() const { | |
510 base::AutoLock lock(state_lock_); | |
511 return state_; | |
512 } | |
513 | |
514 void MediaCodecDecoder::SetState(DecoderState state) { | |
515 DVLOG(1) << class_name() << "::" << __FUNCTION__ << " " << state; | |
516 | |
517 base::AutoLock lock(state_lock_); | |
518 state_ = state; | |
519 } | |
520 | |
521 } // namespace media | |
OLD | NEW |