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

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

Issue 196133020: Reducing the IPC latency for MSE video decoding (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 9 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 | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/base/android/media_decoder_job.h" 5 #include "media/base/android/media_decoder_job.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/callback_helpers.h" 8 #include "base/callback_helpers.h"
9 #include "base/debug/trace_event.h" 9 #include "base/debug/trace_event.h"
10 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/message_loop/message_loop_proxy.h"
(...skipping 15 matching lines...) Expand all
26 : ui_task_runner_(base::MessageLoopProxy::current()), 26 : ui_task_runner_(base::MessageLoopProxy::current()),
27 decoder_task_runner_(decoder_task_runner), 27 decoder_task_runner_(decoder_task_runner),
28 media_codec_bridge_(media_codec_bridge), 28 media_codec_bridge_(media_codec_bridge),
29 needs_flush_(false), 29 needs_flush_(false),
30 input_eos_encountered_(false), 30 input_eos_encountered_(false),
31 output_eos_encountered_(false), 31 output_eos_encountered_(false),
32 skip_eos_enqueue_(true), 32 skip_eos_enqueue_(true),
33 prerolling_(true), 33 prerolling_(true),
34 weak_this_(this), 34 weak_this_(this),
35 request_data_cb_(request_data_cb), 35 request_data_cb_(request_data_cb),
36 access_unit_index_(0), 36 current_demuxer_data_index_(0),
37 input_buf_index_(-1), 37 input_buf_index_(-1),
38 stop_decode_pending_(false), 38 stop_decode_pending_(false),
39 destroy_pending_(false) { 39 destroy_pending_(false),
40 is_requesting_demuxer_data_(false),
41 is_incoming_data_invalid_(false) {
42 for (size_t i = 0; i < 2; ++i)
43 access_unit_index_[i] = 0;
40 } 44 }
41 45
42 MediaDecoderJob::~MediaDecoderJob() {} 46 MediaDecoderJob::~MediaDecoderJob() {}
43 47
44 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) { 48 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) {
45 DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units"; 49 DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units";
46 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 50 DCHECK(ui_task_runner_->BelongsToCurrentThread());
47 DCHECK(!on_data_received_cb_.is_null()); 51 DCHECK(IsDemuxerDataEmpty(false));
48 52
49 TRACE_EVENT_ASYNC_END2( 53 TRACE_EVENT_ASYNC_END2(
50 "media", "MediaDecoderJob::RequestData", this, 54 "media", "MediaDecoderJob::RequestData", this,
51 "Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO", 55 "Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO",
52 "Units read", data.access_units.size()); 56 "Units read", data.access_units.size());
53 57
58 if (is_incoming_data_invalid_) {
59 is_incoming_data_invalid_ = false;
60 // If there is a pending callback, need to request the data again to get
61 // valid data.
62 if (!on_data_received_cb_.is_null())
63 request_data_cb_.Run();
64 else
65 is_requesting_demuxer_data_ = false;
66 return;
67 }
68
69 size_t next_demuxer_data_index = 1 - current_demuxer_data_index_;
70 received_data_[next_demuxer_data_index] = data;
71 access_unit_index_[next_demuxer_data_index] = 0;
72 is_requesting_demuxer_data_ = false;
73
54 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_); 74 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_);
55
56 if (stop_decode_pending_) { 75 if (stop_decode_pending_) {
57 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); 76 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0);
58 return; 77 return;
59 } 78 }
60 79
61 access_unit_index_ = 0; 80 if (!done_cb.is_null())
62 received_data_ = data; 81 done_cb.Run();
63 done_cb.Run();
64 } 82 }
65 83
66 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { 84 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
67 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 85 DCHECK(ui_task_runner_->BelongsToCurrentThread());
68 DCHECK(on_data_received_cb_.is_null()); 86 DCHECK(on_data_received_cb_.is_null());
69 DCHECK(decode_cb_.is_null()); 87 DCHECK(decode_cb_.is_null());
70 88
71 if (HasData()) { 89 if (HasData()) {
72 DVLOG(1) << __FUNCTION__ << " : using previously received data"; 90 DVLOG(1) << __FUNCTION__ << " : using previously received data";
73 ui_task_runner_->PostTask(FROM_HERE, prefetch_cb); 91 ui_task_runner_->PostTask(FROM_HERE, prefetch_cb);
74 return; 92 return;
75 } 93 }
76 94
77 DVLOG(1) << __FUNCTION__ << " : requesting data"; 95 DVLOG(1) << __FUNCTION__ << " : requesting data";
78 RequestData(prefetch_cb); 96 RequestData(prefetch_cb);
79 } 97 }
80 98
81 bool MediaDecoderJob::Decode( 99 bool MediaDecoderJob::Decode(
82 const base::TimeTicks& start_time_ticks, 100 const base::TimeTicks& start_time_ticks,
83 const base::TimeDelta& start_presentation_timestamp, 101 const base::TimeDelta& start_presentation_timestamp,
84 const DecoderCallback& callback) { 102 const DecoderCallback& callback) {
85 DCHECK(decode_cb_.is_null()); 103 DCHECK(decode_cb_.is_null());
86 DCHECK(on_data_received_cb_.is_null()); 104 DCHECK(on_data_received_cb_.is_null());
87 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 105 DCHECK(ui_task_runner_->BelongsToCurrentThread());
88 106
89 decode_cb_ = callback; 107 decode_cb_ = callback;
90 108
91 if (!HasData()) { 109 if (!HasData()) {
92 RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit, 110 RequestData(base::Bind(&MediaDecoderJob::DecodeCurrentAccessUnit,
93 base::Unretained(this), 111 base::Unretained(this),
94 start_time_ticks, 112 start_time_ticks,
95 start_presentation_timestamp)); 113 start_presentation_timestamp));
96 return true; 114 return true;
97 } 115 }
98 116
99 if (DemuxerStream::kConfigChanged == 117 if (DemuxerStream::kConfigChanged == CurrentAccessUnit().status) {
100 received_data_.access_units[access_unit_index_].status) {
101 // Clear received data because we need to handle a config change. 118 // Clear received data because we need to handle a config change.
102 decode_cb_.Reset(); 119 decode_cb_.Reset();
103 received_data_ = DemuxerData(); 120 ClearData();
104 access_unit_index_ = 0;
105 return false; 121 return false;
106 } 122 }
107 123
108 DecodeNextAccessUnit(start_time_ticks, start_presentation_timestamp); 124 DecodeCurrentAccessUnit(start_time_ticks, start_presentation_timestamp);
109 return true; 125 return true;
110 } 126 }
111 127
112 void MediaDecoderJob::StopDecode() { 128 void MediaDecoderJob::StopDecode() {
113 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 129 DCHECK(ui_task_runner_->BelongsToCurrentThread());
114 DCHECK(is_decoding()); 130 DCHECK(is_decoding());
115 stop_decode_pending_ = true; 131 stop_decode_pending_ = true;
116 } 132 }
117 133
118 void MediaDecoderJob::Flush() { 134 void MediaDecoderJob::Flush() {
119 DCHECK(decode_cb_.is_null()); 135 DCHECK(decode_cb_.is_null());
120 136
121 // Do nothing, flush when the next Decode() happens. 137 // Do nothing, flush when the next Decode() happens.
122 needs_flush_ = true; 138 needs_flush_ = true;
123 received_data_ = DemuxerData(); 139 ClearData();
124 input_eos_encountered_ = false; 140 input_eos_encountered_ = false;
125 access_unit_index_ = 0;
126 on_data_received_cb_.Reset();
127 } 141 }
128 142
129 void MediaDecoderJob::BeginPrerolling( 143 void MediaDecoderJob::BeginPrerolling(
130 const base::TimeDelta& preroll_timestamp) { 144 const base::TimeDelta& preroll_timestamp) {
131 DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")"; 145 DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")";
132 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 146 DCHECK(ui_task_runner_->BelongsToCurrentThread());
133 DCHECK(!is_decoding()); 147 DCHECK(!is_decoding());
134 148
135 preroll_timestamp_ = preroll_timestamp; 149 preroll_timestamp_ = preroll_timestamp;
136 prerolling_ = true; 150 prerolling_ = true;
(...skipping 64 matching lines...) Expand 10 before | Expand all | Expand 10 after
201 // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|. 215 // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|.
202 // Otherwise MediaDrm will report errors. 216 // Otherwise MediaDrm will report errors.
203 if (status == MEDIA_CODEC_NO_KEY) 217 if (status == MEDIA_CODEC_NO_KEY)
204 input_buf_index_ = input_buf_index; 218 input_buf_index_ = input_buf_index;
205 219
206 return status; 220 return status;
207 } 221 }
208 222
209 bool MediaDecoderJob::HasData() const { 223 bool MediaDecoderJob::HasData() const {
210 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 224 DCHECK(ui_task_runner_->BelongsToCurrentThread());
211 // When |input_eos_encountered_| is set, |access_units| must not be empty and 225 // When |input_eos_encountered_| is set, |access_unit| must be pointing to an
wolenetz 2014/03/17 19:51:00 s/access_unit/access_unit_index_/ Also, mention d
qinmin 2014/03/18 18:58:58 Done. Good point. ClearData() was called in 2 fun
212 // |access_unit_index_| must be pointing to an EOS unit. We'll reuse this 226 // EOS unit. We'll reuse this unit to flush the decoder until we hit output
213 // unit to flush the decoder until we hit output EOS. 227 // EOS.
214 DCHECK(!input_eos_encountered_ || 228 DCHECK(!input_eos_encountered_ || !IsDemuxerDataEmpty(true));
215 (received_data_.access_units.size() > 0 && 229 return !IsDemuxerDataEmpty(true) || !IsDemuxerDataEmpty(false);
216 access_unit_index_ < received_data_.access_units.size()))
217 << " (access_units.size(): " << received_data_.access_units.size()
218 << ", access_unit_index_: " << access_unit_index_ << ")";
219 return access_unit_index_ < received_data_.access_units.size() ||
220 input_eos_encountered_;
221 } 230 }
222 231
223 void MediaDecoderJob::RequestData(const base::Closure& done_cb) { 232 void MediaDecoderJob::RequestData(const base::Closure& done_cb) {
224 DVLOG(1) << __FUNCTION__; 233 DVLOG(1) << __FUNCTION__;
225 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 234 DCHECK(ui_task_runner_->BelongsToCurrentThread());
226 DCHECK(on_data_received_cb_.is_null()); 235 DCHECK(on_data_received_cb_.is_null());
227 DCHECK(!input_eos_encountered_); 236 DCHECK(!input_eos_encountered_);
237 DCHECK(IsDemuxerDataEmpty(false));
228 238
229 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this); 239 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this);
230 240
231 received_data_ = DemuxerData();
232 access_unit_index_ = 0;
233 on_data_received_cb_ = done_cb; 241 on_data_received_cb_ = done_cb;
234 242
243 // If we are already expecting new data, just set the callback and do
244 // nothing.
245 if (is_requesting_demuxer_data_)
246 return;
247
248 // The new incoming data will be stored as the next demuxer data chunk, since
249 // the decoder might still be decoding the current one.
250 size_t next_demuxer_data_index = 1 - current_demuxer_data_index_;
251 received_data_[next_demuxer_data_index] = DemuxerData();
252 access_unit_index_[next_demuxer_data_index] = 0;
253 is_requesting_demuxer_data_ = true;
254
235 request_data_cb_.Run(); 255 request_data_cb_.Run();
236 } 256 }
237 257
238 void MediaDecoderJob::DecodeNextAccessUnit( 258 void MediaDecoderJob::DecodeCurrentAccessUnit(
239 const base::TimeTicks& start_time_ticks, 259 const base::TimeTicks& start_time_ticks,
240 const base::TimeDelta& start_presentation_timestamp) { 260 const base::TimeDelta& start_presentation_timestamp) {
241 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 261 DCHECK(ui_task_runner_->BelongsToCurrentThread());
242 DCHECK(!decode_cb_.is_null()); 262 DCHECK(!decode_cb_.is_null());
243 263
264 RequestCurrentChunkIfEmpty();
265 const AccessUnit& access_unit = CurrentAccessUnit();
244 // If the first access unit is a config change, request the player to dequeue 266 // If the first access unit is a config change, request the player to dequeue
245 // the input buffer again so that it can request config data. 267 // the input buffer again so that it can request config data.
246 if (received_data_.access_units[access_unit_index_].status == 268 if (access_unit.status == DemuxerStream::kConfigChanged) {
247 DemuxerStream::kConfigChanged) {
248 ui_task_runner_->PostTask(FROM_HERE, 269 ui_task_runner_->PostTask(FROM_HERE,
249 base::Bind(&MediaDecoderJob::OnDecodeCompleted, 270 base::Bind(&MediaDecoderJob::OnDecodeCompleted,
250 base::Unretained(this), 271 base::Unretained(this),
251 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, 272 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER,
252 kNoTimestamp(), 273 kNoTimestamp(),
253 0)); 274 0));
254 return; 275 return;
255 } 276 }
256 277
257 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( 278 decoder_task_runner_->PostTask(FROM_HERE, base::Bind(
258 &MediaDecoderJob::DecodeInternal, base::Unretained(this), 279 &MediaDecoderJob::DecodeInternal, base::Unretained(this),
259 received_data_.access_units[access_unit_index_], 280 access_unit,
wolenetz 2014/03/17 19:51:00 Is it possible for ClearData() to occur while the
qinmin 2014/03/18 18:58:58 No, ClearData() will be called only when the decod
260 start_time_ticks, start_presentation_timestamp, needs_flush_, 281 start_time_ticks, start_presentation_timestamp, needs_flush_,
261 media::BindToCurrentLoop(base::Bind( 282 media::BindToCurrentLoop(base::Bind(
262 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); 283 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this)))));
263 needs_flush_ = false; 284 needs_flush_ = false;
264 } 285 }
265 286
266 void MediaDecoderJob::DecodeInternal( 287 void MediaDecoderJob::DecodeInternal(
267 const AccessUnit& unit, 288 const AccessUnit& unit,
268 const base::TimeTicks& start_time_ticks, 289 const base::TimeTicks& start_time_ticks,
269 const base::TimeDelta& start_presentation_timestamp, 290 const base::TimeDelta& start_presentation_timestamp,
(...skipping 134 matching lines...) Expand 10 before | Expand all | Expand 10 after
404 if (presentation_timestamp != kNoTimestamp()) 425 if (presentation_timestamp != kNoTimestamp())
405 prerolling_ = false; 426 prerolling_ = false;
406 427
407 switch (status) { 428 switch (status) {
408 case MEDIA_CODEC_OK: 429 case MEDIA_CODEC_OK:
409 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: 430 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
410 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: 431 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
411 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: 432 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
412 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: 433 case MEDIA_CODEC_OUTPUT_END_OF_STREAM:
413 if (!input_eos_encountered_) 434 if (!input_eos_encountered_)
414 access_unit_index_++; 435 access_unit_index_[current_demuxer_data_index_]++;
415 break; 436 break;
416 437
417 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: 438 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
418 case MEDIA_CODEC_INPUT_END_OF_STREAM: 439 case MEDIA_CODEC_INPUT_END_OF_STREAM:
419 case MEDIA_CODEC_NO_KEY: 440 case MEDIA_CODEC_NO_KEY:
420 case MEDIA_CODEC_STOPPED: 441 case MEDIA_CODEC_STOPPED:
421 case MEDIA_CODEC_ERROR: 442 case MEDIA_CODEC_ERROR:
422 // Do nothing. 443 // Do nothing.
423 break; 444 break;
424 }; 445 };
425 446
426 stop_decode_pending_ = false; 447 stop_decode_pending_ = false;
427 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, 448 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp,
428 audio_output_bytes); 449 audio_output_bytes);
429 } 450 }
430 451
452 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const {
wolenetz 2014/03/17 19:51:00 nit: clarify with DCHECK any assumption of which t
qinmin 2014/03/18 18:58:58 Done.
453 DCHECK(HasData());
454 int index = IsDemuxerDataEmpty(true) ? 1- current_demuxer_data_index_ :
wolenetz 2014/03/17 19:51:00 nit: s/1-/1 -/
wolenetz 2014/03/17 19:51:00 nit: extract "1 - current_demuxer_data_index_" to
qinmin 2014/03/18 18:58:58 Done.
qinmin 2014/03/18 18:58:58 Done.
455 current_demuxer_data_index_;
456 return received_data_[index].access_units[access_unit_index_[index]];
457 }
458
459 bool MediaDecoderJob::IsDemuxerDataEmpty(bool current_chunk) const {
wolenetz 2014/03/17 19:51:00 nit: ditto thread dcheck
qinmin 2014/03/18 18:58:58 Done.
460 size_t index = current_chunk ? current_demuxer_data_index_ :
461 1 - current_demuxer_data_index_;
462 return received_data_[index].access_units.size() <= access_unit_index_[index];
463 }
464
465 void MediaDecoderJob::ClearData() {
wolenetz 2014/03/17 19:51:00 nit: ditto thread dcheck
qinmin 2014/03/18 18:58:58 Done.
466 current_demuxer_data_index_ = 0;
wolenetz 2014/03/17 19:51:00 ditto: If |input_eos_encountered_| is set, we shou
qinmin 2014/03/18 18:58:58 changed the function to set |input_eos_encountered
467 for (size_t i = 0; i < 2; ++i) {
wolenetz 2014/03/17 19:51:00 nit: Elevate this to a helper function, also use i
qinmin 2014/03/18 18:58:58 Done.
468 received_data_[i] = DemuxerData();
469 access_unit_index_[i] = 0;
470 }
471 on_data_received_cb_.Reset();
472 if (is_requesting_demuxer_data_)
473 is_incoming_data_invalid_ = true;
474 }
475
476 void MediaDecoderJob::RequestCurrentChunkIfEmpty() {
wolenetz 2014/03/17 19:51:00 nit: ditto thread dcheck
qinmin 2014/03/18 18:58:58 Done.
477 DCHECK(HasData());
478 if (!IsDemuxerDataEmpty(true))
479 return;
480
481 // Requests new data if the the last access unit of the next chunk is not EOS.
482 current_demuxer_data_index_ = 1 - current_demuxer_data_index_;
483 const AccessUnit last_access_unit =
484 received_data_[current_demuxer_data_index_].access_units.back();
485 if (!last_access_unit.end_of_stream &&
486 last_access_unit.status != DemuxerStream::kConfigChanged &&
487 last_access_unit.status != DemuxerStream::kAborted) {
488 RequestData(base::Closure());
489 }
490 }
491
431 } // namespace media 492 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698