| 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. |
| 515 // |
| 516 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a |
| 517 // given container will demux all packets after the seek point. Instead it |
| 518 // only guarantees that all packets after the file position of the seek will |
| 519 // be demuxed. It's an open question whether FFmpeg should fix this: |
| 520 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html |
| 521 // Tracked by http://crbug.com/387996. |
| 507 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp()); | 522 DCHECK(preferred_stream_for_seeking_.second != kNoTimestamp()); |
| 508 const int stream_index = seek_time >= preferred_stream_for_seeking_.second | 523 const int stream_index = |
| 509 ? preferred_stream_for_seeking_.first | 524 seek_time < preferred_stream_for_seeking_.second && |
| 510 : fallback_stream_for_seeking_.first; | 525 seek_time >= fallback_stream_for_seeking_.second |
| 526 ? fallback_stream_for_seeking_.first |
| 527 : preferred_stream_for_seeking_.first; |
| 511 DCHECK_NE(stream_index, -1); | 528 DCHECK_NE(stream_index, -1); |
| 512 | 529 |
| 513 const AVStream* seeking_stream = | 530 const AVStream* seeking_stream = |
| 514 glue_->format_context()->streams[stream_index]; | 531 glue_->format_context()->streams[stream_index]; |
| 515 | 532 |
| 516 pending_seek_ = true; | 533 pending_seek_ = true; |
| 517 base::PostTaskAndReplyWithResult( | 534 base::PostTaskAndReplyWithResult( |
| 518 blocking_thread_.message_loop_proxy().get(), | 535 blocking_thread_.message_loop_proxy().get(), |
| 519 FROM_HERE, | 536 FROM_HERE, |
| 520 base::Bind(&av_seek_frame, | 537 base::Bind(&av_seek_frame, |
| (...skipping 194 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 715 VideoDecoderConfig video_config; | 732 VideoDecoderConfig video_config; |
| 716 | 733 |
| 717 // If available, |start_time_| will be set to the lowest stream start time. | 734 // If available, |start_time_| will be set to the lowest stream start time. |
| 718 start_time_ = kInfiniteDuration(); | 735 start_time_ = kInfiniteDuration(); |
| 719 | 736 |
| 720 base::TimeDelta max_duration; | 737 base::TimeDelta max_duration; |
| 721 for (size_t i = 0; i < format_context->nb_streams; ++i) { | 738 for (size_t i = 0; i < format_context->nb_streams; ++i) { |
| 722 AVStream* stream = format_context->streams[i]; | 739 AVStream* stream = format_context->streams[i]; |
| 723 const AVCodecContext* codec_context = stream->codec; | 740 const AVCodecContext* codec_context = stream->codec; |
| 724 const AVMediaType codec_type = codec_context->codec_type; | 741 const AVMediaType codec_type = codec_context->codec_type; |
| 725 bool discard_negative_timestamps = false; | |
| 726 | 742 |
| 727 if (codec_type == AVMEDIA_TYPE_AUDIO) { | 743 if (codec_type == AVMEDIA_TYPE_AUDIO) { |
| 728 if (audio_stream) | 744 if (audio_stream) |
| 729 continue; | 745 continue; |
| 730 | 746 |
| 731 // Log the codec detected, whether it is supported or not. | 747 // Log the codec detected, whether it is supported or not. |
| 732 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec", | 748 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedAudioCodec", |
| 733 codec_context->codec_id); | 749 codec_context->codec_id); |
| 734 // Ensure the codec is supported. IsValidConfig() also checks that the | 750 // Ensure the codec is supported. IsValidConfig() also checks that the |
| 735 // channel layout and sample format are valid. | 751 // channel layout and sample format are valid. |
| 736 AVStreamToAudioDecoderConfig(stream, &audio_config, false); | 752 AVStreamToAudioDecoderConfig(stream, &audio_config, false); |
| 737 if (!audio_config.IsValidConfig()) | 753 if (!audio_config.IsValidConfig()) |
| 738 continue; | 754 continue; |
| 739 audio_stream = stream; | 755 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) { | 756 } else if (codec_type == AVMEDIA_TYPE_VIDEO) { |
| 748 if (video_stream) | 757 if (video_stream) |
| 749 continue; | 758 continue; |
| 750 | 759 |
| 751 // Log the codec detected, whether it is supported or not. | 760 // Log the codec detected, whether it is supported or not. |
| 752 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", | 761 UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", |
| 753 codec_context->codec_id); | 762 codec_context->codec_id); |
| 754 // Ensure the codec is supported. IsValidConfig() also checks that the | 763 // Ensure the codec is supported. IsValidConfig() also checks that the |
| 755 // frame size and visible size are valid. | 764 // frame size and visible size are valid. |
| 756 AVStreamToVideoDecoderConfig(stream, &video_config, false); | 765 AVStreamToVideoDecoderConfig(stream, &video_config, false); |
| 757 | 766 |
| 758 if (!video_config.IsValidConfig()) | 767 if (!video_config.IsValidConfig()) |
| 759 continue; | 768 continue; |
| 760 video_stream = stream; | 769 video_stream = stream; |
| 761 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { | 770 } else if (codec_type == AVMEDIA_TYPE_SUBTITLE) { |
| 762 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { | 771 if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) { |
| 763 continue; | 772 continue; |
| 764 } | 773 } |
| 765 } else { | 774 } else { |
| 766 continue; | 775 continue; |
| 767 } | 776 } |
| 768 | 777 |
| 769 streams_[i] = | 778 streams_[i] = new FFmpegDemuxerStream(this, stream); |
| 770 new FFmpegDemuxerStream(this, stream, discard_negative_timestamps); | |
| 771 max_duration = std::max(max_duration, streams_[i]->duration()); | 779 max_duration = std::max(max_duration, streams_[i]->duration()); |
| 772 | 780 |
| 773 const base::TimeDelta start_time = | 781 const base::TimeDelta start_time = |
| 774 ExtractStartTime(stream, start_time_estimates[i]); | 782 ExtractStartTime(stream, start_time_estimates[i]); |
| 775 const bool has_start_time = start_time != kNoTimestamp(); | 783 const bool has_start_time = start_time != kNoTimestamp(); |
| 776 | 784 |
| 777 // Always prefer the video stream for seeking. If none exists, we'll swap | 785 // Always prefer the video stream for seeking. If none exists, we'll swap |
| 778 // the fallback stream with the preferred stream below. | 786 // the fallback stream with the preferred stream below. |
| 779 if (codec_type == AVMEDIA_TYPE_VIDEO) { | 787 if (codec_type == AVMEDIA_TYPE_VIDEO) { |
| 780 preferred_stream_for_seeking_ = | 788 preferred_stream_for_seeking_ = |
| (...skipping 25 matching lines...) Expand all Loading... |
| 806 // maximum between it and the duration from A/V streams. | 814 // maximum between it and the duration from A/V streams. |
| 807 const AVRational av_time_base = {1, AV_TIME_BASE}; | 815 const AVRational av_time_base = {1, AV_TIME_BASE}; |
| 808 max_duration = | 816 max_duration = |
| 809 std::max(max_duration, | 817 std::max(max_duration, |
| 810 ConvertFromTimeBase(av_time_base, format_context->duration)); | 818 ConvertFromTimeBase(av_time_base, format_context->duration)); |
| 811 } else { | 819 } else { |
| 812 // The duration is unknown, in which case this is likely a live stream. | 820 // The duration is unknown, in which case this is likely a live stream. |
| 813 max_duration = kInfiniteDuration(); | 821 max_duration = kInfiniteDuration(); |
| 814 } | 822 } |
| 815 | 823 |
| 824 // Ogg has some peculiarities around negative timestamps, so use this flag to |
| 825 // setup the FFmpegDemuxerStreams appropriately. |
| 826 // |
| 827 // Post-decode frame dropping for packets with negative timestamps is outlined |
| 828 // in section A.2 in the Ogg Vorbis spec: |
| 829 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
| 830 if (strcmp(format_context->iformat->name, "ogg") == 0 && audio_stream && |
| 831 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS) { |
| 832 for (size_t i = 0; i < streams_.size(); ++i) { |
| 833 if (streams_[i]) |
| 834 streams_[i]->enable_negative_timestamp_fixups_for_ogg(); |
| 835 } |
| 836 |
| 837 // Fixup the seeking information to avoid selecting the audio stream simply |
| 838 // because it has a lower starting time. |
| 839 if (fallback_stream_for_seeking_.first == audio_stream->index && |
| 840 fallback_stream_for_seeking_.second < base::TimeDelta()) { |
| 841 fallback_stream_for_seeking_.second = base::TimeDelta(); |
| 842 } |
| 843 } |
| 844 |
| 816 // If no start time could be determined, default to zero and prefer the video | 845 // If no start time could be determined, default to zero and prefer the video |
| 817 // stream over the audio stream for seeking. E.g., The WAV demuxer does not | 846 // stream over the audio stream for seeking. E.g., The WAV demuxer does not |
| 818 // put timestamps on its frames. | 847 // put timestamps on its frames. |
| 819 if (start_time_ == kInfiniteDuration()) { | 848 if (start_time_ == kInfiniteDuration()) { |
| 820 start_time_ = base::TimeDelta(); | 849 start_time_ = base::TimeDelta(); |
| 821 preferred_stream_for_seeking_ = StreamSeekInfo( | 850 preferred_stream_for_seeking_ = StreamSeekInfo( |
| 822 video_stream ? video_stream->index : audio_stream->index, start_time_); | 851 video_stream ? video_stream->index : audio_stream->index, start_time_); |
| 823 } else if (!video_stream) { | 852 } else if (!video_stream) { |
| 824 // If no video stream exists, use the audio or text stream found above. | 853 // If no video stream exists, use the audio or text stream found above. |
| 825 preferred_stream_for_seeking_ = fallback_stream_for_seeking_; | 854 preferred_stream_for_seeking_ = fallback_stream_for_seeking_; |
| (...skipping 312 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1138 } | 1167 } |
| 1139 for (size_t i = 0; i < buffered.size(); ++i) | 1168 for (size_t i = 0; i < buffered.size(); ++i) |
| 1140 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); | 1169 host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); |
| 1141 } | 1170 } |
| 1142 | 1171 |
| 1143 void FFmpegDemuxer::OnDataSourceError() { | 1172 void FFmpegDemuxer::OnDataSourceError() { |
| 1144 host_->OnDemuxerError(PIPELINE_ERROR_READ); | 1173 host_->OnDemuxerError(PIPELINE_ERROR_READ); |
| 1145 } | 1174 } |
| 1146 | 1175 |
| 1147 } // namespace media | 1176 } // namespace media |
| OLD | NEW |