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 MediaCodecLoop::InputData::InputData() | |
26 : memory(nullptr), length(0), is_eos(false), is_encrypted(false) {} | |
27 | |
28 MediaCodecLoop::InputData::InputData(const InputData& other) | |
29 : memory(other.memory), | |
30 length(other.length), | |
31 key_id(other.key_id), | |
32 iv(other.iv), | |
33 subsamples(other.subsamples), | |
34 presentation_time(other.presentation_time), | |
35 completion_cb(other.completion_cb), | |
36 is_eos(other.is_eos), | |
37 is_encrypted(other.is_encrypted) {} | |
38 | |
39 MediaCodecLoop::InputData::~InputData() {} | |
40 | |
41 MediaCodecLoop::MediaCodecLoop(Client* client, | |
42 std::unique_ptr<MediaCodecBridge>&& media_codec) | |
43 : state_(STATE_READY), | |
44 client_(client), | |
45 media_codec_(std::move(media_codec)), | |
46 pending_input_buf_index_(kInvalidBufferIndex), | |
47 weak_factory_(this) { | |
48 DVLOG(1) << __FUNCTION__; | |
DaleCurtis
2016/06/13 23:16:13
I typically drop these from production code, but u
liberato (no reviews please)
2016/06/14 17:26:44
Done.
| |
49 DCHECK(media_codec_); | |
50 } | |
51 | |
52 MediaCodecLoop::~MediaCodecLoop() { | |
53 DVLOG(1) << __FUNCTION__; | |
54 } | |
55 | |
56 void MediaCodecLoop::OnKeyAdded() { | |
57 DVLOG(1) << __FUNCTION__; | |
58 | |
59 if (state_ == STATE_WAITING_FOR_KEY) | |
60 SetState(STATE_READY); | |
61 | |
62 DoIOTask(); | |
63 } | |
64 | |
65 bool MediaCodecLoop::TryFlush() { | |
66 DVLOG(1) << __FUNCTION__; | |
67 | |
68 // We do not clear the input queue here. It depends on the caller. | |
69 // For decoder reset, then it is appropriate. Otherwise, the requests might | |
70 // simply be sent to us later, such as on a format change. | |
71 | |
72 // Flush if we can, otherwise completely recreate and reconfigure the codec. | |
73 // Prior to JellyBean-MR2, flush() had several bugs (b/8125974, b/8347958) so | |
74 // we have to completely destroy and recreate the codec there. | |
75 bool success = false; | |
DaleCurtis
2016/06/13 23:16:13
Reorder this so that we early return instead of us
liberato (no reviews please)
2016/06/14 17:26:44
Done, also renamed 'flush' pending rebase onto wat
| |
76 if (state_ != STATE_ERROR && state_ != STATE_DRAINED && | |
77 base::android::BuildInfo::GetInstance()->sdk_int() >= 18) { | |
78 io_timer_.Stop(); | |
79 | |
80 // media_codec_->Reset() calls MediaCodec.flush(). | |
81 success = media_codec_->Reset() == MEDIA_CODEC_OK; | |
82 | |
83 // Transition to the error state if the flush failed. | |
84 SetState(success ? STATE_READY : STATE_ERROR); | |
85 } | |
86 | |
87 return success; | |
88 } | |
89 | |
90 void MediaCodecLoop::DoIOTask() { | |
91 if (state_ == STATE_ERROR) | |
92 return; | |
93 | |
94 bool did_work = false, did_input = false, did_output = false; | |
95 do { | |
96 did_input = ProcessOneInputBuffer(); | |
97 did_output = ProcessOneOutputBuffer(); | |
98 if (did_input || did_output) | |
99 did_work = true; | |
100 } while (did_input || did_output); | |
101 | |
102 // TODO(liberato): add "start_timer" for AVDA. | |
103 ManageTimer(did_work); | |
104 } | |
105 | |
106 bool MediaCodecLoop::ProcessOneInputBuffer() { | |
107 DVLOG(2) << __FUNCTION__; | |
108 | |
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 && | |
DaleCurtis
2016/06/13 23:16:13
A single pending_input_buf_index_ isn't going to w
liberato (no reviews please)
2016/06/14 17:26:44
this is only if we're waiting for a key, which lim
| |
113 !client_->IsAnyInputPending()) { | |
114 return false; | |
115 } | |
116 | |
117 if (state_ == STATE_WAITING_FOR_KEY || state_ == STATE_DRAINING || | |
118 state_ == STATE_DRAINED) { | |
DaleCurtis
2016/06/13 23:16:13
STATE_ERROR ?
liberato (no reviews please)
2016/06/14 17:26:44
Done.
| |
119 return false; | |
120 } | |
121 | |
122 // DequeueInputBuffer() may set STATE_ERROR. | |
123 InputBuffer input_buffer = DequeueInputBuffer(); | |
DaleCurtis
2016/06/13 23:16:13
Instead of return by copy should this take a Input
liberato (no reviews please)
2016/06/14 17:26:44
this sounded like a good idea, but i un-did it aft
| |
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 DVLOG(2) << __FUNCTION__ << ": MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER"; | |
153 break; | |
154 | |
155 case media::MEDIA_CODEC_ERROR: | |
156 DVLOG(1) << __FUNCTION__ << ": MEDIA_CODEC_ERROR from DequeInputBuffer"; | |
DaleCurtis
2016/06/13 23:16:13
DLOG(ERROR) ?
liberato (no reviews please)
2016/06/14 17:26:44
Done.
| |
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 DVLOG(2) << __FUNCTION__ << ": index:" << input_buffer.index; | |
174 | |
175 DCHECK_NE(input_buffer.index, kInvalidBufferIndex); | |
176 | |
177 InputData input_data; | |
178 | |
DaleCurtis
2016/06/13 23:16:13
Remove line so it's clear input_data goes with con
liberato (no reviews please)
2016/06/14 17:26:45
Done.
| |
179 if (input_buffer.is_pending) { | |
180 // A pending buffer is already filled with data, no need to copy it again. | |
181 DVLOG(2) << __FUNCTION__ << ": QueueSecureInputBuffer (pending):" | |
182 << " index:" << input_buffer.index | |
183 << " pts:" << pending_input_buf_data_.presentation_time | |
184 << " size:" << pending_input_buf_data_.length; | |
185 | |
186 input_data = pending_input_buf_data_; | |
187 } else { | |
188 input_data = client_->ProvideInputData(); | |
189 } | |
190 | |
191 // Process this buffer. | |
DaleCurtis
2016/06/13 23:16:13
Not really useful.
liberato (no reviews please)
2016/06/14 17:26:44
Done.
| |
192 | |
193 if (input_data.is_eos) { | |
194 media_codec_->QueueEOS(input_buffer.index); | |
195 SetState(STATE_DRAINING); | |
196 | |
197 // For EOS, the completion callback is called when the EOS arrives at the | |
198 // output queue. | |
199 pending_eos_completion_cb_ = input_data.completion_cb; | |
200 return; | |
201 } | |
202 | |
203 media::MediaCodecStatus status = MEDIA_CODEC_OK; | |
204 | |
205 if (input_data.is_encrypted) { | |
206 // Note that input_data might not have a valid memory ptr if this is a | |
207 // re-send of a buffer that was sent before decryption keys arrived. | |
208 | |
209 DVLOG(2) << __FUNCTION__ << ": QueueSecureInputBuffer:" | |
210 << " index:" << input_buffer.index | |
211 << " pts:" << input_data.presentation_time | |
212 << " size:" << input_data.length; | |
213 | |
214 status = media_codec_->QueueSecureInputBuffer( | |
215 input_buffer.index, input_data.memory, input_data.length, | |
216 input_data.key_id, input_data.iv, input_data.subsamples, | |
217 input_data.presentation_time); | |
218 | |
219 } else { | |
220 DVLOG(2) << __FUNCTION__ << ": QueueInputBuffer:" | |
221 << " index:" << input_buffer.index | |
222 << " pts:" << input_data.presentation_time | |
223 << " size:" << input_data.length; | |
224 | |
225 status = media_codec_->QueueInputBuffer( | |
226 input_buffer.index, input_data.memory, input_data.length, | |
227 input_data.presentation_time); | |
228 } | |
229 | |
230 switch (status) { | |
231 case MEDIA_CODEC_ERROR: | |
232 DVLOG(0) << __FUNCTION__ << ": MEDIA_CODEC_ERROR from QueueInputBuffer"; | |
DaleCurtis
2016/06/13 23:16:13
DLOG(ERROR)
liberato (no reviews please)
2016/06/14 17:26:44
Done.
| |
233 input_data.completion_cb.Run(DecodeStatus::DECODE_ERROR); | |
234 // Transition to the error state after running the completion cb, to keep | |
235 // it in order if the client chooses to flush its queue. | |
236 SetState(STATE_ERROR); | |
237 break; | |
238 | |
239 case MEDIA_CODEC_NO_KEY: | |
240 DVLOG(1) << "QueueSecureInputBuffer failed: MEDIA_CODEC_NO_KEY"; | |
241 // Do not call the completion cb here. It will be called when we retry | |
242 // after getting the key. | |
243 pending_input_buf_index_ = input_buffer.index; | |
244 pending_input_buf_data_ = input_data; | |
245 // MediaCodec has a copy of the data already. When we call again, be sure | |
246 // to send in nullptr for the source. Note that the client doesn't | |
247 // guarantee that the pointer will remain valid after we return anyway. | |
248 pending_input_buf_data_.memory = nullptr; | |
249 SetState(STATE_WAITING_FOR_KEY); | |
250 break; | |
251 | |
252 case MEDIA_CODEC_OK: | |
253 input_data.completion_cb.Run(DecodeStatus::OK); | |
254 break; | |
255 | |
256 default: | |
257 NOTREACHED() << "Unknown Queue(Secure)InputBuffer status " << status; | |
258 input_data.completion_cb.Run(DecodeStatus::DECODE_ERROR); | |
259 SetState(STATE_ERROR); | |
260 break; | |
261 } | |
262 } | |
263 | |
264 bool MediaCodecLoop::ProcessOneOutputBuffer() { | |
265 DVLOG(2) << __FUNCTION__; | |
266 | |
267 MediaCodecStatus status; | |
268 OutputBuffer out; | |
269 bool did_work = false; | |
270 | |
271 // TODO(liberato): When merging AVDA, we will also have to ask the client if | |
272 // it can accept another output buffer. | |
273 | |
274 status = media_codec_->DequeueOutputBuffer(kNoWaitTimeout, &out.index, | |
275 &out.offset, &out.size, &out.pts, | |
276 &out.is_eos, &out.is_key_frame); | |
277 | |
278 switch (status) { | |
279 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | |
280 // Output buffers are replaced in MediaCodecBridge, nothing to do. | |
281 DVLOG(2) << __FUNCTION__ << " MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED"; | |
282 did_work = true; | |
283 break; | |
284 | |
285 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | |
286 DVLOG(2) << __FUNCTION__ << " MEDIA_CODEC_OUTPUT_FORMAT_CHANGED"; | |
287 if (!client_->OnOutputFormatChanged()) | |
288 SetState(STATE_ERROR); | |
289 did_work = (state_ != STATE_ERROR); | |
290 break; | |
291 | |
292 case MEDIA_CODEC_OK: | |
293 // We got the decoded frame. | |
294 if (out.is_eos) { | |
295 // Set state STATE_DRAINED after we have received EOS frame at the | |
296 // output. media_decoder_job.cc says: once output EOS has occurred, we | |
297 // should not be asked to decode again. | |
298 DCHECK_EQ(state_, STATE_DRAINING); | |
299 SetState(STATE_DRAINED); | |
300 | |
301 DCHECK_NE(out.index, kInvalidBufferIndex); | |
302 DCHECK(media_codec_); | |
303 | |
304 media_codec_->ReleaseOutputBuffer(out.index, false); | |
305 | |
306 // Run the EOS completion callback now, since we deferred it until | |
307 // the EOS was completely processed. | |
308 pending_eos_completion_cb_.Run(DecodeStatus::OK); | |
309 pending_eos_completion_cb_ = DecodeCB(); | |
310 | |
311 client_->OnDecodedEos(out); | |
312 } else { | |
313 if (!client_->OnDecodedFrame(out)) | |
314 SetState(STATE_ERROR); | |
315 } | |
316 | |
317 did_work = true; | |
318 break; | |
319 | |
320 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | |
321 // Nothing to do. | |
322 DVLOG(2) << __FUNCTION__ << " MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER"; | |
323 break; | |
324 | |
325 case MEDIA_CODEC_ERROR: | |
326 DVLOG(0) << __FUNCTION__ | |
DaleCurtis
2016/06/13 23:16:13
DLOG(ERROR)
liberato (no reviews please)
2016/06/14 17:26:44
Done.
| |
327 << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer"; | |
328 | |
329 // Next Decode() will report the error to the pipeline. | |
330 SetState(STATE_ERROR); | |
331 break; | |
332 | |
333 default: | |
334 NOTREACHED() << "Unknown DequeueOutputBuffer status " << status; | |
335 // Next Decode() will report the error to the pipeline. | |
336 SetState(STATE_ERROR); | |
337 break; | |
338 } | |
339 | |
340 return did_work; | |
341 } | |
342 | |
343 void MediaCodecLoop::ManageTimer(bool did_work) { | |
344 bool should_be_running = true; | |
345 | |
346 base::TimeTicks now = base::TimeTicks::Now(); | |
347 if (did_work || idle_time_begin_ == base::TimeTicks()) { | |
348 idle_time_begin_ = now; | |
349 } else { | |
350 // Make sure that we have done work recently enough, else stop the timer. | |
351 if (now - idle_time_begin_ > kIdleTimerTimeout) | |
352 should_be_running = false; | |
353 } | |
354 | |
355 if (should_be_running && !io_timer_.IsRunning()) { | |
356 io_timer_.Start(FROM_HERE, kDecodePollDelay, this, | |
357 &MediaCodecLoop::DoIOTask); | |
358 } else if (!should_be_running && io_timer_.IsRunning()) { | |
359 io_timer_.Stop(); | |
360 } | |
361 } | |
362 | |
363 void MediaCodecLoop::SetState(State new_state) { | |
364 DVLOG(1) << __FUNCTION__ << ": " << AsString(state_) << "->" | |
365 << AsString(new_state); | |
366 if (state_ != new_state && state_ == STATE_ERROR) | |
367 client_->OnCodecLoopError(); | |
368 state_ = new_state; | |
369 } | |
370 | |
371 MediaCodecBridge* MediaCodecLoop::GetCodec() const { | |
372 return media_codec_.get(); | |
373 } | |
374 | |
375 #if defined(RETURN_STRING) | |
DaleCurtis
2016/06/13 23:16:13
Hmm, actually, is this even necessary? I.e. won't
liberato (no reviews please)
2016/06/14 17:26:44
yes.
| |
376 #error Unexpected macro collision. | |
377 #endif | |
378 | |
379 // static | |
380 const char* MediaCodecLoop::AsString(State state) { | |
381 #define RETURN_STRING(x) \ | |
382 case x: \ | |
383 return #x; | |
384 | |
385 switch (state) { | |
386 RETURN_STRING(STATE_READY); | |
387 RETURN_STRING(STATE_WAITING_FOR_KEY); | |
388 RETURN_STRING(STATE_DRAINING); | |
389 RETURN_STRING(STATE_DRAINED); | |
390 RETURN_STRING(STATE_ERROR); | |
391 } | |
392 #undef RETURN_STRING | |
393 | |
394 NOTREACHED() << "Unknown state " << state; | |
395 return nullptr; | |
396 } | |
397 | |
398 } // namespace media | |
OLD | NEW |