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

Side by Side Diff: media/base/android/media_codec_decoder.cc

Issue 1128383003: Implementation of MediaCodecPlayer stage 1 (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Removed unused media_codec_player_state.h and .cc Created 5 years, 7 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
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698