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 "base/bind.h" | 5 #include "base/bind.h" |
6 #include "base/callback.h" | 6 #include "base/callback.h" |
7 #include "base/command_line.h" | 7 #include "base/command_line.h" |
8 #include "base/memory/scoped_ptr.h" | 8 #include "base/memory/scoped_ptr.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/stl_util.h" | 10 #include "base/stl_util.h" |
(...skipping 258 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
269 const AVRational& time_base, int64 timestamp) { | 269 const AVRational& time_base, int64 timestamp) { |
270 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) | 270 if (timestamp == static_cast<int64>(AV_NOPTS_VALUE)) |
271 return kNoTimestamp(); | 271 return kNoTimestamp(); |
272 | 272 |
273 return ConvertFromTimeBase(time_base, timestamp); | 273 return ConvertFromTimeBase(time_base, timestamp); |
274 } | 274 } |
275 | 275 |
276 // | 276 // |
277 // FFmpegDemuxer | 277 // FFmpegDemuxer |
278 // | 278 // |
279 FFmpegDemuxer::FFmpegDemuxer(MessageLoop* message_loop, bool local_source) | 279 FFmpegDemuxer::FFmpegDemuxer( |
| 280 MessageLoop* message_loop, |
| 281 const scoped_refptr<DataSource>& data_source, |
| 282 bool local_source) |
280 : message_loop_(message_loop), | 283 : message_loop_(message_loop), |
281 local_source_(local_source), | 284 local_source_(local_source), |
282 format_context_(NULL), | 285 format_context_(NULL), |
| 286 data_source_(data_source), |
283 read_event_(false, false), | 287 read_event_(false, false), |
284 read_has_failed_(false), | 288 read_has_failed_(false), |
285 last_read_bytes_(0), | 289 last_read_bytes_(0), |
286 read_position_(0), | 290 read_position_(0), |
287 max_duration_(base::TimeDelta::FromMicroseconds(-1)), | 291 bitrate_(0), |
288 deferred_status_(PIPELINE_OK), | |
289 first_seek_hack_(true), | 292 first_seek_hack_(true), |
290 start_time_(kNoTimestamp()), | 293 start_time_(kNoTimestamp()), |
291 audio_disabled_(false) { | 294 audio_disabled_(false) { |
292 DCHECK(message_loop_); | 295 DCHECK(message_loop_); |
| 296 DCHECK(data_source_); |
293 } | 297 } |
294 | 298 |
295 FFmpegDemuxer::~FFmpegDemuxer() { | 299 FFmpegDemuxer::~FFmpegDemuxer() { |
296 // In this destructor, we clean up resources held by FFmpeg. It is ugly to | 300 // In this destructor, we clean up resources held by FFmpeg. It is ugly to |
297 // close the codec contexts here because the corresponding codecs are opened | 301 // close the codec contexts here because the corresponding codecs are opened |
298 // in the decoder filters. By reaching this point, all filters should have | 302 // in the decoder filters. By reaching this point, all filters should have |
299 // stopped, so this is the only safe place to do the global clean up. | 303 // stopped, so this is the only safe place to do the global clean up. |
300 // TODO(hclam): close the codecs in the corresponding decoders. | 304 // TODO(hclam): close the codecs in the corresponding decoders. |
301 if (!format_context_) | 305 if (!format_context_) |
302 return; | 306 return; |
(...skipping 19 matching lines...) Expand all Loading... |
322 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { | 326 void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { |
323 message_loop_->PostTask(FROM_HERE, | 327 message_loop_->PostTask(FROM_HERE, |
324 base::Bind(&FFmpegDemuxer::SeekTask, this, time, cb)); | 328 base::Bind(&FFmpegDemuxer::SeekTask, this, time, cb)); |
325 } | 329 } |
326 | 330 |
327 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { | 331 void FFmpegDemuxer::SetPlaybackRate(float playback_rate) { |
328 DCHECK(data_source_.get()); | 332 DCHECK(data_source_.get()); |
329 data_source_->SetPlaybackRate(playback_rate); | 333 data_source_->SetPlaybackRate(playback_rate); |
330 } | 334 } |
331 | 335 |
332 void FFmpegDemuxer::SetPreload(Preload preload) { | |
333 DCHECK(data_source_.get()); | |
334 data_source_->SetPreload(preload); | |
335 } | |
336 | |
337 void FFmpegDemuxer::OnAudioRendererDisabled() { | 336 void FFmpegDemuxer::OnAudioRendererDisabled() { |
338 message_loop_->PostTask(FROM_HERE, base::Bind( | 337 message_loop_->PostTask(FROM_HERE, base::Bind( |
339 &FFmpegDemuxer::DisableAudioStreamTask, this)); | 338 &FFmpegDemuxer::DisableAudioStreamTask, this)); |
340 } | 339 } |
341 | 340 |
342 void FFmpegDemuxer::set_host(DemuxerHost* demuxer_host) { | 341 void FFmpegDemuxer::set_host(DemuxerHost* demuxer_host) { |
343 Demuxer::set_host(demuxer_host); | 342 Demuxer::set_host(demuxer_host); |
344 if (data_source_) | 343 data_source_->set_host(demuxer_host); |
345 data_source_->set_host(demuxer_host); | |
346 if (max_duration_.InMicroseconds() >= 0) | |
347 host()->SetDuration(max_duration_); | |
348 if (read_position_ > 0) | |
349 host()->SetCurrentReadPosition(read_position_); | |
350 if (deferred_status_ != PIPELINE_OK) | |
351 host()->OnDemuxerError(deferred_status_); | |
352 } | 344 } |
353 | 345 |
354 void FFmpegDemuxer::Initialize(DataSource* data_source, | 346 void FFmpegDemuxer::Initialize(const PipelineStatusCB& status_cb) { |
355 const PipelineStatusCB& status_cb) { | 347 message_loop_->PostTask(FROM_HERE, base::Bind( |
356 message_loop_->PostTask( | 348 &FFmpegDemuxer::InitializeTask, this, status_cb)); |
357 FROM_HERE, | |
358 base::Bind(&FFmpegDemuxer::InitializeTask, this, | |
359 make_scoped_refptr(data_source), status_cb)); | |
360 } | 349 } |
361 | 350 |
362 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( | 351 scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream( |
363 DemuxerStream::Type type) { | 352 DemuxerStream::Type type) { |
364 StreamVector::iterator iter; | 353 StreamVector::iterator iter; |
365 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { | 354 for (iter = streams_.begin(); iter != streams_.end(); ++iter) { |
366 if (*iter && (*iter)->type() == type) { | 355 if (*iter && (*iter)->type() == type) { |
367 return *iter; | 356 return *iter; |
368 } | 357 } |
369 } | 358 } |
(...skipping 20 matching lines...) Expand all Loading... |
390 | 379 |
391 // Asynchronous read from data source. | 380 // Asynchronous read from data source. |
392 data_source_->Read(read_position_, size, data, base::Bind( | 381 data_source_->Read(read_position_, size, data, base::Bind( |
393 &FFmpegDemuxer::SignalReadCompleted, this)); | 382 &FFmpegDemuxer::SignalReadCompleted, this)); |
394 | 383 |
395 // TODO(hclam): The method is called on the demuxer thread and this method | 384 // TODO(hclam): The method is called on the demuxer thread and this method |
396 // call will block the thread. We need to implemented an additional thread to | 385 // call will block the thread. We need to implemented an additional thread to |
397 // let FFmpeg demuxer methods to run on. | 386 // let FFmpeg demuxer methods to run on. |
398 int last_read_bytes = WaitForRead(); | 387 int last_read_bytes = WaitForRead(); |
399 if (last_read_bytes == DataSource::kReadError) { | 388 if (last_read_bytes == DataSource::kReadError) { |
400 if (host()) | 389 host()->OnDemuxerError(PIPELINE_ERROR_READ); |
401 host()->OnDemuxerError(PIPELINE_ERROR_READ); | |
402 else | |
403 deferred_status_ = PIPELINE_ERROR_READ; | |
404 | 390 |
405 // Returns with a negative number to signal an error to FFmpeg. | 391 // Returns with a negative number to signal an error to FFmpeg. |
406 read_has_failed_ = true; | 392 read_has_failed_ = true; |
407 return AVERROR(EIO); | 393 return AVERROR(EIO); |
408 } | 394 } |
409 read_position_ += last_read_bytes; | 395 read_position_ += last_read_bytes; |
410 | 396 host()->SetCurrentReadPosition(read_position_); |
411 if (host()) | |
412 host()->SetCurrentReadPosition(read_position_); | |
413 | 397 |
414 return last_read_bytes; | 398 return last_read_bytes; |
415 } | 399 } |
416 | 400 |
417 bool FFmpegDemuxer::GetPosition(int64* position_out) { | 401 bool FFmpegDemuxer::GetPosition(int64* position_out) { |
418 *position_out = read_position_; | 402 *position_out = read_position_; |
419 return true; | 403 return true; |
420 } | 404 } |
421 | 405 |
422 bool FFmpegDemuxer::SetPosition(int64 position) { | 406 bool FFmpegDemuxer::SetPosition(int64 position) { |
(...skipping 18 matching lines...) Expand all Loading... |
441 bool FFmpegDemuxer::IsStreaming() { | 425 bool FFmpegDemuxer::IsStreaming() { |
442 DCHECK(data_source_); | 426 DCHECK(data_source_); |
443 | 427 |
444 return data_source_->IsStreaming(); | 428 return data_source_->IsStreaming(); |
445 } | 429 } |
446 | 430 |
447 MessageLoop* FFmpegDemuxer::message_loop() { | 431 MessageLoop* FFmpegDemuxer::message_loop() { |
448 return message_loop_; | 432 return message_loop_; |
449 } | 433 } |
450 | 434 |
451 void FFmpegDemuxer::InitializeTask(DataSource* data_source, | 435 // Helper for calculating the bitrate of the media based on information stored |
452 const PipelineStatusCB& status_cb) { | 436 // in |format_context| or failing that the size and duration of the media. |
| 437 // |
| 438 // Returns 0 if a bitrate could not be determined. |
| 439 static int CalculateBitrate( |
| 440 AVFormatContext* format_context, |
| 441 const base::TimeDelta& duration, |
| 442 int64 filesize_in_bytes) { |
| 443 // If there is a bitrate set on the container, use it. |
| 444 if (format_context->bit_rate > 0) |
| 445 return format_context->bit_rate; |
| 446 |
| 447 // Then try to sum the bitrates individually per stream. |
| 448 int bitrate = 0; |
| 449 for (size_t i = 0; i < format_context->nb_streams; ++i) { |
| 450 AVCodecContext* codec_context = format_context->streams[i]->codec; |
| 451 bitrate += codec_context->bit_rate; |
| 452 } |
| 453 if (bitrate > 0) |
| 454 return bitrate; |
| 455 |
| 456 // See if we can approximate the bitrate as long as we have a filesize and |
| 457 // valid duration. |
| 458 if (duration.InMicroseconds() <= 0 || |
| 459 duration == kInfiniteDuration() || |
| 460 filesize_in_bytes == 0) { |
| 461 return 0; |
| 462 } |
| 463 |
| 464 // Do math in floating point as we'd overflow an int64 if the filesize was |
| 465 // larger than ~1073GB. |
| 466 double bytes = filesize_in_bytes; |
| 467 double duration_us = duration.InMicroseconds(); |
| 468 return bytes * 8000000.0 / duration_us; |
| 469 } |
| 470 |
| 471 void FFmpegDemuxer::InitializeTask(const PipelineStatusCB& status_cb) { |
453 DCHECK_EQ(MessageLoop::current(), message_loop_); | 472 DCHECK_EQ(MessageLoop::current(), message_loop_); |
454 | 473 |
455 data_source_ = data_source; | |
456 if (host()) | |
457 data_source_->set_host(host()); | |
458 | |
459 // Add ourself to Protocol list and get our unique key. | 474 // Add ourself to Protocol list and get our unique key. |
460 std::string key = FFmpegGlue::GetInstance()->AddProtocol(this); | 475 std::string key = FFmpegGlue::GetInstance()->AddProtocol(this); |
461 | 476 |
462 // Open FFmpeg AVFormatContext. | 477 // Open FFmpeg AVFormatContext. |
463 DCHECK(!format_context_); | 478 DCHECK(!format_context_); |
464 AVFormatContext* context = NULL; | 479 AVFormatContext* context = NULL; |
465 int result = avformat_open_input(&context, key.c_str(), NULL, NULL); | 480 int result = avformat_open_input(&context, key.c_str(), NULL, NULL); |
466 | 481 |
467 // Remove ourself from protocol list. | 482 // Remove ourself from protocol list. |
468 FFmpegGlue::GetInstance()->RemoveProtocol(this); | 483 FFmpegGlue::GetInstance()->RemoveProtocol(this); |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
536 max_duration = kInfiniteDuration(); | 551 max_duration = kInfiniteDuration(); |
537 } | 552 } |
538 | 553 |
539 // Some demuxers, like WAV, do not put timestamps on their frames. We | 554 // Some demuxers, like WAV, do not put timestamps on their frames. We |
540 // assume the the start time is 0. | 555 // assume the the start time is 0. |
541 if (start_time_ == kNoTimestamp()) | 556 if (start_time_ == kNoTimestamp()) |
542 start_time_ = base::TimeDelta(); | 557 start_time_ = base::TimeDelta(); |
543 | 558 |
544 // Good to go: set the duration and bitrate and notify we're done | 559 // Good to go: set the duration and bitrate and notify we're done |
545 // initializing. | 560 // initializing. |
546 if (host()) | 561 host()->SetDuration(max_duration); |
547 host()->SetDuration(max_duration); | |
548 max_duration_ = max_duration; | |
549 | 562 |
550 int bitrate = GetBitrate(); | 563 int64 filesize_in_bytes = 0; |
551 if (bitrate > 0) | 564 GetSize(&filesize_in_bytes); |
552 data_source_->SetBitrate(bitrate); | 565 bitrate_ = CalculateBitrate(format_context_, max_duration, filesize_in_bytes); |
| 566 if (bitrate_ > 0) |
| 567 data_source_->SetBitrate(bitrate_); |
553 | 568 |
554 status_cb.Run(PIPELINE_OK); | 569 status_cb.Run(PIPELINE_OK); |
555 } | 570 } |
556 | 571 |
| 572 |
557 int FFmpegDemuxer::GetBitrate() { | 573 int FFmpegDemuxer::GetBitrate() { |
558 DCHECK(format_context_); | 574 DCHECK(format_context_) << "Initialize() has not been called"; |
559 | 575 return bitrate_; |
560 // If there is a bitrate set on the container, use it. | |
561 if (format_context_->bit_rate > 0) | |
562 return format_context_->bit_rate; | |
563 | |
564 // Then try to sum the bitrates individually per stream. | |
565 int bitrate = 0; | |
566 for (size_t i = 0; i < format_context_->nb_streams; ++i) { | |
567 AVCodecContext* codec_context = format_context_->streams[i]->codec; | |
568 bitrate += codec_context->bit_rate; | |
569 } | |
570 if (bitrate > 0) | |
571 return bitrate; | |
572 | |
573 // See if we can approximate the bitrate as long as we have a filesize and | |
574 // valid duration. | |
575 int64 filesize_in_bytes; | |
576 if (max_duration_.InMicroseconds() <= 0 || | |
577 max_duration_ == kInfiniteDuration() || | |
578 !GetSize(&filesize_in_bytes)) { | |
579 return 0; | |
580 } | |
581 | |
582 // Do math in floating point as we'd overflow an int64 if the filesize was | |
583 // larger than ~1073GB. | |
584 double bytes = filesize_in_bytes; | |
585 double duration = max_duration_.InMicroseconds(); | |
586 return bytes * 8000000.0 / duration; | |
587 } | 576 } |
588 | 577 |
589 bool FFmpegDemuxer::IsLocalSource() { | 578 bool FFmpegDemuxer::IsLocalSource() { |
590 return local_source_; | 579 return local_source_; |
591 } | 580 } |
592 | 581 |
593 bool FFmpegDemuxer::IsSeekable() { | 582 bool FFmpegDemuxer::IsSeekable() { |
594 return !IsStreaming(); | 583 return !IsStreaming(); |
595 } | 584 } |
596 | 585 |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
737 read_event_.Wait(); | 726 read_event_.Wait(); |
738 return last_read_bytes_; | 727 return last_read_bytes_; |
739 } | 728 } |
740 | 729 |
741 void FFmpegDemuxer::SignalReadCompleted(int size) { | 730 void FFmpegDemuxer::SignalReadCompleted(int size) { |
742 last_read_bytes_ = size; | 731 last_read_bytes_ = size; |
743 read_event_.Signal(); | 732 read_event_.Signal(); |
744 } | 733 } |
745 | 734 |
746 } // namespace media | 735 } // namespace media |
OLD | NEW |