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