OLD | NEW |
---|---|
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/base/android/media_decoder_job.h" | 5 #include "media/base/android/media_decoder_job.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
9 #include "base/debug/trace_event.h" | 9 #include "base/debug/trace_event.h" |
10 #include "base/message_loop/message_loop_proxy.h" | 10 #include "base/message_loop/message_loop_proxy.h" |
(...skipping 15 matching lines...) Expand all Loading... | |
26 : ui_task_runner_(base::MessageLoopProxy::current()), | 26 : ui_task_runner_(base::MessageLoopProxy::current()), |
27 decoder_task_runner_(decoder_task_runner), | 27 decoder_task_runner_(decoder_task_runner), |
28 media_codec_bridge_(media_codec_bridge), | 28 media_codec_bridge_(media_codec_bridge), |
29 needs_flush_(false), | 29 needs_flush_(false), |
30 input_eos_encountered_(false), | 30 input_eos_encountered_(false), |
31 output_eos_encountered_(false), | 31 output_eos_encountered_(false), |
32 skip_eos_enqueue_(true), | 32 skip_eos_enqueue_(true), |
33 prerolling_(true), | 33 prerolling_(true), |
34 weak_this_(this), | 34 weak_this_(this), |
35 request_data_cb_(request_data_cb), | 35 request_data_cb_(request_data_cb), |
36 access_unit_index_(0), | 36 current_demuxer_data_index_(0), |
37 input_buf_index_(-1), | 37 input_buf_index_(-1), |
38 stop_decode_pending_(false), | 38 stop_decode_pending_(false), |
39 destroy_pending_(false) { | 39 destroy_pending_(false), |
40 is_requesting_demuxer_data_(false), | |
41 is_incoming_data_invalid_(false) { | |
42 for (size_t i = 0; i < 2; ++i) | |
43 access_unit_index_[i] = 0; | |
40 } | 44 } |
41 | 45 |
42 MediaDecoderJob::~MediaDecoderJob() {} | 46 MediaDecoderJob::~MediaDecoderJob() {} |
43 | 47 |
44 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) { | 48 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) { |
45 DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units"; | 49 DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units"; |
46 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 50 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
47 DCHECK(!on_data_received_cb_.is_null()); | 51 DCHECK(IsDemuxerDataEmpty(false)); |
48 | 52 |
49 TRACE_EVENT_ASYNC_END2( | 53 TRACE_EVENT_ASYNC_END2( |
50 "media", "MediaDecoderJob::RequestData", this, | 54 "media", "MediaDecoderJob::RequestData", this, |
51 "Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO", | 55 "Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO", |
52 "Units read", data.access_units.size()); | 56 "Units read", data.access_units.size()); |
53 | 57 |
58 if (is_incoming_data_invalid_) { | |
59 is_incoming_data_invalid_ = false; | |
60 // If there is a pending callback, need to request the data again to get | |
61 // valid data. | |
62 if (!on_data_received_cb_.is_null()) | |
63 request_data_cb_.Run(); | |
64 else | |
65 is_requesting_demuxer_data_ = false; | |
66 return; | |
67 } | |
68 | |
69 size_t next_demuxer_data_index = 1 - current_demuxer_data_index_; | |
70 received_data_[next_demuxer_data_index] = data; | |
71 access_unit_index_[next_demuxer_data_index] = 0; | |
72 is_requesting_demuxer_data_ = false; | |
73 | |
54 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_); | 74 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_); |
55 | |
56 if (stop_decode_pending_) { | 75 if (stop_decode_pending_) { |
57 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); | 76 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); |
58 return; | 77 return; |
59 } | 78 } |
60 | 79 |
61 access_unit_index_ = 0; | 80 if (!done_cb.is_null()) |
62 received_data_ = data; | 81 done_cb.Run(); |
63 done_cb.Run(); | |
64 } | 82 } |
65 | 83 |
66 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { | 84 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { |
67 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 85 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
68 DCHECK(on_data_received_cb_.is_null()); | 86 DCHECK(on_data_received_cb_.is_null()); |
69 DCHECK(decode_cb_.is_null()); | 87 DCHECK(decode_cb_.is_null()); |
70 | 88 |
71 if (HasData()) { | 89 if (HasData()) { |
72 DVLOG(1) << __FUNCTION__ << " : using previously received data"; | 90 DVLOG(1) << __FUNCTION__ << " : using previously received data"; |
73 ui_task_runner_->PostTask(FROM_HERE, prefetch_cb); | 91 ui_task_runner_->PostTask(FROM_HERE, prefetch_cb); |
74 return; | 92 return; |
75 } | 93 } |
76 | 94 |
77 DVLOG(1) << __FUNCTION__ << " : requesting data"; | 95 DVLOG(1) << __FUNCTION__ << " : requesting data"; |
78 RequestData(prefetch_cb); | 96 RequestData(prefetch_cb); |
79 } | 97 } |
80 | 98 |
81 bool MediaDecoderJob::Decode( | 99 bool MediaDecoderJob::Decode( |
82 const base::TimeTicks& start_time_ticks, | 100 const base::TimeTicks& start_time_ticks, |
83 const base::TimeDelta& start_presentation_timestamp, | 101 const base::TimeDelta& start_presentation_timestamp, |
84 const DecoderCallback& callback) { | 102 const DecoderCallback& callback) { |
85 DCHECK(decode_cb_.is_null()); | 103 DCHECK(decode_cb_.is_null()); |
86 DCHECK(on_data_received_cb_.is_null()); | 104 DCHECK(on_data_received_cb_.is_null()); |
87 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 105 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
88 | 106 |
89 decode_cb_ = callback; | 107 decode_cb_ = callback; |
90 | 108 |
91 if (!HasData()) { | 109 if (!HasData()) { |
92 RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit, | 110 RequestData(base::Bind(&MediaDecoderJob::DecodeCurrentAccessUnit, |
93 base::Unretained(this), | 111 base::Unretained(this), |
94 start_time_ticks, | 112 start_time_ticks, |
95 start_presentation_timestamp)); | 113 start_presentation_timestamp)); |
96 return true; | 114 return true; |
97 } | 115 } |
98 | 116 |
99 if (DemuxerStream::kConfigChanged == | 117 if (DemuxerStream::kConfigChanged == CurrentAccessUnit().status) { |
100 received_data_.access_units[access_unit_index_].status) { | |
101 // Clear received data because we need to handle a config change. | 118 // Clear received data because we need to handle a config change. |
102 decode_cb_.Reset(); | 119 decode_cb_.Reset(); |
103 received_data_ = DemuxerData(); | 120 ClearData(); |
104 access_unit_index_ = 0; | |
105 return false; | 121 return false; |
106 } | 122 } |
107 | 123 |
108 DecodeNextAccessUnit(start_time_ticks, start_presentation_timestamp); | 124 DecodeCurrentAccessUnit(start_time_ticks, start_presentation_timestamp); |
109 return true; | 125 return true; |
110 } | 126 } |
111 | 127 |
112 void MediaDecoderJob::StopDecode() { | 128 void MediaDecoderJob::StopDecode() { |
113 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 129 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
114 DCHECK(is_decoding()); | 130 DCHECK(is_decoding()); |
115 stop_decode_pending_ = true; | 131 stop_decode_pending_ = true; |
116 } | 132 } |
117 | 133 |
118 void MediaDecoderJob::Flush() { | 134 void MediaDecoderJob::Flush() { |
119 DCHECK(decode_cb_.is_null()); | 135 DCHECK(decode_cb_.is_null()); |
120 | 136 |
121 // Do nothing, flush when the next Decode() happens. | 137 // Do nothing, flush when the next Decode() happens. |
122 needs_flush_ = true; | 138 needs_flush_ = true; |
123 received_data_ = DemuxerData(); | 139 ClearData(); |
124 input_eos_encountered_ = false; | 140 input_eos_encountered_ = false; |
125 access_unit_index_ = 0; | |
126 on_data_received_cb_.Reset(); | |
127 } | 141 } |
128 | 142 |
129 void MediaDecoderJob::BeginPrerolling( | 143 void MediaDecoderJob::BeginPrerolling( |
130 const base::TimeDelta& preroll_timestamp) { | 144 const base::TimeDelta& preroll_timestamp) { |
131 DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")"; | 145 DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")"; |
132 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 146 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
133 DCHECK(!is_decoding()); | 147 DCHECK(!is_decoding()); |
134 | 148 |
135 preroll_timestamp_ = preroll_timestamp; | 149 preroll_timestamp_ = preroll_timestamp; |
136 prerolling_ = true; | 150 prerolling_ = true; |
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
201 // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|. | 215 // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|. |
202 // Otherwise MediaDrm will report errors. | 216 // Otherwise MediaDrm will report errors. |
203 if (status == MEDIA_CODEC_NO_KEY) | 217 if (status == MEDIA_CODEC_NO_KEY) |
204 input_buf_index_ = input_buf_index; | 218 input_buf_index_ = input_buf_index; |
205 | 219 |
206 return status; | 220 return status; |
207 } | 221 } |
208 | 222 |
209 bool MediaDecoderJob::HasData() const { | 223 bool MediaDecoderJob::HasData() const { |
210 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 224 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
211 // When |input_eos_encountered_| is set, |access_units| must not be empty and | 225 // When |input_eos_encountered_| is set, |access_unit| must be pointing to an |
wolenetz
2014/03/17 19:51:00
s/access_unit/access_unit_index_/
Also, mention d
qinmin
2014/03/18 18:58:58
Done.
Good point. ClearData() was called in 2 fun
| |
212 // |access_unit_index_| must be pointing to an EOS unit. We'll reuse this | 226 // EOS unit. We'll reuse this unit to flush the decoder until we hit output |
213 // unit to flush the decoder until we hit output EOS. | 227 // EOS. |
214 DCHECK(!input_eos_encountered_ || | 228 DCHECK(!input_eos_encountered_ || !IsDemuxerDataEmpty(true)); |
215 (received_data_.access_units.size() > 0 && | 229 return !IsDemuxerDataEmpty(true) || !IsDemuxerDataEmpty(false); |
216 access_unit_index_ < received_data_.access_units.size())) | |
217 << " (access_units.size(): " << received_data_.access_units.size() | |
218 << ", access_unit_index_: " << access_unit_index_ << ")"; | |
219 return access_unit_index_ < received_data_.access_units.size() || | |
220 input_eos_encountered_; | |
221 } | 230 } |
222 | 231 |
223 void MediaDecoderJob::RequestData(const base::Closure& done_cb) { | 232 void MediaDecoderJob::RequestData(const base::Closure& done_cb) { |
224 DVLOG(1) << __FUNCTION__; | 233 DVLOG(1) << __FUNCTION__; |
225 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 234 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
226 DCHECK(on_data_received_cb_.is_null()); | 235 DCHECK(on_data_received_cb_.is_null()); |
227 DCHECK(!input_eos_encountered_); | 236 DCHECK(!input_eos_encountered_); |
237 DCHECK(IsDemuxerDataEmpty(false)); | |
228 | 238 |
229 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this); | 239 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this); |
230 | 240 |
231 received_data_ = DemuxerData(); | |
232 access_unit_index_ = 0; | |
233 on_data_received_cb_ = done_cb; | 241 on_data_received_cb_ = done_cb; |
234 | 242 |
243 // If we are already expecting new data, just set the callback and do | |
244 // nothing. | |
245 if (is_requesting_demuxer_data_) | |
246 return; | |
247 | |
248 // The new incoming data will be stored as the next demuxer data chunk, since | |
249 // the decoder might still be decoding the current one. | |
250 size_t next_demuxer_data_index = 1 - current_demuxer_data_index_; | |
251 received_data_[next_demuxer_data_index] = DemuxerData(); | |
252 access_unit_index_[next_demuxer_data_index] = 0; | |
253 is_requesting_demuxer_data_ = true; | |
254 | |
235 request_data_cb_.Run(); | 255 request_data_cb_.Run(); |
236 } | 256 } |
237 | 257 |
238 void MediaDecoderJob::DecodeNextAccessUnit( | 258 void MediaDecoderJob::DecodeCurrentAccessUnit( |
239 const base::TimeTicks& start_time_ticks, | 259 const base::TimeTicks& start_time_ticks, |
240 const base::TimeDelta& start_presentation_timestamp) { | 260 const base::TimeDelta& start_presentation_timestamp) { |
241 DCHECK(ui_task_runner_->BelongsToCurrentThread()); | 261 DCHECK(ui_task_runner_->BelongsToCurrentThread()); |
242 DCHECK(!decode_cb_.is_null()); | 262 DCHECK(!decode_cb_.is_null()); |
243 | 263 |
264 RequestCurrentChunkIfEmpty(); | |
265 const AccessUnit& access_unit = CurrentAccessUnit(); | |
244 // If the first access unit is a config change, request the player to dequeue | 266 // If the first access unit is a config change, request the player to dequeue |
245 // the input buffer again so that it can request config data. | 267 // the input buffer again so that it can request config data. |
246 if (received_data_.access_units[access_unit_index_].status == | 268 if (access_unit.status == DemuxerStream::kConfigChanged) { |
247 DemuxerStream::kConfigChanged) { | |
248 ui_task_runner_->PostTask(FROM_HERE, | 269 ui_task_runner_->PostTask(FROM_HERE, |
249 base::Bind(&MediaDecoderJob::OnDecodeCompleted, | 270 base::Bind(&MediaDecoderJob::OnDecodeCompleted, |
250 base::Unretained(this), | 271 base::Unretained(this), |
251 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, | 272 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, |
252 kNoTimestamp(), | 273 kNoTimestamp(), |
253 0)); | 274 0)); |
254 return; | 275 return; |
255 } | 276 } |
256 | 277 |
257 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( | 278 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( |
258 &MediaDecoderJob::DecodeInternal, base::Unretained(this), | 279 &MediaDecoderJob::DecodeInternal, base::Unretained(this), |
259 received_data_.access_units[access_unit_index_], | 280 access_unit, |
wolenetz
2014/03/17 19:51:00
Is it possible for ClearData() to occur while the
qinmin
2014/03/18 18:58:58
No, ClearData() will be called only when the decod
| |
260 start_time_ticks, start_presentation_timestamp, needs_flush_, | 281 start_time_ticks, start_presentation_timestamp, needs_flush_, |
261 media::BindToCurrentLoop(base::Bind( | 282 media::BindToCurrentLoop(base::Bind( |
262 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); | 283 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); |
263 needs_flush_ = false; | 284 needs_flush_ = false; |
264 } | 285 } |
265 | 286 |
266 void MediaDecoderJob::DecodeInternal( | 287 void MediaDecoderJob::DecodeInternal( |
267 const AccessUnit& unit, | 288 const AccessUnit& unit, |
268 const base::TimeTicks& start_time_ticks, | 289 const base::TimeTicks& start_time_ticks, |
269 const base::TimeDelta& start_presentation_timestamp, | 290 const base::TimeDelta& start_presentation_timestamp, |
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
404 if (presentation_timestamp != kNoTimestamp()) | 425 if (presentation_timestamp != kNoTimestamp()) |
405 prerolling_ = false; | 426 prerolling_ = false; |
406 | 427 |
407 switch (status) { | 428 switch (status) { |
408 case MEDIA_CODEC_OK: | 429 case MEDIA_CODEC_OK: |
409 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | 430 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: |
410 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | 431 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: |
411 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | 432 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: |
412 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: | 433 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: |
413 if (!input_eos_encountered_) | 434 if (!input_eos_encountered_) |
414 access_unit_index_++; | 435 access_unit_index_[current_demuxer_data_index_]++; |
415 break; | 436 break; |
416 | 437 |
417 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | 438 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: |
418 case MEDIA_CODEC_INPUT_END_OF_STREAM: | 439 case MEDIA_CODEC_INPUT_END_OF_STREAM: |
419 case MEDIA_CODEC_NO_KEY: | 440 case MEDIA_CODEC_NO_KEY: |
420 case MEDIA_CODEC_STOPPED: | 441 case MEDIA_CODEC_STOPPED: |
421 case MEDIA_CODEC_ERROR: | 442 case MEDIA_CODEC_ERROR: |
422 // Do nothing. | 443 // Do nothing. |
423 break; | 444 break; |
424 }; | 445 }; |
425 | 446 |
426 stop_decode_pending_ = false; | 447 stop_decode_pending_ = false; |
427 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, | 448 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, |
428 audio_output_bytes); | 449 audio_output_bytes); |
429 } | 450 } |
430 | 451 |
452 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const { | |
wolenetz
2014/03/17 19:51:00
nit: clarify with DCHECK any assumption of which t
qinmin
2014/03/18 18:58:58
Done.
| |
453 DCHECK(HasData()); | |
454 int index = IsDemuxerDataEmpty(true) ? 1- current_demuxer_data_index_ : | |
wolenetz
2014/03/17 19:51:00
nit: s/1-/1 -/
wolenetz
2014/03/17 19:51:00
nit: extract "1 - current_demuxer_data_index_" to
qinmin
2014/03/18 18:58:58
Done.
qinmin
2014/03/18 18:58:58
Done.
| |
455 current_demuxer_data_index_; | |
456 return received_data_[index].access_units[access_unit_index_[index]]; | |
457 } | |
458 | |
459 bool MediaDecoderJob::IsDemuxerDataEmpty(bool current_chunk) const { | |
wolenetz
2014/03/17 19:51:00
nit: ditto thread dcheck
qinmin
2014/03/18 18:58:58
Done.
| |
460 size_t index = current_chunk ? current_demuxer_data_index_ : | |
461 1 - current_demuxer_data_index_; | |
462 return received_data_[index].access_units.size() <= access_unit_index_[index]; | |
463 } | |
464 | |
465 void MediaDecoderJob::ClearData() { | |
wolenetz
2014/03/17 19:51:00
nit: ditto thread dcheck
qinmin
2014/03/18 18:58:58
Done.
| |
466 current_demuxer_data_index_ = 0; | |
wolenetz
2014/03/17 19:51:00
ditto: If |input_eos_encountered_| is set, we shou
qinmin
2014/03/18 18:58:58
changed the function to set |input_eos_encountered
| |
467 for (size_t i = 0; i < 2; ++i) { | |
wolenetz
2014/03/17 19:51:00
nit: Elevate this to a helper function, also use i
qinmin
2014/03/18 18:58:58
Done.
| |
468 received_data_[i] = DemuxerData(); | |
469 access_unit_index_[i] = 0; | |
470 } | |
471 on_data_received_cb_.Reset(); | |
472 if (is_requesting_demuxer_data_) | |
473 is_incoming_data_invalid_ = true; | |
474 } | |
475 | |
476 void MediaDecoderJob::RequestCurrentChunkIfEmpty() { | |
wolenetz
2014/03/17 19:51:00
nit: ditto thread dcheck
qinmin
2014/03/18 18:58:58
Done.
| |
477 DCHECK(HasData()); | |
478 if (!IsDemuxerDataEmpty(true)) | |
479 return; | |
480 | |
481 // Requests new data if the the last access unit of the next chunk is not EOS. | |
482 current_demuxer_data_index_ = 1 - current_demuxer_data_index_; | |
483 const AccessUnit last_access_unit = | |
484 received_data_[current_demuxer_data_index_].access_units.back(); | |
485 if (!last_access_unit.end_of_stream && | |
486 last_access_unit.status != DemuxerStream::kConfigChanged && | |
487 last_access_unit.status != DemuxerStream::kAborted) { | |
488 RequestData(base::Closure()); | |
489 } | |
490 } | |
491 | |
431 } // namespace media | 492 } // namespace media |
OLD | NEW |