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

Unified Diff: media/filters/ffmpeg_demuxer.cc

Issue 325503003: Fix seeking when the start time is non-zero. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Comments. Created 6 years, 6 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 side-by-side diff with in-line comments
Download patch
Index: media/filters/ffmpeg_demuxer.cc
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index f5b4fddad3b97f03040d8f5f209f940f9d8d3d4c..2b095214be882acafd43bb571e36c5afd61fe397 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -55,19 +55,39 @@ static base::TimeDelta FramesToTimeDelta(int frames, double sample_rate) {
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)
+ AVStream* stream,
+ bool discard_negative_timestamps)
: demuxer_(demuxer),
task_runner_(base::MessageLoopProxy::current()),
stream_(stream),
type_(UNKNOWN),
end_of_stream_(false),
last_packet_timestamp_(kNoTimestamp()),
- bitstream_converter_enabled_(false) {
+ bitstream_converter_enabled_(false),
+ discard_negative_timestamps_(discard_negative_timestamps) {
DCHECK(demuxer_);
bool is_encrypted = false;
@@ -226,15 +246,40 @@ void FFmpegDemuxerStream::EnqueuePacket(ScopedAVPacket packet) {
buffer->set_decrypt_config(decrypt_config.Pass());
}
- 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();
+ 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());
}
last_packet_timestamp_ = buffer->timestamp();
acolwell GONE FROM CHROMIUM 2014/06/13 18:12:13 Can't this cause gaps in the buffered ranges repor
DaleCurtis 2014/06/13 20:40:02 We should probably just stop allowing kNoTimestamp
@@ -268,10 +313,6 @@ void FFmpegDemuxerStream::Stop() {
end_of_stream_ = true;
}
-base::TimeDelta FFmpegDemuxerStream::duration() {
- return duration_;
-}
-
DemuxerStream::Type FFmpegDemuxerStream::type() {
DCHECK(task_runner_->BelongsToCurrentThread());
return type_;
@@ -420,6 +461,7 @@ FFmpegDemuxer::FFmpegDemuxer(
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_);
@@ -445,21 +487,20 @@ void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) {
// 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.
- // Always seek to a timestamp less than or equal to the desired timestamp.
- int flags = AVSEEK_FLAG_BACKWARD;
+ const AVStream* seeking_stream =
+ glue_->format_context()->streams[stream_index_for_seeking_];
- // 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(),
- -1,
- time.InMicroseconds(),
- flags),
+ 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(
&FFmpegDemuxer::OnSeekFrameDone, weak_factory_.GetWeakPtr(), cb));
}
@@ -508,11 +549,6 @@ FFmpegDemuxerStream* FFmpegDemuxer::GetFFmpegStream(
return NULL;
}
-base::TimeDelta FFmpegDemuxer::GetStartTime() const {
- DCHECK(task_runner_->BelongsToCurrentThread());
- return start_time_;
-}
-
base::Time FFmpegDemuxer::GetTimelineOffset() const {
return timeline_offset_;
}
@@ -628,11 +664,15 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
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];
AVCodecContext* codec_context = stream->codec;
AVMediaType codec_type = codec_context->codec_type;
+ bool discard_negative_timestamps = false;
if (codec_type == AVMEDIA_TYPE_AUDIO) {
if (audio_stream)
@@ -647,6 +687,20 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
if (!audio_config.IsValidConfig())
continue;
audio_stream = stream;
+
+ const base::TimeDelta audio_start_time = ExtractStartTime(stream);
+ if (audio_start_time != kNoTimestamp() &&
+ audio_start_time < start_time_) {
+ stream_index_for_seeking_ = i;
+ start_time_ = audio_start_time;
+ }
+
+ // 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;
@@ -661,6 +715,15 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
if (!video_config.IsValidConfig())
continue;
video_stream = stream;
+
+ // Find the video start time. Note the <= comparison causes the video
+ // stream to be preferred for seeking when start times match.
+ const base::TimeDelta video_start_time = ExtractStartTime(stream);
+ if (video_start_time != kNoTimestamp() &&
+ video_start_time <= start_time_) {
+ stream_index_for_seeking_ = i;
+ start_time_ = video_start_time;
+ }
} else if (codec_type == AVMEDIA_TYPE_SUBTITLE) {
if (codec_context->codec_id != AV_CODEC_ID_WEBVTT || !text_enabled_) {
continue;
@@ -669,15 +732,9 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
continue;
}
acolwell GONE FROM CHROMIUM 2014/06/13 18:12:12 Can't we move the start_time check/update here and
DaleCurtis 2014/06/13 20:40:02 Done.
- streams_[i] = new FFmpegDemuxerStream(this, stream);
+ streams_[i] =
+ new FFmpegDemuxerStream(this, stream, discard_negative_timestamps);
max_duration = std::max(max_duration, streams_[i]->duration());
-
- 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;
- }
}
if (!audio_stream && !video_stream) {
@@ -700,10 +757,14 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
max_duration = kInfiniteDuration();
}
- // Some demuxers, like WAV, do not put timestamps on their frames. We
- // assume the the start time is 0.
- if (start_time_ == kNoTimestamp())
+ // 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()) {
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
@@ -712,6 +773,11 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
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;
} else if (max_duration != kInfiniteDuration()) {
@@ -783,7 +849,6 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb,
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_);

Powered by Google App Engine
This is Rietveld 408576698