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