| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "media/base/android/media_decoder_job.h" | 5 #include "media/base/android/media_decoder_job.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
| 9 #include "base/message_loop/message_loop.h" | 9 #include "base/message_loop/message_loop.h" |
| 10 #include "media/base/android/media_codec_bridge.h" | 10 #include "media/base/android/media_codec_bridge.h" |
| (...skipping 11 matching lines...) Expand all Loading... |
| 22 MediaCodecBridge* media_codec_bridge, | 22 MediaCodecBridge* media_codec_bridge, |
| 23 const base::Closure& request_data_cb) | 23 const base::Closure& request_data_cb) |
| 24 : ui_loop_(base::MessageLoopProxy::current()), | 24 : ui_loop_(base::MessageLoopProxy::current()), |
| 25 decoder_loop_(decoder_loop), | 25 decoder_loop_(decoder_loop), |
| 26 media_codec_bridge_(media_codec_bridge), | 26 media_codec_bridge_(media_codec_bridge), |
| 27 needs_flush_(false), | 27 needs_flush_(false), |
| 28 input_eos_encountered_(false), | 28 input_eos_encountered_(false), |
| 29 weak_this_(this), | 29 weak_this_(this), |
| 30 request_data_cb_(request_data_cb), | 30 request_data_cb_(request_data_cb), |
| 31 access_unit_index_(0), | 31 access_unit_index_(0), |
| 32 input_buf_index_(-1), |
| 32 stop_decode_pending_(false), | 33 stop_decode_pending_(false), |
| 33 destroy_pending_(false) { | 34 destroy_pending_(false) { |
| 34 } | 35 } |
| 35 | 36 |
| 36 MediaDecoderJob::~MediaDecoderJob() {} | 37 MediaDecoderJob::~MediaDecoderJob() {} |
| 37 | 38 |
| 38 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) { | 39 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) { |
| 40 DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units"; |
| 39 DCHECK(ui_loop_->BelongsToCurrentThread()); | 41 DCHECK(ui_loop_->BelongsToCurrentThread()); |
| 40 DCHECK(!on_data_received_cb_.is_null()); | 42 DCHECK(!on_data_received_cb_.is_null()); |
| 41 | 43 |
| 42 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_); | 44 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_); |
| 43 | 45 |
| 44 if (stop_decode_pending_) { | 46 if (stop_decode_pending_) { |
| 45 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); | 47 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); |
| 46 return; | 48 return; |
| 47 } | 49 } |
| 48 | 50 |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 125 request_data_cb_.Reset(); | 127 request_data_cb_.Reset(); |
| 126 on_data_received_cb_.Reset(); | 128 on_data_received_cb_.Reset(); |
| 127 decode_cb_.Reset(); | 129 decode_cb_.Reset(); |
| 128 | 130 |
| 129 if (destroy_pending_) | 131 if (destroy_pending_) |
| 130 return; | 132 return; |
| 131 | 133 |
| 132 delete this; | 134 delete this; |
| 133 } | 135 } |
| 134 | 136 |
| 135 MediaCodecStatus MediaDecoderJob::QueueInputBuffer( | 137 MediaCodecStatus MediaDecoderJob::QueueInputBuffer(const AccessUnit& unit) { |
| 136 const AccessUnit& unit) { | 138 DVLOG(1) << __FUNCTION__; |
| 137 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( | 139 DCHECK(decoder_loop_->BelongsToCurrentThread()); |
| 138 kMediaCodecTimeoutInMilliseconds); | 140 |
| 139 int input_buf_index = 0; | 141 int input_buf_index = input_buf_index_; |
| 140 MediaCodecStatus status = | 142 input_buf_index_ = -1; |
| 141 media_codec_bridge_->DequeueInputBuffer(timeout, &input_buf_index); | 143 |
| 142 if (status != MEDIA_CODEC_OK) | 144 // TODO(xhwang): Hide DequeueInputBuffer() and the index in MediaCodecBridge. |
| 143 return status; | 145 if (input_buf_index == -1) { |
| 146 base::TimeDelta timeout = base::TimeDelta::FromMilliseconds( |
| 147 kMediaCodecTimeoutInMilliseconds); |
| 148 MediaCodecStatus status = |
| 149 media_codec_bridge_->DequeueInputBuffer(timeout, &input_buf_index); |
| 150 if (status != MEDIA_CODEC_OK) { |
| 151 DVLOG(1) << "DequeueInputBuffer fails: " << status; |
| 152 return status; |
| 153 } |
| 154 } |
| 144 | 155 |
| 145 // TODO(qinmin): skip frames if video is falling far behind. | 156 // TODO(qinmin): skip frames if video is falling far behind. |
| 146 DCHECK_GE(input_buf_index, 0); | 157 DCHECK_GE(input_buf_index, 0); |
| 147 if (unit.end_of_stream || unit.data.empty()) { | 158 if (unit.end_of_stream || unit.data.empty()) { |
| 148 media_codec_bridge_->QueueEOS(input_buf_index); | 159 media_codec_bridge_->QueueEOS(input_buf_index_); |
| 149 return MEDIA_CODEC_INPUT_END_OF_STREAM; | 160 return MEDIA_CODEC_INPUT_END_OF_STREAM; |
| 150 } | 161 } |
| 151 | 162 |
| 152 if (unit.key_id.empty()) { | 163 if (unit.key_id.empty()) { |
| 153 media_codec_bridge_->QueueInputBuffer( | 164 return media_codec_bridge_->QueueInputBuffer( |
| 154 input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp); | 165 input_buf_index, &unit.data[0], unit.data.size(), unit.timestamp); |
| 155 return MEDIA_CODEC_OK; | |
| 156 } | 166 } |
| 157 | 167 |
| 158 if (unit.iv.empty() || unit.subsamples.empty()) { | 168 if (unit.iv.empty() || unit.subsamples.empty()) { |
| 159 LOG(ERROR) << "The access unit doesn't have iv or subsamples while it " | 169 DVLOG(1) << "The access unit doesn't have iv or subsamples while it " |
| 160 << "has key IDs!"; | 170 << "has key IDs!"; |
| 161 return MEDIA_CODEC_ERROR; | 171 return MEDIA_CODEC_ERROR; |
| 162 } | 172 } |
| 163 | 173 |
| 164 media_codec_bridge_->QueueSecureInputBuffer( | 174 MediaCodecStatus status = media_codec_bridge_->QueueSecureInputBuffer( |
| 165 input_buf_index, &unit.data[0], unit.data.size(), | 175 input_buf_index, &unit.data[0], unit.data.size(), |
| 166 reinterpret_cast<const uint8*>(&unit.key_id[0]), unit.key_id.size(), | 176 reinterpret_cast<const uint8*>(&unit.key_id[0]), unit.key_id.size(), |
| 167 reinterpret_cast<const uint8*>(&unit.iv[0]), unit.iv.size(), | 177 reinterpret_cast<const uint8*>(&unit.iv[0]), unit.iv.size(), |
| 168 &unit.subsamples[0], unit.subsamples.size(), unit.timestamp); | 178 &unit.subsamples[0], unit.subsamples.size(), unit.timestamp); |
| 169 return MEDIA_CODEC_OK; | 179 |
| 180 // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|. |
| 181 // Otherwise MediaDrm will report errors. |
| 182 if (status == MEDIA_CODEC_NO_KEY) |
| 183 input_buf_index_ = input_buf_index; |
| 184 |
| 185 return status; |
| 170 } | 186 } |
| 171 | 187 |
| 172 void MediaDecoderJob::RequestData(const base::Closure& done_cb) { | 188 void MediaDecoderJob::RequestData(const base::Closure& done_cb) { |
| 189 DVLOG(1) << __FUNCTION__; |
| 173 DCHECK(ui_loop_->BelongsToCurrentThread()); | 190 DCHECK(ui_loop_->BelongsToCurrentThread()); |
| 174 DCHECK(on_data_received_cb_.is_null()); | 191 DCHECK(on_data_received_cb_.is_null()); |
| 175 | 192 |
| 176 received_data_ = DemuxerData(); | 193 received_data_ = DemuxerData(); |
| 177 access_unit_index_ = 0; | 194 access_unit_index_ = 0; |
| 178 on_data_received_cb_ = done_cb; | 195 on_data_received_cb_ = done_cb; |
| 179 | 196 |
| 180 request_data_cb_.Run(); | 197 request_data_cb_.Run(); |
| 181 } | 198 } |
| 182 | 199 |
| (...skipping 11 matching lines...) Expand all Loading... |
| 194 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); | 211 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); |
| 195 needs_flush_ = false; | 212 needs_flush_ = false; |
| 196 } | 213 } |
| 197 | 214 |
| 198 void MediaDecoderJob::DecodeInternal( | 215 void MediaDecoderJob::DecodeInternal( |
| 199 const AccessUnit& unit, | 216 const AccessUnit& unit, |
| 200 const base::TimeTicks& start_time_ticks, | 217 const base::TimeTicks& start_time_ticks, |
| 201 const base::TimeDelta& start_presentation_timestamp, | 218 const base::TimeDelta& start_presentation_timestamp, |
| 202 bool needs_flush, | 219 bool needs_flush, |
| 203 const MediaDecoderJob::DecoderCallback& callback) { | 220 const MediaDecoderJob::DecoderCallback& callback) { |
| 221 DVLOG(1) << __FUNCTION__; |
| 222 DCHECK(decoder_loop_->BelongsToCurrentThread()); |
| 223 |
| 204 if (needs_flush) { | 224 if (needs_flush) { |
| 205 DVLOG(1) << "DecodeInternal needs flush."; | 225 DVLOG(1) << "DecodeInternal needs flush."; |
| 206 input_eos_encountered_ = false; | 226 input_eos_encountered_ = false; |
| 207 media_codec_bridge_->Reset(); | 227 media_codec_bridge_->Reset(); |
| 208 } | 228 } |
| 209 | 229 |
| 210 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; | 230 MediaCodecStatus input_status = MEDIA_CODEC_INPUT_END_OF_STREAM; |
| 211 if (!input_eos_encountered_) { | 231 if (!input_eos_encountered_) { |
| 212 input_status = QueueInputBuffer(unit); | 232 input_status = QueueInputBuffer(unit); |
| 213 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { | 233 if (input_status == MEDIA_CODEC_INPUT_END_OF_STREAM) { |
| (...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 252 | 272 |
| 253 base::TimeDelta time_to_render; | 273 base::TimeDelta time_to_render; |
| 254 DCHECK(!start_time_ticks.is_null()); | 274 DCHECK(!start_time_ticks.is_null()); |
| 255 if (ComputeTimeToRender()) { | 275 if (ComputeTimeToRender()) { |
| 256 time_to_render = presentation_timestamp - (base::TimeTicks::Now() - | 276 time_to_render = presentation_timestamp - (base::TimeTicks::Now() - |
| 257 start_time_ticks + start_presentation_timestamp); | 277 start_time_ticks + start_presentation_timestamp); |
| 258 } | 278 } |
| 259 | 279 |
| 260 // TODO(acolwell): Change to > since the else will never run for audio. | 280 // TODO(acolwell): Change to > since the else will never run for audio. |
| 261 if (time_to_render >= base::TimeDelta()) { | 281 if (time_to_render >= base::TimeDelta()) { |
| 262 base::MessageLoop::current()->PostDelayedTask( | 282 decoder_loop_->PostDelayedTask( |
| 263 FROM_HERE, | 283 FROM_HERE, |
| 264 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, | 284 base::Bind(&MediaDecoderJob::ReleaseOutputBuffer, |
| 265 weak_this_.GetWeakPtr(), buffer_index, size, | 285 weak_this_.GetWeakPtr(), buffer_index, size, |
| 266 presentation_timestamp, callback, status), | 286 presentation_timestamp, callback, status), |
| 267 time_to_render); | 287 time_to_render); |
| 268 return; | 288 return; |
| 269 } | 289 } |
| 270 | 290 |
| 271 // TODO(qinmin): The codec is lagging behind, need to recalculate the | 291 // TODO(qinmin): The codec is lagging behind, need to recalculate the |
| 272 // |start_presentation_timestamp_| and |start_time_ticks_|. | 292 // |start_presentation_timestamp_| and |start_time_ticks_|. |
| 273 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); | 293 DVLOG(1) << "codec is lagging behind :" << time_to_render.InMicroseconds(); |
| 274 ReleaseOutputBuffer(buffer_index, size, presentation_timestamp, | 294 ReleaseOutputBuffer(buffer_index, size, presentation_timestamp, |
| 275 callback, status); | 295 callback, status); |
| 276 } | 296 } |
| 277 | 297 |
| 278 void MediaDecoderJob::OnDecodeCompleted( | 298 void MediaDecoderJob::OnDecodeCompleted( |
| 279 MediaCodecStatus status, const base::TimeDelta& presentation_timestamp, | 299 MediaCodecStatus status, const base::TimeDelta& presentation_timestamp, |
| 280 size_t audio_output_bytes) { | 300 size_t audio_output_bytes) { |
| 281 DCHECK(ui_loop_->BelongsToCurrentThread()); | 301 DCHECK(ui_loop_->BelongsToCurrentThread()); |
| 282 DCHECK(status != MEDIA_CODEC_STOPPED || received_data_.access_units.empty()); | 302 DCHECK(status != MEDIA_CODEC_STOPPED || received_data_.access_units.empty()); |
| 283 | 303 |
| 284 if (destroy_pending_) { | 304 if (destroy_pending_) { |
| 285 delete this; | 305 delete this; |
| 286 return; | 306 return; |
| 287 } | 307 } |
| 288 | 308 |
| 289 DCHECK(!decode_cb_.is_null()); | 309 DCHECK(!decode_cb_.is_null()); |
| 290 | 310 |
| 291 if (status != MEDIA_CODEC_ERROR && | 311 if (status != MEDIA_CODEC_ERROR && |
| 292 status != MEDIA_CODEC_ENQUEUE_INPUT_AGAIN_LATER && | 312 status != MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER && |
| 293 status != MEDIA_CODEC_INPUT_END_OF_STREAM && | 313 status != MEDIA_CODEC_INPUT_END_OF_STREAM && |
| 314 status != MEDIA_CODEC_NO_KEY && |
| 294 status != MEDIA_CODEC_STOPPED) { | 315 status != MEDIA_CODEC_STOPPED) { |
| 295 access_unit_index_++; | 316 access_unit_index_++; |
| 296 } | 317 } |
| 297 | 318 |
| 298 stop_decode_pending_ = false; | 319 stop_decode_pending_ = false; |
| 299 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, | 320 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, |
| 300 audio_output_bytes); | 321 audio_output_bytes); |
| 301 } | 322 } |
| 302 | 323 |
| 303 } // namespace media | 324 } // namespace media |
| OLD | NEW |