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

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

Issue 1260193005: Fix incorrect opus seek preroll and flaky pre-skip removal. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix mojo renderer. Created 5 years, 4 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
« 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 8
9 #include "base/base64.h" 9 #include "base/base64.h"
10 #include "base/bind.h" 10 #include "base/bind.h"
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after
87 AVStream* stream) 87 AVStream* stream)
88 : demuxer_(demuxer), 88 : demuxer_(demuxer),
89 task_runner_(base::ThreadTaskRunnerHandle::Get()), 89 task_runner_(base::ThreadTaskRunnerHandle::Get()),
90 stream_(stream), 90 stream_(stream),
91 type_(UNKNOWN), 91 type_(UNKNOWN),
92 liveness_(LIVENESS_UNKNOWN), 92 liveness_(LIVENESS_UNKNOWN),
93 end_of_stream_(false), 93 end_of_stream_(false),
94 last_packet_timestamp_(kNoTimestamp()), 94 last_packet_timestamp_(kNoTimestamp()),
95 last_packet_duration_(kNoTimestamp()), 95 last_packet_duration_(kNoTimestamp()),
96 video_rotation_(VIDEO_ROTATION_0), 96 video_rotation_(VIDEO_ROTATION_0),
97 fixup_negative_ogg_timestamps_(false) { 97 fixup_negative_timestamps_(false) {
98 DCHECK(demuxer_); 98 DCHECK(demuxer_);
99 99
100 bool is_encrypted = false; 100 bool is_encrypted = false;
101 int rotation = 0; 101 int rotation = 0;
102 AVDictionaryEntry* rotation_entry = NULL; 102 AVDictionaryEntry* rotation_entry = NULL;
103 103
104 // Determine our media format. 104 // Determine our media format.
105 switch (stream->codec->codec_type) { 105 switch (stream->codec->codec_type) {
106 case AVMEDIA_TYPE_AUDIO: 106 case AVMEDIA_TYPE_AUDIO:
107 type_ = AUDIO; 107 type_ = AUDIO;
(...skipping 179 matching lines...) Expand 10 before | Expand all | Expand 10 after
287 buffer->set_duration(kNoTimestamp()); 287 buffer->set_duration(kNoTimestamp());
288 } 288 }
289 289
290 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp(). 290 // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp().
291 const base::TimeDelta stream_timestamp = 291 const base::TimeDelta stream_timestamp =
292 ConvertStreamTimestamp(stream_->time_base, packet->pts); 292 ConvertStreamTimestamp(stream_->time_base, packet->pts);
293 293
294 if (stream_timestamp != kNoTimestamp()) { 294 if (stream_timestamp != kNoTimestamp()) {
295 const bool is_audio = type() == AUDIO; 295 const bool is_audio = type() == AUDIO;
296 296
297 // If this is an OGG file with negative timestamps don't rebase any other 297 // If this file has negative timestamps don't rebase any other stream types
298 // stream types against the negative starting time. 298 // against the negative starting time.
299 base::TimeDelta start_time = demuxer_->start_time(); 299 base::TimeDelta start_time = demuxer_->start_time();
300 if (fixup_negative_ogg_timestamps_ && !is_audio && 300 if (fixup_negative_timestamps_ && !is_audio &&
301 start_time < base::TimeDelta()) { 301 start_time < base::TimeDelta()) {
302 start_time = base::TimeDelta(); 302 start_time = base::TimeDelta();
303 } 303 }
304 304
305 // Don't rebase timestamps for positive start times, the HTML Media Spec 305 // Don't rebase timestamps for positive start times, the HTML Media Spec
306 // details this in section "4.8.10.6 Offsets into the media resource." We 306 // details this in section "4.8.10.6 Offsets into the media resource." We
307 // will still need to rebase timestamps before seeking with FFmpeg though. 307 // will still need to rebase timestamps before seeking with FFmpeg though.
308 if (start_time > base::TimeDelta()) 308 if (start_time > base::TimeDelta())
309 start_time = base::TimeDelta(); 309 start_time = base::TimeDelta();
310 310
311 buffer->set_timestamp(stream_timestamp - start_time); 311 buffer->set_timestamp(stream_timestamp - start_time);
312 312
313 // If enabled, mark audio packets with negative timestamps for post-decode 313 // If enabled, and no codec delay is present, mark audio packets with
314 // discard. 314 // negative timestamps for post-decode discard.
315 if (fixup_negative_ogg_timestamps_ && is_audio && 315 if (fixup_negative_timestamps_ && is_audio &&
316 stream_timestamp < base::TimeDelta() && 316 stream_timestamp < base::TimeDelta() &&
317 buffer->duration() != kNoTimestamp()) { 317 buffer->duration() != kNoTimestamp()) {
318 if (stream_timestamp + buffer->duration() < base::TimeDelta()) { 318 if (!stream_->codec->delay) {
319 // Discard the entire packet if it's entirely before zero. 319 DCHECK_EQ(buffer->discard_padding().first, base::TimeDelta());
320 buffer->set_discard_padding( 320
321 std::make_pair(kInfiniteDuration(), base::TimeDelta())); 321 if (stream_timestamp + buffer->duration() < base::TimeDelta()) {
322 DCHECK_EQ(buffer->discard_padding().second, base::TimeDelta());
323
324 // Discard the entire packet if it's entirely before zero.
325 buffer->set_discard_padding(
326 std::make_pair(kInfiniteDuration(), base::TimeDelta()));
327 } else {
328 // Only discard part of the frame if it overlaps zero.
329 buffer->set_discard_padding(std::make_pair(
330 -stream_timestamp, buffer->discard_padding().second));
331 }
322 } else { 332 } else {
323 // Only discard part of the frame if it overlaps zero. 333 // Verify that codec delay would cover discard and that we don't need to
324 buffer->set_discard_padding( 334 // mark the packet for post decode discard. Since timestamps may be in
325 std::make_pair(-stream_timestamp, base::TimeDelta())); 335 // milliseconds and codec delay in nanosecond precision, round up to the
336 // nearest millisecond. See enable_negative_timestamp_fixups().
337 DCHECK_LE(-std::ceil(FramesToTimeDelta(
338 audio_decoder_config().codec_delay(),
339 audio_decoder_config().samples_per_second())
340 .InMillisecondsF()),
341 stream_timestamp.InMillisecondsF());
326 } 342 }
327 } 343 }
328 } else { 344 } else {
329 // If this happens on the first packet, decoders will throw an error. 345 // If this happens on the first packet, decoders will throw an error.
330 buffer->set_timestamp(kNoTimestamp()); 346 buffer->set_timestamp(kNoTimestamp());
331 } 347 }
332 348
333 if (last_packet_timestamp_ != kNoTimestamp()) { 349 if (last_packet_timestamp_ != kNoTimestamp()) {
334 // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing 350 // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing
335 // continuity across links in the chain it uses the timestamp information 351 // continuity across links in the chain it uses the timestamp information
336 // from each link directly. Doing so can lead to timestamps which appear to 352 // from each link directly. Doing so can lead to timestamps which appear to
337 // go backwards in time. 353 // go backwards in time.
338 // 354 //
339 // If the new link starts with a negative timestamp or a timestamp less than 355 // If the new link starts with a negative timestamp or a timestamp less than
340 // the original (positive) |start_time|, we will get a negative timestamp 356 // the original (positive) |start_time|, we will get a negative timestamp
341 // here. It's also possible FFmpeg returns kNoTimestamp() here if it's not 357 // here. It's also possible FFmpeg returns kNoTimestamp() here if it's not
342 // able to work out a timestamp using the previous link and the next. 358 // able to work out a timestamp using the previous link and the next.
343 // 359 //
344 // Fixing chained ogg is non-trivial, so for now just reuse the last good 360 // Fixing chained ogg is non-trivial, so for now just reuse the last good
345 // timestamp. The decoder will rewrite the timestamps to be sample accurate 361 // timestamp. The decoder will rewrite the timestamps to be sample accurate
346 // later. See http://crbug.com/396864. 362 // later. See http://crbug.com/396864.
347 if (fixup_negative_ogg_timestamps_ && 363 if (fixup_negative_timestamps_ &&
348 (buffer->timestamp() == kNoTimestamp() || 364 (buffer->timestamp() == kNoTimestamp() ||
349 buffer->timestamp() < last_packet_timestamp_)) { 365 buffer->timestamp() < last_packet_timestamp_)) {
350 buffer->set_timestamp(last_packet_timestamp_ + 366 buffer->set_timestamp(last_packet_timestamp_ +
351 (last_packet_duration_ != kNoTimestamp() 367 (last_packet_duration_ != kNoTimestamp()
352 ? last_packet_duration_ 368 ? last_packet_duration_
353 : base::TimeDelta::FromMicroseconds(1))); 369 : base::TimeDelta::FromMicroseconds(1)));
354 } 370 }
355 371
356 // The demuxer should always output positive timestamps. 372 // The demuxer should always output positive timestamps.
357 DCHECK(buffer->timestamp() >= base::TimeDelta()); 373 DCHECK(buffer->timestamp() >= base::TimeDelta());
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
621 // otherwise we can end up waiting for a pre-seek read to complete even though 637 // otherwise we can end up waiting for a pre-seek read to complete even though
622 // we know we're going to drop it on the floor. 638 // we know we're going to drop it on the floor.
623 639
624 // FFmpeg requires seeks to be adjusted according to the lowest starting time. 640 // FFmpeg requires seeks to be adjusted according to the lowest starting time.
625 // Since EnqueuePacket() rebased negative timestamps by the start time, we 641 // Since EnqueuePacket() rebased negative timestamps by the start time, we
626 // must correct the shift here. 642 // must correct the shift here.
627 // 643 //
628 // Additionally, to workaround limitations in how we expose seekable ranges to 644 // Additionally, to workaround limitations in how we expose seekable ranges to
629 // Blink (http://crbug.com/137275), we also want to clamp seeks before the 645 // Blink (http://crbug.com/137275), we also want to clamp seeks before the
630 // start time to the start time. 646 // start time to the start time.
631 const base::TimeDelta seek_time = 647 base::TimeDelta seek_time = start_time_ < base::TimeDelta()
632 start_time_ < base::TimeDelta() ? time + start_time_ 648 ? time + start_time_
633 : time < start_time_ ? start_time_ : time; 649 : time < start_time_ ? start_time_ : time;
650
651 // When seeking in an opus stream we need to ensure we deliver enough data to
652 // satisfy the seek preroll; otherwise the audio at the actual seek time will
653 // not be entirely accurate.
654 FFmpegDemuxerStream* audio_stream = GetFFmpegStream(DemuxerStream::AUDIO);
655 if (audio_stream) {
656 const AudioDecoderConfig& config = audio_stream->audio_decoder_config();
657 if (config.codec() == kCodecOpus)
658 seek_time = std::max(start_time_, seek_time - config.seek_preroll());
659 }
634 660
635 // Choose the seeking stream based on whether it contains the seek time, if no 661 // Choose the seeking stream based on whether it contains the seek time, if no
636 // match can be found prefer the preferred stream. 662 // match can be found prefer the preferred stream.
637 // 663 //
638 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a 664 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a
639 // given container will demux all packets after the seek point. Instead it 665 // given container will demux all packets after the seek point. Instead it
640 // only guarantees that all packets after the file position of the seek will 666 // only guarantees that all packets after the file position of the seek will
641 // be demuxed. It's an open question whether FFmpeg should fix this: 667 // be demuxed. It's an open question whether FFmpeg should fix this:
642 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html 668 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html
643 // Tracked by http://crbug.com/387996. 669 // Tracked by http://crbug.com/387996.
(...skipping 309 matching lines...) Expand 10 before | Expand all | Expand 10 after
953 // maximum between it and the duration from A/V streams. 979 // maximum between it and the duration from A/V streams.
954 const AVRational av_time_base = {1, AV_TIME_BASE}; 980 const AVRational av_time_base = {1, AV_TIME_BASE};
955 max_duration = 981 max_duration =
956 std::max(max_duration, 982 std::max(max_duration,
957 ConvertFromTimeBase(av_time_base, format_context->duration)); 983 ConvertFromTimeBase(av_time_base, format_context->duration));
958 } else { 984 } else {
959 // The duration is unknown, in which case this is likely a live stream. 985 // The duration is unknown, in which case this is likely a live stream.
960 max_duration = kInfiniteDuration(); 986 max_duration = kInfiniteDuration();
961 } 987 }
962 988
963 // Ogg has some peculiarities around negative timestamps, so use this flag to 989 // FFmpeg represents audio data marked as before the beginning of stream as
964 // setup the FFmpegDemuxerStreams appropriately. 990 // having negative timestamps. This data must be discarded after it has been
991 // decoded, not before since it is used to warmup the decoder. There are
992 // currently two known cases for this: vorbis in ogg and opus in ogg and webm.
993 //
994 // For API clarity, it was decided that the rest of the media pipeline should
995 // not be exposed to negative timestamps. Which means we need to rebase these
996 // negative timestamps and mark them for discard post decoding.
965 // 997 //
966 // Post-decode frame dropping for packets with negative timestamps is outlined 998 // Post-decode frame dropping for packets with negative timestamps is outlined
967 // in section A.2 in the Ogg Vorbis spec: 999 // in section A.2 in the Ogg Vorbis spec:
968 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html 1000 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
969 if (strcmp(format_context->iformat->name, "ogg") == 0 && audio_stream && 1001 //
970 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS) { 1002 // FFmpeg's use of negative timestamps for opus pre-skip is nonstandard, but
1003 // for more information on pre-skip see section 4.2 of the Ogg Opus spec:
1004 // https://tools.ietf.org/html/draft-ietf-codec-oggopus-08#section-4.2
1005 if (audio_stream && (audio_stream->codec->codec_id == AV_CODEC_ID_OPUS ||
1006 (strcmp(format_context->iformat->name, "ogg") == 0 &&
1007 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS))) {
971 for (size_t i = 0; i < streams_.size(); ++i) { 1008 for (size_t i = 0; i < streams_.size(); ++i) {
972 if (streams_[i]) 1009 if (streams_[i])
973 streams_[i]->enable_negative_timestamp_fixups_for_ogg(); 1010 streams_[i]->enable_negative_timestamp_fixups();
974 } 1011 }
975 1012
976 // Fixup the seeking information to avoid selecting the audio stream simply 1013 // Fixup the seeking information to avoid selecting the audio stream simply
977 // because it has a lower starting time. 1014 // because it has a lower starting time.
978 if (fallback_stream_for_seeking_.first == audio_stream->index && 1015 if (fallback_stream_for_seeking_.first == audio_stream->index &&
979 fallback_stream_for_seeking_.second < base::TimeDelta()) { 1016 fallback_stream_for_seeking_.second < base::TimeDelta()) {
980 fallback_stream_for_seeking_.second = base::TimeDelta(); 1017 fallback_stream_for_seeking_.second = base::TimeDelta();
981 } 1018 }
982 } 1019 }
983 1020
(...skipping 213 matching lines...) Expand 10 before | Expand all | Expand 10 after
1197 // when av_read_frame() returns success code. See bug comment for ideas: 1234 // when av_read_frame() returns success code. See bug comment for ideas:
1198 // 1235 //
1199 // https://code.google.com/p/chromium/issues/detail?id=169133#c10 1236 // https://code.google.com/p/chromium/issues/detail?id=169133#c10
1200 if (!packet->data) { 1237 if (!packet->data) {
1201 ScopedAVPacket new_packet(new AVPacket()); 1238 ScopedAVPacket new_packet(new AVPacket());
1202 av_new_packet(new_packet.get(), 0); 1239 av_new_packet(new_packet.get(), 0);
1203 av_packet_copy_props(new_packet.get(), packet.get()); 1240 av_packet_copy_props(new_packet.get(), packet.get());
1204 packet.swap(new_packet); 1241 packet.swap(new_packet);
1205 } 1242 }
1206 1243
1207 // Special case for opus in ogg. FFmpeg is pre-trimming the codec delay
1208 // from the packet timestamp. Chrome expects to handle this itself inside
1209 // the decoder, so shift timestamps by the delay in this case.
1210 // TODO(dalecurtis): Try to get fixed upstream. See http://crbug.com/328207
1211 if (strcmp(glue_->format_context()->iformat->name, "ogg") == 0) {
1212 const AVCodecContext* codec_context =
1213 glue_->format_context()->streams[packet->stream_index]->codec;
1214 if (codec_context->codec_id == AV_CODEC_ID_OPUS &&
1215 codec_context->delay > 0) {
1216 packet->pts += codec_context->delay;
1217 }
1218 }
1219
1220 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index]; 1244 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index];
1221 demuxer_stream->EnqueuePacket(packet.Pass()); 1245 demuxer_stream->EnqueuePacket(packet.Pass());
1222 } 1246 }
1223 1247
1224 // Keep reading until we've reached capacity. 1248 // Keep reading until we've reached capacity.
1225 ReadFrameIfNeeded(); 1249 ReadFrameIfNeeded();
1226 } 1250 }
1227 1251
1228 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { 1252 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() {
1229 DCHECK(task_runner_->BelongsToCurrentThread()); 1253 DCHECK(task_runner_->BelongsToCurrentThread());
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
1303 1327
1304 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) { 1328 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) {
1305 DCHECK(task_runner_->BelongsToCurrentThread()); 1329 DCHECK(task_runner_->BelongsToCurrentThread());
1306 for (const auto& stream : streams_) { // |stream| is a ref to a pointer. 1330 for (const auto& stream : streams_) { // |stream| is a ref to a pointer.
1307 if (stream) 1331 if (stream)
1308 stream->SetLiveness(liveness); 1332 stream->SetLiveness(liveness);
1309 } 1333 }
1310 } 1334 }
1311 1335
1312 } // namespace media 1336 } // 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