Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 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 | 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_codec_loop.h" | 5 #include "media/base/android/media_codec_loop.h" |
| 6 | 6 |
| 7 #include "base/android/build_info.h" | |
| 8 #include "base/bind.h" | 7 #include "base/bind.h" |
| 9 #include "base/callback_helpers.h" | 8 #include "base/callback_helpers.h" |
| 10 #include "base/logging.h" | 9 #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" | 10 #include "media/base/bind_to_current_loop.h" |
| 16 #include "media/base/timestamp_constants.h" | 11 #include "media/base/timestamp_constants.h" |
| 17 | 12 |
| 18 namespace media { | 13 namespace media { |
| 19 | 14 |
| 20 constexpr base::TimeDelta kDecodePollDelay = | 15 // Declaring these as constexpr variables doesn't work in windows -- they |
| 21 base::TimeDelta::FromMilliseconds(10); | 16 // always are 0. The exception is FromMicroseconds, which doesn't do any |
| 22 constexpr base::TimeDelta kNoWaitTimeout = base::TimeDelta::FromMicroseconds(0); | 17 // conversion. However, declaring these as constexpr functions seesm to work |
| 23 constexpr base::TimeDelta kIdleTimerTimeout = base::TimeDelta::FromSeconds(1); | 18 // fine everywhere. We care that this works in windows because our unit tests |
| 19 // run on non-android platforms. | |
| 20 constexpr base::TimeDelta DecodePollDelay() { | |
| 21 return base::TimeDelta::FromMilliseconds(10); | |
| 22 } | |
| 24 | 23 |
| 25 static inline bool codec_flush_requires_destruction() { | 24 constexpr base::TimeDelta NoWaitTimeout() { |
| 26 // Return true if and only if Flush() isn't supported / doesn't work. | 25 return base::TimeDelta::FromMicroseconds(0); |
| 27 // Prior to JellyBean-MR2, flush() had several bugs (b/8125974, b/8347958) so | 26 } |
| 28 // we have to completely destroy and recreate the codec there. | 27 |
| 29 return base::android::BuildInfo::GetInstance()->sdk_int() < 18; | 28 constexpr base::TimeDelta IdleTimerTimeout() { |
| 29 return base::TimeDelta::FromSeconds(1); | |
| 30 } | 30 } |
| 31 | 31 |
| 32 MediaCodecLoop::InputData::InputData() {} | 32 MediaCodecLoop::InputData::InputData() {} |
| 33 | 33 |
| 34 MediaCodecLoop::InputData::InputData(const InputData& other) | 34 MediaCodecLoop::InputData::InputData(const InputData& other) |
| 35 : memory(other.memory), | 35 : memory(other.memory), |
| 36 length(other.length), | 36 length(other.length), |
| 37 key_id(other.key_id), | 37 key_id(other.key_id), |
| 38 iv(other.iv), | 38 iv(other.iv), |
| 39 subsamples(other.subsamples), | 39 subsamples(other.subsamples), |
| 40 presentation_time(other.presentation_time), | 40 presentation_time(other.presentation_time), |
| 41 is_eos(other.is_eos), | 41 is_eos(other.is_eos), |
| 42 is_encrypted(other.is_encrypted) {} | 42 is_encrypted(other.is_encrypted) {} |
| 43 | 43 |
| 44 MediaCodecLoop::InputData::~InputData() {} | 44 MediaCodecLoop::InputData::~InputData() {} |
| 45 | 45 |
| 46 MediaCodecLoop::MediaCodecLoop(Client* client, | 46 MediaCodecLoop::MediaCodecLoop( |
| 47 std::unique_ptr<MediaCodecBridge> media_codec) | 47 int sdk_int, |
| 48 Client* client, | |
| 49 std::unique_ptr<MediaCodecBridge> media_codec, | |
| 50 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | |
| 48 : state_(STATE_READY), | 51 : state_(STATE_READY), |
| 49 client_(client), | 52 client_(client), |
| 50 media_codec_(std::move(media_codec)), | 53 media_codec_(std::move(media_codec)), |
| 51 pending_input_buf_index_(kInvalidBufferIndex), | 54 pending_input_buf_index_(kInvalidBufferIndex), |
| 55 sdk_int_(sdk_int), | |
| 52 weak_factory_(this) { | 56 weak_factory_(this) { |
| 57 if (task_runner) | |
| 58 io_timer_.SetTaskRunner(task_runner); | |
| 53 // TODO(liberato): should this DCHECK? | 59 // TODO(liberato): should this DCHECK? |
| 54 if (media_codec_ == nullptr) | 60 if (media_codec_ == nullptr) |
| 55 SetState(STATE_ERROR); | 61 SetState(STATE_ERROR); |
| 56 } | 62 } |
| 57 | 63 |
| 58 MediaCodecLoop::~MediaCodecLoop() { | 64 MediaCodecLoop::~MediaCodecLoop() { |
| 59 io_timer_.Stop(); | 65 io_timer_.Stop(); |
| 60 } | 66 } |
| 61 | 67 |
| 68 void MediaCodecLoop::SetTestTickClock(base::TickClock* test_tick_clock) { | |
| 69 test_tick_clock_ = test_tick_clock; | |
| 70 } | |
| 71 | |
| 62 void MediaCodecLoop::OnKeyAdded() { | 72 void MediaCodecLoop::OnKeyAdded() { |
| 63 if (state_ == STATE_WAITING_FOR_KEY) | 73 if (state_ == STATE_WAITING_FOR_KEY) |
| 64 SetState(STATE_READY); | 74 SetState(STATE_READY); |
| 65 | 75 |
| 66 DoPendingWork(); | 76 DoPendingWork(); |
| 67 } | 77 } |
| 68 | 78 |
| 69 bool MediaCodecLoop::TryFlush() { | 79 bool MediaCodecLoop::TryFlush() { |
| 70 // We do not clear the input queue here. It depends on the caller. | 80 // We do not clear the input queue here. It depends on the caller. |
| 71 // For decoder reset, then it is appropriate. Otherwise, the requests might | 81 // For decoder reset, then it is appropriate. Otherwise, the requests might |
| 72 // simply be sent to us later, such as on a format change. | 82 // simply be sent to us later, such as on a format change. |
| 73 | 83 |
| 74 // STATE_DRAINED seems like it allows flush, but it causes test failures. | 84 // STATE_DRAINED seems like it allows flush, but it causes test failures. |
| 75 // crbug.com/624878 | 85 // crbug.com/624878 |
| 76 if (state_ == STATE_ERROR || state_ == STATE_DRAINED) | 86 if (state_ == STATE_ERROR || state_ == STATE_DRAINED) |
| 77 return false; | 87 return false; |
| 78 | 88 |
| 79 if (codec_flush_requires_destruction()) | 89 if (DoesCodecFlushRequireDestruction()) |
| 80 return false; | 90 return false; |
| 81 | 91 |
| 82 // Actually try to flush! | 92 // Actually try to flush! |
| 83 io_timer_.Stop(); | 93 io_timer_.Stop(); |
| 84 | 94 |
| 85 if (media_codec_->Flush() != MEDIA_CODEC_OK) { | 95 if (media_codec_->Flush() != MEDIA_CODEC_OK) { |
| 86 // TODO(liberato): we might not want to notify the client about this. | 96 // TODO(liberato): we might not want to notify the client about this. |
| 87 SetState(STATE_ERROR); | 97 SetState(STATE_ERROR); |
| 88 return false; | 98 return false; |
| 89 } | 99 } |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 140 // MediaCodec's QueueSecureInputBuffer(). | 150 // MediaCodec's QueueSecureInputBuffer(). |
| 141 if (pending_input_buf_index_ != kInvalidBufferIndex) { | 151 if (pending_input_buf_index_ != kInvalidBufferIndex) { |
| 142 InputBuffer result(pending_input_buf_index_, true); | 152 InputBuffer result(pending_input_buf_index_, true); |
| 143 pending_input_buf_index_ = kInvalidBufferIndex; | 153 pending_input_buf_index_ = kInvalidBufferIndex; |
| 144 return result; | 154 return result; |
| 145 } | 155 } |
| 146 | 156 |
| 147 int input_buf_index = kInvalidBufferIndex; | 157 int input_buf_index = kInvalidBufferIndex; |
| 148 | 158 |
| 149 media::MediaCodecStatus status = | 159 media::MediaCodecStatus status = |
| 150 media_codec_->DequeueInputBuffer(kNoWaitTimeout, &input_buf_index); | 160 media_codec_->DequeueInputBuffer(NoWaitTimeout(), &input_buf_index); |
| 151 switch (status) { | 161 switch (status) { |
| 152 case media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | 162 case media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: |
| 153 break; | 163 break; |
| 154 | 164 |
| 155 case media::MEDIA_CODEC_ERROR: | 165 case media::MEDIA_CODEC_ERROR: |
| 156 DLOG(ERROR) << __FUNCTION__ | 166 DLOG(ERROR) << __FUNCTION__ |
| 157 << ": MEDIA_CODEC_ERROR from DequeInputBuffer"; | 167 << ": MEDIA_CODEC_ERROR from DequeInputBuffer"; |
| 158 SetState(STATE_ERROR); | 168 SetState(STATE_ERROR); |
| 159 break; | 169 break; |
| 160 | 170 |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 242 | 252 |
| 243 bool MediaCodecLoop::ProcessOneOutputBuffer() { | 253 bool MediaCodecLoop::ProcessOneOutputBuffer() { |
| 244 // TODO(liberato): When merging AVDA, we will also have to ask the client if | 254 // TODO(liberato): When merging AVDA, we will also have to ask the client if |
| 245 // it can accept another output buffer. | 255 // it can accept another output buffer. |
| 246 | 256 |
| 247 if (state_ == STATE_ERROR) | 257 if (state_ == STATE_ERROR) |
| 248 return false; | 258 return false; |
| 249 | 259 |
| 250 OutputBuffer out; | 260 OutputBuffer out; |
| 251 MediaCodecStatus status = media_codec_->DequeueOutputBuffer( | 261 MediaCodecStatus status = media_codec_->DequeueOutputBuffer( |
| 252 kNoWaitTimeout, &out.index, &out.offset, &out.size, &out.pts, &out.is_eos, | 262 NoWaitTimeout(), &out.index, &out.offset, &out.size, &out.pts, |
| 253 &out.is_key_frame); | 263 &out.is_eos, &out.is_key_frame); |
| 254 | 264 |
| 255 bool did_work = false; | 265 bool did_work = false; |
| 256 switch (status) { | 266 switch (status) { |
| 257 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | 267 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: |
| 258 // Output buffers are replaced in MediaCodecBridge, nothing to do. | 268 // Output buffers are replaced in MediaCodecBridge, nothing to do. |
| 259 did_work = true; | 269 did_work = true; |
| 260 break; | 270 break; |
| 261 | 271 |
| 262 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | 272 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: |
| 263 if (!client_->OnOutputFormatChanged()) | 273 if (!client_->OnOutputFormatChanged()) |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 303 SetState(STATE_ERROR); | 313 SetState(STATE_ERROR); |
| 304 break; | 314 break; |
| 305 } | 315 } |
| 306 | 316 |
| 307 return did_work; | 317 return did_work; |
| 308 } | 318 } |
| 309 | 319 |
| 310 void MediaCodecLoop::ManageTimer(bool did_work) { | 320 void MediaCodecLoop::ManageTimer(bool did_work) { |
| 311 bool should_be_running = true; | 321 bool should_be_running = true; |
| 312 | 322 |
| 313 base::TimeTicks now = base::TimeTicks::Now(); | 323 // One might also use DefaultTickClock, but then ownership becomes harder. |
| 324 base::TimeTicks now = (test_tick_clock_ ? test_tick_clock_->NowTicks() | |
| 325 : base::TimeTicks::Now()); | |
| 314 if (did_work || idle_time_begin_ == base::TimeTicks()) { | 326 if (did_work || idle_time_begin_ == base::TimeTicks()) { |
| 315 idle_time_begin_ = now; | 327 idle_time_begin_ = now; |
| 316 } else { | 328 } else { |
| 317 // Make sure that we have done work recently enough, else stop the timer. | 329 // Make sure that we have done work recently enough, else stop the timer. |
| 318 if (now - idle_time_begin_ > kIdleTimerTimeout) | 330 if (now - idle_time_begin_ > IdleTimerTimeout()) |
| 319 should_be_running = false; | 331 should_be_running = false; |
| 320 } | 332 } |
| 321 | 333 |
| 322 if (should_be_running && !io_timer_.IsRunning()) { | 334 if (should_be_running && !io_timer_.IsRunning()) { |
| 323 io_timer_.Start(FROM_HERE, kDecodePollDelay, this, | 335 io_timer_.Start(FROM_HERE, DecodePollDelay(), this, |
| 324 &MediaCodecLoop::DoPendingWork); | 336 &MediaCodecLoop::DoPendingWork); |
| 325 } else if (!should_be_running && io_timer_.IsRunning()) { | 337 } else if (!should_be_running && io_timer_.IsRunning()) { |
| 326 io_timer_.Stop(); | 338 io_timer_.Stop(); |
| 327 } | 339 } |
| 328 } | 340 } |
| 329 | 341 |
| 330 void MediaCodecLoop::SetState(State new_state) { | 342 void MediaCodecLoop::SetState(State new_state) { |
| 331 const State old_state = state_; | 343 const State old_state = state_; |
| 332 state_ = new_state; | 344 state_ = new_state; |
| 333 if (old_state != new_state && new_state == STATE_ERROR) | 345 if (old_state != new_state && new_state == STATE_ERROR) |
| 334 client_->OnCodecLoopError(); | 346 client_->OnCodecLoopError(); |
| 335 } | 347 } |
| 336 | 348 |
| 337 MediaCodecBridge* MediaCodecLoop::GetCodec() const { | 349 MediaCodecBridge* MediaCodecLoop::GetCodec() const { |
| 338 return media_codec_.get(); | 350 return media_codec_.get(); |
| 339 } | 351 } |
| 340 | 352 |
| 353 bool MediaCodecLoop::DoesCodecFlushRequireDestruction() const { | |
| 354 // Return true if and only if Flush() isn't supported / doesn't work. | |
| 355 // Prior to JellyBean-MR2, flush() had several bugs (b/8125974, b/8347958) so | |
| 356 // we have to completely destroy and recreate the codec there. | |
| 357 return sdk_int_ < 18; | |
|
watk
2016/08/09 18:32:55
Should call the MediaCodecUtil one
liberato (no reviews please)
2016/08/09 21:20:47
MediaCodecUtil doesn't build anywhere yet, and i d
| |
| 358 } | |
| 359 | |
| 341 // static | 360 // static |
| 342 const char* MediaCodecLoop::AsString(State state) { | 361 const char* MediaCodecLoop::AsString(State state) { |
| 343 #define RETURN_STRING(x) \ | 362 #define RETURN_STRING(x) \ |
| 344 case x: \ | 363 case x: \ |
| 345 return #x; | 364 return #x; |
| 346 | 365 |
| 347 switch (state) { | 366 switch (state) { |
| 348 RETURN_STRING(STATE_READY); | 367 RETURN_STRING(STATE_READY); |
| 349 RETURN_STRING(STATE_WAITING_FOR_KEY); | 368 RETURN_STRING(STATE_WAITING_FOR_KEY); |
| 350 RETURN_STRING(STATE_DRAINING); | 369 RETURN_STRING(STATE_DRAINING); |
| 351 RETURN_STRING(STATE_DRAINED); | 370 RETURN_STRING(STATE_DRAINED); |
| 352 RETURN_STRING(STATE_ERROR); | 371 RETURN_STRING(STATE_ERROR); |
| 353 } | 372 } |
| 354 #undef RETURN_STRING | 373 #undef RETURN_STRING |
| 355 | 374 |
| 356 NOTREACHED() << "Unknown state " << state; | 375 NOTREACHED() << "Unknown state " << state; |
| 357 return nullptr; | 376 return nullptr; |
| 358 } | 377 } |
| 359 | 378 |
| 360 } // namespace media | 379 } // namespace media |
| OLD | NEW |