Index: media/filters/ffmpeg_demuxer.cc |
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc |
index aac8002145c6625d29394c73210230aaf01ef9c3..f5b4fddad3b97f03040d8f5f209f940f9d8d3d4c 100644 |
--- a/media/filters/ffmpeg_demuxer.cc |
+++ b/media/filters/ffmpeg_demuxer.cc |
@@ -55,39 +55,19 @@ |
frames * base::Time::kMicrosecondsPerSecond / sample_rate); |
} |
-static base::TimeDelta ExtractStartTime(AVStream* stream) { |
- if (stream->start_time == static_cast<int64_t>(AV_NOPTS_VALUE)) |
- return kNoTimestamp(); |
- |
- // First try to use the |start_time| value directly. |
- const base::TimeDelta start_time = |
- ConvertFromTimeBase(stream->time_base, stream->start_time); |
- |
- // Then compare against the first timestamp to see if adjustment is required. |
- if (stream->first_dts == static_cast<int64_t>(AV_NOPTS_VALUE)) |
- return start_time; |
- |
- const base::TimeDelta first_dts = |
- ConvertFromTimeBase(stream->time_base, stream->first_dts); |
- |
- return first_dts < start_time ? first_dts : start_time; |
-} |
- |
// |
// FFmpegDemuxerStream |
// |
FFmpegDemuxerStream::FFmpegDemuxerStream( |
FFmpegDemuxer* demuxer, |
- AVStream* stream, |
- bool discard_negative_timestamps) |
+ AVStream* stream) |
: demuxer_(demuxer), |
task_runner_(base::MessageLoopProxy::current()), |
stream_(stream), |
type_(UNKNOWN), |
end_of_stream_(false), |
last_packet_timestamp_(kNoTimestamp()), |
- bitstream_converter_enabled_(false), |
- discard_negative_timestamps_(discard_negative_timestamps) { |
+ bitstream_converter_enabled_(false) { |
DCHECK(demuxer_); |
bool is_encrypted = false; |
@@ -246,44 +226,16 @@ |
buffer->set_decrypt_config(decrypt_config.Pass()); |
} |
- buffer->set_duration( |
- ConvertStreamTimestamp(stream_->time_base, packet->duration)); |
- |
- // Note: If pts is AV_NOPTS_VALUE, stream_timestamp will be kNoTimestamp(). |
- const base::TimeDelta stream_timestamp = |
- ConvertStreamTimestamp(stream_->time_base, packet->pts); |
- |
- if (stream_timestamp != kNoTimestamp()) { |
- buffer->set_timestamp(stream_timestamp - demuxer_->start_time()); |
- |
- // If enabled, mark packets with negative timestamps for post-decode |
- // discard. |
- if (discard_negative_timestamps_ && stream_timestamp < base::TimeDelta()) { |
- if (stream_timestamp + buffer->duration() < base::TimeDelta()) { |
- // Discard the entier packet if it's entirely before zero. |
- buffer->set_discard_padding( |
- std::make_pair(kInfiniteDuration(), base::TimeDelta())); |
- } else { |
- // Only discard part of the frame if it overlaps zero. |
- buffer->set_discard_padding( |
- std::make_pair(-stream_timestamp, base::TimeDelta())); |
- } |
- } |
- |
- if (last_packet_timestamp_ != kNoTimestamp() && |
- last_packet_timestamp_ < buffer->timestamp()) { |
- buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); |
- demuxer_->NotifyBufferingChanged(); |
- } |
- |
- // The demuxer should always output positive timestamps. |
- DCHECK(buffer->timestamp() >= base::TimeDelta()); |
- } else { |
- buffer->set_timestamp(kNoTimestamp()); |
- } |
- |
- // TODO(dalecurtis): This allows transitions from <valid ts> -> <no timestamp> |
- // which shouldn't be allowed. See http://crbug.com/384532 |
+ buffer->set_timestamp(ConvertStreamTimestamp( |
+ stream_->time_base, packet->pts)); |
+ buffer->set_duration(ConvertStreamTimestamp( |
+ stream_->time_base, packet->duration)); |
+ if (buffer->timestamp() != kNoTimestamp() && |
+ last_packet_timestamp_ != kNoTimestamp() && |
+ last_packet_timestamp_ < buffer->timestamp()) { |
+ buffered_ranges_.Add(last_packet_timestamp_, buffer->timestamp()); |
+ demuxer_->NotifyBufferingChanged(); |
+ } |
last_packet_timestamp_ = buffer->timestamp(); |
buffer_queue_.Push(buffer); |
@@ -314,6 +266,10 @@ |
demuxer_ = NULL; |
stream_ = NULL; |
end_of_stream_ = true; |
+} |
+ |
+base::TimeDelta FFmpegDemuxerStream::duration() { |
+ return duration_; |
} |
DemuxerStream::Type FFmpegDemuxerStream::type() { |
@@ -464,7 +420,6 @@ |
text_enabled_(false), |
duration_known_(false), |
need_key_cb_(need_key_cb), |
- stream_index_for_seeking_(0), |
weak_factory_(this) { |
DCHECK(task_runner_.get()); |
DCHECK(data_source_); |
@@ -490,20 +445,21 @@ |
// otherwise we can end up waiting for a pre-seek read to complete even though |
// we know we're going to drop it on the floor. |
- const AVStream* seeking_stream = |
- glue_->format_context()->streams[stream_index_for_seeking_]; |
- |
+ // Always seek to a timestamp less than or equal to the desired timestamp. |
+ int flags = AVSEEK_FLAG_BACKWARD; |
+ |
+ // Passing -1 as our stream index lets FFmpeg pick a default stream. FFmpeg |
+ // will attempt to use the lowest-index video stream, if present, followed by |
+ // the lowest-index audio stream. |
pending_seek_ = true; |
base::PostTaskAndReplyWithResult( |
blocking_thread_.message_loop_proxy().get(), |
FROM_HERE, |
- base::Bind( |
- &av_seek_frame, |
- glue_->format_context(), |
- stream_index_for_seeking_, |
- ConvertToTimeBase(seeking_stream->time_base, time + start_time()), |
- // Always seek to a timestamp <= to the desired timestamp. |
- AVSEEK_FLAG_BACKWARD), |
+ base::Bind(&av_seek_frame, |
+ glue_->format_context(), |
+ -1, |
+ time.InMicroseconds(), |
+ flags), |
base::Bind( |
&FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr(), cb)); |
} |
@@ -550,6 +506,11 @@ |
} |
} |
return NULL; |
+} |
+ |
+base::TimeDelta FFmpegDemuxer::GetStartTime() const { |
+ DCHECK(task_runner_->BelongsToCurrentThread()); |
+ return start_time_; |
} |
base::Time FFmpegDemuxer::GetTimelineOffset() const { |
@@ -667,15 +628,11 @@ |
AVStream* video_stream = NULL; |
VideoDecoderConfig video_config; |
- // If available, |start_time_| will be set to the lowest stream start time. |
- start_time_ = kInfiniteDuration(); |
- |
base::TimeDelta max_duration; |
for (size_t i = 0; i < format_context->nb_streams; ++i) { |
AVStream* stream = format_context->streams[i]; |
- const AVCodecContext* codec_context = stream->codec; |
- const AVMediaType codec_type = codec_context->codec_type; |
- bool discard_negative_timestamps = false; |
+ AVCodecContext* codec_context = stream->codec; |
+ AVMediaType codec_type = codec_context->codec_type; |
if (codec_type == AVMEDIA_TYPE_AUDIO) { |
if (audio_stream) |
@@ -690,13 +647,6 @@ |
if (!audio_config.IsValidConfig()) |
continue; |
audio_stream = stream; |
- |
- // Enable post-decode frame dropping for packets with negative timestamps |
- // as outlined in section A.2 in the Ogg Vorbis spec: |
- // http://xiph.org/vorbis/doc/Vorbis_I_spec.html |
- discard_negative_timestamps = |
- audio_config.codec() == kCodecVorbis && |
- strcmp(glue_->format_context()->iformat->name, "ogg") == 0; |
} else if (codec_type == AVMEDIA_TYPE_VIDEO) { |
if (video_stream) |
continue; |
@@ -719,20 +669,14 @@ |
continue; |
} |
- streams_[i] = |
- new FFmpegDemuxerStream(this, stream, discard_negative_timestamps); |
+ streams_[i] = new FFmpegDemuxerStream(this, stream); |
max_duration = std::max(max_duration, streams_[i]->duration()); |
- const base::TimeDelta start_time = ExtractStartTime(stream); |
- if (start_time == kNoTimestamp()) |
- continue; |
- |
- // Find the lowest stream start time. Prefer the video stream for seeking |
- // in the event of matching stream start times. |
- if (start_time < start_time_ || |
- (codec_type == AVMEDIA_TYPE_VIDEO && start_time <= start_time_)) { |
- stream_index_for_seeking_ = i; |
- start_time_ = start_time; |
+ if (stream->first_dts != static_cast<int64_t>(AV_NOPTS_VALUE)) { |
+ const base::TimeDelta first_dts = ConvertFromTimeBase( |
+ stream->time_base, stream->first_dts); |
+ if (start_time_ == kNoTimestamp() || first_dts < start_time_) |
+ start_time_ = first_dts; |
} |
} |
@@ -756,14 +700,10 @@ |
max_duration = kInfiniteDuration(); |
} |
- // If no start time could be determined, default to zero and prefer the video |
- // stream over the audio stream for seeking. E.g., The WAV demuxer does not |
- // put timestamps on its frames. |
- if (start_time_ == kInfiniteDuration()) { |
+ // Some demuxers, like WAV, do not put timestamps on their frames. We |
+ // assume the the start time is 0. |
+ if (start_time_ == kNoTimestamp()) |
start_time_ = base::TimeDelta(); |
- stream_index_for_seeking_ = |
- video_stream ? video_stream->index : audio_stream->index; |
- } |
// MPEG-4 B-frames cause grief for a simple container like AVI. Enable PTS |
// generation so we always get timestamps, see http://crbug.com/169570 |
@@ -771,11 +711,6 @@ |
format_context->flags |= AVFMT_FLAG_GENPTS; |
timeline_offset_ = ExtractTimelineOffset(format_context); |
- |
- // Since we're shifting the externally visible start time to zero, we need to |
- // adjust the timeline offset to compensate. |
- if (!timeline_offset_.is_null()) |
- timeline_offset_ += start_time_; |
if (max_duration == kInfiniteDuration() && !timeline_offset_.is_null()) { |
liveness_ = LIVENESS_LIVE; |
@@ -848,6 +783,7 @@ |
media_log_->SetBooleanProperty("found_video_stream", false); |
} |
+ |
media_log_->SetTimeProperty("max_duration", max_duration); |
media_log_->SetTimeProperty("start_time", start_time_); |
media_log_->SetIntegerProperty("bitrate", bitrate_); |