Fix for browser_plugin_host_browsertest when embedder is not yet available.
[chromium-blink-merge.git] / media / base / android / media_decoder_job.cc
blobcd8d04cfe3fdba4d6ccdf93ffa33dcff732b3e06
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"
7 #include "base/bind.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"
15 namespace media {
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),
29 needs_flush_(false),
30 input_eos_encountered_(false),
31 prerolling_(true),
32 weak_this_(this),
33 request_data_cb_(request_data_cb),
34 access_unit_index_(0),
35 input_buf_index_(-1),
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);
56 return;
59 access_unit_index_ = 0;
60 received_data_ = data;
61 done_cb.Run();
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());
69 if (HasData()) {
70 ui_loop_->PostTask(FROM_HERE, prefetch_cb);
71 return;
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;
87 if (!HasData()) {
88 RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit,
89 base::Unretained(this),
90 start_time_ticks,
91 start_presentation_timestamp));
92 return true;
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.
98 decode_cb_.Reset();
99 received_data_ = DemuxerData();
100 access_unit_index_ = 0;
101 return false;
104 DecodeNextAccessUnit(start_time_ticks, start_presentation_timestamp);
105 return true;
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.
118 needs_flush_ = true;
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;
132 prerolling_ = true;
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();
144 decode_cb_.Reset();
146 if (destroy_pending_)
147 return;
149 delete this;
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;
168 return 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(
186 input_buf_index,
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(),
192 unit.timestamp);
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;
199 return status;
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,
245 kNoTimestamp(),
246 0));
247 return;
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,
263 bool needs_flush,
264 const MediaDecoderJob::DecoderCallback& callback) {
265 DVLOG(1) << __FUNCTION__;
266 DCHECK(decoder_loop_->BelongsToCurrentThread());
267 TRACE_EVENT0("media", __FUNCTION__);
269 if (needs_flush) {
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);
275 return;
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);
283 return;
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);
293 return;
297 int buffer_index = 0;
298 size_t offset = 0;
299 size_t size = 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);
316 return;
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(
343 FROM_HERE,
344 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer,
345 weak_this_.GetWeakPtr(), buffer_index, size, render_output,
346 base::Bind(callback, status, presentation_timestamp)),
347 time_to_render);
348 return;
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();
355 if (render_output) {
356 // The player won't expect a timestamp smaller than the
357 // |start_presentation_timestamp|. However, this could happen due to decoder
358 // errors.
359 presentation_timestamp = std::max(
360 presentation_timestamp, start_presentation_timestamp);
361 } else {
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_) {
375 delete this;
376 return;
379 DCHECK(!decode_cb_.is_null());
381 // If output was queued for rendering, then we have completed prerolling.
382 if (presentation_timestamp != kNoTimestamp())
383 prerolling_ = false;
385 switch (status) {
386 case MEDIA_CODEC_OK:
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_++;
393 break;
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:
400 // Do nothing.
401 break;
404 stop_decode_pending_ = false;
405 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp,
406 audio_output_bytes);
409 } // namespace media