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

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: Simplify 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
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 is an OGG file with negative timestamps don't rebase any other
wolenetz 2015/08/06 22:21:27 nit: s/an OGG/a/ ?
DaleCurtis 2015/08/06 23:45:27 Already fixed in later patch set.
298 // stream types against the negative starting time. 298 // stream types 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.
chcunningham 2015/07/31 19:48:44 just to check my understanding.... 1. we see nega
DaleCurtis 2015/08/03 22:49:53 Yes that is correct. We're confident because this
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() && !stream_->codec->delay) {
318 DCHECK_EQ(buffer->discard_padding().first, base::TimeDelta());
319
318 if (stream_timestamp + buffer->duration() < base::TimeDelta()) { 320 if (stream_timestamp + buffer->duration() < base::TimeDelta()) {
321 DCHECK_EQ(buffer->discard_padding().second, base::TimeDelta());
322
319 // Discard the entire packet if it's entirely before zero. 323 // Discard the entire packet if it's entirely before zero.
320 buffer->set_discard_padding( 324 buffer->set_discard_padding(
321 std::make_pair(kInfiniteDuration(), base::TimeDelta())); 325 std::make_pair(kInfiniteDuration(), base::TimeDelta()));
322 } else { 326 } else {
323 // Only discard part of the frame if it overlaps zero. 327 buffer->set_discard_padding(std::make_pair(
chcunningham 2015/07/31 19:48:44 Did you mean to kill the comment?
DaleCurtis 2015/08/03 22:49:53 Nope, reverted.
324 buffer->set_discard_padding( 328 -stream_timestamp, buffer->discard_padding().second));
chcunningham 2015/07/31 19:48:44 I can see why this is more correct, but I'm curiou
DaleCurtis 2015/08/03 22:49:53 Just fixing this as wolenetz@ complained about it
325 std::make_pair(-stream_timestamp, base::TimeDelta()));
326 } 329 }
327 } 330 }
328 } else { 331 } else {
329 // If this happens on the first packet, decoders will throw an error. 332 // If this happens on the first packet, decoders will throw an error.
330 buffer->set_timestamp(kNoTimestamp()); 333 buffer->set_timestamp(kNoTimestamp());
331 } 334 }
332 335
333 if (last_packet_timestamp_ != kNoTimestamp()) { 336 if (last_packet_timestamp_ != kNoTimestamp()) {
334 // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing 337 // FFmpeg doesn't support chained ogg correctly. Instead of guaranteeing
335 // continuity across links in the chain it uses the timestamp information 338 // 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 339 // from each link directly. Doing so can lead to timestamps which appear to
337 // go backwards in time. 340 // go backwards in time.
338 // 341 //
339 // If the new link starts with a negative timestamp or a timestamp less than 342 // 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 343 // 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 344 // 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. 345 // able to work out a timestamp using the previous link and the next.
343 // 346 //
344 // Fixing chained ogg is non-trivial, so for now just reuse the last good 347 // 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 348 // timestamp. The decoder will rewrite the timestamps to be sample accurate
346 // later. See http://crbug.com/396864. 349 // later. See http://crbug.com/396864.
347 if (fixup_negative_ogg_timestamps_ && 350 if (fixup_negative_timestamps_ &&
348 (buffer->timestamp() == kNoTimestamp() || 351 (buffer->timestamp() == kNoTimestamp() ||
349 buffer->timestamp() < last_packet_timestamp_)) { 352 buffer->timestamp() < last_packet_timestamp_)) {
350 buffer->set_timestamp(last_packet_timestamp_ + 353 buffer->set_timestamp(last_packet_timestamp_ +
351 (last_packet_duration_ != kNoTimestamp() 354 (last_packet_duration_ != kNoTimestamp()
352 ? last_packet_duration_ 355 ? last_packet_duration_
353 : base::TimeDelta::FromMicroseconds(1))); 356 : base::TimeDelta::FromMicroseconds(1)));
354 } 357 }
355 358
356 // The demuxer should always output positive timestamps. 359 // The demuxer should always output positive timestamps.
357 DCHECK(buffer->timestamp() >= base::TimeDelta()); 360 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 624 // 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. 625 // we know we're going to drop it on the floor.
623 626
624 // FFmpeg requires seeks to be adjusted according to the lowest starting time. 627 // FFmpeg requires seeks to be adjusted according to the lowest starting time.
625 // Since EnqueuePacket() rebased negative timestamps by the start time, we 628 // Since EnqueuePacket() rebased negative timestamps by the start time, we
626 // must correct the shift here. 629 // must correct the shift here.
627 // 630 //
628 // Additionally, to workaround limitations in how we expose seekable ranges to 631 // 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 632 // Blink (http://crbug.com/137275), we also want to clamp seeks before the
630 // start time to the start time. 633 // start time to the start time.
631 const base::TimeDelta seek_time = 634 base::TimeDelta seek_time = start_time_ < base::TimeDelta()
632 start_time_ < base::TimeDelta() ? time + start_time_ 635 ? time + start_time_
633 : time < start_time_ ? start_time_ : time; 636 : time < start_time_ ? start_time_ : time;
chcunningham 2015/07/31 19:48:44 I don't follow why we only add start_time_ in the
DaleCurtis 2015/08/03 22:49:53 See l.305, the gist is that we don't rebase positi
chcunningham 2015/08/04 20:38:09 I see. This is a fragile contract between two meth
637
638 // When seeking in an opus stream we need to ensure we deliver enough data to
639 // satisfy the seek preroll; otherwise the audio at the actual seek time will
640 // not be entirely accurate.
641 FFmpegDemuxerStream* audio_stream = GetFFmpegStream(DemuxerStream::AUDIO);
642 if (audio_stream) {
643 const AudioDecoderConfig& config = audio_stream->audio_decoder_config();
644 if (config.codec() == kCodecOpus)
chcunningham 2015/07/31 19:48:44 How does this work for other codecs that need pre-
DaleCurtis 2015/08/03 22:49:53 It doesn't :) No other codecs specify this informa
chcunningham 2015/08/04 20:38:09 Is this something we can extract into a utility us
DaleCurtis 2015/08/04 21:38:34 This is already encoded in the seek_preroll() and
645 seek_time = std::max(start_time_, seek_time - config.seek_preroll());
646 }
634 647
635 // Choose the seeking stream based on whether it contains the seek time, if no 648 // Choose the seeking stream based on whether it contains the seek time, if no
636 // match can be found prefer the preferred stream. 649 // match can be found prefer the preferred stream.
637 // 650 //
638 // TODO(dalecurtis): Currently FFmpeg does not ensure that all streams in a 651 // 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 652 // 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 653 // 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: 654 // be demuxed. It's an open question whether FFmpeg should fix this:
642 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html 655 // http://lists.ffmpeg.org/pipermail/ffmpeg-devel/2014-June/159212.html
643 // Tracked by http://crbug.com/387996. 656 // 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. 966 // maximum between it and the duration from A/V streams.
954 const AVRational av_time_base = {1, AV_TIME_BASE}; 967 const AVRational av_time_base = {1, AV_TIME_BASE};
955 max_duration = 968 max_duration =
956 std::max(max_duration, 969 std::max(max_duration,
957 ConvertFromTimeBase(av_time_base, format_context->duration)); 970 ConvertFromTimeBase(av_time_base, format_context->duration));
958 } else { 971 } else {
959 // The duration is unknown, in which case this is likely a live stream. 972 // The duration is unknown, in which case this is likely a live stream.
960 max_duration = kInfiniteDuration(); 973 max_duration = kInfiniteDuration();
961 } 974 }
962 975
963 // Ogg has some peculiarities around negative timestamps, so use this flag to 976 // FFmpeg represents audio data marked as before the beginning of stream as
964 // setup the FFmpegDemuxerStreams appropriately. 977 // having negative timestamps. This data must be discarded after it has been
978 // decoded, not before since it is used to warmup the decoder. There are
979 // currently two known cases for this: vorbis in ogg and opus in ogg and webm.
980 //
981 // For API clarity, it was decided that the rest of the media pipeline should
982 // not be exposed to negative timestamps. Which means we need to rebase these
983 // negative timestamps and mark them for discard post decoding.
965 // 984 //
966 // Post-decode frame dropping for packets with negative timestamps is outlined 985 // Post-decode frame dropping for packets with negative timestamps is outlined
967 // in section A.2 in the Ogg Vorbis spec: 986 // in section A.2 in the Ogg Vorbis spec:
968 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html 987 // http://xiph.org/vorbis/doc/Vorbis_I_spec.html
969 if (strcmp(format_context->iformat->name, "ogg") == 0 && audio_stream && 988 //
970 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS) { 989 // FFmpeg's use of negative timestamps for opus pre-skip is nonstandard, but
990 // for more information on pre-skip see section 4.2 of the Ogg Opus spec:
991 // https://tools.ietf.org/html/draft-ietf-codec-oggopus-08#section-4.2
992 if (audio_stream && (audio_stream->codec->codec_id == AV_CODEC_ID_OPUS ||
993 (strcmp(format_context->iformat->name, "ogg") == 0 &&
994 audio_stream->codec->codec_id == AV_CODEC_ID_VORBIS))) {
971 for (size_t i = 0; i < streams_.size(); ++i) { 995 for (size_t i = 0; i < streams_.size(); ++i) {
972 if (streams_[i]) 996 if (streams_[i])
973 streams_[i]->enable_negative_timestamp_fixups_for_ogg(); 997 streams_[i]->enable_negative_timestamp_fixups();
chcunningham 2015/07/31 19:48:44 Why do we do this for all streams and not just the
DaleCurtis 2015/08/03 22:49:53 Because we need to tell the video stream that it s
chcunningham 2015/08/04 20:38:09 I see. I feel like this should really be called e
DaleCurtis 2015/08/04 21:38:34 Well, it has different effects on audio and video,
974 } 998 }
975 999
976 // Fixup the seeking information to avoid selecting the audio stream simply 1000 // Fixup the seeking information to avoid selecting the audio stream simply
977 // because it has a lower starting time. 1001 // because it has a lower starting time.
978 if (fallback_stream_for_seeking_.first == audio_stream->index && 1002 if (fallback_stream_for_seeking_.first == audio_stream->index &&
979 fallback_stream_for_seeking_.second < base::TimeDelta()) { 1003 fallback_stream_for_seeking_.second < base::TimeDelta()) {
980 fallback_stream_for_seeking_.second = base::TimeDelta(); 1004 fallback_stream_for_seeking_.second = base::TimeDelta();
981 } 1005 }
982 } 1006 }
983 1007
(...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: 1221 // when av_read_frame() returns success code. See bug comment for ideas:
1198 // 1222 //
1199 // https://code.google.com/p/chromium/issues/detail?id=169133#c10 1223 // https://code.google.com/p/chromium/issues/detail?id=169133#c10
1200 if (!packet->data) { 1224 if (!packet->data) {
1201 ScopedAVPacket new_packet(new AVPacket()); 1225 ScopedAVPacket new_packet(new AVPacket());
1202 av_new_packet(new_packet.get(), 0); 1226 av_new_packet(new_packet.get(), 0);
1203 av_packet_copy_props(new_packet.get(), packet.get()); 1227 av_packet_copy_props(new_packet.get(), packet.get());
1204 packet.swap(new_packet); 1228 packet.swap(new_packet);
1205 } 1229 }
1206 1230
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]; 1231 FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index];
1221 demuxer_stream->EnqueuePacket(packet.Pass()); 1232 demuxer_stream->EnqueuePacket(packet.Pass());
1222 } 1233 }
1223 1234
1224 // Keep reading until we've reached capacity. 1235 // Keep reading until we've reached capacity.
1225 ReadFrameIfNeeded(); 1236 ReadFrameIfNeeded();
1226 } 1237 }
1227 1238
1228 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() { 1239 bool FFmpegDemuxer::StreamsHaveAvailableCapacity() {
1229 DCHECK(task_runner_->BelongsToCurrentThread()); 1240 DCHECK(task_runner_->BelongsToCurrentThread());
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after
1303 1314
1304 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) { 1315 void FFmpegDemuxer::SetLiveness(DemuxerStream::Liveness liveness) {
1305 DCHECK(task_runner_->BelongsToCurrentThread()); 1316 DCHECK(task_runner_->BelongsToCurrentThread());
1306 for (const auto& stream : streams_) { // |stream| is a ref to a pointer. 1317 for (const auto& stream : streams_) { // |stream| is a ref to a pointer.
1307 if (stream) 1318 if (stream)
1308 stream->SetLiveness(liveness); 1319 stream->SetLiveness(liveness);
1309 } 1320 }
1310 } 1321 }
1311 1322
1312 } // namespace media 1323 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698