Chromium Code Reviews| 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 | 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 Loading... | |
| 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 |
| OLD | NEW |