Chromium Code Reviews| 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/base64.h" | 10 #include "base/base64.h" |
| (...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 265 | 265 |
| 266 // | 266 // |
| 267 // FFmpegDemuxer | 267 // FFmpegDemuxer |
| 268 // | 268 // |
| 269 FFmpegDemuxer::FFmpegDemuxer( | 269 FFmpegDemuxer::FFmpegDemuxer( |
| 270 const scoped_refptr<base::MessageLoopProxy>& message_loop, | 270 const scoped_refptr<base::MessageLoopProxy>& message_loop, |
| 271 const scoped_refptr<DataSource>& data_source, | 271 const scoped_refptr<DataSource>& data_source, |
| 272 const FFmpegNeedKeyCB& need_key_cb) | 272 const FFmpegNeedKeyCB& need_key_cb) |
| 273 : host_(NULL), | 273 : host_(NULL), |
| 274 message_loop_(message_loop), | 274 message_loop_(message_loop), |
| 275 weak_factory_(this), | |
| 275 blocking_thread_("FFmpegDemuxer"), | 276 blocking_thread_("FFmpegDemuxer"), |
| 276 pending_read_(false), | 277 pending_read_(false), |
| 277 pending_seek_(false), | 278 pending_seek_(false), |
| 278 data_source_(data_source), | 279 data_source_(data_source), |
| 279 bitrate_(0), | 280 bitrate_(0), |
| 280 start_time_(kNoTimestamp()), | 281 start_time_(kNoTimestamp()), |
| 281 audio_disabled_(false), | 282 audio_disabled_(false), |
| 282 duration_known_(false), | 283 duration_known_(false), |
| 283 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( | 284 url_protocol_(data_source, BindToLoop(message_loop_, base::Bind( |
| 284 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), | 285 &FFmpegDemuxer::OnDataSourceError, base::Unretained(this)))), |
| 285 need_key_cb_(need_key_cb) { | 286 need_key_cb_(need_key_cb) { |
| 286 DCHECK(message_loop_); | 287 DCHECK(message_loop_); |
| 287 DCHECK(data_source_); | 288 DCHECK(data_source_); |
| 288 } | 289 } |
| 289 | 290 |
| 290 FFmpegDemuxer::~FFmpegDemuxer() {} | 291 FFmpegDemuxer::~FFmpegDemuxer() {} |
| 291 | 292 |
| 292 void FFmpegDemuxer::Stop(const base::Closure& callback) { | 293 void FFmpegDemuxer::Stop(const base::Closure& callback) { |
| 293 DCHECK(message_loop_->BelongsToCurrentThread()); | 294 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 294 url_protocol_.Abort(); | 295 url_protocol_.Abort(); |
| 295 data_source_->Stop(BindToCurrentLoop(base::Bind( | 296 data_source_->Stop(BindToCurrentLoop(base::Bind( |
| 296 &FFmpegDemuxer::OnDataSourceStopped, this, BindToCurrentLoop(callback)))); | 297 &FFmpegDemuxer::OnDataSourceStopped, weak_this_, |
| 298 BindToCurrentLoop(callback)))); | |
| 297 } | 299 } |
| 298 | 300 |
| 299 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 301 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
| 300 DCHECK(message_loop_->BelongsToCurrentThread()); | 302 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 301 CHECK(!pending_seek_); | 303 CHECK(!pending_seek_); |
| 302 | 304 |
| 303 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 305 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
| 304 // otherwise we can end up waiting for a pre-seek read to complete even though | 306 // otherwise we can end up waiting for a pre-seek read to complete even though |
| 305 // we know we're going to drop it on the floor. | 307 // we know we're going to drop it on the floor. |
| 306 | 308 |
| 307 // Always seek to a timestamp less than or equal to the desired timestamp. | 309 // Always seek to a timestamp less than or equal to the desired timestamp. |
| 308 int flags = AVSEEK_FLAG_BACKWARD; | 310 int flags = AVSEEK_FLAG_BACKWARD; |
| 309 | 311 |
| 310 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg | 312 // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
| 311 // will attempt to use the lowest-index video stream, if present, followed by | 313 // will attempt to use the lowest-index video stream, if present, followed by |
| 312 // the lowest-index audio stream. | 314 // the lowest-index audio stream. |
| 313 pending_seek_ = true; | 315 pending_seek_ = true; |
| 314 base::PostTaskAndReplyWithResult( | 316 base::PostTaskAndReplyWithResult( |
| 315 blocking_thread_.message_loop_proxy(), FROM_HERE, | 317 blocking_thread_.message_loop_proxy(), FROM_HERE, |
| 316 base::Bind(&av_seek_frame, glue_->format_context(), -1, | 318 base::Bind(&av_seek_frame, glue_->format_context(), -1, |
| 317 time.InMicroseconds(), flags), | 319 time.InMicroseconds(), flags), |
| 318 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, this, cb)); | 320 base::Bind(&FFmpegDemuxer::OnSeekFrameDone, weak_this_, cb)); |
| 319 } | 321 } |
| 320 | 322 |
| 321 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { | 323 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { |
| 322 DCHECK(message_loop_->BelongsToCurrentThread()); | 324 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 323 data_source_->SetPlaybackRate(playback_rate); | 325 data_source_->SetPlaybackRate(playback_rate); |
| 324 } | 326 } |
| 325 | 327 |
| 326 void FFmpegDemuxer::OnAudioRendererDisabled() { | 328 void FFmpegDemuxer::OnAudioRendererDisabled() { |
| 327 DCHECK(message_loop_->BelongsToCurrentThread()); | 329 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 328 audio_disabled_ = true; | 330 audio_disabled_ = true; |
| 329 StreamVector::iterator iter; | 331 StreamVector::iterator iter; |
| 330 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 332 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 331 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { | 333 if (*iter && (*iter)->type() == DemuxerStream::AUDIO) { |
| 332 (*iter)->Stop(); | 334 (*iter)->Stop(); |
| 333 } | 335 } |
| 334 } | 336 } |
| 335 } | 337 } |
| 336 | 338 |
| 337 void FFmpegDemuxer::Initialize(DemuxerHost* host, | 339 void FFmpegDemuxer::Initialize(DemuxerHost* host, |
| 338 const PipelineStatusCB& status_cb) { | 340 const PipelineStatusCB& status_cb) { |
| 339 DCHECK(message_loop_->BelongsToCurrentThread()); | 341 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 340 host_ = host; | 342 host_ = host; |
| 343 weak_this_ = weak_factory_.GetWeakPtr(); | |
|
acolwell GONE FROM CHROMIUM
2013/04/17 20:24:53
nit: Why is this deferred until here? Why not do t
scherkus (not reviewing)
2013/04/19 01:07:22
WeakPtr binds to thread it's created on.
Since we
| |
| 341 | 344 |
| 342 // TODO(scherkus): DataSource should have a host by this point, | 345 // TODO(scherkus): DataSource should have a host by this point, |
| 343 // see http://crbug.com/122071 | 346 // see http://crbug.com/122071 |
| 344 data_source_->set_host(host); | 347 data_source_->set_host(host); |
| 345 | 348 |
| 346 glue_.reset(new FFmpegGlue(&url_protocol_)); | 349 glue_.reset(new FFmpegGlue(&url_protocol_)); |
| 347 AVFormatContext* format_context = glue_->format_context(); | 350 AVFormatContext* format_context = glue_->format_context(); |
| 348 | 351 |
| 349 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we | 352 // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we |
| 350 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is | 353 // don't use. FFmpeg will only read ID3v1 tags if no other metadata is |
| 351 // available, so add a metadata entry to ensure some is always present. | 354 // available, so add a metadata entry to ensure some is always present. |
| 352 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); | 355 av_dict_set(&format_context->metadata, "skip_id3v1_tags", "", 0); |
| 353 | 356 |
| 354 // Open the AVFormatContext using our glue layer. | 357 // Open the AVFormatContext using our glue layer. |
| 355 CHECK(blocking_thread_.Start()); | 358 CHECK(blocking_thread_.Start()); |
| 356 base::PostTaskAndReplyWithResult( | 359 base::PostTaskAndReplyWithResult( |
| 357 blocking_thread_.message_loop_proxy(), FROM_HERE, | 360 blocking_thread_.message_loop_proxy(), FROM_HERE, |
| 358 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), | 361 base::Bind(&FFmpegGlue::OpenContext, base::Unretained(glue_.get())), |
| 359 base::Bind(&FFmpegDemuxer::OnOpenContextDone, this, status_cb)); | 362 base::Bind(&FFmpegDemuxer::OnOpenContextDone, weak_this_, status_cb)); |
| 360 } | 363 } |
| 361 | 364 |
| 362 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( | 365 DemuxerStream* FFmpegDemuxer::GetStream(DemuxerStream::Type type) { |
| 363 DemuxerStream::Type type) { | |
| 364 DCHECK(message_loop_->BelongsToCurrentThread()); | 366 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 365 return GetFFmpegStream(type); | 367 return GetFFmpegStream(type); |
| 366 } | 368 } |
| 367 | 369 |
| 368 scoped_refptr<FFmpegDemuxerStream> FFmpegDemuxer::GetFFmpegStream( | 370 FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream( |
| 369 DemuxerStream::Type type) const { | 371 DemuxerStream::Type type) const { |
| 370 StreamVector::const_iterator iter; | 372 StreamVector::const_iterator iter; |
| 371 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 373 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
| 372 if (*iter && (*iter)->type() == type) { | 374 if (*iter && (*iter)->type() == type) { |
| 373 return *iter; | 375 return *iter; |
| 374 } | 376 } |
| 375 } | 377 } |
| 376 return NULL; | 378 return NULL; |
| 377 } | 379 } |
| 378 | 380 |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 428 if (!result) { | 430 if (!result) { |
| 429 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); | 431 status_cb.Run(DEMUXER_ERROR_COULD_NOT_OPEN); |
| 430 return; | 432 return; |
| 431 } | 433 } |
| 432 | 434 |
| 433 // Fully initialize AVFormatContext by parsing the stream a little. | 435 // Fully initialize AVFormatContext by parsing the stream a little. |
| 434 base::PostTaskAndReplyWithResult( | 436 base::PostTaskAndReplyWithResult( |
| 435 blocking_thread_.message_loop_proxy(), FROM_HERE, | 437 blocking_thread_.message_loop_proxy(), FROM_HERE, |
| 436 base::Bind(&avformat_find_stream_info, glue_->format_context(), | 438 base::Bind(&avformat_find_stream_info, glue_->format_context(), |
| 437 static_cast<AVDictionary**>(NULL)), | 439 static_cast<AVDictionary**>(NULL)), |
| 438 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, this, status_cb)); | 440 base::Bind(&FFmpegDemuxer::OnFindStreamInfoDone, weak_this_, status_cb)); |
| 439 } | 441 } |
| 440 | 442 |
| 441 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, | 443 void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, |
| 442 int result) { | 444 int result) { |
| 443 DCHECK(message_loop_->BelongsToCurrentThread()); | 445 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 444 if (!blocking_thread_.IsRunning()) { | 446 if (!blocking_thread_.IsRunning()) { |
| 445 status_cb.Run(PIPELINE_ERROR_ABORT); | 447 status_cb.Run(PIPELINE_ERROR_ABORT); |
| 446 return; | 448 return; |
| 447 } | 449 } |
| 448 | 450 |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 474 continue; | 476 continue; |
| 475 // Ensure the codec is supported. | 477 // Ensure the codec is supported. |
| 476 if (CodecIDToVideoCodec(codec_context->codec_id) == kUnknownVideoCodec) | 478 if (CodecIDToVideoCodec(codec_context->codec_id) == kUnknownVideoCodec) |
| 477 continue; | 479 continue; |
| 478 found_video_stream = true; | 480 found_video_stream = true; |
| 479 } else { | 481 } else { |
| 480 continue; | 482 continue; |
| 481 } | 483 } |
| 482 | 484 |
| 483 AVStream* stream = format_context->streams[i]; | 485 AVStream* stream = format_context->streams[i]; |
| 484 scoped_refptr<FFmpegDemuxerStream> demuxer_stream( | 486 streams_[i] = new FFmpegDemuxerStream(this, stream); |
| 485 new FFmpegDemuxerStream(this, stream)); | 487 max_duration = std::max(max_duration, streams_[i]->duration()); |
| 486 | |
| 487 streams_[i] = demuxer_stream; | |
| 488 max_duration = std::max(max_duration, demuxer_stream->duration()); | |
| 489 | 488 |
| 490 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 489 if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 491 const base::TimeDelta first_dts = ConvertFromTimeBase( | 490 const base::TimeDelta first_dts = ConvertFromTimeBase( |
| 492 stream->time_base, stream->first_dts); | 491 stream->time_base, stream->first_dts); |
| 493 if (start_time_ == kNoTimestamp() || first_dts < start_time_) | 492 if (start_time_ == kNoTimestamp() || first_dts < start_time_) |
| 494 start_time_ = first_dts; | 493 start_time_ = first_dts; |
| 495 } | 494 } |
| 496 } | 495 } |
| 497 | 496 |
| 498 if (!found_audio_stream && !found_video_stream) { | 497 if (!found_audio_stream && !found_video_stream) { |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 579 // Allocate and read an AVPacket from the media. Save |packet_ptr| since | 578 // Allocate and read an AVPacket from the media. Save |packet_ptr| since |
| 580 // evaluation order of packet.get() and base::Passed(&packet) is | 579 // evaluation order of packet.get() and base::Passed(&packet) is |
| 581 // undefined. | 580 // undefined. |
| 582 ScopedAVPacket packet(new AVPacket()); | 581 ScopedAVPacket packet(new AVPacket()); |
| 583 AVPacket* packet_ptr = packet.get(); | 582 AVPacket* packet_ptr = packet.get(); |
| 584 | 583 |
| 585 pending_read_ = true; | 584 pending_read_ = true; |
| 586 base::PostTaskAndReplyWithResult( | 585 base::PostTaskAndReplyWithResult( |
| 587 blocking_thread_.message_loop_proxy(), FROM_HERE, | 586 blocking_thread_.message_loop_proxy(), FROM_HERE, |
| 588 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), | 587 base::Bind(&av_read_frame, glue_->format_context(), packet_ptr), |
| 589 base::Bind(&FFmpegDemuxer::OnReadFrameDone, this, base::Passed(&packet))); | 588 base::Bind(&FFmpegDemuxer::OnReadFrameDone, weak_this_, |
| 589 base::Passed(&packet))); | |
| 590 } | 590 } |
| 591 | 591 |
| 592 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { | 592 void FFmpegDemuxer::OnReadFrameDone(ScopedAVPacket packet, int result) { |
| 593 DCHECK(message_loop_->BelongsToCurrentThread()); | 593 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 594 DCHECK(pending_read_); | 594 DCHECK(pending_read_); |
| 595 pending_read_ = false; | 595 pending_read_ = false; |
| 596 | 596 |
| 597 if (!blocking_thread_.IsRunning() || pending_seek_) { | 597 if (!blocking_thread_.IsRunning() || pending_seek_) { |
| 598 return; | 598 return; |
| 599 } | 599 } |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 709 } | 709 } |
| 710 | 710 |
| 711 void FFmpegDemuxer::NotifyCapacityAvailable() { | 711 void FFmpegDemuxer::NotifyCapacityAvailable() { |
| 712 DCHECK(message_loop_->BelongsToCurrentThread()); | 712 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 713 ReadFrameIfNeeded(); | 713 ReadFrameIfNeeded(); |
| 714 } | 714 } |
| 715 | 715 |
| 716 void FFmpegDemuxer::NotifyBufferingChanged() { | 716 void FFmpegDemuxer::NotifyBufferingChanged() { |
| 717 DCHECK(message_loop_->BelongsToCurrentThread()); | 717 DCHECK(message_loop_->BelongsToCurrentThread()); |
| 718 Ranges<base::TimeDelta> buffered; | 718 Ranges<base::TimeDelta> buffered; |
| 719 scoped_refptr<FFmpegDemuxerStream> audio = | 719 FFmpegDemuxerStream* audio = |
| 720 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); | 720 audio_disabled_ ? NULL : GetFFmpegStream(DemuxerStream::AUDIO); |
| 721 scoped_refptr<FFmpegDemuxerStream> video = | 721 FFmpegDemuxerStream* video = GetFFmpegStream(DemuxerStream::VIDEO); |
| 722 GetFFmpegStream(DemuxerStream::VIDEO); | |
| 723 if (audio && video) { | 722 if (audio && video) { |
| 724 buffered = audio->GetBufferedRanges().IntersectionWith( | 723 buffered = audio->GetBufferedRanges().IntersectionWith( |
| 725 video->GetBufferedRanges()); | 724 video->GetBufferedRanges()); |
| 726 } else if (audio) { | 725 } else if (audio) { |
| 727 buffered = audio->GetBufferedRanges(); | 726 buffered = audio->GetBufferedRanges(); |
| 728 } else if (video) { | 727 } else if (video) { |
| 729 buffered = video->GetBufferedRanges(); | 728 buffered = video->GetBufferedRanges(); |
| 730 } | 729 } |
| 731 for (size_t i = 0; i < buffered.size(); ++i) | 730 for (size_t i = 0; i < buffered.size(); ++i) |
| 732 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 731 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
| 733 } | 732 } |
| 734 | 733 |
| 735 void FFmpegDemuxer::OnDataSourceError() { | 734 void FFmpegDemuxer::OnDataSourceError() { |
| 736 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 735 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
| 737 } | 736 } |
| 738 | 737 |
| 739 } // namespace media | 738 } // namespace media |
| OLD | NEW |