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

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: cl feedback. 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 : 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698