Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / media / base / android / media_decoder_job.cc
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.
4
5 #include "media/base/android/media_decoder_job.h"
6
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_proxy.h"
11 #include "media/base/android/media_codec_bridge.h"
12 #include "media/base/bind_to_current_loop.h"
13 #include "media/base/buffers.h"
14
15 namespace media {
16
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;
21
22 MediaDecoderJob::MediaDecoderJob(
23     const scoped_refptr<base::SingleThreadTaskRunner>& decoder_task_runner,
24     MediaCodecBridge* media_codec_bridge,
25     const base::Closure& request_data_cb)
26     : ui_task_runner_(base::MessageLoopProxy::current()),
27       decoder_task_runner_(decoder_task_runner),
28       media_codec_bridge_(media_codec_bridge),
29       needs_flush_(false),
30       input_eos_encountered_(false),
31       output_eos_encountered_(false),
32       skip_eos_enqueue_(true),
33       prerolling_(true),
34       weak_this_(this),
35       request_data_cb_(request_data_cb),
36       access_unit_index_(0),
37       input_buf_index_(-1),
38       stop_decode_pending_(false),
39       destroy_pending_(false) {
40 }
41
42 MediaDecoderJob::~MediaDecoderJob() {}
43
44 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) {
45   DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units";
46   DCHECK(ui_task_runner_->BelongsToCurrentThread());
47   DCHECK(!on_data_received_cb_.is_null());
48
49   TRACE_EVENT_ASYNC_END2(
50       "media", "MediaDecoderJob::RequestData", this,
51       "Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO",
52       "Units read", data.access_units.size());
53
54   base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_);
55
56   if (stop_decode_pending_) {
57     OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0);
58     return;
59   }
60
61   access_unit_index_ = 0;
62   received_data_ = data;
63   done_cb.Run();
64 }
65
66 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
67   DCHECK(ui_task_runner_->BelongsToCurrentThread());
68   DCHECK(on_data_received_cb_.is_null());
69   DCHECK(decode_cb_.is_null());
70
71   if (HasData()) {
72     DVLOG(1) << __FUNCTION__ << " : using previously received data";
73     ui_task_runner_->PostTask(FROM_HERE, prefetch_cb);
74     return;
75   }
76
77   DVLOG(1) << __FUNCTION__ << " : requesting data";
78   RequestData(prefetch_cb);
79 }
80
81 bool MediaDecoderJob::Decode(
82     const base::TimeTicks& start_time_ticks,
83     const base::TimeDelta& start_presentation_timestamp,
84     const DecoderCallback& callback) {
85   DCHECK(decode_cb_.is_null());
86   DCHECK(on_data_received_cb_.is_null());
87   DCHECK(ui_task_runner_->BelongsToCurrentThread());
88
89   decode_cb_ = callback;
90
91   if (!HasData()) {
92     RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit,
93                            base::Unretained(this),
94                            start_time_ticks,
95                            start_presentation_timestamp));
96     return true;
97   }
98
99   if (DemuxerStream::kConfigChanged ==
100       received_data_.access_units[access_unit_index_].status) {
101     // Clear received data because we need to handle a config change.
102     decode_cb_.Reset();
103     received_data_ = DemuxerData();
104     access_unit_index_ = 0;
105     return false;
106   }
107
108   DecodeNextAccessUnit(start_time_ticks, start_presentation_timestamp);
109   return true;
110 }
111
112 void MediaDecoderJob::StopDecode() {
113   DCHECK(ui_task_runner_->BelongsToCurrentThread());
114   DCHECK(is_decoding());
115   stop_decode_pending_ = true;
116 }
117
118 void MediaDecoderJob::Flush() {
119   DCHECK(decode_cb_.is_null());
120
121   // Do nothing, flush when the next Decode() happens.
122   needs_flush_ = true;
123   received_data_ = DemuxerData();
124   input_eos_encountered_ = false;
125   access_unit_index_ = 0;
126   on_data_received_cb_.Reset();
127 }
128
129 void MediaDecoderJob::BeginPrerolling(
130     const base::TimeDelta& preroll_timestamp) {
131   DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")";
132   DCHECK(ui_task_runner_->BelongsToCurrentThread());
133   DCHECK(!is_decoding());
134
135   preroll_timestamp_ = preroll_timestamp;
136   prerolling_ = true;
137 }
138
139 void MediaDecoderJob::Release() {
140   DCHECK(ui_task_runner_->BelongsToCurrentThread());
141   DVLOG(1) << __FUNCTION__;
142
143   // If the decoder job is not waiting for data, and is still decoding, we
144   // cannot delete the job immediately.
145   destroy_pending_ = on_data_received_cb_.is_null() && is_decoding();
146
147   request_data_cb_.Reset();
148   on_data_received_cb_.Reset();
149   decode_cb_.Reset();
150
151   if (destroy_pending_) {
152     DVLOG(1) << __FUNCTION__ << " : delete is pending decode completion";
153     return;
154   }
155
156   delete this;
157 }
158
159 MediaCodecStatus MediaDecoderJob::QueueInputBuffer(const AccessUnit& unit) {
160   DVLOG(1) << __FUNCTION__;
161   DCHECK(decoder_task_runner_->BelongsToCurrentThread());
162   TRACE_EVENT0("media", __FUNCTION__);
163
164   int input_buf_index = input_buf_index_;
165   input_buf_index_ = -1;
166
167   // TODO(xhwang): Hide DequeueInputBuffer() and the index in MediaCodecBridge.
168   if (input_buf_index == -1) {
169     base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
170         kMediaCodecTimeoutInMilliseconds);
171     MediaCodecStatus status =
172         media_codec_bridge_->DequeueInputBuffer(timeout, &input_buf_index);
173     if (status != MEDIA_CODEC_OK) {
174       DVLOG(1) << "DequeueInputBuffer fails: " << status;
175       return status;
176     }
177   }
178
179   // TODO(qinmin): skip frames if video is falling far behind.
180   DCHECK_GE(input_buf_index, 0);
181   if (unit.end_of_stream || unit.data.empty()) {
182     media_codec_bridge_->QueueEOS(input_buf_index);
183     return MEDIA_CODEC_INPUT_END_OF_STREAM;
184   }
185
186   if (unit.key_id.empty() || unit.iv.empty()) {
187     DCHECK(unit.iv.empty() || !unit.key_id.empty());
188     return media_codec_bridge_->QueueInputBuffer(
189         input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp);
190   }
191
192   MediaCodecStatus status = media_codec_bridge_->QueueSecureInputBuffer(
193       input_buf_index,
194       &unit.data[0], unit.data.size(),
195       reinterpret_cast<const uint8*>(&unit.key_id[0]), unit.key_id.size(),
196       reinterpret_cast<const uint8*>(&unit.iv[0]), unit.iv.size(),
197       unit.subsamples.empty() ? NULL : &unit.subsamples[0],
198       unit.subsamples.size(),
199       unit.timestamp);
200
201   // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|.
202   // Otherwise MediaDrm will report errors.
203   if (status == MEDIA_CODEC_NO_KEY)
204     input_buf_index_ = input_buf_index;
205
206   return status;
207 }
208
209 bool MediaDecoderJob::HasData() const {
210   DCHECK(ui_task_runner_->BelongsToCurrentThread());
211   // When |input_eos_encountered_| is set, |access_units| must not be empty and
212   // |access_unit_index_| must be pointing to an EOS unit. We'll reuse this
213   // unit to flush the decoder until we hit output EOS.
214   DCHECK(!input_eos_encountered_ ||
215          (received_data_.access_units.size() > 0 &&
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 }
222
223 void MediaDecoderJob::RequestData(const base::Closure& done_cb) {
224   DVLOG(1) << __FUNCTION__;
225   DCHECK(ui_task_runner_->BelongsToCurrentThread());
226   DCHECK(on_data_received_cb_.is_null());
227   DCHECK(!input_eos_encountered_);
228
229   TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this);
230
231   received_data_ = DemuxerData();
232   access_unit_index_ = 0;
233   on_data_received_cb_ = done_cb;
234
235   request_data_cb_.Run();
236 }
237
238 void MediaDecoderJob::DecodeNextAccessUnit(
239     const base::TimeTicks& start_time_ticks,
240     const base::TimeDelta& start_presentation_timestamp) {
241   DCHECK(ui_task_runner_->BelongsToCurrentThread());
242   DCHECK(!decode_cb_.is_null());
243
244   // 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.
246   if (received_data_.access_units[access_unit_index_].status ==
247       DemuxerStream::kConfigChanged) {
248     ui_task_runner_->PostTask(FROM_HERE,
249                               base::Bind(&MediaDecoderJob::OnDecodeCompleted,
250                                          base::Unretained(this),
251                                          MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER,
252                                          kNoTimestamp(),
253                                          0));
254     return;
255   }
256
257   decoder_task_runner_->PostTask(FROM_HERE, base::Bind(
258       &MediaDecoderJob::DecodeInternal, base::Unretained(this),
259       received_data_.access_units[access_unit_index_],
260       start_time_ticks, start_presentation_timestamp, needs_flush_,
261       media::BindToCurrentLoop(base::Bind(
262           &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this)))));
263   needs_flush_ = false;
264 }
265
266 void MediaDecoderJob::DecodeInternal(
267     const AccessUnit& unit,
268     const base::TimeTicks& start_time_ticks,
269     const base::TimeDelta& start_presentation_timestamp,
270     bool needs_flush,
271     const MediaDecoderJob::DecoderCallback& callback) {
272   DVLOG(1) << __FUNCTION__;
273   DCHECK(decoder_task_runner_->BelongsToCurrentThread());
274   TRACE_EVENT0("media", __FUNCTION__);
275
276   if (needs_flush) {
277     DVLOG(1) << "DecodeInternal needs flush.";
278     input_eos_encountered_ = false;
279     output_eos_encountered_ = false;
280     MediaCodecStatus reset_status = media_codec_bridge_->Reset();
281     if (MEDIA_CODEC_OK != reset_status) {
282       callback.Run(reset_status, kNoTimestamp(), 0);
283       return;
284     }
285   }
286
287   // Once output EOS has occurred, we should not be asked to decode again.
288   // MediaCodec has undefined behavior if similarly asked to decode after output
289   // EOS.
290   DCHECK(!output_eos_encountered_);
291
292   // For aborted access unit, just skip it and inform the player.
293   if (unit.status == DemuxerStream::kAborted) {
294     // TODO(qinmin): use a new enum instead of MEDIA_CODEC_STOPPED.
295     callback.Run(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0);
296     return;
297   }
298
299   if (skip_eos_enqueue_) {
300     if (unit.end_of_stream || unit.data.empty()) {
301       input_eos_encountered_ = true;
302       output_eos_encountered_ = true;
303       callback.Run(MEDIA_CODEC_OUTPUT_END_OF_STREAM, kNoTimestamp(), 0);
304       return;
305     }
306
307     skip_eos_enqueue_ = false;
308   }
309
310   MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM;
311   if (!input_eos_encountered_) {
312     input_status = QueueInputBuffer(unit);
313     if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) {
314       input_eos_encountered_ = true;
315     } else if (input_status != MEDIA_CODEC_OK) {
316       callback.Run(input_status, kNoTimestamp(), 0);
317       return;
318     }
319   }
320
321   int buffer_index = 0;
322   size_t offset = 0;
323   size_t size = 0;
324   base::TimeDelta presentation_timestamp;
325
326   base::TimeDelta timeout = base::TimeDelta::FromMilliseconds(
327       kMediaCodecTimeoutInMilliseconds);
328
329   MediaCodecStatus status =
330       media_codec_bridge_->DequeueOutputBuffer(timeout,
331                                                &buffer_index,
332                                                &offset,
333                                                &size,
334                                                &presentation_timestamp,
335                                                &output_eos_encountered_,
336                                                NULL);
337
338   if (status != MEDIA_CODEC_OK) {
339     if (status == MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED &&
340         !media_codec_bridge_->GetOutputBuffers()) {
341       status = MEDIA_CODEC_ERROR;
342     }
343     callback.Run(status, kNoTimestamp(), 0);
344     return;
345   }
346
347   // TODO(xhwang/qinmin): This logic is correct but strange. Clean it up.
348   if (output_eos_encountered_)
349     status = MEDIA_CODEC_OUTPUT_END_OF_STREAM;
350   else if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM)
351     status = MEDIA_CODEC_INPUT_END_OF_STREAM;
352
353   bool render_output  = presentation_timestamp >= preroll_timestamp_ &&
354       (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u);
355   base::TimeDelta time_to_render;
356   DCHECK(!start_time_ticks.is_null());
357   if (render_output && ComputeTimeToRender()) {
358     time_to_render = presentation_timestamp - (base::TimeTicks::Now() -
359         start_time_ticks + start_presentation_timestamp);
360   }
361
362   if (time_to_render > base::TimeDelta()) {
363     decoder_task_runner_->PostDelayedTask(
364         FROM_HERE,
365         base::Bind(&MediaDecoderJob::ReleaseOutputBuffer,
366                    weak_this_.GetWeakPtr(), buffer_index, size, render_output,
367                    base::Bind(callback, status, presentation_timestamp)),
368         time_to_render);
369     return;
370   }
371
372   // TODO(qinmin): The codec is lagging behind, need to recalculate the
373   // |start_presentation_timestamp_| and |start_time_ticks_| in
374   // media_source_player.cc.
375   DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds();
376   if (render_output) {
377     // The player won't expect a timestamp smaller than the
378     // |start_presentation_timestamp|. However, this could happen due to decoder
379     // errors.
380     presentation_timestamp = std::max(
381         presentation_timestamp, start_presentation_timestamp);
382   } else {
383     presentation_timestamp = kNoTimestamp();
384   }
385   ReleaseOutputCompletionCallback completion_callback = base::Bind(
386       callback, status, presentation_timestamp);
387   ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback);
388 }
389
390 void MediaDecoderJob::OnDecodeCompleted(
391     MediaCodecStatus status, const base::TimeDelta& presentation_timestamp,
392     size_t audio_output_bytes) {
393   DCHECK(ui_task_runner_->BelongsToCurrentThread());
394
395   if (destroy_pending_) {
396     DVLOG(1) << __FUNCTION__ << " : completing pending deletion";
397     delete this;
398     return;
399   }
400
401   DCHECK(!decode_cb_.is_null());
402
403   // If output was queued for rendering, then we have completed prerolling.
404   if (presentation_timestamp != kNoTimestamp())
405     prerolling_ = false;
406
407   switch (status) {
408     case MEDIA_CODEC_OK:
409     case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
410     case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
411     case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
412     case MEDIA_CODEC_OUTPUT_END_OF_STREAM:
413       if (!input_eos_encountered_)
414         access_unit_index_++;
415       break;
416
417     case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
418     case MEDIA_CODEC_INPUT_END_OF_STREAM:
419     case MEDIA_CODEC_NO_KEY:
420     case MEDIA_CODEC_STOPPED:
421     case MEDIA_CODEC_ERROR:
422       // Do nothing.
423       break;
424   };
425
426   stop_decode_pending_ = false;
427   base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp,
428                                         audio_output_bytes);
429 }
430
431 }  // namespace media