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 |