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 constexpr base::TimeDelta kDecodePollDelay = |
21 base::TimeDelta::FromMilliseconds(10); | 16 base::TimeDelta::FromMilliseconds(10); |
22 constexpr base::TimeDelta kNoWaitTimeout = base::TimeDelta::FromMicroseconds(0); | 17 constexpr base::TimeDelta kNoWaitTimeout = base::TimeDelta::FromMicroseconds(0); |
23 constexpr base::TimeDelta kIdleTimerTimeout = base::TimeDelta::FromSeconds(1); | 18 constexpr base::TimeDelta kIdleTimerTimeout = base::TimeDelta::FromSeconds(1); |
24 | 19 |
25 static inline bool codec_flush_requires_destruction() { | |
26 // Return true if and only if Flush() isn't supported / doesn't work. | |
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() {} | 20 MediaCodecLoop::InputData::InputData() {} |
33 | 21 |
34 MediaCodecLoop::InputData::InputData(const InputData& other) | 22 MediaCodecLoop::InputData::InputData(const InputData& other) |
35 : memory(other.memory), | 23 : memory(other.memory), |
36 length(other.length), | 24 length(other.length), |
37 key_id(other.key_id), | 25 key_id(other.key_id), |
38 iv(other.iv), | 26 iv(other.iv), |
39 subsamples(other.subsamples), | 27 subsamples(other.subsamples), |
40 presentation_time(other.presentation_time), | 28 presentation_time(other.presentation_time), |
41 is_eos(other.is_eos), | 29 is_eos(other.is_eos), |
42 is_encrypted(other.is_encrypted) {} | 30 is_encrypted(other.is_encrypted) {} |
43 | 31 |
44 MediaCodecLoop::InputData::~InputData() {} | 32 MediaCodecLoop::InputData::~InputData() {} |
45 | 33 |
46 MediaCodecLoop::MediaCodecLoop(Client* client, | 34 MediaCodecLoop::MediaCodecLoop( |
47 std::unique_ptr<MediaCodecBridge> media_codec) | 35 int sdk_int, |
36 Client* client, | |
37 std::unique_ptr<MediaCodecBridge> media_codec, | |
38 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | |
48 : state_(STATE_READY), | 39 : state_(STATE_READY), |
49 client_(client), | 40 client_(client), |
50 media_codec_(std::move(media_codec)), | 41 media_codec_(std::move(media_codec)), |
51 pending_input_buf_index_(kInvalidBufferIndex), | 42 pending_input_buf_index_(kInvalidBufferIndex), |
43 sdk_int_(sdk_int), | |
52 weak_factory_(this) { | 44 weak_factory_(this) { |
45 if (task_runner) | |
46 io_timer_.SetTaskRunner(task_runner); | |
53 // TODO(liberato): should this DCHECK? | 47 // TODO(liberato): should this DCHECK? |
54 if (media_codec_ == nullptr) | 48 if (media_codec_ == nullptr) |
55 SetState(STATE_ERROR); | 49 SetState(STATE_ERROR); |
56 } | 50 } |
57 | 51 |
58 MediaCodecLoop::~MediaCodecLoop() { | 52 MediaCodecLoop::~MediaCodecLoop() { |
59 io_timer_.Stop(); | 53 io_timer_.Stop(); |
60 } | 54 } |
61 | 55 |
56 void MediaCodecLoop::SetTestTickClock(base::TickClock* test_tick_clock) { | |
57 test_tick_clock_ = test_tick_clock; | |
58 } | |
59 | |
62 void MediaCodecLoop::OnKeyAdded() { | 60 void MediaCodecLoop::OnKeyAdded() { |
63 if (state_ == STATE_WAITING_FOR_KEY) | 61 if (state_ == STATE_WAITING_FOR_KEY) |
64 SetState(STATE_READY); | 62 SetState(STATE_READY); |
65 | 63 |
66 DoPendingWork(); | 64 DoPendingWork(); |
67 } | 65 } |
68 | 66 |
69 bool MediaCodecLoop::TryFlush() { | 67 bool MediaCodecLoop::TryFlush() { |
70 // We do not clear the input queue here. It depends on the caller. | 68 // 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 | 69 // For decoder reset, then it is appropriate. Otherwise, the requests might |
72 // simply be sent to us later, such as on a format change. | 70 // simply be sent to us later, such as on a format change. |
73 | 71 |
74 // STATE_DRAINED seems like it allows flush, but it causes test failures. | 72 // STATE_DRAINED seems like it allows flush, but it causes test failures. |
75 // crbug.com/624878 | 73 // crbug.com/624878 |
76 if (state_ == STATE_ERROR || state_ == STATE_DRAINED) | 74 if (state_ == STATE_ERROR || state_ == STATE_DRAINED) |
77 return false; | 75 return false; |
78 | 76 |
79 if (codec_flush_requires_destruction()) | 77 if (DoesCodecFlushRequireDestruction()) |
80 return false; | 78 return false; |
81 | 79 |
82 // Actually try to flush! | 80 // Actually try to flush! |
83 io_timer_.Stop(); | 81 io_timer_.Stop(); |
84 | 82 |
85 if (media_codec_->Flush() != MEDIA_CODEC_OK) { | 83 if (media_codec_->Flush() != MEDIA_CODEC_OK) { |
86 // TODO(liberato): we might not want to notify the client about this. | 84 // TODO(liberato): we might not want to notify the client about this. |
87 SetState(STATE_ERROR); | 85 SetState(STATE_ERROR); |
88 return false; | 86 return false; |
89 } | 87 } |
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
303 SetState(STATE_ERROR); | 301 SetState(STATE_ERROR); |
304 break; | 302 break; |
305 } | 303 } |
306 | 304 |
307 return did_work; | 305 return did_work; |
308 } | 306 } |
309 | 307 |
310 void MediaCodecLoop::ManageTimer(bool did_work) { | 308 void MediaCodecLoop::ManageTimer(bool did_work) { |
311 bool should_be_running = true; | 309 bool should_be_running = true; |
312 | 310 |
313 base::TimeTicks now = base::TimeTicks::Now(); | 311 // One might also use DefaultTickClock, but then ownership becomes harder. |
312 base::TimeTicks now = (test_tick_clock_ ? test_tick_clock_->NowTicks() | |
DaleCurtis
2016/08/01 18:23:16
Instead of doing this, always create tick_clock_.
| |
313 : base::TimeTicks::Now()); | |
314 if (did_work || idle_time_begin_ == base::TimeTicks()) { | 314 if (did_work || idle_time_begin_ == base::TimeTicks()) { |
315 idle_time_begin_ = now; | 315 idle_time_begin_ = now; |
316 } else { | 316 } else { |
317 // Make sure that we have done work recently enough, else stop the timer. | 317 // Make sure that we have done work recently enough, else stop the timer. |
318 if (now - idle_time_begin_ > kIdleTimerTimeout) | 318 if (now - idle_time_begin_ > kIdleTimerTimeout) |
319 should_be_running = false; | 319 should_be_running = false; |
320 } | 320 } |
321 | 321 |
322 if (should_be_running && !io_timer_.IsRunning()) { | 322 if (should_be_running && !io_timer_.IsRunning()) { |
323 io_timer_.Start(FROM_HERE, kDecodePollDelay, this, | 323 io_timer_.Start(FROM_HERE, kDecodePollDelay, this, |
324 &MediaCodecLoop::DoPendingWork); | 324 &MediaCodecLoop::DoPendingWork); |
325 } else if (!should_be_running && io_timer_.IsRunning()) { | 325 } else if (!should_be_running && io_timer_.IsRunning()) { |
326 io_timer_.Stop(); | 326 io_timer_.Stop(); |
327 } | 327 } |
328 } | 328 } |
329 | 329 |
330 void MediaCodecLoop::SetState(State new_state) { | 330 void MediaCodecLoop::SetState(State new_state) { |
331 const State old_state = state_; | 331 const State old_state = state_; |
332 state_ = new_state; | 332 state_ = new_state; |
333 if (old_state != new_state && new_state == STATE_ERROR) | 333 if (old_state != new_state && new_state == STATE_ERROR) |
334 client_->OnCodecLoopError(); | 334 client_->OnCodecLoopError(); |
335 } | 335 } |
336 | 336 |
337 MediaCodecBridge* MediaCodecLoop::GetCodec() const { | 337 MediaCodecBridge* MediaCodecLoop::GetCodec() const { |
338 return media_codec_.get(); | 338 return media_codec_.get(); |
339 } | 339 } |
340 | 340 |
341 bool MediaCodecLoop::DoesCodecFlushRequireDestruction() const { | |
342 // Return true if and only if Flush() isn't supported / doesn't work. | |
343 // Prior to JellyBean-MR2, flush() had several bugs (b/8125974, b/8347958) so | |
344 // we have to completely destroy and recreate the codec there. | |
345 return sdk_int_ < 18; | |
346 } | |
347 | |
341 // static | 348 // static |
342 const char* MediaCodecLoop::AsString(State state) { | 349 const char* MediaCodecLoop::AsString(State state) { |
343 #define RETURN_STRING(x) \ | 350 #define RETURN_STRING(x) \ |
344 case x: \ | 351 case x: \ |
345 return #x; | 352 return #x; |
346 | 353 |
347 switch (state) { | 354 switch (state) { |
348 RETURN_STRING(STATE_READY); | 355 RETURN_STRING(STATE_READY); |
349 RETURN_STRING(STATE_WAITING_FOR_KEY); | 356 RETURN_STRING(STATE_WAITING_FOR_KEY); |
350 RETURN_STRING(STATE_DRAINING); | 357 RETURN_STRING(STATE_DRAINING); |
351 RETURN_STRING(STATE_DRAINED); | 358 RETURN_STRING(STATE_DRAINED); |
352 RETURN_STRING(STATE_ERROR); | 359 RETURN_STRING(STATE_ERROR); |
353 } | 360 } |
354 #undef RETURN_STRING | 361 #undef RETURN_STRING |
355 | 362 |
356 NOTREACHED() << "Unknown state " << state; | 363 NOTREACHED() << "Unknown state " << state; |
357 return nullptr; | 364 return nullptr; |
358 } | 365 } |
359 | 366 |
360 } // namespace media | 367 } // namespace media |
OLD | NEW |