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

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 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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698