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.h" 11 #include "media/base/android/media_codec_bridge.h" 12 #include "media/base/bind_to_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::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 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_loop_->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_loop_->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_loop_->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_loop_->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_loop_->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_loop_->BelongsToCurrentThread()); 133 DCHECK(!is_decoding()); 134 135 preroll_timestamp_ = preroll_timestamp; 136 prerolling_ = true; 137 } 138 139 void MediaDecoderJob::Release() { 140 DCHECK(ui_loop_->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_loop_->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_loop_->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_loop_->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_loop_->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_loop_->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_loop_->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::BindToLoop(ui_loop_, 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_loop_->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 // Check whether we need to render the output. 354 // TODO(qinmin): comparing most recently queued input's |unit.timestamp| with 355 // |preroll_timestamp_| is not accurate due to data reordering and possible 356 // input queueing without immediate dequeue when |input_status| != 357 // |MEDIA_CODEC_OK|. Need to use the |presentation_timestamp| for video, and 358 // use |size| to calculate the timestamp for audio. See 359 // http://crbug.com/310823 and http://b/11356652. 360 bool render_output = unit.timestamp >= preroll_timestamp_ && 361 (status != MEDIA_CODEC_OUTPUT_END_OF_STREAM || size != 0u); 362 base::TimeDelta time_to_render; 363 DCHECK(!start_time_ticks.is_null()); 364 if (render_output && ComputeTimeToRender()) { 365 time_to_render = presentation_timestamp - (base::TimeTicks::Now() - 366 start_time_ticks + start_presentation_timestamp); 367 } 368 369 if (time_to_render > base::TimeDelta()) { 370 decoder_loop_->PostDelayedTask( 371 FROM_HERE, 372 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, 373 weak_this_.GetWeakPtr(), buffer_index, size, render_output, 374 base::Bind(callback, status, presentation_timestamp)), 375 time_to_render); 376 return; 377 } 378 379 // TODO(qinmin): The codec is lagging behind, need to recalculate the 380 // |start_presentation_timestamp_| and |start_time_ticks_| in 381 // media_source_player.cc. 382 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); 383 if (render_output) { 384 // The player won't expect a timestamp smaller than the 385 // |start_presentation_timestamp|. However, this could happen due to decoder 386 // errors. 387 presentation_timestamp = std::max( 388 presentation_timestamp, start_presentation_timestamp); 389 } else { 390 presentation_timestamp = kNoTimestamp(); 391 } 392 ReleaseOutputCompletionCallback completion_callback = base::Bind( 393 callback, status, presentation_timestamp); 394 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback); 395 } 396 397 void MediaDecoderJob::OnDecodeCompleted( 398 MediaCodecStatus status, const base::TimeDelta& presentation_timestamp, 399 size_t audio_output_bytes) { 400 DCHECK(ui_loop_->BelongsToCurrentThread()); 401 402 if (destroy_pending_) { 403 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; 404 delete this; 405 return; 406 } 407 408 DCHECK(!decode_cb_.is_null()); 409 410 // If output was queued for rendering, then we have completed prerolling. 411 if (presentation_timestamp != kNoTimestamp()) 412 prerolling_ = false; 413 414 switch (status) { 415 case MEDIA_CODEC_OK: 416 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: 417 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: 418 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: 419 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: 420 if (!input_eos_encountered_) 421 access_unit_index_++; 422 break; 423 424 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: 425 case MEDIA_CODEC_INPUT_END_OF_STREAM: 426 case MEDIA_CODEC_NO_KEY: 427 case MEDIA_CODEC_STOPPED: 428 case MEDIA_CODEC_ERROR: 429 // Do nothing. 430 break; 431 }; 432 433 stop_decode_pending_ = false; 434 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, 435 audio_output_bytes); 436 } 437 438 } // namespace media 439