OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 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 // Implements a Demuxer that can switch among different data sources mid-stream. | 5 // Implements a Demuxer that can switch among different data sources mid-stream. |
6 // Uses FFmpegDemuxer under the covers, so see the caveats at the top of | 6 // Uses FFmpegDemuxer under the covers, so see the caveats at the top of |
7 // ffmpeg_demuxer.h. | 7 // ffmpeg_demuxer.h. |
8 | 8 |
9 #include "media/filters/chunk_demuxer.h" | 9 #include "media/filters/chunk_demuxer.h" |
10 | 10 |
(...skipping 305 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
316 | 316 |
317 Shutdown(); | 317 Shutdown(); |
318 | 318 |
319 callback->Run(); | 319 callback->Run(); |
320 delete callback; | 320 delete callback; |
321 } | 321 } |
322 | 322 |
323 void ChunkDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { | 323 void ChunkDemuxer::Seek(base::TimeDelta time, const FilterStatusCB& cb) { |
324 VLOG(1) << "Seek(" << time.InSecondsF() << ")"; | 324 VLOG(1) << "Seek(" << time.InSecondsF() << ")"; |
325 | 325 |
| 326 PipelineStatus status = PIPELINE_ERROR_INVALID_STATE; |
326 { | 327 { |
327 base::AutoLock auto_lock(lock_); | 328 base::AutoLock auto_lock(lock_); |
328 | 329 |
329 if (seek_waits_for_data_) { | 330 if (state_ == INITIALIZED || state_ == ENDED) { |
330 VLOG(1) << "Seek() : waiting for more data to arrive."; | 331 if (seek_waits_for_data_) { |
331 seek_cb_ = cb; | 332 VLOG(1) << "Seek() : waiting for more data to arrive."; |
332 return; | 333 seek_cb_ = cb; |
| 334 return; |
| 335 } |
| 336 |
| 337 status = PIPELINE_OK; |
333 } | 338 } |
334 } | 339 } |
335 | 340 |
336 cb.Run(PIPELINE_OK); | 341 cb.Run(status); |
337 } | 342 } |
338 | 343 |
339 void ChunkDemuxer::OnAudioRendererDisabled() { | 344 void ChunkDemuxer::OnAudioRendererDisabled() { |
340 base::AutoLock auto_lock(lock_); | 345 base::AutoLock auto_lock(lock_); |
341 audio_ = NULL; | 346 audio_ = NULL; |
342 } | 347 } |
343 | 348 |
344 void ChunkDemuxer::SetPreload(Preload preload) {} | 349 void ChunkDemuxer::SetPreload(Preload preload) {} |
345 | 350 |
346 // Demuxer implementation. | 351 // Demuxer implementation. |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
387 int64 buffered_bytes = 0; | 392 int64 buffered_bytes = 0; |
388 base::TimeDelta buffered_ts = base::TimeDelta::FromSeconds(-1); | 393 base::TimeDelta buffered_ts = base::TimeDelta::FromSeconds(-1); |
389 | 394 |
390 FilterStatusCB cb; | 395 FilterStatusCB cb; |
391 { | 396 { |
392 base::AutoLock auto_lock(lock_); | 397 base::AutoLock auto_lock(lock_); |
393 switch(state_) { | 398 switch(state_) { |
394 case INITIALIZING: | 399 case INITIALIZING: |
395 if (!ParseInfoAndTracks_Locked(data, length)) { | 400 if (!ParseInfoAndTracks_Locked(data, length)) { |
396 VLOG(1) << "AppendData(): parsing info & tracks failed"; | 401 VLOG(1) << "AppendData(): parsing info & tracks failed"; |
397 return false; | 402 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
398 } | 403 } |
399 return true; | 404 return true; |
400 break; | 405 break; |
401 | 406 |
402 case INITIALIZED: | 407 case INITIALIZED: |
403 if (!ParseAndAppendData_Locked(data, length)) { | 408 if (!ParseAndAppendData_Locked(data, length)) { |
404 VLOG(1) << "AppendData(): parsing data failed"; | 409 VLOG(1) << "AppendData(): parsing data failed"; |
405 return false; | 410 ReportError_Locked(PIPELINE_ERROR_DECODE); |
| 411 return true; |
406 } | 412 } |
407 break; | 413 break; |
408 | 414 |
409 case WAITING_FOR_INIT: | 415 case WAITING_FOR_INIT: |
410 case ENDED: | 416 case ENDED: |
411 case INIT_ERROR: | 417 case PARSE_ERROR: |
412 case SHUTDOWN: | 418 case SHUTDOWN: |
413 VLOG(1) << "AppendData(): called in unexpected state " << state_; | 419 VLOG(1) << "AppendData(): called in unexpected state " << state_; |
414 return false; | 420 return false; |
415 break; | |
416 } | 421 } |
417 | 422 |
418 seek_waits_for_data_ = false; | 423 seek_waits_for_data_ = false; |
419 | 424 |
420 base::TimeDelta tmp; | 425 base::TimeDelta tmp; |
421 if (audio_.get() && audio_->GetLastBufferTimestamp(&tmp) && | 426 if (audio_.get() && audio_->GetLastBufferTimestamp(&tmp) && |
422 tmp > buffered_ts) { | 427 tmp > buffered_ts) { |
423 buffered_ts = tmp; | 428 buffered_ts = tmp; |
424 } | 429 } |
425 | 430 |
(...skipping 21 matching lines...) Expand all Loading... |
447 | 452 |
448 if (!cb.is_null()) | 453 if (!cb.is_null()) |
449 cb.Run(PIPELINE_OK); | 454 cb.Run(PIPELINE_OK); |
450 | 455 |
451 return true; | 456 return true; |
452 } | 457 } |
453 | 458 |
454 void ChunkDemuxer::EndOfStream(PipelineStatus status) { | 459 void ChunkDemuxer::EndOfStream(PipelineStatus status) { |
455 VLOG(1) << "EndOfStream(" << status << ")"; | 460 VLOG(1) << "EndOfStream(" << status << ")"; |
456 base::AutoLock auto_lock(lock_); | 461 base::AutoLock auto_lock(lock_); |
457 DCHECK((state_ == INITIALIZING) || (state_ == INITIALIZED) || | 462 DCHECK_NE(state_, WAITING_FOR_INIT); |
458 (state_ == SHUTDOWN)); | 463 DCHECK_NE(state_, ENDED); |
459 | 464 |
460 if (state_ == SHUTDOWN) | 465 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) |
461 return; | 466 return; |
462 | 467 |
463 if (state_ == INITIALIZING) { | 468 if (state_ == INITIALIZING) { |
464 InitFailed_Locked(); | 469 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
465 return; | 470 return; |
466 } | 471 } |
467 | 472 |
468 ChangeState(ENDED); | 473 ChangeState(ENDED); |
469 | 474 |
470 if (status != PIPELINE_OK) { | 475 if (status != PIPELINE_OK) { |
471 host()->SetError(status); | 476 ReportError_Locked(status); |
472 return; | 477 return; |
473 } | 478 } |
474 | 479 |
475 // Create an end of stream buffer. | 480 // Create an end of stream buffer. |
476 ChunkDemuxerStream::BufferQueue buffers; | 481 ChunkDemuxerStream::BufferQueue buffers; |
477 buffers.push_back(CreateEOSBuffer()); | 482 buffers.push_back(CreateEOSBuffer()); |
478 | 483 |
479 if (audio_.get()) | 484 if (audio_.get()) |
480 audio_->AddBuffers(buffers); | 485 audio_->AddBuffers(buffers); |
481 | 486 |
482 if (video_.get()) | 487 if (video_.get()) |
483 video_->AddBuffers(buffers); | 488 video_->AddBuffers(buffers); |
484 } | 489 } |
485 | 490 |
486 bool ChunkDemuxer::HasEnded() { | 491 bool ChunkDemuxer::HasEnded() { |
487 base::AutoLock auto_lock(lock_); | 492 base::AutoLock auto_lock(lock_); |
488 return (state_ == ENDED); | 493 return (state_ == ENDED); |
489 } | 494 } |
490 | 495 |
491 | |
492 void ChunkDemuxer::Shutdown() { | 496 void ChunkDemuxer::Shutdown() { |
493 VLOG(1) << "Shutdown()"; | 497 VLOG(1) << "Shutdown()"; |
494 FilterStatusCB cb; | 498 FilterStatusCB cb; |
495 { | 499 { |
496 base::AutoLock auto_lock(lock_); | 500 base::AutoLock auto_lock(lock_); |
497 | 501 |
498 if (state_ == SHUTDOWN) | 502 if (state_ == SHUTDOWN) |
499 return; | 503 return; |
500 | 504 |
501 std::swap(cb, seek_cb_); | 505 std::swap(cb, seek_cb_); |
(...skipping 22 matching lines...) Expand all Loading... |
524 DCHECK(data); | 528 DCHECK(data); |
525 DCHECK_GT(size, 0); | 529 DCHECK_GT(size, 0); |
526 | 530 |
527 DCHECK_EQ(state_, INITIALIZING); | 531 DCHECK_EQ(state_, INITIALIZING); |
528 | 532 |
529 const uint8* cur = data; | 533 const uint8* cur = data; |
530 int cur_size = size; | 534 int cur_size = size; |
531 WebMInfoParser info_parser; | 535 WebMInfoParser info_parser; |
532 int res = info_parser.Parse(cur, cur_size); | 536 int res = info_parser.Parse(cur, cur_size); |
533 | 537 |
534 if (res <= 0) { | 538 if (res <= 0) |
535 InitFailed_Locked(); | |
536 return false; | 539 return false; |
537 } | |
538 | 540 |
539 cur += res; | 541 cur += res; |
540 cur_size -= res; | 542 cur_size -= res; |
541 | 543 |
542 WebMTracksParser tracks_parser(info_parser.timecode_scale()); | 544 WebMTracksParser tracks_parser(info_parser.timecode_scale()); |
543 res = tracks_parser.Parse(cur, cur_size); | 545 res = tracks_parser.Parse(cur, cur_size); |
544 | 546 |
545 if (res <= 0) { | 547 if (res <= 0) |
546 InitFailed_Locked(); | |
547 return false; | 548 return false; |
548 } | |
549 | 549 |
550 double mult = info_parser.timecode_scale() / 1000.0; | 550 double mult = info_parser.timecode_scale() / 1000.0; |
551 duration_ = base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); | 551 duration_ = base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); |
552 | 552 |
553 cluster_parser_.reset(new WebMClusterParser( | 553 cluster_parser_.reset(new WebMClusterParser( |
554 info_parser.timecode_scale(), | 554 info_parser.timecode_scale(), |
555 tracks_parser.audio_track_num(), | 555 tracks_parser.audio_track_num(), |
556 tracks_parser.audio_default_duration(), | 556 tracks_parser.audio_default_duration(), |
557 tracks_parser.video_track_num(), | 557 tracks_parser.video_track_num(), |
558 tracks_parser.video_default_duration())); | 558 tracks_parser.video_default_duration())); |
559 | 559 |
560 format_context_ = CreateFormatContext(data, size); | 560 format_context_ = CreateFormatContext(data, size); |
561 | 561 |
562 if (!format_context_ || !SetupStreams()) { | 562 if (!format_context_ || !SetupStreams()) { |
563 InitFailed_Locked(); | |
564 return false; | 563 return false; |
565 } | 564 } |
566 | 565 |
567 ChangeState(INITIALIZED); | 566 ChangeState(INITIALIZED); |
568 init_cb_.Run(PIPELINE_OK); | 567 init_cb_.Run(PIPELINE_OK); |
569 init_cb_.Reset(); | 568 init_cb_.Reset(); |
570 return true; | 569 return true; |
571 } | 570 } |
572 | 571 |
573 AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data, | 572 AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data, |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
670 cur_size -= res; | 669 cur_size -= res; |
671 } | 670 } |
672 | 671 |
673 // TODO(acolwell) : make this more representative of what is actually | 672 // TODO(acolwell) : make this more representative of what is actually |
674 // buffered. | 673 // buffered. |
675 buffered_bytes_ += length; | 674 buffered_bytes_ += length; |
676 | 675 |
677 return true; | 676 return true; |
678 } | 677 } |
679 | 678 |
680 void ChunkDemuxer::InitFailed_Locked() { | 679 void ChunkDemuxer::ReportError_Locked(PipelineStatus error) { |
681 ChangeState(INIT_ERROR); | 680 DCHECK_NE(error, PIPELINE_OK); |
| 681 |
| 682 ChangeState(PARSE_ERROR); |
| 683 |
682 PipelineStatusCB cb; | 684 PipelineStatusCB cb; |
683 std::swap(cb, init_cb_); | 685 |
684 cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 686 if (!init_cb_.is_null()) { |
| 687 std::swap(cb, init_cb_); |
| 688 } else { |
| 689 if (!seek_cb_.is_null()) |
| 690 std::swap(cb, seek_cb_); |
| 691 |
| 692 if (audio_.get()) |
| 693 audio_->Shutdown(); |
| 694 |
| 695 if (video_.get()) |
| 696 video_->Shutdown(); |
| 697 } |
| 698 |
| 699 { |
| 700 base::AutoUnlock auto_unlock(lock_); |
| 701 if (cb.is_null()) { |
| 702 host()->SetError(error); |
| 703 return; |
| 704 } |
| 705 cb.Run(error); |
| 706 } |
685 } | 707 } |
686 | 708 |
687 } // namespace media | 709 } // namespace media |
OLD | NEW |