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 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() {} | |
33 | |
34 MediaCodecLoop::InputData::InputData(const InputData& other) | |
35 : memory(other.memory), | |
36 length(other.length), | |
37 key_id(other.key_id), | |
38 iv(other.iv), | |
39 subsamples(other.subsamples), | |
40 presentation_time(other.presentation_time), | |
41 completion_cb(other.completion_cb), | |
42 is_eos(other.is_eos), | |
43 is_encrypted(other.is_encrypted) {} | |
44 | |
45 MediaCodecLoop::InputData::~InputData() {} | |
46 | |
47 MediaCodecLoop::MediaCodecLoop(Client* client, | |
48 std::unique_ptr<MediaCodecBridge>&& media_codec) | |
49 : state_(STATE_READY), | |
50 client_(client), | |
51 media_codec_(std::move(media_codec)), | |
52 pending_input_buf_index_(kInvalidBufferIndex), | |
53 weak_factory_(this) { | |
54 // TODO(liberato): should this DCHECK? | |
55 if (media_codec_ == nullptr) | |
56 SetState(STATE_ERROR); | |
57 } | |
58 | |
59 MediaCodecLoop::~MediaCodecLoop() {} | |
60 | |
61 void MediaCodecLoop::OnKeyAdded() { | |
62 if (state_ == STATE_WAITING_FOR_KEY) | |
63 SetState(STATE_READY); | |
64 | |
65 DoPendingWork(); | |
66 } | |
67 | |
68 bool MediaCodecLoop::TryFlush() { | |
69 // We do not clear the input queue here. It depends on the caller. | |
70 // For decoder reset, then it is appropriate. Otherwise, the requests might | |
71 // simply be sent to us later, such as on a format change. | |
72 | |
73 if (state_ == STATE_ERROR) | |
74 return false; | |
75 | |
76 if (codec_flush_requires_destruction()) | |
77 return false; | |
78 | |
79 // Actually try to flush! | |
80 io_timer_.Stop(); | |
81 | |
82 if (media_codec_->Flush() != MEDIA_CODEC_OK) { | |
83 // TODO(liberato): we might not want to notify the client about this. | |
84 SetState(STATE_ERROR); | |
85 return false; | |
86 } | |
87 | |
88 SetState(STATE_READY); | |
89 return true; | |
90 } | |
91 | |
92 void MediaCodecLoop::DoPendingWork() { | |
93 if (state_ == STATE_ERROR) | |
94 return; | |
95 | |
96 bool did_work = false, did_input = false, did_output = false; | |
97 do { | |
98 did_input = ProcessOneInputBuffer(); | |
Tima Vaisburd
2016/06/16 21:47:29
If the OUTPUT_FORMAT_CHANGED arrives, this code wi
liberato (no reviews please)
2016/06/27 16:13:37
i don't think that OUTPUT_FORMAT_CHANGED processes
Tima Vaisburd
2016/06/27 17:45:26
Yes, you are right.
| |
99 did_output = ProcessOneOutputBuffer(); | |
100 if (did_input || did_output) | |
101 did_work = true; | |
102 } while (did_input || did_output); | |
103 | |
104 // TODO(liberato): add "start_timer" for AVDA. | |
105 ManageTimer(did_work); | |
106 } | |
107 | |
108 bool MediaCodecLoop::ProcessOneInputBuffer() { | |
109 if (state_ != STATE_READY) | |
110 return false; | |
111 | |
112 // We can only queue a buffer if there is input from the client, or if we | |
113 // tried previously but had to wait for a key. In the latter case, MediaCodec | |
114 // already has the data. | |
115 if (pending_input_buf_index_ == kInvalidBufferIndex && | |
116 !client_->IsAnyInputPending()) { | |
117 return false; | |
118 } | |
119 | |
120 // DequeueInputBuffer() may set STATE_ERROR. | |
121 InputBuffer input_buffer = DequeueInputBuffer(); | |
122 | |
123 if (input_buffer.index == kInvalidBufferIndex) | |
124 return false; | |
125 | |
126 // EnqueueInputBuffer() may change the state. | |
127 EnqueueInputBuffer(input_buffer); | |
128 return state_ != STATE_ERROR; | |
129 } | |
130 | |
131 MediaCodecLoop::InputBuffer MediaCodecLoop::DequeueInputBuffer() { | |
132 DVLOG(2) << __FUNCTION__; | |
133 | |
134 // Do not dequeue a new input buffer if we failed with MEDIA_CODEC_NO_KEY. | |
135 // That status does not return the input buffer back to the pool of | |
136 // available input buffers. We have to reuse it later when calling | |
137 // MediaCodec's QueueSecureInputBuffer(). | |
138 if (pending_input_buf_index_ != kInvalidBufferIndex) { | |
139 InputBuffer result(pending_input_buf_index_, true); | |
140 pending_input_buf_index_ = kInvalidBufferIndex; | |
141 return result; | |
142 } | |
143 | |
144 int input_buf_index = kInvalidBufferIndex; | |
145 | |
146 media::MediaCodecStatus status = | |
147 media_codec_->DequeueInputBuffer(kNoWaitTimeout, &input_buf_index); | |
148 switch (status) { | |
149 case media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | |
150 break; | |
151 | |
152 case media::MEDIA_CODEC_ERROR: | |
153 DLOG(ERROR) << __FUNCTION__ | |
154 << ": MEDIA_CODEC_ERROR from DequeInputBuffer"; | |
155 SetState(STATE_ERROR); | |
156 break; | |
157 | |
158 case media::MEDIA_CODEC_OK: | |
159 break; | |
160 | |
161 default: | |
162 NOTREACHED() << "Unknown DequeueInputBuffer status " << status; | |
163 SetState(STATE_ERROR); | |
164 break; | |
165 } | |
166 | |
167 return InputBuffer(input_buf_index, false); | |
168 } | |
169 | |
170 void MediaCodecLoop::EnqueueInputBuffer(const InputBuffer& input_buffer) { | |
171 DCHECK_NE(input_buffer.index, kInvalidBufferIndex); | |
172 | |
173 InputData input_data; | |
174 if (input_buffer.is_pending) { | |
175 // A pending buffer is already filled with data, no need to copy it again. | |
176 input_data = pending_input_buf_data_; | |
177 } else { | |
178 input_data = client_->ProvideInputData(); | |
179 } | |
180 | |
181 if (input_data.is_eos) { | |
182 media_codec_->QueueEOS(input_buffer.index); | |
183 SetState(STATE_DRAINING); | |
184 | |
185 // For EOS, the completion callback is called when the EOS arrives at the | |
186 // output queue. | |
187 pending_eos_completion_cb_ = input_data.completion_cb; | |
188 return; | |
189 } | |
190 | |
191 media::MediaCodecStatus status = MEDIA_CODEC_OK; | |
192 | |
193 if (input_data.is_encrypted) { | |
194 // Note that input_data might not have a valid memory ptr if this is a | |
195 // re-send of a buffer that was sent before decryption keys arrived. | |
196 | |
197 status = media_codec_->QueueSecureInputBuffer( | |
198 input_buffer.index, input_data.memory, input_data.length, | |
199 input_data.key_id, input_data.iv, input_data.subsamples, | |
200 input_data.presentation_time); | |
201 | |
202 } else { | |
203 status = media_codec_->QueueInputBuffer( | |
204 input_buffer.index, input_data.memory, input_data.length, | |
205 input_data.presentation_time); | |
206 } | |
207 | |
208 switch (status) { | |
209 case MEDIA_CODEC_ERROR: | |
210 DLOG(ERROR) << __FUNCTION__ | |
211 << ": MEDIA_CODEC_ERROR from QueueInputBuffer"; | |
212 input_data.completion_cb.Run(DecodeStatus::DECODE_ERROR); | |
213 // Transition to the error state after running the completion cb, to keep | |
214 // it in order if the client chooses to flush its queue. | |
215 SetState(STATE_ERROR); | |
216 break; | |
217 | |
218 case MEDIA_CODEC_NO_KEY: | |
219 // Do not call the completion cb here. It will be called when we retry | |
220 // after getting the key. | |
221 pending_input_buf_index_ = input_buffer.index; | |
222 pending_input_buf_data_ = input_data; | |
223 // MediaCodec has a copy of the data already. When we call again, be sure | |
224 // to send in nullptr for the source. Note that the client doesn't | |
225 // guarantee that the pointer will remain valid after we return anyway. | |
226 pending_input_buf_data_.memory = nullptr; | |
227 SetState(STATE_WAITING_FOR_KEY); | |
228 break; | |
229 | |
230 case MEDIA_CODEC_OK: | |
231 input_data.completion_cb.Run(DecodeStatus::OK); | |
232 break; | |
233 | |
234 default: | |
235 NOTREACHED() << "Unknown Queue(Secure)InputBuffer status " << status; | |
236 input_data.completion_cb.Run(DecodeStatus::DECODE_ERROR); | |
237 SetState(STATE_ERROR); | |
238 break; | |
239 } | |
240 } | |
241 | |
242 bool MediaCodecLoop::ProcessOneOutputBuffer() { | |
243 // TODO(liberato): When merging AVDA, we will also have to ask the client if | |
244 // it can accept another output buffer. | |
245 | |
246 OutputBuffer out; | |
247 MediaCodecStatus status = media_codec_->DequeueOutputBuffer( | |
248 kNoWaitTimeout, &out.index, &out.offset, &out.size, &out.pts, &out.is_eos, | |
249 &out.is_key_frame); | |
250 | |
251 bool did_work = false; | |
252 switch (status) { | |
253 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | |
254 // Output buffers are replaced in MediaCodecBridge, nothing to do. | |
255 did_work = true; | |
256 break; | |
257 | |
258 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | |
259 if (!client_->OnOutputFormatChanged()) | |
260 SetState(STATE_ERROR); | |
261 did_work = state_ != STATE_ERROR; | |
262 break; | |
263 | |
264 case MEDIA_CODEC_OK: | |
265 // We got the decoded frame or EOS. | |
266 if (out.is_eos) { | |
267 // Set state STATE_DRAINED after we have received EOS frame at the | |
268 // output. media_decoder_job.cc says: once output EOS has occurred, we | |
269 // should not be asked to decode again. | |
270 DCHECK_EQ(state_, STATE_DRAINING); | |
271 SetState(STATE_DRAINED); | |
272 | |
273 DCHECK_NE(out.index, kInvalidBufferIndex); | |
274 DCHECK(media_codec_); | |
275 | |
276 media_codec_->ReleaseOutputBuffer(out.index, false); | |
277 | |
278 // Run the EOS completion callback now, since we deferred it until | |
279 // the EOS was completely processed. | |
280 pending_eos_completion_cb_.Run(DecodeStatus::OK); | |
281 pending_eos_completion_cb_ = DecodeCB(); | |
282 | |
283 client_->OnDecodedEos(out); | |
284 } else { | |
285 if (!client_->OnDecodedFrame(out)) | |
286 SetState(STATE_ERROR); | |
287 } | |
288 | |
289 did_work = true; | |
290 break; | |
291 | |
292 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | |
293 // Nothing to do. | |
294 break; | |
295 | |
296 case MEDIA_CODEC_ERROR: | |
297 DLOG(ERROR) << __FUNCTION__ | |
298 << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer"; | |
299 SetState(STATE_ERROR); | |
300 break; | |
301 | |
302 default: | |
303 NOTREACHED() << "Unknown DequeueOutputBuffer status " << status; | |
304 SetState(STATE_ERROR); | |
305 break; | |
306 } | |
307 | |
308 return did_work; | |
309 } | |
310 | |
311 void MediaCodecLoop::ManageTimer(bool did_work) { | |
312 bool should_be_running = true; | |
313 | |
314 base::TimeTicks now = base::TimeTicks::Now(); | |
315 if (did_work || idle_time_begin_ == base::TimeTicks()) { | |
316 idle_time_begin_ = now; | |
317 } else { | |
318 // Make sure that we have done work recently enough, else stop the timer. | |
319 if (now - idle_time_begin_ > kIdleTimerTimeout) | |
320 should_be_running = false; | |
321 } | |
322 | |
323 if (should_be_running && !io_timer_.IsRunning()) { | |
324 io_timer_.Start(FROM_HERE, kDecodePollDelay, this, | |
325 &MediaCodecLoop::DoPendingWork); | |
326 } else if (!should_be_running && io_timer_.IsRunning()) { | |
327 io_timer_.Stop(); | |
328 } | |
329 } | |
330 | |
331 void MediaCodecLoop::SetState(State new_state) { | |
332 const State old_state = state_; | |
333 state_ = new_state; | |
334 if (old_state != new_state && new_state == STATE_ERROR) | |
335 client_->OnCodecLoopError(); | |
336 } | |
337 | |
338 MediaCodecBridge* MediaCodecLoop::GetCodec() const { | |
339 return media_codec_.get(); | |
340 } | |
341 | |
342 // static | |
343 const char* MediaCodecLoop::AsString(State state) { | |
344 #define RETURN_STRING(x) \ | |
345 case x: \ | |
346 return #x; | |
347 | |
348 switch (state) { | |
349 RETURN_STRING(STATE_READY); | |
350 RETURN_STRING(STATE_WAITING_FOR_KEY); | |
351 RETURN_STRING(STATE_DRAINING); | |
352 RETURN_STRING(STATE_DRAINED); | |
353 RETURN_STRING(STATE_ERROR); | |
354 } | |
355 #undef RETURN_STRING | |
356 | |
357 NOTREACHED() << "Unknown state " << state; | |
358 return nullptr; | |
359 } | |
360 | |
361 } // namespace media | |
OLD | NEW |