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

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

Powered by Google App Engine
This is Rietveld 408576698