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
3 // found in the LICENSE file.
5 #include "media/base/android/media_decoder_job.h"
8 #include "base/callback_helpers.h"
9 #include "base/debug/trace_event.h"
10 #include "base/message_loop/message_loop.h"
11 #include "media/base/android/media_codec_bridge.h"
12 #include "media/base/bind_to_loop.h"
13 #include "media/base/buffers.h"
17 // Timeout value for media codec operations. Because the first
18 // DequeInputBuffer() can take about 150 milliseconds, use 250 milliseconds
19 // here. See http://b/9357571.
20 static const int kMediaCodecTimeoutInMilliseconds
= 250;
22 MediaDecoderJob::MediaDecoderJob(
23 const scoped_refptr
<base::MessageLoopProxy
>& decoder_loop
,
24 MediaCodecBridge
* media_codec_bridge
,
25 const base::Closure
& request_data_cb
)
26 : ui_loop_(base::MessageLoopProxy::current()),
27 decoder_loop_(decoder_loop
),
28 media_codec_bridge_(media_codec_bridge
),
30 input_eos_encountered_(false),
33 request_data_cb_(request_data_cb
),
34 access_unit_index_(0),
36 stop_decode_pending_(false),
37 destroy_pending_(false) {
40 MediaDecoderJob::~MediaDecoderJob() {}
42 void MediaDecoderJob::OnDataReceived(const DemuxerData
& data
) {
43 DVLOG(1) << __FUNCTION__
<< ": " << data
.access_units
.size() << " units";
44 DCHECK(ui_loop_
->BelongsToCurrentThread());
45 DCHECK(!on_data_received_cb_
.is_null());
47 TRACE_EVENT_ASYNC_END2(
48 "media", "MediaDecoderJob::RequestData", this,
49 "Data type", data
.type
== media::DemuxerStream::AUDIO
? "AUDIO" : "VIDEO",
50 "Units read", data
.access_units
.size());
52 base::Closure done_cb
= base::ResetAndReturn(&on_data_received_cb_
);
54 if (stop_decode_pending_
) {
55 OnDecodeCompleted(MEDIA_CODEC_STOPPED
, kNoTimestamp(), 0);
59 access_unit_index_
= 0;
60 received_data_
= data
;
64 void MediaDecoderJob::Prefetch(const base::Closure
& prefetch_cb
) {
65 DCHECK(ui_loop_
->BelongsToCurrentThread());
66 DCHECK(on_data_received_cb_
.is_null());
67 DCHECK(decode_cb_
.is_null());
70 ui_loop_
->PostTask(FROM_HERE
, prefetch_cb
);
74 RequestData(prefetch_cb
);
77 bool MediaDecoderJob::Decode(
78 const base::TimeTicks
& start_time_ticks
,
79 const base::TimeDelta
& start_presentation_timestamp
,
80 const DecoderCallback
& callback
) {
81 DCHECK(decode_cb_
.is_null());
82 DCHECK(on_data_received_cb_
.is_null());
83 DCHECK(ui_loop_
->BelongsToCurrentThread());
85 decode_cb_
= callback
;
88 RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit
,
89 base::Unretained(this),
91 start_presentation_timestamp
));
95 if (DemuxerStream::kConfigChanged
==
96 received_data_
.access_units
[access_unit_index_
].status
) {
97 // Clear received data because we need to handle a config change.
99 received_data_
= DemuxerData();
100 access_unit_index_
= 0;
104 DecodeNextAccessUnit(start_time_ticks
, start_presentation_timestamp
);
108 void MediaDecoderJob::StopDecode() {
109 DCHECK(ui_loop_
->BelongsToCurrentThread());
110 DCHECK(is_decoding());
111 stop_decode_pending_
= true;
114 void MediaDecoderJob::Flush() {
115 DCHECK(decode_cb_
.is_null());
117 // Do nothing, flush when the next Decode() happens.
119 received_data_
= DemuxerData();
120 input_eos_encountered_
= false;
121 access_unit_index_
= 0;
122 on_data_received_cb_
.Reset();
125 void MediaDecoderJob::BeginPrerolling(
126 const base::TimeDelta
& preroll_timestamp
) {
127 DVLOG(1) << __FUNCTION__
<< "(" << preroll_timestamp
.InSecondsF() << ")";
128 DCHECK(ui_loop_
->BelongsToCurrentThread());
129 DCHECK(!is_decoding());
131 preroll_timestamp_
= preroll_timestamp
;
135 void MediaDecoderJob::Release() {
136 DCHECK(ui_loop_
->BelongsToCurrentThread());
138 // If the decoder job is not waiting for data, and is still decoding, we
139 // cannot delete the job immediately.
140 destroy_pending_
= on_data_received_cb_
.is_null() && is_decoding();
142 request_data_cb_
.Reset();
143 on_data_received_cb_
.Reset();
146 if (destroy_pending_
)
152 MediaCodecStatus
MediaDecoderJob::QueueInputBuffer(const AccessUnit
& unit
) {
153 DVLOG(1) << __FUNCTION__
;
154 DCHECK(decoder_loop_
->BelongsToCurrentThread());
155 TRACE_EVENT0("media", __FUNCTION__
);
157 int input_buf_index
= input_buf_index_
;
158 input_buf_index_
= -1;
160 // TODO(xhwang): Hide DequeueInputBuffer() and the index in MediaCodecBridge.
161 if (input_buf_index
== -1) {
162 base::TimeDelta timeout
= base::TimeDelta::FromMilliseconds(
163 kMediaCodecTimeoutInMilliseconds
);
164 MediaCodecStatus status
=
165 media_codec_bridge_
->DequeueInputBuffer(timeout
, &input_buf_index
);
166 if (status
!= MEDIA_CODEC_OK
) {
167 DVLOG(1) << "DequeueInputBuffer fails: " << status
;
172 // TODO(qinmin): skip frames if video is falling far behind.
173 DCHECK_GE(input_buf_index
, 0);
174 if (unit
.end_of_stream
|| unit
.data
.empty()) {
175 media_codec_bridge_
->QueueEOS(input_buf_index
);
176 return MEDIA_CODEC_INPUT_END_OF_STREAM
;
179 if (unit
.key_id
.empty() || unit
.iv
.empty()) {
180 DCHECK(unit
.iv
.empty() || !unit
.key_id
.empty());
181 return media_codec_bridge_
->QueueInputBuffer(
182 input_buf_index
, &unit
.data
[0], unit
.data
.size(), unit
.timestamp
);
185 MediaCodecStatus status
= media_codec_bridge_
->QueueSecureInputBuffer(
187 &unit
.data
[0], unit
.data
.size(),
188 reinterpret_cast<const uint8
*>(&unit
.key_id
[0]), unit
.key_id
.size(),
189 reinterpret_cast<const uint8
*>(&unit
.iv
[0]), unit
.iv
.size(),
190 unit
.subsamples
.empty() ? NULL
: &unit
.subsamples
[0],
191 unit
.subsamples
.size(),
194 // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|.
195 // Otherwise MediaDrm will report errors.
196 if (status
== MEDIA_CODEC_NO_KEY
)
197 input_buf_index_
= input_buf_index
;
202 bool MediaDecoderJob::HasData() const {
203 DCHECK(ui_loop_
->BelongsToCurrentThread());
204 // When |input_eos_encountered_| is set, |access_units| must not be empty and
205 // |access_unit_index_| must be pointing to an EOS unit. We'll reuse this
206 // unit to flush the decoder until we hit output EOS.
207 DCHECK(!input_eos_encountered_
||
208 (received_data_
.access_units
.size() > 0 &&
209 access_unit_index_
< received_data_
.access_units
.size()))
210 << " (access_units.size(): " << received_data_
.access_units
.size()
211 << ", access_unit_index_: " << access_unit_index_
<< ")";
212 return access_unit_index_
< received_data_
.access_units
.size() ||
213 input_eos_encountered_
;
216 void MediaDecoderJob::RequestData(const base::Closure
& done_cb
) {
217 DVLOG(1) << __FUNCTION__
;
218 DCHECK(ui_loop_
->BelongsToCurrentThread());
219 DCHECK(on_data_received_cb_
.is_null());
220 DCHECK(!input_eos_encountered_
);
222 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this);
224 received_data_
= DemuxerData();
225 access_unit_index_
= 0;
226 on_data_received_cb_
= done_cb
;
228 request_data_cb_
.Run();
231 void MediaDecoderJob::DecodeNextAccessUnit(
232 const base::TimeTicks
& start_time_ticks
,
233 const base::TimeDelta
& start_presentation_timestamp
) {
234 DCHECK(ui_loop_
->BelongsToCurrentThread());
235 DCHECK(!decode_cb_
.is_null());
237 // If the first access unit is a config change, request the player to dequeue
238 // the input buffer again so that it can request config data.
239 if (received_data_
.access_units
[access_unit_index_
].status
==
240 DemuxerStream::kConfigChanged
) {
241 ui_loop_
->PostTask(FROM_HERE
,
242 base::Bind(&MediaDecoderJob::OnDecodeCompleted
,
243 base::Unretained(this),
244 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER
,
250 decoder_loop_
->PostTask(FROM_HERE
, base::Bind(
251 &MediaDecoderJob::DecodeInternal
, base::Unretained(this),
252 received_data_
.access_units
[access_unit_index_
],
253 start_time_ticks
, start_presentation_timestamp
, needs_flush_
,
254 media::BindToLoop(ui_loop_
, base::Bind(
255 &MediaDecoderJob::OnDecodeCompleted
, base::Unretained(this)))));
256 needs_flush_
= false;
259 void MediaDecoderJob::DecodeInternal(
260 const AccessUnit
& unit
,
261 const base::TimeTicks
& start_time_ticks
,
262 const base::TimeDelta
& start_presentation_timestamp
,
264 const MediaDecoderJob::DecoderCallback
& callback
) {
265 DVLOG(1) << __FUNCTION__
;
266 DCHECK(decoder_loop_
->BelongsToCurrentThread());
267 TRACE_EVENT0("media", __FUNCTION__
);
270 DVLOG(1) << "DecodeInternal needs flush.";
271 input_eos_encountered_
= false;
272 MediaCodecStatus reset_status
= media_codec_bridge_
->Reset();
273 if (MEDIA_CODEC_OK
!= reset_status
) {
274 callback
.Run(reset_status
, kNoTimestamp(), 0);
279 // For aborted access unit, just skip it and inform the player.
280 if (unit
.status
== DemuxerStream::kAborted
) {
281 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED.
282 callback
.Run(MEDIA_CODEC_STOPPED
, kNoTimestamp(), 0);
286 MediaCodecStatus input_status
= MEDIA_CODEC_INPUT_END_OF_STREAM
;
287 if (!input_eos_encountered_
) {
288 input_status
= QueueInputBuffer(unit
);
289 if (input_status
== MEDIA_CODEC_INPUT_END_OF_STREAM
) {
290 input_eos_encountered_
= true;
291 } else if (input_status
!= MEDIA_CODEC_OK
) {
292 callback
.Run(input_status
, kNoTimestamp(), 0);
297 int buffer_index
= 0;
300 base::TimeDelta presentation_timestamp
;
301 bool output_eos_encountered
= false;
303 base::TimeDelta timeout
= base::TimeDelta::FromMilliseconds(
304 kMediaCodecTimeoutInMilliseconds
);
306 MediaCodecStatus status
= media_codec_bridge_
->DequeueOutputBuffer(
307 timeout
, &buffer_index
, &offset
, &size
, &presentation_timestamp
,
308 &output_eos_encountered
);
310 if (status
!= MEDIA_CODEC_OK
) {
311 if (status
== MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED
&&
312 !media_codec_bridge_
->GetOutputBuffers()) {
313 status
= MEDIA_CODEC_ERROR
;
315 callback
.Run(status
, kNoTimestamp(), 0);
319 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up.
320 if (output_eos_encountered
)
321 status
= MEDIA_CODEC_OUTPUT_END_OF_STREAM
;
322 else if (input_status
== MEDIA_CODEC_INPUT_END_OF_STREAM
)
323 status
= MEDIA_CODEC_INPUT_END_OF_STREAM
;
325 // Check whether we need to render the output.
326 // TODO(qinmin): comparing most recently queued input's |unit.timestamp| with
327 // |preroll_timestamp_| is not accurate due to data reordering and possible
328 // input queueing without immediate dequeue when |input_status| !=
329 // |MEDIA_CODEC_OK|. Need to use the |presentation_timestamp| for video, and
330 // use |size| to calculate the timestamp for audio. See
331 // http://crbug.com/310823 and b/11356652.
332 bool render_output
= unit
.timestamp
>= preroll_timestamp_
&&
333 (status
!= MEDIA_CODEC_OUTPUT_END_OF_STREAM
|| size
!= 0u);
334 base::TimeDelta time_to_render
;
335 DCHECK(!start_time_ticks
.is_null());
336 if (render_output
&& ComputeTimeToRender()) {
337 time_to_render
= presentation_timestamp
- (base::TimeTicks::Now() -
338 start_time_ticks
+ start_presentation_timestamp
);
341 if (time_to_render
> base::TimeDelta()) {
342 decoder_loop_
->PostDelayedTask(
344 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer
,
345 weak_this_
.GetWeakPtr(), buffer_index
, size
, render_output
,
346 base::Bind(callback
, status
, presentation_timestamp
)),
351 // TODO(qinmin): The codec is lagging behind, need to recalculate the
352 // |start_presentation_timestamp_| and |start_time_ticks_| in
353 // media_source_player.cc.
354 DVLOG(1) << "codec is lagging behind :" << time_to_render
.InMicroseconds();
356 // The player won't expect a timestamp smaller than the
357 // |start_presentation_timestamp|. However, this could happen due to decoder
359 presentation_timestamp
= std::max(
360 presentation_timestamp
, start_presentation_timestamp
);
362 presentation_timestamp
= kNoTimestamp();
364 ReleaseOutputCompletionCallback completion_callback
= base::Bind(
365 callback
, status
, presentation_timestamp
);
366 ReleaseOutputBuffer(buffer_index
, size
, render_output
, completion_callback
);
369 void MediaDecoderJob::OnDecodeCompleted(
370 MediaCodecStatus status
, const base::TimeDelta
& presentation_timestamp
,
371 size_t audio_output_bytes
) {
372 DCHECK(ui_loop_
->BelongsToCurrentThread());
374 if (destroy_pending_
) {
379 DCHECK(!decode_cb_
.is_null());
381 // If output was queued for rendering, then we have completed prerolling.
382 if (presentation_timestamp
!= kNoTimestamp())
387 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER
:
388 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED
:
389 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED
:
390 case MEDIA_CODEC_OUTPUT_END_OF_STREAM
:
391 if (!input_eos_encountered_
)
392 access_unit_index_
++;
395 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER
:
396 case MEDIA_CODEC_INPUT_END_OF_STREAM
:
397 case MEDIA_CODEC_NO_KEY
:
398 case MEDIA_CODEC_STOPPED
:
399 case MEDIA_CODEC_ERROR
:
404 stop_decode_pending_
= false;
405 base::ResetAndReturn(&decode_cb_
).Run(status
, presentation_timestamp
,