Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(141)

Side by Side Diff: media/base/android/media_codec_loop.cc

Issue 2016213003: Separate MediaCodecLoop from MediaCodecAudioDecoder (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebased, removed include. Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698