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 66 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 77 } | 77 } |
| 78 | 78 |
| 79 // NOTE: Do not use AVStream->first_dts since |start_time| should be a | 79 // NOTE: Do not use AVStream->first_dts since |start_time| should be a |
| 80 // presentation timestamp. | 80 // presentation timestamp. |
| 81 return start_time; | 81 return start_time; |
| 82 } | 82 } |
| 83 | 83 |
| 84 // | 84 // |
| 85 // FFmpegDemuxerStream | 85 // FFmpegDemuxerStream |
| 86 // | 86 // |
| 87 FFmpegDemuxerStream::FFmpegDemuxerStream( | 87 FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer, |
| 88 FFmpegDemuxer* demuxer, | 88 AVStream* stream) |
| 89 AVStream* stream, | |
| 90 bool discard_negative_timestamps) | |
| 91 : demuxer_(demuxer), | 89 : demuxer_(demuxer), |
| 92 task_runner_(base::MessageLoopProxy::current()), | 90 task_runner_(base::MessageLoopProxy::current()), |
| 93 stream_(stream), | 91 stream_(stream), |
| 94 type_(UNKNOWN), | 92 type_(UNKNOWN), |
| 95 end_of_stream_(false), | 93 end_of_stream_(false), |
| 96 last_packet_timestamp_(kNoTimestamp()), | 94 last_packet_timestamp_(kNoTimestamp()), |
| 97 bitstream_converter_enabled_(false), | 95 bitstream_converter_enabled_(false), |
| 98 discard_negative_timestamps_(discard_negative_timestamps) { | 96 fixup_negative_ogg_timestamps_(false) { |
| 99 DCHECK(demuxer_); | 97 DCHECK(demuxer_); |
| 100 | 98 |
| 101 bool is_encrypted = false; | 99 bool is_encrypted = false; |
| 102 | 100 |
| 103 // Determine our media format. | 101 // Determine our media format. |
| 104 switch (stream->codec->codec_type) { | 102 switch (stream->codec->codec_type) { |
| 105 case AVMEDIA_TYPE_AUDIO: | 103 case AVMEDIA_TYPE_AUDIO: |
| 106 type_ = AUDIO; | 104 type_ = AUDIO; |
| 107 AVStreamToAudioDecoderConfig(stream, &audio_config_, true); | 105 AVStreamToAudioDecoderConfig(stream, &audio_config_, true); |
| 108 is_encrypted = audio_config_.is_encrypted(); | 106 is_encrypted = audio_config_.is_encrypted(); |
| (...skipping 146 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 255 } | 253 } |
| 256 | 254 |
| 257 buffer->set_duration( | 255 buffer->set_duration( |
| 258 ConvertStreamTimestamp(stream_->time_base, packet->duration)); | 256 ConvertStreamTimestamp(stream_->time_base, packet->duration)); |
| 259 | 257 |
| 260 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp(). | 258 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp(). |
| 261 const base::TimeDelta stream_timestamp = | 259 const base::TimeDelta stream_timestamp = |
| 262 ConvertStreamTimestamp(stream_->time_base, packet->pts); | 260 ConvertStreamTimestamp(stream_->time_base, packet->pts); |
| 263 | 261 |
| 264 if (stream_timestamp != kNoTimestamp()) { | 262 if (stream_timestamp != kNoTimestamp()) { |
| 265 buffer->set_timestamp(stream_timestamp - demuxer_->start_time()); | 263 // If this is an OGG file with negative timestamps don't rebase any other |
| 264 // stream types against the negative starting time. | |
| 265 base::TimeDelta start_time = demuxer_->start_time(); | |
| 266 if (fixup_negative_ogg_timestamps_ && type() != AUDIO && | |
| 267 start_time < base::TimeDelta()) { | |
| 268 DCHECK(stream_timestamp >= base::TimeDelta()); | |
| 269 start_time = base::TimeDelta(); | |
| 270 } | |
| 271 | |
| 272 buffer->set_timestamp(stream_timestamp - start_time); | |
| 266 | 273 |
| 267 // If enabled, mark packets with negative timestamps for post-decode | 274 // If enabled, mark packets with negative timestamps for post-decode |
| 268 // discard. | 275 // discard. |
| 269 if (discard_negative_timestamps_ && stream_timestamp < base::TimeDelta()) { | 276 if (fixup_negative_ogg_timestamps_ && |
| 277 stream_timestamp < base::TimeDelta()) { | |
| 270 if (stream_timestamp + buffer->duration() < base::TimeDelta()) { | 278 if (stream_timestamp + buffer->duration() < base::TimeDelta()) { |
| 271 // Discard the entire packet if it's entirely before zero. | 279 // Discard the entire packet if it's entirely before zero. |
| 272 buffer->set_discard_padding( | 280 buffer->set_discard_padding( |
| 273 std::make_pair(kInfiniteDuration(), base::TimeDelta())); | 281 std::make_pair(kInfiniteDuration(), base::TimeDelta())); |
| 274 } else { | 282 } else { |
| 275 // Only discard part of the frame if it overlaps zero. | 283 // Only discard part of the frame if it overlaps zero. |
| 276 buffer->set_discard_padding( | 284 buffer->set_discard_padding( |
| 277 std::make_pair(-stream_timestamp, base::TimeDelta())); | 285 std::make_pair(-stream_timestamp, base::TimeDelta())); |
| 278 } | 286 } |
| 279 } | 287 } |
| (...skipping 215 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 495 DCHECK(task_runner_->BelongsToCurrentThread()); | 503 DCHECK(task_runner_->BelongsToCurrentThread()); |
| 496 CHECK(!pending_seek_); | 504 CHECK(!pending_seek_); |
| 497 | 505 |
| 498 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, | 506 // TODO(scherkus): Inspect |pending_read_| and cancel IO via |blocking_url_|, |
| 499 // otherwise we can end up waiting for a pre-seek read to complete even though | 507 // otherwise we can end up waiting for a pre-seek read to complete even though |
| 500 // we know we're going to drop it on the floor. | 508 // we know we're going to drop it on the floor. |
| 501 | 509 |
| 502 // FFmpeg requires seeks to be adjusted according to the lowest starting time. | 510 // FFmpeg requires seeks to be adjusted according to the lowest starting time. |
| 503 const base::TimeDelta seek_time = time + start_time_; | 511 const base::TimeDelta seek_time = time + start_time_; |
| 504 | 512 |
| 505 // Choose the preferred stream if |seek_time| occurs after its starting time, | 513 // Choose the seeking stream based on whether it contains the seek time, if no |
| 506 // otherwise use the fallback stream. | 514 // match can be found prefer the preferred stream. |
| 507 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp()); | 515 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp()); |
| 508 const int stream_index = seek_time >= preferred_stream_for_seeking_.second | 516 const int stream_index = |
| 509 ? preferred_stream_for_seeking_.first | 517 seek_time < preferred_stream_for_seeking_.second && |
| 510 : fallback_stream_for_seeking_.first; | 518 seek_time >= fallback_stream_for_seeking_.second |
| 519 ? fallback_stream_for_seeking_.first | |
| 520 : preferred_stream_for_seeking_.first; | |
| 511 DCHECK_NE(stream_index, -1); | 521 DCHECK_NE(stream_index, -1); |
| 512 | 522 |
| 513 const AVStream* seeking_stream = | 523 const AVStream* seeking_stream = |
| 514 glue_->format_context()->streams[stream_index]; | 524 glue_->format_context()->streams[stream_index]; |
| 515 | 525 |
| 516 pending_seek_ = true; | 526 pending_seek_ = true; |
| 517 base::PostTaskAndReplyWithResult( | 527 base::PostTaskAndReplyWithResult( |
| 518 blocking_thread_.message_loop_proxy().get(), | 528 blocking_thread_.message_loop_proxy().get(), |
| 519 FROM_HERE, | 529 FROM_HERE, |
| 520 base::Bind(&av_seek_frame, | 530 base::Bind(&av_seek_frame, |
| (...skipping 152 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 673 return; | 683 return; |
| 674 } | 684 } |
| 675 | 685 |
| 676 // Create demuxer stream entries for each possible AVStream. Each stream | 686 // Create demuxer stream entries for each possible AVStream. Each stream |
| 677 // is examined to determine if it is supported or not (is the codec enabled | 687 // is examined to determine if it is supported or not (is the codec enabled |
| 678 // for it in this release?). Unsupported streams are skipped, allowing for | 688 // for it in this release?). Unsupported streams are skipped, allowing for |
| 679 // partial playback. At least one audio or video stream must be playable. | 689 // partial playback. At least one audio or video stream must be playable. |
| 680 AVFormatContext* format_context = glue_->format_context(); | 690 AVFormatContext* format_context = glue_->format_context(); |
| 681 streams_.resize(format_context->nb_streams); | 691 streams_.resize(format_context->nb_streams); |
| 682 | 692 |
| 693 // FFmpeg doesn't guarantee packets will be in timestamp order across streams | |
|
DaleCurtis
2014/06/27 21:10:33
If you decide we should keep this, I can refactor
| |
| 694 // in an ogg container after a seek. Internally it only considers the file | |
| 695 // position when demuxing. It's an open question whether FFmpeg should fix | |
| 696 // this: http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html | |
| 697 // | |
| 698 // As a workaround to ensure playback starts correctly, use the position of | |
| 699 // packets demuxed during avformat_find_stream_info() to adjust the seek info. | |
| 700 int first_ogg_stream = -1; | |
| 701 int64_t first_ogg_packet_pos = std::numeric_limits<int64_t>::max(); | |
| 702 | |
| 683 // Estimate the start time for each stream by looking through the packets | 703 // Estimate the start time for each stream by looking through the packets |
| 684 // buffered during avformat_find_stream_info(). These values will be | 704 // buffered during avformat_find_stream_info(). These values will be |
| 685 // considered later when determining the actual stream start time. | 705 // considered later when determining the actual stream start time. |
| 686 // | 706 // |
| 687 // These packets haven't been completely processed yet, so only look through | 707 // These packets haven't been completely processed yet, so only look through |
| 688 // these values if the AVFormatContext has a valid start time. | 708 // these values if the AVFormatContext has a valid start time. |
| 689 // | 709 // |
| 690 // If no estimate is found, the stream entry will be kInfiniteDuration(). | 710 // If no estimate is found, the stream entry will be kInfiniteDuration(). |
| 691 std::vector<base::TimeDelta> start_time_estimates(format_context->nb_streams, | 711 std::vector<base::TimeDelta> start_time_estimates(format_context->nb_streams, |
| 692 kInfiniteDuration()); | 712 kInfiniteDuration()); |
| 693 if (format_context->packet_buffer && | 713 if (format_context->packet_buffer && |
| 694 format_context->start_time != static_cast<int64>(AV_NOPTS_VALUE)) { | 714 format_context->start_time != static_cast<int64>(AV_NOPTS_VALUE)) { |
| 695 struct AVPacketList* packet_buffer = format_context->packet_buffer; | 715 struct AVPacketList* packet_buffer = format_context->packet_buffer; |
| 696 while (packet_buffer != format_context->packet_buffer_end) { | 716 while (packet_buffer != format_context->packet_buffer_end) { |
| 697 DCHECK_LT(static_cast<size_t>(packet_buffer->pkt.stream_index), | 717 DCHECK_LT(static_cast<size_t>(packet_buffer->pkt.stream_index), |
| 698 start_time_estimates.size()); | 718 start_time_estimates.size()); |
| 699 const AVStream* stream = | 719 const AVStream* stream = |
| 700 format_context->streams[packet_buffer->pkt.stream_index]; | 720 format_context->streams[packet_buffer->pkt.stream_index]; |
| 701 if (packet_buffer->pkt.pts != static_cast<int64>(AV_NOPTS_VALUE)) { | 721 if (packet_buffer->pkt.pts != static_cast<int64>(AV_NOPTS_VALUE)) { |
| 702 const base::TimeDelta packet_pts = | 722 const base::TimeDelta packet_pts = |
| 703 ConvertFromTimeBase(stream->time_base, packet_buffer->pkt.pts); | 723 ConvertFromTimeBase(stream->time_base, packet_buffer->pkt.pts); |
| 704 if (packet_pts < start_time_estimates[stream->index]) | 724 if (packet_pts < start_time_estimates[stream->index]) |
| 705 start_time_estimates[stream->index] = packet_pts; | 725 start_time_estimates[stream->index] = packet_pts; |
| 706 } | 726 } |
| 727 if (packet_buffer->pkt.pos != -1 && | |
| 728 packet_buffer->pkt.pos < first_ogg_packet_pos) { | |
| 729 first_ogg_stream = stream->index; | |
| 730 first_ogg_packet_pos = packet_buffer->pkt.pos; | |
| 731 } | |
| 707 packet_buffer = packet_buffer->next; | 732 packet_buffer = packet_buffer->next; |
| 708 } | 733 } |
| 709 } | 734 } |
| 710 | 735 |
| 711 AVStream* audio_stream = NULL; | 736 AVStream* audio_stream = NULL; |
| 712 AudioDecoderConfig audio_config; | 737 AudioDecoderConfig audio_config; |
| 713 | 738 |
| 714 AVStream* video_stream = NULL; | 739 AVStream* video_stream = NULL; |
| 715 VideoDecoderConfig video_config; | 740 VideoDecoderConfig video_config; |
| 716 | 741 |
| 717 // If available, |start_time_| will be set to the lowest stream start time. | 742 // If available, |start_time_| will be set to the lowest stream start time. |
| 718 start_time_ = kInfiniteDuration(); | 743 start_time_ = kInfiniteDuration(); |
| 719 | 744 |
| 720 base::TimeDelta max_duration; | 745 base::TimeDelta max_duration; |
| 721 for (size_t i = 0; i < format_context->nb_streams; ++i) { | 746 for (size_t i = 0; i < format_context->nb_streams; ++i) { |
| 722 AVStream* stream = format_context->streams[i]; | 747 AVStream* stream = format_context->streams[i]; |
| 723 const AVCodecContext* codec_context = stream->codec; | 748 const AVCodecContext* codec_context = stream->codec; |
| 724 const AVMediaType codec_type = codec_context->codec_type; | 749 const AVMediaType codec_type = codec_context->codec_type; |
| 725 bool discard_negative_timestamps = false; | |
| 726 | 750 |
| 727 if (codec_type == AVMEDIA_TYPE_AUDIO) { | 751 if (codec_type == AVMEDIA_TYPE_AUDIO) { |
| 728 if (audio_stream) | 752 if (audio_stream) |
| 729 continue; | 753 continue; |
| 730 | 754 |
| 731 // Log the codec detected, whether it is supported or not. | 755 // Log the codec detected, whether it is supported or not. |
| 732 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec", | 756 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec", |
| 733 codec_context->codec_id); | 757 codec_context->codec_id); |
| 734 // Ensure the codec is supported. IsValidConfig() also checks that the | 758 // Ensure the codec is supported. IsValidConfig() also checks that the |
| 735 // channel layout and sample format are valid. | 759 // channel layout and sample format are valid. |
| 736 AVStreamToAudioDecoderConfig(stream, &audio_config, false); | 760 AVStreamToAudioDecoderConfig(stream, &audio_config, false); |
| 737 if (!audio_config.IsValidConfig()) | 761 if (!audio_config.IsValidConfig()) |
| 738 continue; | 762 continue; |
| 739 audio_stream = stream; | 763 audio_stream = stream; |
| 740 | |
| 741 // Enable post-decode frame dropping for packets with negative timestamps | |
| 742 // as outlined in section A.2 in the Ogg Vorbis spec: | |
| 743 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html | |
| 744 discard_negative_timestamps = | |
| 745 audio_config.codec() == kCodecVorbis && | |
| 746 strcmp(glue_->format_context()->iformat->name, "ogg") == 0; | |
| 747 } else if (codec_type == AVMEDIA_TYPE_VIDEO) { | 764 } else if (codec_type == AVMEDIA_TYPE_VIDEO) { |
| 748 if (video_stream) | 765 if (video_stream) |
| 749 continue; | 766 continue; |
| 750 | 767 |
| 751 // Log the codec detected, whether it is supported or not. | 768 // Log the codec detected, whether it is supported or not. |
| 752 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", | 769 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", |
| 753 codec_context->codec_id); | 770 codec_context->codec_id); |
| 754 // Ensure the codec is supported. IsValidConfig() also checks that the | 771 // Ensure the codec is supported. IsValidConfig() also checks that the |
| 755 // frame size and visible size are valid. | 772 // frame size and visible size are valid. |
| 756 AVStreamToVideoDecoderConfig(stream, &video_config, false); | 773 AVStreamToVideoDecoderConfig(stream, &video_config, false); |
| 757 | 774 |
| 758 if (!video_config.IsValidConfig()) | 775 if (!video_config.IsValidConfig()) |
| 759 continue; | 776 continue; |
| 760 video_stream = stream; | 777 video_stream = stream; |
| 761 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { | 778 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { |
| 762 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { | 779 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { |
| 763 continue; | 780 continue; |
| 764 } | 781 } |
| 765 } else { | 782 } else { |
| 766 continue; | 783 continue; |
| 767 } | 784 } |
| 768 | 785 |
| 769 streams_[i] = | 786 streams_[i] = new FFmpegDemuxerStream(this, stream); |
| 770 new FFmpegDemuxerStream(this, stream, discard_negative_timestamps); | |
| 771 max_duration = std::max(max_duration, streams_[i]->duration()); | 787 max_duration = std::max(max_duration, streams_[i]->duration()); |
| 772 | 788 |
| 773 const base::TimeDelta start_time = | 789 const base::TimeDelta start_time = |
| 774 ExtractStartTime(stream, start_time_estimates[i]); | 790 ExtractStartTime(stream, start_time_estimates[i]); |
| 775 if (start_time == kNoTimestamp()) | 791 const bool has_start_time = start_time != kNoTimestamp(); |
| 792 | |
| 793 // Always prefer the video stream for seeking. If none exists, we'll swap | |
| 794 // the fallback stream with the preferred stream below. | |
| 795 if (codec_type == AVMEDIA_TYPE_VIDEO) { | |
| 796 preferred_stream_for_seeking_ = | |
| 797 StreamSeekInfo(i, has_start_time ? start_time : base::TimeDelta()); | |
| 798 } | |
| 799 | |
| 800 if (!has_start_time) | |
| 776 continue; | 801 continue; |
| 777 | 802 |
| 778 if (start_time < start_time_) { | 803 if (start_time < start_time_) { |
| 779 start_time_ = start_time; | 804 start_time_ = start_time; |
| 780 | 805 |
| 781 // Choose the stream with the lowest starting time as the fallback stream | 806 // Choose the stream with the lowest starting time as the fallback stream |
| 782 // for seeking. Video should always be preferred. | 807 // for seeking. Video should always be preferred. |
| 783 fallback_stream_for_seeking_ = std::make_pair(i, start_time); | 808 fallback_stream_for_seeking_ = StreamSeekInfo(i, start_time); |
| 784 } | 809 } |
| 785 | |
| 786 // Always prefer the video stream for seeking. If none exists, we'll swap | |
| 787 // the fallback stream with the preferred stream below. | |
| 788 if (codec_type == AVMEDIA_TYPE_VIDEO) | |
| 789 preferred_stream_for_seeking_ = std::make_pair(i, start_time); | |
| 790 } | 810 } |
| 791 | 811 |
| 792 if (!audio_stream && !video_stream) { | 812 if (!audio_stream && !video_stream) { |
| 793 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); | 813 status_cb.Run(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); |
| 794 return; | 814 return; |
| 795 } | 815 } |
| 796 | 816 |
| 797 if (text_enabled_) | 817 if (text_enabled_) |
| 798 AddTextStreams(); | 818 AddTextStreams(); |
| 799 | 819 |
| 800 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { | 820 if (format_context->duration != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
| 801 // If there is a duration value in the container use that to find the | 821 // If there is a duration value in the container use that to find the |
| 802 // maximum between it and the duration from A/V streams. | 822 // maximum between it and the duration from A/V streams. |
| 803 const AVRational av_time_base = {1, AV_TIME_BASE}; | 823 const AVRational av_time_base = {1, AV_TIME_BASE}; |
| 804 max_duration = | 824 max_duration = |
| 805 std::max(max_duration, | 825 std::max(max_duration, |
| 806 ConvertFromTimeBase(av_time_base, format_context->duration)); | 826 ConvertFromTimeBase(av_time_base, format_context->duration)); |
| 807 } else { | 827 } else { |
| 808 // The duration is unknown, in which case this is likely a live stream. | 828 // The duration is unknown, in which case this is likely a live stream. |
| 809 max_duration = kInfiniteDuration(); | 829 max_duration = kInfiniteDuration(); |
| 810 } | 830 } |
| 811 | 831 |
| 832 // Ogg has some peculiarities around negative timestamps, so use this flag to | |
| 833 // setup the FFmpegDemuxerStreams appropriately. | |
| 834 // | |
| 835 // Post-decode frame dropping for packets with negative timestamps is outlined | |
| 836 // in section A.2 in the Ogg Vorbis spec: | |
| 837 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html | |
| 838 if (strcmp(format_context->iformat->name, "ogg") == 0 && audio_stream && | |
| 839 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS) { | |
| 840 for (size_t i = 0; i < streams_.size(); ++i) { | |
| 841 if (streams_[i]) | |
| 842 streams_[i]->enable_negative_timestamp_fixups_for_ogg(); | |
| 843 } | |
| 844 | |
| 845 // Fixup the seeking information to avoid selecting the audio stream unless | |
| 846 // it has a lower file position. See notes on |first_ogg_stream|. | |
| 847 if (first_ogg_stream != audio_stream->index && | |
| 848 fallback_stream_for_seeking_.first == audio_stream->index && | |
| 849 fallback_stream_for_seeking_.second < base::TimeDelta()) { | |
| 850 fallback_stream_for_seeking_.second = base::TimeDelta(); | |
| 851 } | |
| 852 } | |
| 853 | |
| 812 // If no start time could be determined, default to zero and prefer the video | 854 // If no start time could be determined, default to zero and prefer the video |
| 813 // stream over the audio stream for seeking. E.g., The WAV demuxer does not | 855 // stream over the audio stream for seeking. E.g., The WAV demuxer does not |
| 814 // put timestamps on its frames. | 856 // put timestamps on its frames. |
| 815 if (start_time_ == kInfiniteDuration()) { | 857 if (start_time_ == kInfiniteDuration()) { |
| 816 start_time_ = base::TimeDelta(); | 858 start_time_ = base::TimeDelta(); |
| 817 preferred_stream_for_seeking_ = std::make_pair( | 859 preferred_stream_for_seeking_ = StreamSeekInfo( |
| 818 video_stream ? video_stream->index : audio_stream->index, start_time_); | 860 video_stream ? video_stream->index : audio_stream->index, start_time_); |
| 819 } else if (!video_stream) { | 861 } else if (!video_stream) { |
| 820 // If no video stream exists, use the audio or text stream found above. | 862 // If no video stream exists, use the audio or text stream found above. |
| 821 preferred_stream_for_seeking_ = fallback_stream_for_seeking_; | 863 preferred_stream_for_seeking_ = fallback_stream_for_seeking_; |
| 822 } | 864 } |
| 823 | 865 |
| 824 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS | 866 // MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS |
| 825 // generation so we always get timestamps, see http://crbug.com/169570 | 867 // generation so we always get timestamps, see http://crbug.com/169570 |
| 826 if (strcmp(format_context->iformat->name, "avi") == 0) | 868 if (strcmp(format_context->iformat->name, "avi") == 0) |
| 827 format_context->flags |= AVFMT_FLAG_GENPTS; | 869 format_context->flags |= AVFMT_FLAG_GENPTS; |
| (...skipping 306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1134 } | 1176 } |
| 1135 for (size_t i = 0; i < buffered.size(); ++i) | 1177 for (size_t i = 0; i < buffered.size(); ++i) |
| 1136 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 1178 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
| 1137 } | 1179 } |
| 1138 | 1180 |
| 1139 void FFmpegDemuxer::OnDataSourceError() { | 1181 void FFmpegDemuxer::OnDataSourceError() { |
| 1140 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 1182 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
| 1141 } | 1183 } |
| 1142 | 1184 |
| 1143 } // namespace media | 1185 } // namespace media |
| OLD | NEW |