Chromium Code Reviews| 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; | 421 break; |
|
Ami GONE FROM CHROMIUM
2011/08/03 21:06:02
This break; is necessary?
acolwell GONE FROM CHROMIUM
2011/08/03 22:08:11
No. Done.
| |
| 416 } | 422 } |
| 417 | 423 |
| 418 seek_waits_for_data_ = false; | 424 seek_waits_for_data_ = false; |
| 419 | 425 |
| 420 base::TimeDelta tmp; | 426 base::TimeDelta tmp; |
| 421 if (audio_.get() && audio_->GetLastBufferTimestamp(&tmp) && | 427 if (audio_.get() && audio_->GetLastBufferTimestamp(&tmp) && |
| 422 tmp > buffered_ts) { | 428 tmp > buffered_ts) { |
| 423 buffered_ts = tmp; | 429 buffered_ts = tmp; |
| 424 } | 430 } |
| 425 | 431 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 447 | 453 |
| 448 if (!cb.is_null()) | 454 if (!cb.is_null()) |
| 449 cb.Run(PIPELINE_OK); | 455 cb.Run(PIPELINE_OK); |
| 450 | 456 |
| 451 return true; | 457 return true; |
| 452 } | 458 } |
| 453 | 459 |
| 454 void ChunkDemuxer::EndOfStream(PipelineStatus status) { | 460 void ChunkDemuxer::EndOfStream(PipelineStatus status) { |
| 455 VLOG(1) << "EndOfStream(" << status << ")"; | 461 VLOG(1) << "EndOfStream(" << status << ")"; |
| 456 base::AutoLock auto_lock(lock_); | 462 base::AutoLock auto_lock(lock_); |
| 457 DCHECK((state_ == INITIALIZING) || (state_ == INITIALIZED) || | 463 DCHECK_NE(state_, WAITING_FOR_INIT); |
| 458 (state_ == SHUTDOWN)); | 464 DCHECK_NE(state_, ENDED); |
| 459 | 465 |
| 460 if (state_ == SHUTDOWN) | 466 if (state_ == SHUTDOWN || state_ == PARSE_ERROR) |
| 461 return; | 467 return; |
| 462 | 468 |
| 463 if (state_ == INITIALIZING) { | 469 if (state_ == INITIALIZING) { |
| 464 InitFailed_Locked(); | 470 ReportError_Locked(DEMUXER_ERROR_COULD_NOT_OPEN); |
| 465 return; | 471 return; |
| 466 } | 472 } |
| 467 | 473 |
| 468 ChangeState(ENDED); | 474 ChangeState(ENDED); |
| 469 | 475 |
| 470 if (status != PIPELINE_OK) { | 476 if (status != PIPELINE_OK) { |
| 471 host()->SetError(status); | 477 ReportError_Locked(status); |
| 472 return; | 478 return; |
| 473 } | 479 } |
| 474 | 480 |
| 475 // Create an end of stream buffer. | 481 // Create an end of stream buffer. |
| 476 ChunkDemuxerStream::BufferQueue buffers; | 482 ChunkDemuxerStream::BufferQueue buffers; |
| 477 buffers.push_back(CreateEOSBuffer()); | 483 buffers.push_back(CreateEOSBuffer()); |
| 478 | 484 |
| 479 if (audio_.get()) | 485 if (audio_.get()) |
| 480 audio_->AddBuffers(buffers); | 486 audio_->AddBuffers(buffers); |
| 481 | 487 |
| 482 if (video_.get()) | 488 if (video_.get()) |
| 483 video_->AddBuffers(buffers); | 489 video_->AddBuffers(buffers); |
| 484 } | 490 } |
| 485 | 491 |
| 486 bool ChunkDemuxer::HasEnded() { | 492 bool ChunkDemuxer::HasEnded() { |
| 487 base::AutoLock auto_lock(lock_); | 493 base::AutoLock auto_lock(lock_); |
| 488 return (state_ == ENDED); | 494 return (state_ == ENDED); |
| 489 } | 495 } |
| 490 | 496 |
| 491 | |
| 492 void ChunkDemuxer::Shutdown() { | 497 void ChunkDemuxer::Shutdown() { |
| 493 VLOG(1) << "Shutdown()"; | 498 VLOG(1) << "Shutdown()"; |
| 494 FilterStatusCB cb; | 499 FilterStatusCB cb; |
| 495 { | 500 { |
| 496 base::AutoLock auto_lock(lock_); | 501 base::AutoLock auto_lock(lock_); |
| 497 | 502 |
| 498 if (state_ == SHUTDOWN) | 503 if (state_ == SHUTDOWN) |
| 499 return; | 504 return; |
| 500 | 505 |
| 501 std::swap(cb, seek_cb_); | 506 std::swap(cb, seek_cb_); |
| (...skipping 22 matching lines...) Expand all Loading... | |
| 524 DCHECK(data); | 529 DCHECK(data); |
| 525 DCHECK_GT(size, 0); | 530 DCHECK_GT(size, 0); |
| 526 | 531 |
| 527 DCHECK_EQ(state_, INITIALIZING); | 532 DCHECK_EQ(state_, INITIALIZING); |
| 528 | 533 |
| 529 const uint8* cur = data; | 534 const uint8* cur = data; |
| 530 int cur_size = size; | 535 int cur_size = size; |
| 531 WebMInfoParser info_parser; | 536 WebMInfoParser info_parser; |
| 532 int res = info_parser.Parse(cur, cur_size); | 537 int res = info_parser.Parse(cur, cur_size); |
| 533 | 538 |
| 534 if (res <= 0) { | 539 if (res <= 0) |
| 535 InitFailed_Locked(); | |
| 536 return false; | 540 return false; |
| 537 } | |
| 538 | 541 |
| 539 cur += res; | 542 cur += res; |
| 540 cur_size -= res; | 543 cur_size -= res; |
| 541 | 544 |
| 542 WebMTracksParser tracks_parser(info_parser.timecode_scale()); | 545 WebMTracksParser tracks_parser(info_parser.timecode_scale()); |
| 543 res = tracks_parser.Parse(cur, cur_size); | 546 res = tracks_parser.Parse(cur, cur_size); |
| 544 | 547 |
| 545 if (res <= 0) { | 548 if (res <= 0) |
| 546 InitFailed_Locked(); | |
| 547 return false; | 549 return false; |
| 548 } | |
| 549 | 550 |
| 550 double mult = info_parser.timecode_scale() / 1000.0; | 551 double mult = info_parser.timecode_scale() / 1000.0; |
| 551 duration_ = base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); | 552 duration_ = base::TimeDelta::FromMicroseconds(info_parser.duration() * mult); |
| 552 | 553 |
| 553 cluster_parser_.reset(new WebMClusterParser( | 554 cluster_parser_.reset(new WebMClusterParser( |
| 554 info_parser.timecode_scale(), | 555 info_parser.timecode_scale(), |
| 555 tracks_parser.audio_track_num(), | 556 tracks_parser.audio_track_num(), |
| 556 tracks_parser.audio_default_duration(), | 557 tracks_parser.audio_default_duration(), |
| 557 tracks_parser.video_track_num(), | 558 tracks_parser.video_track_num(), |
| 558 tracks_parser.video_default_duration())); | 559 tracks_parser.video_default_duration())); |
| 559 | 560 |
| 560 format_context_ = CreateFormatContext(data, size); | 561 format_context_ = CreateFormatContext(data, size); |
| 561 | 562 |
| 562 if (!format_context_ || !SetupStreams()) { | 563 if (!format_context_ || !SetupStreams()) { |
| 563 InitFailed_Locked(); | |
| 564 return false; | 564 return false; |
| 565 } | 565 } |
| 566 | 566 |
| 567 ChangeState(INITIALIZED); | 567 ChangeState(INITIALIZED); |
| 568 init_cb_.Run(PIPELINE_OK); | 568 init_cb_.Run(PIPELINE_OK); |
| 569 init_cb_.Reset(); | 569 init_cb_.Reset(); |
| 570 return true; | 570 return true; |
| 571 } | 571 } |
| 572 | 572 |
| 573 AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data, | 573 AVFormatContext* ChunkDemuxer::CreateFormatContext(const uint8* data, |
| (...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 670 cur_size -= res; | 670 cur_size -= res; |
| 671 } | 671 } |
| 672 | 672 |
| 673 // TODO(acolwell) : make this more representative of what is actually | 673 // TODO(acolwell) : make this more representative of what is actually |
| 674 // buffered. | 674 // buffered. |
| 675 buffered_bytes_ += length; | 675 buffered_bytes_ += length; |
| 676 | 676 |
| 677 return true; | 677 return true; |
| 678 } | 678 } |
| 679 | 679 |
| 680 void ChunkDemuxer::InitFailed_Locked() { | 680 void ChunkDemuxer::ReportError_Locked(PipelineStatus status) { |
|
Ami GONE FROM CHROMIUM
2011/08/03 21:06:02
FWIW, when you know status is not OK, I prefer nam
acolwell GONE FROM CHROMIUM
2011/08/03 22:08:11
Done.
| |
| 681 ChangeState(INIT_ERROR); | 681 DCHECK_NE(status, PIPELINE_OK); |
| 682 | |
| 683 ChangeState(PARSE_ERROR); | |
| 684 MessageLoop::current()->PostTask( | |
| 685 FROM_HERE, | |
| 686 NewRunnableMethod(this, &ChunkDemuxer::ReportErrorTask, status)); | |
| 687 } | |
| 688 | |
| 689 void ChunkDemuxer::ReportErrorTask(PipelineStatus status) { | |
| 682 PipelineStatusCB cb; | 690 PipelineStatusCB cb; |
| 683 std::swap(cb, init_cb_); | 691 { |
| 684 cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 692 base::AutoLock auto_lock(lock_); |
| 693 if (state_ != PARSE_ERROR) | |
|
Ami GONE FROM CHROMIUM
2011/08/03 21:06:02
When can this happen?
acolwell GONE FROM CHROMIUM
2011/08/03 22:08:11
This can happen if Stop() is called before this ta
Ami GONE FROM CHROMIUM
2011/08/03 23:41:35
Add a comment?
acolwell GONE FROM CHROMIUM
2011/08/04 00:22:16
No longer necessary now that we aren't posting a t
| |
| 694 return; | |
| 695 | |
| 696 if (!init_cb_.is_null()) { | |
| 697 std::swap(cb, init_cb_); | |
| 698 } else { | |
| 699 if (!seek_cb_.is_null()) | |
| 700 std::swap(cb, seek_cb_); | |
| 701 | |
| 702 if (audio_.get()) | |
| 703 audio_->Shutdown(); | |
| 704 | |
| 705 if (video_.get()) | |
| 706 video_->Shutdown(); | |
| 707 } | |
| 708 } | |
| 709 | |
| 710 if (cb.is_null()) { | |
| 711 host()->SetError(status); | |
| 712 return; | |
| 713 } | |
| 714 cb.Run(status); | |
|
Ami GONE FROM CHROMIUM
2011/08/03 21:06:02
Seems strange that in this case host()->SetError()
acolwell GONE FROM CHROMIUM
2011/08/03 22:08:11
SetError() is only used for error reporting when w
| |
| 685 } | 715 } |
| 686 | 716 |
| 687 } // namespace media | 717 } // namespace media |
| OLD | NEW |