Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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/filters/decoder_stream.h" | 5 #include "media/filters/decoder_stream.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/location.h" | 10 #include "base/location.h" |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 43 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, | 43 const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
| 44 ScopedVector<Decoder> decoders, | 44 ScopedVector<Decoder> decoders, |
| 45 const SetDecryptorReadyCB& set_decryptor_ready_cb) | 45 const SetDecryptorReadyCB& set_decryptor_ready_cb) |
| 46 : task_runner_(task_runner), | 46 : task_runner_(task_runner), |
| 47 state_(STATE_UNINITIALIZED), | 47 state_(STATE_UNINITIALIZED), |
| 48 stream_(NULL), | 48 stream_(NULL), |
| 49 decoder_selector_( | 49 decoder_selector_( |
| 50 new DecoderSelector<StreamType>(task_runner, | 50 new DecoderSelector<StreamType>(task_runner, |
| 51 decoders.Pass(), | 51 decoders.Pass(), |
| 52 set_decryptor_ready_cb)), | 52 set_decryptor_ready_cb)), |
| 53 weak_factory_(this) {} | 53 pending_decode_requests_(0), |
| 54 weak_factory_(this) { | |
| 55 } | |
| 54 | 56 |
| 55 template <DemuxerStream::Type StreamType> | 57 template <DemuxerStream::Type StreamType> |
| 56 DecoderStream<StreamType>::~DecoderStream() { | 58 DecoderStream<StreamType>::~DecoderStream() { |
| 57 DCHECK(state_ == STATE_UNINITIALIZED || state_ == STATE_STOPPED) << state_; | 59 DCHECK(state_ == STATE_UNINITIALIZED || state_ == STATE_STOPPED) << state_; |
| 58 } | 60 } |
| 59 | 61 |
| 60 template <DemuxerStream::Type StreamType> | 62 template <DemuxerStream::Type StreamType> |
| 61 void DecoderStream<StreamType>::Initialize(DemuxerStream* stream, | 63 void DecoderStream<StreamType>::Initialize(DemuxerStream* stream, |
| 62 const StatisticsCB& statistics_cb, | 64 const StatisticsCB& statistics_cb, |
| 63 const InitCB& init_cb) { | 65 const InitCB& init_cb) { |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 77 stream, | 79 stream, |
| 78 base::Bind(&DecoderStream<StreamType>::OnDecoderSelected, | 80 base::Bind(&DecoderStream<StreamType>::OnDecoderSelected, |
| 79 weak_factory_.GetWeakPtr())); | 81 weak_factory_.GetWeakPtr())); |
| 80 } | 82 } |
| 81 | 83 |
| 82 template <DemuxerStream::Type StreamType> | 84 template <DemuxerStream::Type StreamType> |
| 83 void DecoderStream<StreamType>::Read(const ReadCB& read_cb) { | 85 void DecoderStream<StreamType>::Read(const ReadCB& read_cb) { |
| 84 FUNCTION_DVLOG(2); | 86 FUNCTION_DVLOG(2); |
| 85 DCHECK(task_runner_->BelongsToCurrentThread()); | 87 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 86 DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER || | 88 DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER || |
| 87 state_ == STATE_ERROR) << state_; | 89 state_ == STATE_ERROR || state_ == STATE_REINITIALIZING_DECODER) |
| 90 << state_; | |
| 88 // No two reads in the flight at any time. | 91 // No two reads in the flight at any time. |
| 89 DCHECK(read_cb_.is_null()); | 92 DCHECK(read_cb_.is_null()); |
| 90 // No read during resetting or stopping process. | 93 // No read during resetting or stopping process. |
| 91 DCHECK(reset_cb_.is_null()); | 94 DCHECK(reset_cb_.is_null()); |
| 92 DCHECK(stop_cb_.is_null()); | 95 DCHECK(stop_cb_.is_null()); |
| 93 | 96 |
| 94 if (state_ == STATE_ERROR) { | 97 if (state_ == STATE_ERROR) { |
| 95 task_runner_->PostTask(FROM_HERE, base::Bind( | 98 task_runner_->PostTask(FROM_HERE, base::Bind( |
| 96 read_cb, DECODE_ERROR, scoped_refptr<Output>())); | 99 read_cb, DECODE_ERROR, scoped_refptr<Output>())); |
| 97 return; | 100 return; |
| 98 } | 101 } |
| 99 | 102 |
| 100 read_cb_ = read_cb; | 103 read_cb_ = read_cb; |
| 101 | 104 |
| 105 if (!ready_output_buffers_.empty()) { | |
| 106 task_runner_->PostTask(FROM_HERE, base::Bind( | |
| 107 base::ResetAndReturn(&read_cb_), OK, ready_output_buffers_.front())); | |
| 108 ready_output_buffers_.pop_front(); | |
| 109 } | |
| 110 | |
| 111 if (state_ == STATE_REINITIALIZING_DECODER) | |
| 112 return; | |
| 113 | |
| 114 if (!CanDecodeAnotherBuffer()) | |
| 115 return; | |
| 116 | |
| 102 if (state_ == STATE_FLUSHING_DECODER) { | 117 if (state_ == STATE_FLUSHING_DECODER) { |
| 103 FlushDecoder(); | 118 FlushDecoder(); |
| 104 return; | 119 return; |
| 105 } | 120 } |
| 106 | 121 |
| 107 scoped_refptr<Output> output = decoder_->GetDecodeOutput(); | 122 if (state_ != STATE_PENDING_DEMUXER_READ) |
| 108 | 123 ReadFromDemuxerStream(); |
| 109 // If the decoder has queued output ready to go we don't need a demuxer read. | |
| 110 if (output) { | |
| 111 task_runner_->PostTask( | |
| 112 FROM_HERE, base::Bind(base::ResetAndReturn(&read_cb_), OK, output)); | |
| 113 return; | |
| 114 } | |
| 115 | |
| 116 ReadFromDemuxerStream(); | |
| 117 } | 124 } |
| 118 | 125 |
| 119 template <DemuxerStream::Type StreamType> | 126 template <DemuxerStream::Type StreamType> |
| 120 void DecoderStream<StreamType>::Reset(const base::Closure& closure) { | 127 void DecoderStream<StreamType>::Reset(const base::Closure& closure) { |
| 121 FUNCTION_DVLOG(2); | 128 FUNCTION_DVLOG(2); |
| 122 DCHECK(task_runner_->BelongsToCurrentThread()); | 129 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 123 DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_; | 130 DCHECK(state_ != STATE_UNINITIALIZED && state_ != STATE_STOPPED) << state_; |
| 124 DCHECK(reset_cb_.is_null()); | 131 DCHECK(reset_cb_.is_null()); |
| 125 DCHECK(stop_cb_.is_null()); | 132 DCHECK(stop_cb_.is_null()); |
| 126 | 133 |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 203 return decoder_->CanReadWithoutStalling(); | 210 return decoder_->CanReadWithoutStalling(); |
| 204 } | 211 } |
| 205 | 212 |
| 206 template <> | 213 template <> |
| 207 bool DecoderStream<DemuxerStream::AUDIO>::CanReadWithoutStalling() const { | 214 bool DecoderStream<DemuxerStream::AUDIO>::CanReadWithoutStalling() const { |
| 208 DCHECK(task_runner_->BelongsToCurrentThread()); | 215 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 209 return true; | 216 return true; |
| 210 } | 217 } |
| 211 | 218 |
| 212 template <DemuxerStream::Type StreamType> | 219 template <DemuxerStream::Type StreamType> |
| 220 bool DecoderStream<StreamType>::CanDecodeAnotherBuffer() const { | |
| 221 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 222 int buffers_in_queue = | |
| 223 static_cast<int>(ready_output_buffers_.size()) + pending_decode_requests_; | |
|
Ami GONE FROM CHROMIUM
2014/04/16 01:00:00
Why does it matter how many ready_output_buffers_
Sergey Ulanov
2014/04/16 01:44:14
We need to limit number of output buffers waiting
| |
| 224 return buffers_in_queue < decoder_->GetMaxDecodeRequests(); | |
| 225 } | |
| 226 | |
| 227 template <> | |
| 228 bool DecoderStream<DemuxerStream::AUDIO>::CanDecodeAnotherBuffer() const { | |
| 229 DCHECK(task_runner_->BelongsToCurrentThread()); | |
| 230 return !pending_decode_requests_ && ready_output_buffers_.empty(); | |
| 231 } | |
| 232 | |
| 233 template <DemuxerStream::Type StreamType> | |
| 213 void DecoderStream<StreamType>::OnDecoderSelected( | 234 void DecoderStream<StreamType>::OnDecoderSelected( |
| 214 scoped_ptr<Decoder> selected_decoder, | 235 scoped_ptr<Decoder> selected_decoder, |
| 215 scoped_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) { | 236 scoped_ptr<DecryptingDemuxerStream> decrypting_demuxer_stream) { |
| 216 FUNCTION_DVLOG(2); | 237 FUNCTION_DVLOG(2); |
| 217 DCHECK(task_runner_->BelongsToCurrentThread()); | 238 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 218 DCHECK_EQ(state_, STATE_INITIALIZING) << state_; | 239 DCHECK_EQ(state_, STATE_INITIALIZING) << state_; |
| 219 DCHECK(!init_cb_.is_null()); | 240 DCHECK(!init_cb_.is_null()); |
| 220 DCHECK(read_cb_.is_null()); | 241 DCHECK(read_cb_.is_null()); |
| 221 DCHECK(reset_cb_.is_null()); | 242 DCHECK(reset_cb_.is_null()); |
| 222 | 243 |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 260 // caller. | 281 // caller. |
| 261 DCHECK(!reset_cb_.is_null()); | 282 DCHECK(!reset_cb_.is_null()); |
| 262 SatisfyRead(ABORTED, NULL); | 283 SatisfyRead(ABORTED, NULL); |
| 263 } | 284 } |
| 264 | 285 |
| 265 template <DemuxerStream::Type StreamType> | 286 template <DemuxerStream::Type StreamType> |
| 266 void DecoderStream<StreamType>::Decode( | 287 void DecoderStream<StreamType>::Decode( |
| 267 const scoped_refptr<DecoderBuffer>& buffer) { | 288 const scoped_refptr<DecoderBuffer>& buffer) { |
| 268 FUNCTION_DVLOG(2); | 289 FUNCTION_DVLOG(2); |
| 269 DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; | 290 DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; |
| 270 DCHECK(!read_cb_.is_null()); | 291 DCHECK(CanDecodeAnotherBuffer()); |
| 271 DCHECK(reset_cb_.is_null()); | 292 DCHECK(reset_cb_.is_null()); |
| 272 DCHECK(stop_cb_.is_null()); | 293 DCHECK(stop_cb_.is_null()); |
| 273 DCHECK(buffer); | 294 DCHECK(buffer); |
| 274 | 295 |
| 275 int buffer_size = buffer->end_of_stream() ? 0 : buffer->data_size(); | 296 int buffer_size = buffer->end_of_stream() ? 0 : buffer->data_size(); |
| 276 | 297 |
| 277 TRACE_EVENT_ASYNC_BEGIN0("media", GetTraceString<StreamType>(), this); | 298 TRACE_EVENT_ASYNC_BEGIN0("media", GetTraceString<StreamType>(), this); |
| 299 ++pending_decode_requests_; | |
| 278 decoder_->Decode(buffer, | 300 decoder_->Decode(buffer, |
| 279 base::Bind(&DecoderStream<StreamType>::OnDecodeOutputReady, | 301 base::Bind(&DecoderStream<StreamType>::OnDecodeOutputReady, |
| 280 weak_factory_.GetWeakPtr(), | 302 weak_factory_.GetWeakPtr(), |
| 281 buffer_size)); | 303 buffer_size)); |
| 282 } | 304 } |
| 283 | 305 |
| 284 template <DemuxerStream::Type StreamType> | 306 template <DemuxerStream::Type StreamType> |
| 285 void DecoderStream<StreamType>::FlushDecoder() { | 307 void DecoderStream<StreamType>::FlushDecoder() { |
| 286 Decode(DecoderBuffer::CreateEOSBuffer()); | 308 Decode(DecoderBuffer::CreateEOSBuffer()); |
| 287 } | 309 } |
| 288 | 310 |
| 289 template <DemuxerStream::Type StreamType> | 311 template <DemuxerStream::Type StreamType> |
| 290 void DecoderStream<StreamType>::OnDecodeOutputReady( | 312 void DecoderStream<StreamType>::OnDecodeOutputReady( |
| 291 int buffer_size, | 313 int buffer_size, |
| 292 typename Decoder::Status status, | 314 typename Decoder::Status status, |
| 293 const scoped_refptr<Output>& output) { | 315 const scoped_refptr<Output>& output) { |
| 294 FUNCTION_DVLOG(2); | 316 FUNCTION_DVLOG(2); |
| 295 DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; | 317 DCHECK(state_ == STATE_NORMAL || state_ == STATE_FLUSHING_DECODER) << state_; |
| 296 DCHECK(!read_cb_.is_null()); | |
| 297 DCHECK(stop_cb_.is_null()); | 318 DCHECK(stop_cb_.is_null()); |
| 298 DCHECK_EQ(status == Decoder::kOk, output != NULL); | 319 DCHECK_EQ(status == Decoder::kOk, output != NULL); |
| 320 DCHECK_GT(pending_decode_requests_, 0); | |
| 321 | |
| 322 --pending_decode_requests_; | |
| 299 | 323 |
| 300 TRACE_EVENT_ASYNC_END0("media", GetTraceString<StreamType>(), this); | 324 TRACE_EVENT_ASYNC_END0("media", GetTraceString<StreamType>(), this); |
| 301 | 325 |
| 302 if (status == Decoder::kDecodeError) { | 326 if (status == Decoder::kDecodeError) { |
| 303 state_ = STATE_ERROR; | 327 state_ = STATE_ERROR; |
| 304 SatisfyRead(DECODE_ERROR, NULL); | 328 SatisfyRead(DECODE_ERROR, NULL); |
| 305 return; | 329 return; |
| 306 } | 330 } |
| 307 | 331 |
| 308 if (status == Decoder::kDecryptError) { | 332 if (status == Decoder::kDecryptError) { |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 337 | 361 |
| 338 if (status == Decoder::kNotEnoughData) { | 362 if (status == Decoder::kNotEnoughData) { |
| 339 if (state_ == STATE_NORMAL) | 363 if (state_ == STATE_NORMAL) |
| 340 ReadFromDemuxerStream(); | 364 ReadFromDemuxerStream(); |
| 341 else if (state_ == STATE_FLUSHING_DECODER) | 365 else if (state_ == STATE_FLUSHING_DECODER) |
| 342 FlushDecoder(); | 366 FlushDecoder(); |
| 343 return; | 367 return; |
| 344 } | 368 } |
| 345 | 369 |
| 346 DCHECK(output); | 370 DCHECK(output); |
| 347 SatisfyRead(OK, output); | 371 |
| 372 // Store decoded output. | |
| 373 ready_output_buffers_.push_back(output); | |
| 374 scoped_refptr<Output> extra_output; | |
| 375 while ((extra_output = decoder_->GetDecodeOutput()) != NULL) { | |
| 376 ready_output_buffers_.push_back(extra_output); | |
| 377 } | |
| 378 | |
| 379 // Satisfy outstanding read request, if any. | |
| 380 if (!read_cb_.is_null()) { | |
| 381 scoped_refptr<Output> read_result = ready_output_buffers_.front(); | |
| 382 ready_output_buffers_.pop_front(); | |
| 383 SatisfyRead(OK, output); | |
| 384 } | |
| 348 } | 385 } |
| 349 | 386 |
| 350 template <DemuxerStream::Type StreamType> | 387 template <DemuxerStream::Type StreamType> |
| 351 void DecoderStream<StreamType>::ReadFromDemuxerStream() { | 388 void DecoderStream<StreamType>::ReadFromDemuxerStream() { |
| 352 FUNCTION_DVLOG(2); | 389 FUNCTION_DVLOG(2); |
| 353 DCHECK_EQ(state_, STATE_NORMAL) << state_; | 390 DCHECK_EQ(state_, STATE_NORMAL) << state_; |
| 354 DCHECK(!read_cb_.is_null()); | 391 DCHECK(CanDecodeAnotherBuffer()); |
| 355 DCHECK(reset_cb_.is_null()); | 392 DCHECK(reset_cb_.is_null()); |
| 356 DCHECK(stop_cb_.is_null()); | 393 DCHECK(stop_cb_.is_null()); |
| 357 | 394 |
| 358 state_ = STATE_PENDING_DEMUXER_READ; | 395 state_ = STATE_PENDING_DEMUXER_READ; |
| 359 stream_->Read(base::Bind(&DecoderStream<StreamType>::OnBufferReady, | 396 stream_->Read(base::Bind(&DecoderStream<StreamType>::OnBufferReady, |
| 360 weak_factory_.GetWeakPtr())); | 397 weak_factory_.GetWeakPtr())); |
| 361 } | 398 } |
| 362 | 399 |
| 363 template <DemuxerStream::Type StreamType> | 400 template <DemuxerStream::Type StreamType> |
| 364 void DecoderStream<StreamType>::OnBufferReady( | 401 void DecoderStream<StreamType>::OnBufferReady( |
| 365 DemuxerStream::Status status, | 402 DemuxerStream::Status status, |
| 366 const scoped_refptr<DecoderBuffer>& buffer) { | 403 const scoped_refptr<DecoderBuffer>& buffer) { |
| 367 FUNCTION_DVLOG(2) << ": " << status; | 404 FUNCTION_DVLOG(2) << ": " << status; |
| 368 DCHECK(task_runner_->BelongsToCurrentThread()); | 405 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 369 DCHECK_EQ(state_, STATE_PENDING_DEMUXER_READ) << state_; | 406 DCHECK_EQ(state_, STATE_PENDING_DEMUXER_READ) << state_; |
| 370 DCHECK_EQ(buffer.get() != NULL, status == DemuxerStream::kOk) << status; | 407 DCHECK_EQ(buffer.get() != NULL, status == DemuxerStream::kOk) << status; |
| 371 DCHECK(!read_cb_.is_null()); | |
| 372 DCHECK(stop_cb_.is_null()); | 408 DCHECK(stop_cb_.is_null()); |
| 373 | 409 |
| 374 state_ = STATE_NORMAL; | 410 state_ = STATE_NORMAL; |
| 375 | 411 |
| 376 if (status == DemuxerStream::kConfigChanged) { | 412 if (status == DemuxerStream::kConfigChanged) { |
| 377 FUNCTION_DVLOG(2) << ": " << "ConfigChanged"; | 413 FUNCTION_DVLOG(2) << ": " << "ConfigChanged"; |
| 378 DCHECK(stream_->SupportsConfigChanges()); | 414 DCHECK(stream_->SupportsConfigChanges()); |
| 379 | 415 |
| 380 if (!config_change_observer_cb_.is_null()) | 416 if (!config_change_observer_cb_.is_null()) |
| 381 config_change_observer_cb_.Run(); | 417 config_change_observer_cb_.Run(); |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 408 return; | 444 return; |
| 409 } | 445 } |
| 410 | 446 |
| 411 if (!splice_observer_cb_.is_null() && !buffer->end_of_stream() && | 447 if (!splice_observer_cb_.is_null() && !buffer->end_of_stream() && |
| 412 buffer->splice_timestamp() != kNoTimestamp()) { | 448 buffer->splice_timestamp() != kNoTimestamp()) { |
| 413 splice_observer_cb_.Run(buffer->splice_timestamp()); | 449 splice_observer_cb_.Run(buffer->splice_timestamp()); |
| 414 } | 450 } |
| 415 | 451 |
| 416 DCHECK(status == DemuxerStream::kOk) << status; | 452 DCHECK(status == DemuxerStream::kOk) << status; |
| 417 Decode(buffer); | 453 Decode(buffer); |
| 454 | |
| 455 // Read more data if the decoder supports multiple parallel decoding requests. | |
| 456 if (state_ == STATE_NORMAL && CanDecodeAnotherBuffer()) { | |
| 457 ReadFromDemuxerStream(); | |
| 458 } | |
| 418 } | 459 } |
| 419 | 460 |
| 420 template <DemuxerStream::Type StreamType> | 461 template <DemuxerStream::Type StreamType> |
| 421 void DecoderStream<StreamType>::ReinitializeDecoder() { | 462 void DecoderStream<StreamType>::ReinitializeDecoder() { |
| 422 FUNCTION_DVLOG(2); | 463 FUNCTION_DVLOG(2); |
| 423 DCHECK(task_runner_->BelongsToCurrentThread()); | 464 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 424 DCHECK_EQ(state_, STATE_FLUSHING_DECODER) << state_; | 465 DCHECK_EQ(state_, STATE_FLUSHING_DECODER) << state_; |
| 466 DCHECK_EQ(pending_decode_requests_, 0); | |
| 425 | 467 |
| 426 DCHECK(StreamTraits::GetDecoderConfig(*stream_).IsValidConfig()); | 468 DCHECK(StreamTraits::GetDecoderConfig(*stream_).IsValidConfig()); |
| 427 state_ = STATE_REINITIALIZING_DECODER; | 469 state_ = STATE_REINITIALIZING_DECODER; |
| 428 decoder_->Initialize( | 470 decoder_->Initialize( |
| 429 StreamTraits::GetDecoderConfig(*stream_), | 471 StreamTraits::GetDecoderConfig(*stream_), |
| 430 base::Bind(&DecoderStream<StreamType>::OnDecoderReinitialized, | 472 base::Bind(&DecoderStream<StreamType>::OnDecoderReinitialized, |
| 431 weak_factory_.GetWeakPtr())); | 473 weak_factory_.GetWeakPtr())); |
| 432 } | 474 } |
| 433 | 475 |
| 434 template <DemuxerStream::Type StreamType> | 476 template <DemuxerStream::Type StreamType> |
| (...skipping 75 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 510 decrypting_demuxer_stream_.reset(); | 552 decrypting_demuxer_stream_.reset(); |
| 511 // Post |stop_cb_| because pending |read_cb_| and/or |reset_cb_| are also | 553 // Post |stop_cb_| because pending |read_cb_| and/or |reset_cb_| are also |
| 512 // posted in Stop(). | 554 // posted in Stop(). |
| 513 task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_)); | 555 task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_)); |
| 514 } | 556 } |
| 515 | 557 |
| 516 template class DecoderStream<DemuxerStream::VIDEO>; | 558 template class DecoderStream<DemuxerStream::VIDEO>; |
| 517 template class DecoderStream<DemuxerStream::AUDIO>; | 559 template class DecoderStream<DemuxerStream::AUDIO>; |
| 518 | 560 |
| 519 } // namespace media | 561 } // namespace media |
| OLD | NEW |