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 (CodecNeedsFlushWorkaround()) |
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::CodecNeedsFlushWorkaround() 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 // TODO(liberato): MediaCodecUtil implements the same function. We should |
| 358 // call that one, except that it doesn't compile outside of android right now. |
| 359 return sdk_int_ < 18; |
| 360 } |
| 361 |
341 // static | 362 // static |
342 const char* MediaCodecLoop::AsString(State state) { | 363 const char* MediaCodecLoop::AsString(State state) { |
343 #define RETURN_STRING(x) \ | 364 #define RETURN_STRING(x) \ |
344 case x: \ | 365 case x: \ |
345 return #x; | 366 return #x; |
346 | 367 |
347 switch (state) { | 368 switch (state) { |
348 RETURN_STRING(STATE_READY); | 369 RETURN_STRING(STATE_READY); |
349 RETURN_STRING(STATE_WAITING_FOR_KEY); | 370 RETURN_STRING(STATE_WAITING_FOR_KEY); |
350 RETURN_STRING(STATE_DRAINING); | 371 RETURN_STRING(STATE_DRAINING); |
351 RETURN_STRING(STATE_DRAINED); | 372 RETURN_STRING(STATE_DRAINED); |
352 RETURN_STRING(STATE_ERROR); | 373 RETURN_STRING(STATE_ERROR); |
353 } | 374 } |
354 #undef RETURN_STRING | 375 #undef RETURN_STRING |
355 | 376 |
356 NOTREACHED() << "Unknown state " << state; | 377 NOTREACHED() << "Unknown state " << state; |
357 return nullptr; | 378 return nullptr; |
358 } | 379 } |
359 | 380 |
360 } // namespace media | 381 } // namespace media |
OLD | NEW |