_log->log (String::compose ("-> ADS has request for %1 %2", frame, length), LogEntry::TYPE_DEBUG_DECODE);
- Frame const end = frame + length - 1;
-
- /* If we are less than (about) 5 seconds behind the data that we want we'll
- run through it rather than seeking.
- */
- Frame const slack = 5 * 48000;
+ Frame const from = frame;
+ Frame const to = from + length;
+ Frame const have_from = _decoded.frame;
+ Frame const have_to = _decoded.frame + _decoded.audio->frames();
+
+ optional<Frame> missing;
+ if (have_from > from || have_to < to) {
+ /* We need something */
+ if (have_from < from && from < have_to) {
+ missing = have_to;
+ } else {
+ missing = from;
+ }
+ }
- if (frame < _decoded.frame || end > (_decoded.frame + _decoded.audio->frames() + slack)) {
- /* Either we have no decoded data, all our data is after the time that we
- want, or what we do have is a long way from what we want: seek */
- _decoder->seek (ContentTime::from_frames (frame, _content->resampled_frame_rate()), accurate);
+ if (missing) {
+ _decoder->maybe_seek (ContentTime::from_frames (*missing, _content->resampled_frame_rate()), accurate);
}
/* Offset of the data that we want from the start of _decoded.audio
if (accurate) {
/* Keep stuffing data into _decoded until we have enough data, or the subclass does not want to give us any more */
while (
- (_decoded.frame > frame || (_decoded.frame + _decoded.audio->frames()) < end) &&
+ (_decoded.frame > frame || (_decoded.frame + _decoded.audio->frames()) <= to) &&
!_decoder->pass (Decoder::PASS_REASON_AUDIO, accurate)
)
{}
decoded_offset = frame - _decoded.frame;
_log->log (
- String::compose ("Accurate ADS::get has offset %1 from request %2 and available %3", decoded_offset, frame, _decoded.frame),
+ String::compose ("Accurate ADS::get has offset %1 from request %2 and available %3", decoded_offset, frame, have_from),
LogEntry::TYPE_DEBUG_DECODE
);
} else {
}
}
+ _position = _next;
_next += ContentTime::from_frames (1, vfr);
if ((*_reel)->main_picture ()) {
}
subtitle->give_text (p, s);
+ _position = p.from;
return false;
}
--- /dev/null
+/*
+ Copyright (C) 2012-2015 Carl Hetherington <cth@carlh.net>
+
+ This file is part of DCP-o-matic.
+
+ DCP-o-matic is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ DCP-o-matic is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with DCP-o-matic. If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "decoder.h"
+
+void
+Decoder::maybe_seek (ContentTime time, bool accurate)
+{
+ if (!_position) {
+ /* A seek has just happened */
+ return;
+ }
+
+ if (time >= *_position && time < (*_position + ContentTime::from_seconds(1))) {
+ /* No need to seek: caller should just pass() */
+ return;
+ }
+
+ _position.reset ();
+ seek (time, accurate);
+}
PASS_REASON_SUBTITLE
};
+ void maybe_seek (ContentTime time, bool accurate);
+
+ /** @return true if this decoder has already returned all its data and will give no more */
+ virtual bool pass (PassReason, bool accurate) = 0;
+
+ /** Ensure that any future get() calls return data that reflect
+ * changes in our content's settings.
+ */
+ virtual void reset () {}
+
+protected:
+ boost::optional<ContentTime> _position;
+
+private:
/** Seek so that the next pass() will yield the next thing
* (video/sound frame, subtitle etc.) at or after the requested
* time. Pass accurate = true to try harder to ensure that, at worst,
* it may seek to just the right spot.
*/
virtual void seek (ContentTime time, bool accurate) = 0;
-
- /** @return true if this decoder has already returned all its data and will give no more */
- virtual bool pass (PassReason, bool accurate) = 0;
-
- /** Ensure that any future get() calls return data that reflect
- * changes in our content's settings.
- */
- virtual void reset () {}
};
#endif
LOG_WARNING ("Crazy timestamp %s", to_string (ct));
}
+ update_position (ct);
+
/* Give this data provided there is some, and its time is sane */
if (ct >= ContentTime() && data->frames() > 0) {
audio->give (*stream, data, ct);
shared_ptr<ImageProxy> (new RawImageProxy (image)),
llrint (pts * _ffmpeg_content->active_video_frame_rate ())
);
+ update_position (ContentTime::from_seconds (pts));
} else {
LOG_WARNING_NC ("Dropping frame without PTS");
}
FFmpegSubtitlePeriod sub_period = subtitle_period (sub);
ContentTimePeriod period;
period.from = sub_period.from + _pts_offset;
+ update_position (period.from);
if (sub_period.to) {
/* We already know the subtitle period `to' time */
period.to = sub_period.to.get() + _pts_offset;
subtitle->give_text (period, i);
}
}
+
+void
+FFmpegDecoder::update_position (ContentTime p)
+{
+ /* _position should err on the side of being too big, as then there is less
+ chance that we will erroneously decide not to seek when _position > request.
+ */
+ if (!_position) {
+ _position = p;
+ } else {
+ _position = max (*_position, p);
+ }
+}
bool pass (PassReason, bool accurate);
void seek (ContentTime time, bool);
void flush ();
+ void update_position (ContentTime p);
AVSampleFormat audio_sample_format (boost::shared_ptr<FFmpegAudioStream> stream) const;
int bytes_per_audio_sample (boost::shared_ptr<FFmpegAudioStream> stream) const;
void
FFmpegSubtitleStream::add_text_subtitle (string id, ContentTimePeriod period)
{
- cout << id << " " << to_string(period.from) << " " << to_string(period.to) << "\n";
DCPOMATIC_ASSERT (_text_subtitles.find (id) == _text_subtitles.end ());
_text_subtitles[id] = period;
}
}
}
+ _position = ContentTime::from_frames (_video_position, _image_content->active_video_frame_rate ());
video->give (_image, _video_position);
++_video_position;
return false;
return list<T> ();
}
- /* Seek if what we want is before what we have, or a more than a little bit after */
- if (subs.empty() || sp.back().to < subs.front().period().from || sp.front().from > (subs.back().period().to + ContentTime::from_seconds (1))) {
- _parent->seek (sp.front().from, true);
+ /* Find the time of the first subtitle we don't have in subs */
+ optional<ContentTime> missing;
+ BOOST_FOREACH (ContentTimePeriod i, sp) {
+ typename list<T>::const_iterator j = subs.begin();
+ while (j != subs.end() && j->period() != i) {
+ ++j;
+ }
+ if (j == subs.end ()) {
+ missing = i.from;
+ }
+ }
+
+ /* Suggest to our parent decoder that it might want to seek if we haven't got what we're being asked for */
+ if (missing) {
+ _parent->maybe_seek (*missing, true);
}
/* Now enough pass() calls will either:
return true;
}
- subtitle->give_text (content_time_period (_subtitles[_next]), _subtitles[_next]);
+ ContentTimePeriod const p = content_time_period (_subtitles[_next]);
+ subtitle->give_text (p, _subtitles[_next]);
+ _position = p.from;
++_next;
return false;
_log->log (String::compose ("VD has request for %1", frame), LogEntry::TYPE_DEBUG_DECODE);
- if (_decoded.empty() || frame < _decoded.front().frame.index() || frame > (_decoded.back().frame.index() + 1)) {
+ /* See if we have frame, and suggest a seek if not */
+ list<ContentVideo>::const_iterator i = _decoded.begin ();
+ while (i != _decoded.end() && i->frame.index() != frame) {
+ ++i;
+ }
+ if (i == _decoded.end()) {
Frame seek_frame = frame;
if (_content->video->frame_type() == VIDEO_FRAME_TYPE_3D_ALTERNATE) {
/* 3D alternate is a special case as the frame index in the content is not the same
*/
seek_frame *= 2;
}
- _log->log (String::compose ("VD seeks to %1", seek_frame), LogEntry::TYPE_DEBUG_DECODE);
- _parent->seek (ContentTime::from_frames (seek_frame, _content->active_video_frame_rate()), accurate);
+ _parent->maybe_seek (ContentTime::from_frames (seek_frame, _content->active_video_frame_rate()), accurate);
}
/* Work out the number of frames that we should return; we
);
}
+ _position = _next;
_next += ContentTime::from_frames (1, vfr);
return false;
}
dcp_video.cc
dcpomatic_socket.cc
dcpomatic_time.cc
+ decoder.cc
decoder_factory.cc
digester.cc
dolby_cp750.cc