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 DVLOG(1) << __FUNCTION__
<< " : using previously received data";
71 ui_loop_
->PostTask(FROM_HERE
, prefetch_cb
);
75 DVLOG(1) << __FUNCTION__
<< " : requesting data";
76 RequestData(prefetch_cb
);
79 bool MediaDecoderJob::Decode(
80 const base::TimeTicks
& start_time_ticks
,
81 const base::TimeDelta
& start_presentation_timestamp
,
82 const DecoderCallback
& callback
) {
83 DCHECK(decode_cb_
.is_null());
84 DCHECK(on_data_received_cb_
.is_null());
85 DCHECK(ui_loop_
->BelongsToCurrentThread());
87 decode_cb_
= callback
;
90 RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit
,
91 base::Unretained(this),
93 start_presentation_timestamp
));
97 if (DemuxerStream::kConfigChanged
==
98 received_data_
.access_units
[access_unit_index_
].status
) {
99 // Clear received data because we need to handle a config change.
101 received_data_
= DemuxerData();
102 access_unit_index_
= 0;
106 DecodeNextAccessUnit(start_time_ticks
, start_presentation_timestamp
);
110 void MediaDecoderJob::StopDecode() {
111 DCHECK(ui_loop_
->BelongsToCurrentThread());
112 DCHECK(is_decoding());
113 stop_decode_pending_
= true;
116 void MediaDecoderJob::Flush() {
117 DCHECK(decode_cb_
.is_null());
119 // Do nothing, flush when the next Decode() happens.
121 received_data_
= DemuxerData();
122 input_eos_encountered_
= false;
123 access_unit_index_
= 0;
124 on_data_received_cb_
.Reset();
127 void MediaDecoderJob::BeginPrerolling(
128 const base::TimeDelta
& preroll_timestamp
) {
129 DVLOG(1) << __FUNCTION__
<< "(" << preroll_timestamp
.InSecondsF() << ")";
130 DCHECK(ui_loop_
->BelongsToCurrentThread());
131 DCHECK(!is_decoding());
133 preroll_timestamp_
= preroll_timestamp
;
137 void MediaDecoderJob::Release() {
138 DCHECK(ui_loop_
->BelongsToCurrentThread());
139 DVLOG(1) << __FUNCTION__
;
141 // If the decoder job is not waiting for data, and is still decoding, we
142 // cannot delete the job immediately.
143 destroy_pending_
= on_data_received_cb_
.is_null() && is_decoding();
145 request_data_cb_
.Reset();
146 on_data_received_cb_
.Reset();
149 if (destroy_pending_
) {
150 DVLOG(1) << __FUNCTION__
<< " : delete is pending decode completion";
157 MediaCodecStatus
MediaDecoderJob::QueueInputBuffer(const AccessUnit
& unit
) {
158 DVLOG(1) << __FUNCTION__
;
159 DCHECK(decoder_loop_
->BelongsToCurrentThread());
160 TRACE_EVENT0("media", __FUNCTION__
);
162 int input_buf_index
= input_buf_index_
;
163 input_buf_index_
= -1;
165 // TODO(xhwang): Hide DequeueInputBuffer() and the index in MediaCodecBridge.
166 if (input_buf_index
== -1) {
167 base::TimeDelta timeout
= base::TimeDelta::FromMilliseconds(
168 kMediaCodecTimeoutInMilliseconds
);
169 MediaCodecStatus status
=
170 media_codec_bridge_
->DequeueInputBuffer(timeout
, &input_buf_index
);
171 if (status
!= MEDIA_CODEC_OK
) {
172 DVLOG(1) << "DequeueInputBuffer fails: " << status
;
177 // TODO(qinmin): skip frames if video is falling far behind.
178 DCHECK_GE(input_buf_index
, 0);
179 if (unit
.end_of_stream
|| unit
.data
.empty()) {
180 media_codec_bridge_
->QueueEOS(input_buf_index
);
181 return MEDIA_CODEC_INPUT_END_OF_STREAM
;
184 if (unit
.key_id
.empty() || unit
.iv
.empty()) {
185 DCHECK(unit
.iv
.empty() || !unit
.key_id
.empty());
186 return media_codec_bridge_
->QueueInputBuffer(
187 input_buf_index
, &unit
.data
[0], unit
.data
.size(), unit
.timestamp
);
190 MediaCodecStatus status
= media_codec_bridge_
->QueueSecureInputBuffer(
192 &unit
.data
[0], unit
.data
.size(),
193 reinterpret_cast<const uint8
*>(&unit
.key_id
[0]), unit
.key_id
.size(),
194 reinterpret_cast<const uint8
*>(&unit
.iv
[0]), unit
.iv
.size(),
195 unit
.subsamples
.empty() ? NULL
: &unit
.subsamples
[0],
196 unit
.subsamples
.size(),
199 // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|.
200 // Otherwise MediaDrm will report errors.
201 if (status
== MEDIA_CODEC_NO_KEY
)
202 input_buf_index_
= input_buf_index
;
207 bool MediaDecoderJob::HasData() const {
208 DCHECK(ui_loop_
->BelongsToCurrentThread());
209 // When |input_eos_encountered_| is set, |access_units| must not be empty and
210 // |access_unit_index_| must be pointing to an EOS unit. We'll reuse this
211 // unit to flush the decoder until we hit output EOS.
212 DCHECK(!input_eos_encountered_
||
213 (received_data_
.access_units
.size() > 0 &&
214 access_unit_index_
< received_data_
.access_units
.size()))
215 << " (access_units.size(): " << received_data_
.access_units
.size()
216 << ", access_unit_index_: " << access_unit_index_
<< ")";
217 return access_unit_index_
< received_data_
.access_units
.size() ||
218 input_eos_encountered_
;
221 void MediaDecoderJob::RequestData(const base::Closure
& done_cb
) {
222 DVLOG(1) << __FUNCTION__
;
223 DCHECK(ui_loop_
->BelongsToCurrentThread());
224 DCHECK(on_data_received_cb_
.is_null());
225 DCHECK(!input_eos_encountered_
);
227 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this);
229 received_data_
= DemuxerData();
230 access_unit_index_
= 0;
231 on_data_received_cb_
= done_cb
;
233 request_data_cb_
.Run();
236 void MediaDecoderJob::DecodeNextAccessUnit(
237 const base::TimeTicks
& start_time_ticks
,
238 const base::TimeDelta
& start_presentation_timestamp
) {
239 DCHECK(ui_loop_
->BelongsToCurrentThread());
240 DCHECK(!decode_cb_
.is_null());
242 // If the first access unit is a config change, request the player to dequeue
243 // the input buffer again so that it can request config data.
244 if (received_data_
.access_units
[access_unit_index_
].status
==
245 DemuxerStream::kConfigChanged
) {
246 ui_loop_
->PostTask(FROM_HERE
,
247 base::Bind(&MediaDecoderJob::OnDecodeCompleted
,
248 base::Unretained(this),
249 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER
,
255 decoder_loop_
->PostTask(FROM_HERE
, base::Bind(
256 &MediaDecoderJob::DecodeInternal
, base::Unretained(this),
257 received_data_
.access_units
[access_unit_index_
],
258 start_time_ticks
, start_presentation_timestamp
, needs_flush_
,
259 media::BindToLoop(ui_loop_
, base::Bind(
260 &MediaDecoderJob::OnDecodeCompleted
, base::Unretained(this)))));
261 needs_flush_
= false;
264 void MediaDecoderJob::DecodeInternal(
265 const AccessUnit
& unit
,
266 const base::TimeTicks
& start_time_ticks
,
267 const base::TimeDelta
& start_presentation_timestamp
,
269 const MediaDecoderJob::DecoderCallback
& callback
) {
270 DVLOG(1) << __FUNCTION__
;
271 DCHECK(decoder_loop_
->BelongsToCurrentThread());
272 TRACE_EVENT0("media", __FUNCTION__
);
275 DVLOG(1) << "DecodeInternal needs flush.";
276 input_eos_encountered_
= false;
277 MediaCodecStatus reset_status
= media_codec_bridge_
->Reset();
278 if (MEDIA_CODEC_OK
!= reset_status
) {
279 callback
.Run(reset_status
, kNoTimestamp(), 0);
284 // For aborted access unit, just skip it and inform the player.
285 if (unit
.status
== DemuxerStream::kAborted
) {
286 // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED.
287 callback
.Run(MEDIA_CODEC_STOPPED
, kNoTimestamp(), 0);
291 MediaCodecStatus input_status
= MEDIA_CODEC_INPUT_END_OF_STREAM
;
292 if (!input_eos_encountered_
) {
293 input_status
= QueueInputBuffer(unit
);
294 if (input_status
== MEDIA_CODEC_INPUT_END_OF_STREAM
) {
295 input_eos_encountered_
= true;
296 } else if (input_status
!= MEDIA_CODEC_OK
) {
297 callback
.Run(input_status
, kNoTimestamp(), 0);
302 int buffer_index
= 0;
305 base::TimeDelta presentation_timestamp
;
306 bool output_eos_encountered
= false;
308 base::TimeDelta timeout
= base::TimeDelta::FromMilliseconds(
309 kMediaCodecTimeoutInMilliseconds
);
311 MediaCodecStatus status
= media_codec_bridge_
->DequeueOutputBuffer(
312 timeout
, &buffer_index
, &offset
, &size
, &presentation_timestamp
,
313 &output_eos_encountered
);
315 if (status
!= MEDIA_CODEC_OK
) {
316 if (status
== MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED
&&
317 !media_codec_bridge_
->GetOutputBuffers()) {
318 status
= MEDIA_CODEC_ERROR
;
320 callback
.Run(status
, kNoTimestamp(), 0);
324 // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up.
325 if (output_eos_encountered
)
326 status
= MEDIA_CODEC_OUTPUT_END_OF_STREAM
;
327 else if (input_status
== MEDIA_CODEC_INPUT_END_OF_STREAM
)
328 status
= MEDIA_CODEC_INPUT_END_OF_STREAM
;
330 // Check whether we need to render the output.
331 // TODO(qinmin): comparing most recently queued input's |unit.timestamp| with
332 // |preroll_timestamp_| is not accurate due to data reordering and possible
333 // input queueing without immediate dequeue when |input_status| !=
334 // |MEDIA_CODEC_OK|. Need to use the |presentation_timestamp| for video, and
335 // use |size| to calculate the timestamp for audio. See
336 // http://crbug.com/310823 and b/11356652.
337 bool render_output
= unit
.timestamp
>= preroll_timestamp_
&&
338 (status
!= MEDIA_CODEC_OUTPUT_END_OF_STREAM
|| size
!= 0u);
339 base::TimeDelta time_to_render
;
340 DCHECK(!start_time_ticks
.is_null());
341 if (render_output
&& ComputeTimeToRender()) {
342 time_to_render
= presentation_timestamp
- (base::TimeTicks::Now() -
343 start_time_ticks
+ start_presentation_timestamp
);
346 if (time_to_render
> base::TimeDelta()) {
347 decoder_loop_
->PostDelayedTask(
349 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer
,
350 weak_this_
.GetWeakPtr(), buffer_index
, size
, render_output
,
351 base::Bind(callback
, status
, presentation_timestamp
)),
356 // TODO(qinmin): The codec is lagging behind, need to recalculate the
357 // |start_presentation_timestamp_| and |start_time_ticks_| in
358 // media_source_player.cc.
359 DVLOG(1) << "codec is lagging behind :" << time_to_render
.InMicroseconds();
361 // The player won't expect a timestamp smaller than the
362 // |start_presentation_timestamp|. However, this could happen due to decoder
364 presentation_timestamp
= std::max(
365 presentation_timestamp
, start_presentation_timestamp
);
367 presentation_timestamp
= kNoTimestamp();
369 ReleaseOutputCompletionCallback completion_callback
= base::Bind(
370 callback
, status
, presentation_timestamp
);
371 ReleaseOutputBuffer(buffer_index
, size
, render_output
, completion_callback
);
374 void MediaDecoderJob::OnDecodeCompleted(
375 MediaCodecStatus status
, const base::TimeDelta
& presentation_timestamp
,
376 size_t audio_output_bytes
) {
377 DCHECK(ui_loop_
->BelongsToCurrentThread());
379 if (destroy_pending_
) {
380 DVLOG(1) << __FUNCTION__
<< " : completing pending deletion";
385 DCHECK(!decode_cb_
.is_null());
387 // If output was queued for rendering, then we have completed prerolling.
388 if (presentation_timestamp
!= kNoTimestamp())
393 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER
:
394 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED
:
395 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED
:
396 case MEDIA_CODEC_OUTPUT_END_OF_STREAM
:
397 if (!input_eos_encountered_
)
398 access_unit_index_
++;
401 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER
:
402 case MEDIA_CODEC_INPUT_END_OF_STREAM
:
403 case MEDIA_CODEC_NO_KEY
:
404 case MEDIA_CODEC_STOPPED
:
405 case MEDIA_CODEC_ERROR
:
410 stop_decode_pending_
= false;
411 base::ResetAndReturn(&decode_cb_
).Run(status
, presentation_timestamp
,