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

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: addressing comments 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 InitializeReceivedData();
40 } 43 }
41 44
42 MediaDecoderJob::~MediaDecoderJob() {} 45 MediaDecoderJob::~MediaDecoderJob() {}
43 46
44 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) { 47 void MediaDecoderJob::OnDataReceived(const DemuxerData& data) {
45 DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units"; 48 DVLOG(1) << __FUNCTION__ << ": " << data.access_units.size() << " units";
46 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 49 DCHECK(ui_task_runner_->BelongsToCurrentThread());
47 DCHECK(!on_data_received_cb_.is_null()); 50 DCHECK(IsDemuxerDataEmpty(false));
48 51
49 TRACE_EVENT_ASYNC_END2( 52 TRACE_EVENT_ASYNC_END2(
50 "media", "MediaDecoderJob::RequestData", this, 53 "media", "MediaDecoderJob::RequestData", this,
51 "Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO", 54 "Data type", data.type == media::DemuxerStream::AUDIO ? "AUDIO" : "VIDEO",
52 "Units read", data.access_units.size()); 55 "Units read", data.access_units.size());
53 56
57 if (is_incoming_data_invalid_) {
58 is_incoming_data_invalid_ = false;
59 // If there is a pending callback, need to request the data again to get
wolenetz 2014/03/18 21:14:47 nit: insert line before comment?
qinmin 2014/03/19 02:45:22 Done.
60 // valid data.
61 if (!on_data_received_cb_.is_null())
62 request_data_cb_.Run();
63 else
64 is_requesting_demuxer_data_ = false;
65 return;
66 }
67
68 size_t next_demuxer_data_index = inactive_demuxer_data_index();
69 received_data_[next_demuxer_data_index] = data;
70 access_unit_index_[next_demuxer_data_index] = 0;
71 is_requesting_demuxer_data_ = false;
72
54 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_); 73 base::Closure done_cb = base::ResetAndReturn(&on_data_received_cb_);
55
56 if (stop_decode_pending_) { 74 if (stop_decode_pending_) {
57 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0); 75 OnDecodeCompleted(MEDIA_CODEC_STOPPED, kNoTimestamp(), 0);
58 return; 76 return;
59 } 77 }
60 78
61 access_unit_index_ = 0; 79 if (!done_cb.is_null())
62 received_data_ = data; 80 done_cb.Run();
63 done_cb.Run();
64 } 81 }
65 82
66 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) { 83 void MediaDecoderJob::Prefetch(const base::Closure& prefetch_cb) {
67 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 84 DCHECK(ui_task_runner_->BelongsToCurrentThread());
68 DCHECK(on_data_received_cb_.is_null()); 85 DCHECK(on_data_received_cb_.is_null());
69 DCHECK(decode_cb_.is_null()); 86 DCHECK(decode_cb_.is_null());
70 87
71 if (HasData()) { 88 if (HasData()) {
72 DVLOG(1) << __FUNCTION__ << " : using previously received data"; 89 DVLOG(1) << __FUNCTION__ << " : using previously received data";
73 ui_task_runner_->PostTask(FROM_HERE, prefetch_cb); 90 ui_task_runner_->PostTask(FROM_HERE, prefetch_cb);
74 return; 91 return;
75 } 92 }
76 93
77 DVLOG(1) << __FUNCTION__ << " : requesting data"; 94 DVLOG(1) << __FUNCTION__ << " : requesting data";
78 RequestData(prefetch_cb); 95 RequestData(prefetch_cb);
79 } 96 }
80 97
81 bool MediaDecoderJob::Decode( 98 bool MediaDecoderJob::Decode(
82 const base::TimeTicks& start_time_ticks, 99 base::TimeTicks start_time_ticks,
83 const base::TimeDelta& start_presentation_timestamp, 100 base::TimeDelta start_presentation_timestamp,
84 const DecoderCallback& callback) { 101 const DecoderCallback& callback) {
85 DCHECK(decode_cb_.is_null()); 102 DCHECK(decode_cb_.is_null());
86 DCHECK(on_data_received_cb_.is_null()); 103 DCHECK(on_data_received_cb_.is_null());
87 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 104 DCHECK(ui_task_runner_->BelongsToCurrentThread());
88 105
89 decode_cb_ = callback; 106 decode_cb_ = callback;
90 107
91 if (!HasData()) { 108 if (!HasData()) {
92 RequestData(base::Bind(&MediaDecoderJob::DecodeNextAccessUnit, 109 RequestData(base::Bind(&MediaDecoderJob::DecodeCurrentAccessUnit,
93 base::Unretained(this), 110 base::Unretained(this),
94 start_time_ticks, 111 start_time_ticks,
95 start_presentation_timestamp)); 112 start_presentation_timestamp));
96 return true; 113 return true;
97 } 114 }
98 115
99 if (DemuxerStream::kConfigChanged == 116 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. 117 // Clear received data because we need to handle a config change.
102 decode_cb_.Reset(); 118 decode_cb_.Reset();
103 received_data_ = DemuxerData(); 119 ClearData();
104 access_unit_index_ = 0;
105 return false; 120 return false;
106 } 121 }
107 122
108 DecodeNextAccessUnit(start_time_ticks, start_presentation_timestamp); 123 DecodeCurrentAccessUnit(start_time_ticks, start_presentation_timestamp);
109 return true; 124 return true;
110 } 125 }
111 126
112 void MediaDecoderJob::StopDecode() { 127 void MediaDecoderJob::StopDecode() {
113 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 128 DCHECK(ui_task_runner_->BelongsToCurrentThread());
114 DCHECK(is_decoding()); 129 DCHECK(is_decoding());
115 stop_decode_pending_ = true; 130 stop_decode_pending_ = true;
116 } 131 }
117 132
118 void MediaDecoderJob::Flush() { 133 void MediaDecoderJob::Flush() {
119 DCHECK(decode_cb_.is_null()); 134 DCHECK(decode_cb_.is_null());
120 135
121 // Do nothing, flush when the next Decode() happens. 136 // Do nothing, flush when the next Decode() happens.
122 needs_flush_ = true; 137 needs_flush_ = true;
123 received_data_ = DemuxerData(); 138 ClearData();
124 input_eos_encountered_ = false;
125 access_unit_index_ = 0;
126 on_data_received_cb_.Reset();
127 } 139 }
128 140
129 void MediaDecoderJob::BeginPrerolling( 141 void MediaDecoderJob::BeginPrerolling(base::TimeDelta preroll_timestamp) {
130 const base::TimeDelta& preroll_timestamp) {
131 DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")"; 142 DVLOG(1) << __FUNCTION__ << "(" << preroll_timestamp.InSecondsF() << ")";
132 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 143 DCHECK(ui_task_runner_->BelongsToCurrentThread());
133 DCHECK(!is_decoding()); 144 DCHECK(!is_decoding());
134 145
135 preroll_timestamp_ = preroll_timestamp; 146 preroll_timestamp_ = preroll_timestamp;
136 prerolling_ = true; 147 prerolling_ = true;
137 } 148 }
138 149
139 void MediaDecoderJob::Release() { 150 void MediaDecoderJob::Release() {
140 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 151 DCHECK(ui_task_runner_->BelongsToCurrentThread());
(...skipping 60 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_|. 212 // In case of MEDIA_CODEC_NO_KEY, we must reuse the |input_buf_index_|.
202 // Otherwise MediaDrm will report errors. 213 // Otherwise MediaDrm will report errors.
203 if (status == MEDIA_CODEC_NO_KEY) 214 if (status == MEDIA_CODEC_NO_KEY)
204 input_buf_index_ = input_buf_index; 215 input_buf_index_ = input_buf_index;
205 216
206 return status; 217 return status;
207 } 218 }
208 219
209 bool MediaDecoderJob::HasData() const { 220 bool MediaDecoderJob::HasData() const {
210 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 221 DCHECK(ui_task_runner_->BelongsToCurrentThread());
211 // When |input_eos_encountered_| is set, |access_units| must not be empty and 222 // When |input_eos_encountered_| is set, |access_unit_index_| and
212 // |access_unit_index_| must be pointing to an EOS unit. We'll reuse this 223 // |current_demuxer_data_index_| must be pointing to an EOS unit.
213 // unit to flush the decoder until we hit output EOS. 224 // We'll reuse this unit to flush the decoder until we hit outpu EOS.
wolenetz 2014/03/18 21:14:47 nit: s/outpu/output/
qinmin 2014/03/19 02:45:22 Done.
214 DCHECK(!input_eos_encountered_ || 225 DCHECK(!input_eos_encountered_ || !IsDemuxerDataEmpty(true));
215 (received_data_.access_units.size() > 0 && 226 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 } 227 }
222 228
223 void MediaDecoderJob::RequestData(const base::Closure& done_cb) { 229 void MediaDecoderJob::RequestData(const base::Closure& done_cb) {
224 DVLOG(1) << __FUNCTION__; 230 DVLOG(1) << __FUNCTION__;
225 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 231 DCHECK(ui_task_runner_->BelongsToCurrentThread());
226 DCHECK(on_data_received_cb_.is_null()); 232 DCHECK(on_data_received_cb_.is_null());
227 DCHECK(!input_eos_encountered_); 233 DCHECK(!input_eos_encountered_);
234 DCHECK(IsDemuxerDataEmpty(false));
228 235
229 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this); 236 TRACE_EVENT_ASYNC_BEGIN0("media", "MediaDecoderJob::RequestData", this);
230 237
231 received_data_ = DemuxerData();
232 access_unit_index_ = 0;
233 on_data_received_cb_ = done_cb; 238 on_data_received_cb_ = done_cb;
234 239
240 // If we are already expecting new data, just set the callback and do
241 // nothing.
242 if (is_requesting_demuxer_data_)
243 return;
244
245 // The new incoming data will be stored as the next demuxer data chunk, since
246 // the decoder might still be decoding the current one.
247 size_t next_demuxer_data_index = inactive_demuxer_data_index();
248 received_data_[next_demuxer_data_index] = DemuxerData();
249 access_unit_index_[next_demuxer_data_index] = 0;
250 is_requesting_demuxer_data_ = true;
251
235 request_data_cb_.Run(); 252 request_data_cb_.Run();
236 } 253 }
237 254
238 void MediaDecoderJob::DecodeNextAccessUnit( 255 void MediaDecoderJob::DecodeCurrentAccessUnit(
239 const base::TimeTicks& start_time_ticks, 256 base::TimeTicks start_time_ticks,
240 const base::TimeDelta& start_presentation_timestamp) { 257 base::TimeDelta start_presentation_timestamp) {
241 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 258 DCHECK(ui_task_runner_->BelongsToCurrentThread());
242 DCHECK(!decode_cb_.is_null()); 259 DCHECK(!decode_cb_.is_null());
243 260
261 RequestCurrentChunkIfEmpty();
262 const AccessUnit& access_unit = CurrentAccessUnit();
244 // If the first access unit is a config change, request the player to dequeue 263 // 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. 264 // the input buffer again so that it can request config data.
246 if (received_data_.access_units[access_unit_index_].status == 265 if (access_unit.status == DemuxerStream::kConfigChanged) {
247 DemuxerStream::kConfigChanged) {
248 ui_task_runner_->PostTask(FROM_HERE, 266 ui_task_runner_->PostTask(FROM_HERE,
249 base::Bind(&MediaDecoderJob::OnDecodeCompleted, 267 base::Bind(&MediaDecoderJob::OnDecodeCompleted,
250 base::Unretained(this), 268 base::Unretained(this),
251 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER, 269 MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER,
252 kNoTimestamp(), 270 kNoTimestamp(),
253 0)); 271 0));
254 return; 272 return;
255 } 273 }
256 274
257 decoder_task_runner_->PostTask(FROM_HERE, base::Bind( 275 decoder_task_runner_->PostTask(FROM_HERE, base::Bind(
258 &MediaDecoderJob::DecodeInternal, base::Unretained(this), 276 &MediaDecoderJob::DecodeInternal, base::Unretained(this),
259 received_data_.access_units[access_unit_index_], 277 access_unit,
260 start_time_ticks, start_presentation_timestamp, needs_flush_, 278 start_time_ticks, start_presentation_timestamp, needs_flush_,
261 media::BindToCurrentLoop(base::Bind( 279 media::BindToCurrentLoop(base::Bind(
262 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this))))); 280 &MediaDecoderJob::OnDecodeCompleted, base::Unretained(this)))));
263 needs_flush_ = false; 281 needs_flush_ = false;
264 } 282 }
265 283
266 void MediaDecoderJob::DecodeInternal( 284 void MediaDecoderJob::DecodeInternal(
267 const AccessUnit& unit, 285 const AccessUnit& unit,
268 const base::TimeTicks& start_time_ticks, 286 base::TimeTicks start_time_ticks,
269 const base::TimeDelta& start_presentation_timestamp, 287 base::TimeDelta start_presentation_timestamp,
270 bool needs_flush, 288 bool needs_flush,
271 const MediaDecoderJob::DecoderCallback& callback) { 289 const MediaDecoderJob::DecoderCallback& callback) {
272 DVLOG(1) << __FUNCTION__; 290 DVLOG(1) << __FUNCTION__;
273 DCHECK(decoder_task_runner_->BelongsToCurrentThread()); 291 DCHECK(decoder_task_runner_->BelongsToCurrentThread());
274 TRACE_EVENT0("media", __FUNCTION__); 292 TRACE_EVENT0("media", __FUNCTION__);
275 293
276 if (needs_flush) { 294 if (needs_flush) {
277 DVLOG(1) << "DecodeInternal needs flush."; 295 DVLOG(1) << "DecodeInternal needs flush.";
278 input_eos_encountered_ = false; 296 input_eos_encountered_ = false;
279 output_eos_encountered_ = false; 297 output_eos_encountered_ = false;
(...skipping 101 matching lines...) Expand 10 before | Expand all | Expand 10 after
381 presentation_timestamp, start_presentation_timestamp); 399 presentation_timestamp, start_presentation_timestamp);
382 } else { 400 } else {
383 presentation_timestamp = kNoTimestamp(); 401 presentation_timestamp = kNoTimestamp();
384 } 402 }
385 ReleaseOutputCompletionCallback completion_callback = base::Bind( 403 ReleaseOutputCompletionCallback completion_callback = base::Bind(
386 callback, status, presentation_timestamp); 404 callback, status, presentation_timestamp);
387 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback); 405 ReleaseOutputBuffer(buffer_index, size, render_output, completion_callback);
388 } 406 }
389 407
390 void MediaDecoderJob::OnDecodeCompleted( 408 void MediaDecoderJob::OnDecodeCompleted(
391 MediaCodecStatus status, const base::TimeDelta& presentation_timestamp, 409 MediaCodecStatus status, base::TimeDelta presentation_timestamp,
392 size_t audio_output_bytes) { 410 size_t audio_output_bytes) {
393 DCHECK(ui_task_runner_->BelongsToCurrentThread()); 411 DCHECK(ui_task_runner_->BelongsToCurrentThread());
394 412
395 if (destroy_pending_) { 413 if (destroy_pending_) {
396 DVLOG(1) << __FUNCTION__ << " : completing pending deletion"; 414 DVLOG(1) << __FUNCTION__ << " : completing pending deletion";
397 delete this; 415 delete this;
398 return; 416 return;
399 } 417 }
400 418
401 DCHECK(!decode_cb_.is_null()); 419 DCHECK(!decode_cb_.is_null());
402 420
403 // If output was queued for rendering, then we have completed prerolling. 421 // If output was queued for rendering, then we have completed prerolling.
404 if (presentation_timestamp != kNoTimestamp()) 422 if (presentation_timestamp != kNoTimestamp())
405 prerolling_ = false; 423 prerolling_ = false;
406 424
407 switch (status) { 425 switch (status) {
408 case MEDIA_CODEC_OK: 426 case MEDIA_CODEC_OK:
409 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER: 427 case MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER:
410 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED: 428 case MEDIA_CODEC_OUTPUT_BUFFERS_CHANGED:
411 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED: 429 case MEDIA_CODEC_OUTPUT_FORMAT_CHANGED:
412 case MEDIA_CODEC_OUTPUT_END_OF_STREAM: 430 case MEDIA_CODEC_OUTPUT_END_OF_STREAM:
413 if (!input_eos_encountered_) 431 if (!input_eos_encountered_)
414 access_unit_index_++; 432 access_unit_index_[current_demuxer_data_index_]++;
415 break; 433 break;
416 434
417 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER: 435 case MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER:
418 case MEDIA_CODEC_INPUT_END_OF_STREAM: 436 case MEDIA_CODEC_INPUT_END_OF_STREAM:
419 case MEDIA_CODEC_NO_KEY: 437 case MEDIA_CODEC_NO_KEY:
420 case MEDIA_CODEC_STOPPED: 438 case MEDIA_CODEC_STOPPED:
421 case MEDIA_CODEC_ERROR: 439 case MEDIA_CODEC_ERROR:
422 // Do nothing. 440 // Do nothing.
423 break; 441 break;
424 }; 442 };
425 443
426 stop_decode_pending_ = false; 444 stop_decode_pending_ = false;
427 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp, 445 base::ResetAndReturn(&decode_cb_).Run(status, presentation_timestamp,
428 audio_output_bytes); 446 audio_output_bytes);
429 } 447 }
430 448
449 const AccessUnit& MediaDecoderJob::CurrentAccessUnit() const {
450 DCHECK(ui_task_runner_->BelongsToCurrentThread());
451 DCHECK(HasData());
452 int index = IsDemuxerDataEmpty(true) ? inactive_demuxer_data_index() :
453 current_demuxer_data_index_;
454 return received_data_[index].access_units[access_unit_index_[index]];
455 }
456
457 bool MediaDecoderJob::IsDemuxerDataEmpty(bool current_chunk) const {
wolenetz 2014/03/18 21:14:47 nit: I'm not too sure this is the best name. Maybe
qinmin 2014/03/19 02:45:22 renamed to NoAccessUnitsRemainingInChunk. On 2014/
458 DCHECK(ui_task_runner_->BelongsToCurrentThread());
459 size_t index = current_chunk ? current_demuxer_data_index_ :
460 inactive_demuxer_data_index();
461 return received_data_[index].access_units.size() <= access_unit_index_[index];
462 }
463
464 void MediaDecoderJob::ClearData() {
465 DCHECK(ui_task_runner_->BelongsToCurrentThread());
466 current_demuxer_data_index_ = 0;
467 InitializeReceivedData();
468 on_data_received_cb_.Reset();
469 if (is_requesting_demuxer_data_)
470 is_incoming_data_invalid_ = true;
471 input_eos_encountered_ = false;
472 }
473
474 void MediaDecoderJob::RequestCurrentChunkIfEmpty() {
475 DCHECK(ui_task_runner_->BelongsToCurrentThread());
476 DCHECK(HasData());
477 if (!IsDemuxerDataEmpty(true))
478 return;
479
480 // Requests new data if the the last access unit of the next chunk is not EOS.
481 current_demuxer_data_index_ = inactive_demuxer_data_index();
482 const AccessUnit last_access_unit =
483 received_data_[current_demuxer_data_index_].access_units.back();
484 if (!last_access_unit.end_of_stream &&
485 last_access_unit.status != DemuxerStream::kConfigChanged &&
486 last_access_unit.status != DemuxerStream::kAborted) {
487 RequestData(base::Closure());
488 }
489 }
490
491 void MediaDecoderJob::InitializeReceivedData() {
492 for (size_t i = 0; i < 2; ++i) {
493 received_data_[i] = DemuxerData();
494 access_unit_index_[i] = 0;
495 }
496 }
497
431 } // namespace media 498 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698