OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 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_codec_loop.h" |
| 6 |
| 7 #include "base/android/build_info.h" |
| 8 #include "base/bind.h" |
| 9 #include "base/callback_helpers.h" |
| 10 #include "base/logging.h" |
| 11 #include "base/threading/thread_task_runner_handle.h" |
| 12 #include "media/base/android/sdk_media_codec_bridge.h" |
| 13 #include "media/base/audio_buffer.h" |
| 14 #include "media/base/audio_timestamp_helper.h" |
| 15 #include "media/base/bind_to_current_loop.h" |
| 16 #include "media/base/timestamp_constants.h" |
| 17 |
| 18 namespace media { |
| 19 |
| 20 constexpr base::TimeDelta kDecodePollDelay = |
| 21 base::TimeDelta::FromMilliseconds(10); |
| 22 constexpr base::TimeDelta kNoWaitTimeout = base::TimeDelta::FromMicroseconds(0); |
| 23 constexpr base::TimeDelta kIdleTimerTimeout = base::TimeDelta::FromSeconds(1); |
| 24 |
| 25 static bool codec_flush_requires_destruction() { |
| 26 // Flush if we can, otherwise completely recreate and reconfigure the codec. |
| 27 // Prior to JellyBean-MR2, flush() had several bugs (b/8125974, b/8347958) so |
| 28 // we have to completely destroy and recreate the codec there. |
| 29 return base::android::BuildInfo::GetInstance()->sdk_int() < 18; |
| 30 } |
| 31 |
| 32 MediaCodecLoop::InputData::InputData() {} |
| 33 |
| 34 MediaCodecLoop::InputData::InputData(const InputData& other) |
| 35 : memory(other.memory), |
| 36 length(other.length), |
| 37 key_id(other.key_id), |
| 38 iv(other.iv), |
| 39 subsamples(other.subsamples), |
| 40 presentation_time(other.presentation_time), |
| 41 completion_cb(other.completion_cb), |
| 42 is_eos(other.is_eos), |
| 43 is_encrypted(other.is_encrypted) {} |
| 44 |
| 45 MediaCodecLoop::InputData::~InputData() {} |
| 46 |
| 47 MediaCodecLoop::MediaCodecLoop(Client* client, |
| 48 std::unique_ptr<MediaCodecBridge>&& media_codec) |
| 49 : state_(STATE_READY), |
| 50 client_(client), |
| 51 media_codec_(std::move(media_codec)), |
| 52 pending_input_buf_index_(kInvalidBufferIndex), |
| 53 weak_factory_(this) { |
| 54 // TODO(liberato): should this DCHECK? |
| 55 if (media_codec_ == nullptr) |
| 56 SetState(STATE_ERROR); |
| 57 } |
| 58 |
| 59 MediaCodecLoop::~MediaCodecLoop() {} |
| 60 |
| 61 void MediaCodecLoop::OnKeyAdded() { |
| 62 if (state_ == STATE_WAITING_FOR_KEY) |
| 63 SetState(STATE_READY); |
| 64 |
| 65 DoIOTask(); |
| 66 } |
| 67 |
| 68 bool MediaCodecLoop::TryFlush() { |
| 69 // We do not clear the input queue here. It depends on the caller. |
| 70 // For decoder reset, then it is appropriate. Otherwise, the requests might |
| 71 // simply be sent to us later, such as on a format change. |
| 72 |
| 73 if (state_ == STATE_ERROR || state_ == STATE_DRAINED) |
| 74 return false; |
| 75 |
| 76 if (codec_flush_requires_destruction()) |
| 77 return false; |
| 78 |
| 79 // Actually try to flush! |
| 80 io_timer_.Stop(); |
| 81 |
| 82 if (media_codec_->Flush() != MEDIA_CODEC_OK) { |
| 83 // Transition to the error state if the flush failed. |
| 84 SetState(STATE_ERROR); |
| 85 return false; |
| 86 } |
| 87 |
| 88 SetState(STATE_READY); |
| 89 return true; |
| 90 } |
| 91 |
| 92 void MediaCodecLoop::DoIOTask() { |
| 93 if (state_ == STATE_ERROR) |
| 94 return; |
| 95 |
| 96 bool did_work = false, did_input = false, did_output = false; |
| 97 do { |
| 98 did_input = ProcessOneInputBuffer(); |
| 99 did_output = ProcessOneOutputBuffer(); |
| 100 if (did_input || did_output) |
| 101 did_work = true; |
| 102 } while (did_input || did_output); |
| 103 |
| 104 // TODO(liberato): add "start_timer" for AVDA. |
| 105 ManageTimer(did_work); |
| 106 } |
| 107 |
| 108 bool MediaCodecLoop::ProcessOneInputBuffer() { |
| 109 // We can only queue a buffer if there is input from the client, or if we |
| 110 // tried previously but had to wait for a key. In the latter case, MediaCodec |
| 111 // already has the data. |
| 112 if (pending_input_buf_index_ == kInvalidBufferIndex && |
| 113 !client_->IsAnyInputPending()) { |
| 114 return false; |
| 115 } |
| 116 |
| 117 if (state_ == STATE_WAITING_FOR_KEY || state_ == STATE_DRAINING || |
| 118 state_ == STATE_DRAINED || state_ == STATE_ERROR) { |
| 119 return false; |
| 120 } |
| 121 |
| 122 // DequeueInputBuffer() may set STATE_ERROR. |
| 123 InputBuffer input_buffer = DequeueInputBuffer(); |
| 124 |
| 125 if (input_buffer.index == kInvalidBufferIndex) |
| 126 return false; |
| 127 |
| 128 // EnqueueInputBuffer() may set STATE_DRAINING, STATE_WAITING_FOR_KEY or |
| 129 // STATE_ERROR. |
| 130 EnqueueInputBuffer(input_buffer); |
| 131 return state_ == STATE_READY; |
| 132 } |
| 133 |
| 134 MediaCodecLoop::InputBuffer MediaCodecLoop::DequeueInputBuffer() { |
| 135 DVLOG(2) << __FUNCTION__; |
| 136 |
| 137 // Do not dequeue a new input buffer if we failed with MEDIA_CODEC_NO_KEY. |
| 138 // That status does not return the input buffer back to the pool of |
| 139 // available input buffers. We have to reuse it in QueueSecureInputBuffer(). |
| 140 if (pending_input_buf_index_ != kInvalidBufferIndex) { |
| 141 InputBuffer result(pending_input_buf_index_, true); |
| 142 pending_input_buf_index_ = kInvalidBufferIndex; |
| 143 return result; |
| 144 } |
| 145 |
| 146 int input_buf_index = kInvalidBufferIndex; |
| 147 |
| 148 media::MediaCodecStatus status = |
| 149 media_codec_->DequeueInputBuffer(kNoWaitTimeout, &input_buf_index); |
| 150 switch (status) { |
| 151 case media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: |
| 152 break; |
| 153 |
| 154 case media::MEDIA_CODEC_ERROR: |
| 155 DLOG(ERROR) << __FUNCTION__ |
| 156 << ": MEDIA_CODEC_ERROR from DequeInputBuffer"; |
| 157 SetState(STATE_ERROR); |
| 158 break; |
| 159 |
| 160 case media::MEDIA_CODEC_OK: |
| 161 break; |
| 162 |
| 163 default: |
| 164 NOTREACHED() << "Unknown DequeueInputBuffer status " << status; |
| 165 SetState(STATE_ERROR); |
| 166 break; |
| 167 } |
| 168 |
| 169 return InputBuffer(input_buf_index, false); |
| 170 } |
| 171 |
| 172 void MediaCodecLoop::EnqueueInputBuffer(const InputBuffer& input_buffer) { |
| 173 DCHECK_NE(input_buffer.index, kInvalidBufferIndex); |
| 174 |
| 175 InputData input_data; |
| 176 if (input_buffer.is_pending) { |
| 177 // A pending buffer is already filled with data, no need to copy it again. |
| 178 input_data = pending_input_buf_data_; |
| 179 } else { |
| 180 input_data = client_->ProvideInputData(); |
| 181 } |
| 182 |
| 183 if (input_data.is_eos) { |
| 184 media_codec_->QueueEOS(input_buffer.index); |
| 185 SetState(STATE_DRAINING); |
| 186 |
| 187 // For EOS, the completion callback is called when the EOS arrives at the |
| 188 // output queue. |
| 189 pending_eos_completion_cb_ = input_data.completion_cb; |
| 190 return; |
| 191 } |
| 192 |
| 193 media::MediaCodecStatus status = MEDIA_CODEC_OK; |
| 194 |
| 195 if (input_data.is_encrypted) { |
| 196 // Note that input_data might not have a valid memory ptr if this is a |
| 197 // re-send of a buffer that was sent before decryption keys arrived. |
| 198 |
| 199 status = media_codec_->QueueSecureInputBuffer( |
| 200 input_buffer.index, input_data.memory, input_data.length, |
| 201 input_data.key_id, input_data.iv, input_data.subsamples, |
| 202 input_data.presentation_time); |
| 203 |
| 204 } else { |
| 205 status = media_codec_->QueueInputBuffer( |
| 206 input_buffer.index, input_data.memory, input_data.length, |
| 207 input_data.presentation_time); |
| 208 } |
| 209 |
| 210 switch (status) { |
| 211 case MEDIA_CODEC_ERROR: |
| 212 DLOG(ERROR) << __FUNCTION__ |
| 213 << ": MEDIA_CODEC_ERROR from QueueInputBuffer"; |
| 214 input_data.completion_cb.Run(DecodeStatus::DECODE_ERROR); |
| 215 // Transition to the error state after running the completion cb, to keep |
| 216 // it in order if the client chooses to flush its queue. |
| 217 SetState(STATE_ERROR); |
| 218 break; |
| 219 |
| 220 case MEDIA_CODEC_NO_KEY: |
| 221 // Do not call the completion cb here. It will be called when we retry |
| 222 // after getting the key. |
| 223 pending_input_buf_index_ = input_buffer.index; |
| 224 pending_input_buf_data_ = input_data; |
| 225 // MediaCodec has a copy of the data already. When we call again, be sure |
| 226 // to send in nullptr for the source. Note that the client doesn't |
| 227 // guarantee that the pointer will remain valid after we return anyway. |
| 228 pending_input_buf_data_.memory = nullptr; |
| 229 SetState(STATE_WAITING_FOR_KEY); |
| 230 break; |
| 231 |
| 232 case MEDIA_CODEC_OK: |
| 233 input_data.completion_cb.Run(DecodeStatus::OK); |
| 234 break; |
| 235 |
| 236 default: |
| 237 NOTREACHED() << "Unknown Queue(Secure)InputBuffer status " << status; |
| 238 input_data.completion_cb.Run(DecodeStatus::DECODE_ERROR); |
| 239 SetState(STATE_ERROR); |
| 240 break; |
| 241 } |
| 242 } |
| 243 |
| 244 bool MediaCodecLoop::ProcessOneOutputBuffer() { |
| 245 // TODO(liberato): When merging AVDA, we will also have to ask the client if |
| 246 // it can accept another output buffer. |
| 247 |
| 248 OutputBuffer out; |
| 249 MediaCodecStatus status = media_codec_->DequeueOutputBuffer( |
| 250 kNoWaitTimeout, &out.index, &out.offset, &out.size, &out.pts, &out.is_eos, |
| 251 &out.is_key_frame); |
| 252 |
| 253 bool did_work = false; |
| 254 switch (status) { |
| 255 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: |
| 256 // Output buffers are replaced in MediaCodecBridge, nothing to do. |
| 257 did_work = true; |
| 258 break; |
| 259 |
| 260 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: |
| 261 if (!client_->OnOutputFormatChanged()) |
| 262 SetState(STATE_ERROR); |
| 263 did_work = state_ != STATE_ERROR; |
| 264 break; |
| 265 |
| 266 case MEDIA_CODEC_OK: |
| 267 // We got the decoded frame. |
| 268 if (out.is_eos) { |
| 269 // Set state STATE_DRAINED after we have received EOS frame at the |
| 270 // output. media_decoder_job.cc says: once output EOS has occurred, we |
| 271 // should not be asked to decode again. |
| 272 DCHECK_EQ(state_, STATE_DRAINING); |
| 273 SetState(STATE_DRAINED); |
| 274 |
| 275 DCHECK_NE(out.index, kInvalidBufferIndex); |
| 276 DCHECK(media_codec_); |
| 277 |
| 278 media_codec_->ReleaseOutputBuffer(out.index, false); |
| 279 |
| 280 // Run the EOS completion callback now, since we deferred it until |
| 281 // the EOS was completely processed. |
| 282 pending_eos_completion_cb_.Run(DecodeStatus::OK); |
| 283 pending_eos_completion_cb_ = DecodeCB(); |
| 284 |
| 285 client_->OnDecodedEos(out); |
| 286 } else { |
| 287 if (!client_->OnDecodedFrame(out)) |
| 288 SetState(STATE_ERROR); |
| 289 } |
| 290 |
| 291 did_work = true; |
| 292 break; |
| 293 |
| 294 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: |
| 295 // Nothing to do. |
| 296 break; |
| 297 |
| 298 case MEDIA_CODEC_ERROR: |
| 299 DLOG(ERROR) << __FUNCTION__ |
| 300 << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer"; |
| 301 // Next Decode() will report the error to the pipeline. |
| 302 SetState(STATE_ERROR); |
| 303 break; |
| 304 |
| 305 default: |
| 306 NOTREACHED() << "Unknown DequeueOutputBuffer status " << status; |
| 307 // Next Decode() will report the error to the pipeline. |
| 308 SetState(STATE_ERROR); |
| 309 break; |
| 310 } |
| 311 |
| 312 return did_work; |
| 313 } |
| 314 |
| 315 void MediaCodecLoop::ManageTimer(bool did_work) { |
| 316 bool should_be_running = true; |
| 317 |
| 318 base::TimeTicks now = base::TimeTicks::Now(); |
| 319 if (did_work || idle_time_begin_ == base::TimeTicks()) { |
| 320 idle_time_begin_ = now; |
| 321 } else { |
| 322 // Make sure that we have done work recently enough, else stop the timer. |
| 323 if (now - idle_time_begin_ > kIdleTimerTimeout) |
| 324 should_be_running = false; |
| 325 } |
| 326 |
| 327 if (should_be_running && !io_timer_.IsRunning()) { |
| 328 io_timer_.Start(FROM_HERE, kDecodePollDelay, this, |
| 329 &MediaCodecLoop::DoIOTask); |
| 330 } else if (!should_be_running && io_timer_.IsRunning()) { |
| 331 io_timer_.Stop(); |
| 332 } |
| 333 } |
| 334 |
| 335 void MediaCodecLoop::SetState(State new_state) { |
| 336 if (state_ != new_state && new_state == STATE_ERROR) |
| 337 client_->OnCodecLoopError(); |
| 338 state_ = new_state; |
| 339 } |
| 340 |
| 341 MediaCodecBridge* MediaCodecLoop::GetCodec() const { |
| 342 return media_codec_.get(); |
| 343 } |
| 344 |
| 345 // static |
| 346 const char* MediaCodecLoop::AsString(State state) { |
| 347 #define RETURN_STRING(x) \ |
| 348 case x: \ |
| 349 return #x; |
| 350 |
| 351 switch (state) { |
| 352 RETURN_STRING(STATE_READY); |
| 353 RETURN_STRING(STATE_WAITING_FOR_KEY); |
| 354 RETURN_STRING(STATE_DRAINING); |
| 355 RETURN_STRING(STATE_DRAINED); |
| 356 RETURN_STRING(STATE_ERROR); |
| 357 } |
| 358 #undef RETURN_STRING |
| 359 |
| 360 NOTREACHED() << "Unknown state " << state; |
| 361 return nullptr; |
| 362 } |
| 363 |
| 364 } // namespace media |
OLD | NEW |