Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(132)

Side by Side Diff: media/filters/ffmpeg_demuxer.cc

Issue 353563002: Fix corrupted audio and video at playback start in ogg containers. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remove file code. Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « media/filters/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
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
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
OLDNEW
« no previous file with comments | « media/filters/ffmpeg_demuxer.h ('k') | media/filters/ffmpeg_demuxer_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698