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 | |
27 MediaCodecLoop::InputData::InputData(const InputData& other) | |
28 : memory(other.memory), | |
29 length(other.length), | |
30 key_id(other.key_id), | |
31 iv(other.iv), | |
32 subsamples(other.subsamples), | |
33 presentation_time(other.presentation_time), | |
34 completion_cb(other.completion_cb), | |
35 is_eos(other.is_eos), | |
36 is_encrypted(other.is_encrypted) {} | |
37 | |
38 MediaCodecLoop::InputData::~InputData() {} | |
39 | |
40 MediaCodecLoop::MediaCodecLoop(Client* client, | |
41 std::unique_ptr<MediaCodecBridge>&& media_codec) | |
42 : state_(STATE_READY), | |
43 client_(client), | |
44 media_codec_(std::move(media_codec)), | |
45 pending_input_buf_index_(kInvalidBufferIndex), | |
46 weak_factory_(this) { | |
47 if (media_codec_ == nullptr) | |
DaleCurtis
2016/06/14 19:29:03
if (!media_codec_) -- but really should this be a
liberato (no reviews please)
2016/06/14 21:56:44
probably, but then i've nothing to test without a
| |
48 SetState(STATE_ERROR); | |
49 } | |
50 | |
51 MediaCodecLoop::~MediaCodecLoop() {} | |
52 | |
53 void MediaCodecLoop::OnKeyAdded() { | |
54 if (state_ == STATE_WAITING_FOR_KEY) | |
55 SetState(STATE_READY); | |
56 | |
57 DoIOTask(); | |
58 } | |
59 | |
60 static bool codec_flush_requires_destruction() { | |
DaleCurtis
2016/06/14 19:29:03
Should go above all other methods.
liberato (no reviews please)
2016/06/14 21:56:44
Done.
| |
61 // Flush if we can, otherwise completely recreate and reconfigure the codec. | |
watk
2016/06/15 00:29:17
First sentence of this comment should be reworded
liberato (no reviews please)
2016/06/15 20:27:29
TIL?
it mentions to use unix_hacker_style for inl
watk
2016/06/15 20:52:11
Today I learned :) I looked up the style guide and
| |
62 // Prior to JellyBean-MR2, flush() had several bugs (b/8125974, b/8347958) so | |
63 // we have to completely destroy and recreate the codec there. | |
64 return base::android::BuildInfo::GetInstance()->sdk_int() < 18; | |
65 } | |
66 | |
67 bool MediaCodecLoop::TryFlush() { | |
watk
2016/06/15 00:29:17
I think this should become void Flush(), because c
liberato (no reviews please)
2016/06/15 20:27:30
i don't understand why the caller cares about the
watk
2016/06/15 20:52:11
Gotcha. I assumed we wanted to treat a codec error
| |
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 if (state_ == STATE_ERROR || state_ == STATE_DRAINED) | |
watk
2016/06/15 00:29:17
It's not clear to me why we can't flush in STATE_D
liberato (no reviews please)
2016/06/15 20:27:30
that's a very good question. we can, according to
| |
73 return false; | |
74 | |
75 if (codec_flush_requires_destruction()) | |
76 return false; | |
77 | |
78 // Actually try to flush! | |
watk
2016/06/15 00:29:17
nit: Comment seems attached to an unrelated line.
liberato (no reviews please)
2016/06/15 20:27:29
Done.
| |
79 io_timer_.Stop(); | |
80 | |
81 if (media_codec_->Flush() != MEDIA_CODEC_OK) { | |
82 // Transition to the error state if the flush failed. | |
watk
2016/06/15 00:29:17
Code is clear. I'd delete this comment.
liberato (no reviews please)
2016/06/15 20:27:30
Done.
| |
83 SetState(STATE_ERROR); | |
84 return false; | |
85 } | |
86 | |
87 SetState(STATE_READY); | |
88 return true; | |
89 } | |
90 | |
91 void MediaCodecLoop::DoIOTask() { | |
92 if (state_ == STATE_ERROR) | |
93 return; | |
94 | |
95 bool did_work = false, did_input = false, did_output = false; | |
96 do { | |
97 did_input = ProcessOneInputBuffer(); | |
98 did_output = ProcessOneOutputBuffer(); | |
99 if (did_input || did_output) | |
100 did_work = true; | |
101 } while (did_input || did_output); | |
102 | |
103 // TODO(liberato): add "start_timer" for AVDA. | |
104 ManageTimer(did_work); | |
105 } | |
106 | |
107 bool MediaCodecLoop::ProcessOneInputBuffer() { | |
108 // We can only queue a buffer if there is input from the client, or if we | |
109 // tried previously but had to wait for a key. In the latter case, MediaCodec | |
110 // already has the data. | |
111 if (pending_input_buf_index_ == kInvalidBufferIndex && | |
112 !client_->IsAnyInputPending()) { | |
113 return false; | |
114 } | |
115 | |
116 if (state_ == STATE_WAITING_FOR_KEY || state_ == STATE_DRAINING || | |
117 state_ == STATE_DRAINED || state_ == STATE_ERROR) { | |
118 return false; | |
119 } | |
watk
2016/06/15 00:29:17
nit: this could be != READY, and checking if the s
liberato (no reviews please)
2016/06/15 20:27:30
Done, good point.
| |
120 | |
121 // DequeueInputBuffer() may set STATE_ERROR. | |
122 InputBuffer input_buffer = DequeueInputBuffer(); | |
123 | |
124 if (input_buffer.index == kInvalidBufferIndex) | |
125 return false; | |
126 | |
127 // EnqueueInputBuffer() may set STATE_DRAINING, STATE_WAITING_FOR_KEY or | |
128 // STATE_ERROR. | |
watk
2016/06/15 00:29:17
Up to you, but feels fragile to list these here. C
liberato (no reviews please)
2016/06/15 20:27:30
Done.
| |
129 EnqueueInputBuffer(input_buffer); | |
130 return state_ == STATE_READY; | |
watk
2016/06/15 00:29:17
If state is DRAINING we still did work right?
liberato (no reviews please)
2016/06/15 20:27:29
good point. it could just return true at this poi
| |
131 } | |
132 | |
133 MediaCodecLoop::InputBuffer MediaCodecLoop::DequeueInputBuffer() { | |
134 DVLOG(2) << __FUNCTION__; | |
135 | |
136 // Do not dequeue a new input buffer if we failed with MEDIA_CODEC_NO_KEY. | |
137 // That status does not return the input buffer back to the pool of | |
138 // available input buffers. We have to reuse it in QueueSecureInputBuffer(). | |
watk
2016/06/15 00:29:17
We don't have a QueueSecureInputBuffer here (but w
liberato (no reviews please)
2016/06/15 20:27:29
Done.
| |
139 if (pending_input_buf_index_ != kInvalidBufferIndex) { | |
140 InputBuffer result(pending_input_buf_index_, true); | |
141 pending_input_buf_index_ = kInvalidBufferIndex; | |
142 return result; | |
143 } | |
144 | |
145 int input_buf_index = kInvalidBufferIndex; | |
146 | |
147 media::MediaCodecStatus status = | |
148 media_codec_->DequeueInputBuffer(kNoWaitTimeout, &input_buf_index); | |
149 switch (status) { | |
150 case media::MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: | |
151 break; | |
152 | |
153 case media::MEDIA_CODEC_ERROR: | |
154 DLOG(ERROR) << __FUNCTION__ | |
155 << ": MEDIA_CODEC_ERROR from DequeInputBuffer"; | |
156 SetState(STATE_ERROR); | |
157 break; | |
158 | |
159 case media::MEDIA_CODEC_OK: | |
160 break; | |
161 | |
162 default: | |
163 NOTREACHED() << "Unknown DequeueInputBuffer status " << status; | |
164 SetState(STATE_ERROR); | |
165 break; | |
166 } | |
167 | |
168 return InputBuffer(input_buf_index, false); | |
169 } | |
170 | |
171 void MediaCodecLoop::EnqueueInputBuffer(const InputBuffer& input_buffer) { | |
172 DCHECK_NE(input_buffer.index, kInvalidBufferIndex); | |
173 | |
174 InputData input_data; | |
DaleCurtis
2016/06/14 19:29:03
Could be written with ternary:
InputData input_da
liberato (no reviews please)
2016/06/14 21:56:44
i'll keep it as is, so that the comment is clearer
| |
175 if (input_buffer.is_pending) { | |
176 // A pending buffer is already filled with data, no need to copy it again. | |
177 input_data = pending_input_buf_data_; | |
178 } else { | |
179 input_data = client_->ProvideInputData(); | |
180 } | |
181 | |
182 if (input_data.is_eos) { | |
183 media_codec_->QueueEOS(input_buffer.index); | |
184 SetState(STATE_DRAINING); | |
185 | |
186 // For EOS, the completion callback is called when the EOS arrives at the | |
187 // output queue. | |
188 pending_eos_completion_cb_ = input_data.completion_cb; | |
189 return; | |
190 } | |
191 | |
192 media::MediaCodecStatus status = MEDIA_CODEC_OK; | |
DaleCurtis
2016/06/14 19:29:03
Ditto on ternary usage, again up to you.
liberato (no reviews please)
2016/06/14 21:56:44
Acknowledged.
| |
193 | |
194 if (input_data.is_encrypted) { | |
195 // Note that input_data might not have a valid memory ptr if this is a | |
196 // re-send of a buffer that was sent before decryption keys arrived. | |
197 | |
198 status = media_codec_->QueueSecureInputBuffer( | |
199 input_buffer.index, input_data.memory, input_data.length, | |
200 input_data.key_id, input_data.iv, input_data.subsamples, | |
201 input_data.presentation_time); | |
202 | |
203 } else { | |
204 status = media_codec_->QueueInputBuffer( | |
205 input_buffer.index, input_data.memory, input_data.length, | |
206 input_data.presentation_time); | |
207 } | |
208 | |
209 switch (status) { | |
210 case MEDIA_CODEC_ERROR: | |
211 DLOG(ERROR) << __FUNCTION__ | |
212 << ": MEDIA_CODEC_ERROR from QueueInputBuffer"; | |
213 input_data.completion_cb.Run(DecodeStatus::DECODE_ERROR); | |
214 // Transition to the error state after running the completion cb, to keep | |
215 // it in order if the client chooses to flush its queue. | |
216 SetState(STATE_ERROR); | |
217 break; | |
218 | |
219 case MEDIA_CODEC_NO_KEY: | |
220 // Do not call the completion cb here. It will be called when we retry | |
221 // after getting the key. | |
222 pending_input_buf_index_ = input_buffer.index; | |
223 pending_input_buf_data_ = input_data; | |
224 // MediaCodec has a copy of the data already. When we call again, be sure | |
225 // to send in nullptr for the source. Note that the client doesn't | |
226 // guarantee that the pointer will remain valid after we return anyway. | |
227 pending_input_buf_data_.memory = nullptr; | |
228 SetState(STATE_WAITING_FOR_KEY); | |
229 break; | |
230 | |
231 case MEDIA_CODEC_OK: | |
232 input_data.completion_cb.Run(DecodeStatus::OK); | |
233 break; | |
234 | |
235 default: | |
236 NOTREACHED() << "Unknown Queue(Secure)InputBuffer status " << status; | |
237 input_data.completion_cb.Run(DecodeStatus::DECODE_ERROR); | |
238 SetState(STATE_ERROR); | |
239 break; | |
240 } | |
241 } | |
242 | |
243 bool MediaCodecLoop::ProcessOneOutputBuffer() { | |
244 MediaCodecStatus status; | |
DaleCurtis
2016/06/14 19:29:03
Avoid c-style variable declarations where possible
liberato (no reviews please)
2016/06/14 21:56:44
done, i think.
watk
2016/06/15 00:29:17
I believe the point was that these declarations ar
liberato (no reviews please)
2016/06/15 20:27:30
that's what i ended up doing.
| |
245 OutputBuffer out; | |
246 bool did_work = false; | |
247 | |
248 // TODO(liberato): When merging AVDA, we will also have to ask the client if | |
249 // it can accept another output buffer. | |
250 | |
251 status = media_codec_->DequeueOutputBuffer(kNoWaitTimeout, &out.index, | |
252 &out.offset, &out.size, &out.pts, | |
253 &out.is_eos, &out.is_key_frame); | |
254 | |
255 switch (status) { | |
256 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: | |
257 // Output buffers are replaced in MediaCodecBridge, nothing to do. | |
258 did_work = true; | |
259 break; | |
260 | |
261 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: | |
262 if (!client_->OnOutputFormatChanged()) | |
263 SetState(STATE_ERROR); | |
264 did_work = (state_ != STATE_ERROR); | |
DaleCurtis
2016/06/14 19:29:03
Remove unnesscary parens.
liberato (no reviews please)
2016/06/14 21:56:44
Done.
| |
265 break; | |
266 | |
267 case MEDIA_CODEC_OK: | |
268 // We got the decoded frame. | |
watk
2016/06/15 00:29:17
minor nit: we didn't necessarily get a frame. Coul
liberato (no reviews please)
2016/06/15 20:27:30
Done.
| |
269 if (out.is_eos) { | |
270 // Set state STATE_DRAINED after we have received EOS frame at the | |
271 // output. media_decoder_job.cc says: once output EOS has occurred, we | |
272 // should not be asked to decode again. | |
273 DCHECK_EQ(state_, STATE_DRAINING); | |
274 SetState(STATE_DRAINED); | |
275 | |
276 DCHECK_NE(out.index, kInvalidBufferIndex); | |
277 DCHECK(media_codec_); | |
278 | |
279 media_codec_->ReleaseOutputBuffer(out.index, false); | |
280 | |
281 // Run the EOS completion callback now, since we deferred it until | |
282 // the EOS was completely processed. | |
283 pending_eos_completion_cb_.Run(DecodeStatus::OK); | |
284 pending_eos_completion_cb_ = DecodeCB(); | |
285 | |
286 client_->OnDecodedEos(out); | |
287 } else { | |
288 if (!client_->OnDecodedFrame(out)) | |
289 SetState(STATE_ERROR); | |
290 } | |
291 | |
292 did_work = true; | |
293 break; | |
294 | |
295 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: | |
296 // Nothing to do. | |
297 break; | |
298 | |
299 case MEDIA_CODEC_ERROR: | |
300 DLOG(ERROR) << __FUNCTION__ | |
301 << ": MEDIA_CODEC_ERROR from DequeueOutputBuffer"; | |
302 // Next Decode() will report the error to the pipeline. | |
watk
2016/06/15 00:29:17
pipeline comment is not applicable.
liberato (no reviews please)
2016/06/15 20:27:30
Done.
| |
303 SetState(STATE_ERROR); | |
304 break; | |
305 | |
306 default: | |
307 NOTREACHED() << "Unknown DequeueOutputBuffer status " << status; | |
308 // Next Decode() will report the error to the pipeline. | |
watk
2016/06/15 00:29:17
same pipeline comment
liberato (no reviews please)
2016/06/15 20:27:30
Done.
| |
309 SetState(STATE_ERROR); | |
310 break; | |
311 } | |
312 | |
313 return did_work; | |
314 } | |
315 | |
316 void MediaCodecLoop::ManageTimer(bool did_work) { | |
317 bool should_be_running = true; | |
318 | |
319 base::TimeTicks now = base::TimeTicks::Now(); | |
320 if (did_work || idle_time_begin_ == base::TimeTicks()) { | |
321 idle_time_begin_ = now; | |
322 } else { | |
323 // Make sure that we have done work recently enough, else stop the timer. | |
324 if (now - idle_time_begin_ > kIdleTimerTimeout) | |
325 should_be_running = false; | |
326 } | |
327 | |
328 if (should_be_running && !io_timer_.IsRunning()) { | |
329 io_timer_.Start(FROM_HERE, kDecodePollDelay, this, | |
330 &MediaCodecLoop::DoIOTask); | |
331 } else if (!should_be_running && io_timer_.IsRunning()) { | |
332 io_timer_.Stop(); | |
333 } | |
334 } | |
335 | |
336 void MediaCodecLoop::SetState(State new_state) { | |
337 if (state_ != new_state && new_state == STATE_ERROR) | |
338 client_->OnCodecLoopError(); | |
watk
2016/06/15 00:29:17
Should this set the state first? To avoid any weir
liberato (no reviews please)
2016/06/15 20:27:30
Done.
| |
339 state_ = new_state; | |
340 } | |
341 | |
342 MediaCodecBridge* MediaCodecLoop::GetCodec() const { | |
343 return media_codec_.get(); | |
344 } | |
345 | |
346 // static | |
347 const char* MediaCodecLoop::AsString(State state) { | |
348 #define RETURN_STRING(x) \ | |
349 case x: \ | |
350 return #x; | |
351 | |
352 switch (state) { | |
353 RETURN_STRING(STATE_READY); | |
354 RETURN_STRING(STATE_WAITING_FOR_KEY); | |
355 RETURN_STRING(STATE_DRAINING); | |
356 RETURN_STRING(STATE_DRAINED); | |
357 RETURN_STRING(STATE_ERROR); | |
358 } | |
359 #undef RETURN_STRING | |
360 | |
361 NOTREACHED() << "Unknown state " << state; | |
362 return nullptr; | |
363 } | |
364 | |
365 } // namespace media | |
OLD | NEW |