| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/ffmpeg_demuxer.h" | 5 #include "media/filters/ffmpeg_demuxer.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <string> | 8 #include <string> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 245 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 256 | 256 |
| 257 // | 257 // |
| 258 // FFmpegDemuxer | 258 // FFmpegDemuxer |
| 259 // | 259 // |
| 260 FFmpegDemuxer::FFmpegDemuxer( | 260 FFmpegDemuxer::FFmpegDemuxer( |
| 261 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 261 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
| 262 const scoped_refptr<DataSource>& data_source) | 262 const scoped_refptr<DataSource>& data_source) |
| 263 : host_(NULL), | 263 : host_(NULL), |
| 264 message_loop_(message_loop), | 264 message_loop_(message_loop), |
| 265 data_source_(data_source), | 265 data_source_(data_source), |
| 266 read_event_(false, false), | |
| 267 read_has_failed_(false), | |
| 268 last_read_bytes_(0), | |
| 269 read_position_(0), | |
| 270 bitrate_(0), | 266 bitrate_(0), |
| 271 start_time_(kNoTimestamp()), | 267 start_time_(kNoTimestamp()), |
| 272 audio_disabled_(false), | 268 audio_disabled_(false), |
| 273 duration_known_(false) { | 269 duration_known_(false), |
| 270 url_protocol_(data_source, base::Bind( |
| 271 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this))) { |
| 274 DCHECK(message_loop_); | 272 DCHECK(message_loop_); |
| 275 DCHECK(data_source_); | 273 DCHECK(data_source_); |
| 276 } | 274 } |
| 277 | 275 |
| 278 FFmpegDemuxer::~FFmpegDemuxer() {} | 276 FFmpegDemuxer::~FFmpegDemuxer() {} |
| 279 | 277 |
| 280 void FFmpegDemuxer::PostDemuxTask() { | 278 void FFmpegDemuxer::PostDemuxTask() { |
| 281 message_loop_->PostTask(FROM_HERE, | 279 message_loop_->PostTask(FROM_HERE, |
| 282 base::Bind(&FFmpegDemuxer::DemuxTask, this)); | 280 base::Bind(&FFmpegDemuxer::DemuxTask, this)); |
| 283 } | 281 } |
| 284 | 282 |
| 285 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 283 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
| 286 // Post a task to notify the streams to stop as well. | 284 // Post a task to notify the streams to stop as well. |
| 287 message_loop_->PostTask(FROM_HERE, | 285 message_loop_->PostTask(FROM_HERE, |
| 288 base::Bind(&FFmpegDemuxer::StopTask, this, callback)); | 286 base::Bind(&FFmpegDemuxer::StopTask, this, callback)); |
| 289 | 287 |
| 290 // Then wakes up the thread from reading. | 288 // TODO(scherkus): This should be on |message_loop_| but today that thread is |
| 291 SignalReadCompleted(DataSource::kReadError); | 289 // potentially blocked. Move to StopTask() after all blocking calls are |
| 290 // guaranteed to run on a separate thread. |
| 291 url_protocol_.Abort(); |
| 292 } | 292 } |
| 293 | 293 |
| 294 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 294 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
| 295 message_loop_->PostTask(FROM_HERE, | 295 message_loop_->PostTask(FROM_HERE, |
| 296 base::Bind(&FFmpegDemuxer::SeekTask, this, time, cb)); | 296 base::Bind(&FFmpegDemuxer::SeekTask, this, time, cb)); |
| 297 } | 297 } |
| 298 | 298 |
| 299 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { | 299 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { |
| 300 DCHECK(data_source_.get()); | 300 DCHECK(data_source_.get()); |
| 301 data_source_->SetPlaybackRate(playback_rate); | 301 data_source_->SetPlaybackRate(playback_rate); |
| (...skipping 23 matching lines...) Expand all Loading... |
| 325 return *iter; | 325 return *iter; |
| 326 } | 326 } |
| 327 } | 327 } |
| 328 return NULL; | 328 return NULL; |
| 329 } | 329 } |
| 330 | 330 |
| 331 base::TimeDelta FFmpegDemuxer::GetStartTime() const { | 331 base::TimeDelta FFmpegDemuxer::GetStartTime() const { |
| 332 return start_time_; | 332 return start_time_; |
| 333 } | 333 } |
| 334 | 334 |
| 335 int FFmpegDemuxer::Read(int size, uint8* data) { | |
| 336 DCHECK(host_); | |
| 337 DCHECK(data_source_); | |
| 338 | |
| 339 // If read has ever failed, return with an error. | |
| 340 // TODO(hclam): use a more meaningful constant as error. | |
| 341 if (read_has_failed_) | |
| 342 return AVERROR(EIO); | |
| 343 | |
| 344 // Even though FFmpeg defines AVERROR_EOF, it's not to be used with I/O | |
| 345 // routines. Instead return 0 for any read at or past EOF. | |
| 346 int64 file_size; | |
| 347 if (data_source_->GetSize(&file_size) && read_position_ >= file_size) | |
| 348 return 0; | |
| 349 | |
| 350 // Asynchronous read from data source. | |
| 351 data_source_->Read(read_position_, size, data, base::Bind( | |
| 352 &FFmpegDemuxer::SignalReadCompleted, this)); | |
| 353 | |
| 354 // TODO(hclam): The method is called on the demuxer thread and this method | |
| 355 // call will block the thread. We need to implemented an additional thread to | |
| 356 // let FFmpeg demuxer methods to run on. | |
| 357 int last_read_bytes = WaitForRead(); | |
| 358 if (last_read_bytes == DataSource::kReadError) { | |
| 359 host_->OnDemuxerError(PIPELINE_ERROR_READ); | |
| 360 | |
| 361 // Returns with a negative number to signal an error to FFmpeg. | |
| 362 read_has_failed_ = true; | |
| 363 return AVERROR(EIO); | |
| 364 } | |
| 365 read_position_ += last_read_bytes; | |
| 366 | |
| 367 return last_read_bytes; | |
| 368 } | |
| 369 | |
| 370 bool FFmpegDemuxer::GetPosition(int64* position_out) { | |
| 371 DCHECK(host_); | |
| 372 *position_out = read_position_; | |
| 373 return true; | |
| 374 } | |
| 375 | |
| 376 bool FFmpegDemuxer::SetPosition(int64 position) { | |
| 377 DCHECK(host_); | |
| 378 DCHECK(data_source_); | |
| 379 | |
| 380 int64 file_size; | |
| 381 if ((data_source_->GetSize(&file_size) && position >= file_size) || | |
| 382 position < 0) { | |
| 383 return false; | |
| 384 } | |
| 385 | |
| 386 read_position_ = position; | |
| 387 return true; | |
| 388 } | |
| 389 | |
| 390 bool FFmpegDemuxer::GetSize(int64* size_out) { | |
| 391 DCHECK(host_); | |
| 392 DCHECK(data_source_); | |
| 393 return data_source_->GetSize(size_out); | |
| 394 } | |
| 395 | |
| 396 bool FFmpegDemuxer::IsStreaming() { | |
| 397 DCHECK(host_); | |
| 398 DCHECK(data_source_); | |
| 399 return data_source_->IsStreaming(); | |
| 400 } | |
| 401 | |
| 402 scoped_refptr<base::MessageLoopProxy> FFmpegDemuxer::message_loop() { | 335 scoped_refptr<base::MessageLoopProxy> FFmpegDemuxer::message_loop() { |
| 403 return message_loop_; | 336 return message_loop_; |
| 404 } | 337 } |
| 405 | 338 |
| 406 // Helper for calculating the bitrate of the media based on information stored | 339 // Helper for calculating the bitrate of the media based on information stored |
| 407 // in |format_context| or failing that the size and duration of the media. | 340 // in |format_context| or failing that the size and duration of the media. |
| 408 // | 341 // |
| 409 // Returns 0 if a bitrate could not be determined. | 342 // Returns 0 if a bitrate could not be determined. |
| 410 static int CalculateBitrate( | 343 static int CalculateBitrate( |
| 411 AVFormatContext* format_context, | 344 AVFormatContext* format_context, |
| (...skipping 29 matching lines...) Expand all Loading... |
| 441 | 374 |
| 442 void FFmpegDemuxer::InitializeTask(DemuxerHost* host, | 375 void FFmpegDemuxer::InitializeTask(DemuxerHost* host, |
| 443 const PipelineStatusCB& status_cb) { | 376 const PipelineStatusCB& status_cb) { |
| 444 DCHECK(message_loop_->BelongsToCurrentThread()); | 377 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 445 host_ = host; | 378 host_ = host; |
| 446 | 379 |
| 447 // TODO(scherkus): DataSource should have a host by this point, | 380 // TODO(scherkus): DataSource should have a host by this point, |
| 448 // see http://crbug.com/122071 | 381 // see http://crbug.com/122071 |
| 449 data_source_->set_host(host); | 382 data_source_->set_host(host); |
| 450 | 383 |
| 451 glue_.reset(new FFmpegGlue(this)); | 384 glue_.reset(new FFmpegGlue(&url_protocol_)); |
| 452 AVFormatContext* format_context = glue_->format_context(); | 385 AVFormatContext* format_context = glue_->format_context(); |
| 453 | 386 |
| 454 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | 387 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
| 455 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | 388 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
| 456 // available, so add a metadata entry to ensure some is always present. | 389 // available, so add a metadata entry to ensure some is always present. |
| 457 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | 390 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
| 458 | 391 |
| 459 if (!glue_->OpenContext()) { | 392 if (!glue_->OpenContext()) { |
| 460 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 393 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
| 461 return; | 394 return; |
| (...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 532 // assume the the start time is 0. | 465 // assume the the start time is 0. |
| 533 if (start_time_ == kNoTimestamp()) | 466 if (start_time_ == kNoTimestamp()) |
| 534 start_time_ = base::TimeDelta(); | 467 start_time_ = base::TimeDelta(); |
| 535 | 468 |
| 536 // Good to go: set the duration and bitrate and notify we're done | 469 // Good to go: set the duration and bitrate and notify we're done |
| 537 // initializing. | 470 // initializing. |
| 538 host_->SetDuration(max_duration); | 471 host_->SetDuration(max_duration); |
| 539 duration_known_ = (max_duration != kInfiniteDuration()); | 472 duration_known_ = (max_duration != kInfiniteDuration()); |
| 540 | 473 |
| 541 int64 filesize_in_bytes = 0; | 474 int64 filesize_in_bytes = 0; |
| 542 GetSize(&filesize_in_bytes); | 475 url_protocol_.GetSize(&filesize_in_bytes); |
| 543 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes); | 476 bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes); |
| 544 if (bitrate_ > 0) | 477 if (bitrate_ > 0) |
| 545 data_source_->SetBitrate(bitrate_); | 478 data_source_->SetBitrate(bitrate_); |
| 546 | 479 |
| 547 status_cb.Run(PIPELINE_OK); | 480 status_cb.Run(PIPELINE_OK); |
| 548 } | 481 } |
| 549 | 482 |
| 550 void FFmpegDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) { | 483 void FFmpegDemuxer::SeekTask(base::TimeDelta time, const PipelineStatusCB& cb) { |
| 551 DCHECK(message_loop_->BelongsToCurrentThread()); | 484 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 552 | 485 |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 676 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 609 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 677 if (!*iter || | 610 if (!*iter || |
| 678 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { | 611 (audio_disabled_ && (*iter)->type() == DemuxerStream::AUDIO)) { |
| 679 continue; | 612 continue; |
| 680 } | 613 } |
| 681 (*iter)->EnqueuePacket( | 614 (*iter)->EnqueuePacket( |
| 682 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket>()); | 615 scoped_ptr_malloc<AVPacket, ScopedPtrAVFreePacket>()); |
| 683 } | 616 } |
| 684 } | 617 } |
| 685 | 618 |
| 686 int FFmpegDemuxer::WaitForRead() { | |
| 687 read_event_.Wait(); | |
| 688 return last_read_bytes_; | |
| 689 } | |
| 690 | |
| 691 void FFmpegDemuxer::SignalReadCompleted(int size) { | |
| 692 last_read_bytes_ = size; | |
| 693 read_event_.Signal(); | |
| 694 } | |
| 695 | |
| 696 void FFmpegDemuxer::NotifyBufferingChanged() { | 619 void FFmpegDemuxer::NotifyBufferingChanged() { |
| 697 DCHECK(message_loop_->BelongsToCurrentThread()); | 620 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 698 Ranges<base::TimeDelta> buffered; | 621 Ranges<base::TimeDelta> buffered; |
| 699 scoped_refptr<FFmpegDemuxerStream> audio = | 622 scoped_refptr<FFmpegDemuxerStream> audio = |
| 700 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); | 623 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); |
| 701 scoped_refptr<FFmpegDemuxerStream> video = | 624 scoped_refptr<FFmpegDemuxerStream> video = |
| 702 GetFFmpegStream(DemuxerStream::VIDEO); | 625 GetFFmpegStream(DemuxerStream::VIDEO); |
| 703 if (audio && video) { | 626 if (audio && video) { |
| 704 buffered = audio->GetBufferedRanges().IntersectionWith( | 627 buffered = audio->GetBufferedRanges().IntersectionWith( |
| 705 video->GetBufferedRanges()); | 628 video->GetBufferedRanges()); |
| 706 } else if (audio) { | 629 } else if (audio) { |
| 707 buffered = audio->GetBufferedRanges(); | 630 buffered = audio->GetBufferedRanges(); |
| 708 } else if (video) { | 631 } else if (video) { |
| 709 buffered = video->GetBufferedRanges(); | 632 buffered = video->GetBufferedRanges(); |
| 710 } | 633 } |
| 711 for (size_t i = 0; i < buffered.size(); ++i) | 634 for (size_t i = 0; i < buffered.size(); ++i) |
| 712 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 635 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
| 713 } | 636 } |
| 714 | 637 |
| 638 void FFmpegDemuxer::OnDataSourceError() { |
| 639 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
| 640 } |
| 641 |
| 715 } // namespace media | 642 } // namespace media |
| OLD | NEW |