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