bool
-AtmosMXFDecoder::pass ()
+AtmosMXFDecoder::do_pass ()
{
auto const vfr = _content->active_video_frame_rate (film());
auto const frame = _next.frames_round (vfr);
public:
AtmosMXFDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const AtmosMXFContent>);
- bool pass () override;
void seek (dcpomatic::ContentTime t, bool accurate) override;
private:
+ bool do_pass () override;
+
std::shared_ptr<const AtmosMXFContent> _content;
dcpomatic::ContentTime _next;
std::shared_ptr<dcp::AtmosAssetReader> _reader;
bool
-DCPDecoder::pass ()
+DCPDecoder::do_pass ()
{
if (!_dcp_content->can_be_played()) {
return true;
void set_decode_referenced (bool r);
void set_forced_reduction (boost::optional<int> reduction);
- bool pass () override;
void seek (dcpomatic::ContentTime t, bool accurate) override;
std::vector<dcpomatic::FontData> fonts () const override;
private:
friend struct dcp_subtitle_within_dcp_test;
+ bool do_pass () override;
void next_reel ();
void get_readers ();
void pass_texts (dcpomatic::ContentTime next, dcp::Size size);
bool
-DCPSubtitleDecoder::pass ()
+DCPSubtitleDecoder::do_pass ()
{
if (_next == _subtitles.end ()) {
return true;
public:
DCPSubtitleDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const DCPSubtitleContent>);
- bool pass ();
void seek (dcpomatic::ContentTime time, bool accurate);
std::vector<dcpomatic::FontData> fonts () const;
private:
+ bool do_pass ();
+
dcpomatic::ContentTimePeriod content_time_period (std::shared_ptr<const dcp::Subtitle> s) const;
std::vector<std::shared_ptr<const dcp::Subtitle>> _subtitles;
for (auto i: text) {
i->seek ();
}
+
+ _done = false;
}
}
return text.front();
}
+
+
+void
+Decoder::pass ()
+{
+ _done = do_pass ();
+}
+
std::shared_ptr<TextDecoder> only_text () const;
- /** Do some decoding and perhaps emit video, audio or subtitle data.
- * @return true if this decoder will emit no more data unless a seek() happens.
- */
- virtual bool pass () = 0;
+ bool done () const {
+ return _done;
+ }
+
+ /** Do some decoding and perhaps emit video, audio or subtitle data */
+ void pass ();
+
virtual void seek (dcpomatic::ContentTime time, bool accurate);
virtual dcpomatic::ContentTime position () const;
}
boost::signals2::signal<void ()> Flush;
+
+private:
+ virtual bool do_pass () = 0;
+ bool _done = false;
};
bool
-FFmpegDecoder::pass ()
+FFmpegDecoder::do_pass ()
{
auto packet = av_packet_alloc();
DCPOMATIC_ASSERT (packet);
data->move (data->frames() - remove, remove, 0);
data->set_frames (data->frames() - remove);
ct += ContentTime::from_frames (remove, stream->frame_rate());
+ std::cout << "FF discarded " << remove << "\n";
}
if (ct < ContentTime()) {
public:
FFmpegDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const FFmpegContent>);
- bool pass () override;
void seek (dcpomatic::ContentTime time, bool) override;
private:
friend struct ::ffmpeg_pts_offset_test;
+ bool do_pass () override;
bool flush ();
static std::shared_ptr<AudioBuffers> deinterleave_audio (AVFrame* frame);
bool
-ImageDecoder::pass ()
+ImageDecoder::do_pass ()
{
if (_frame_video_position >= _image_content->video->length()) {
return true;
return _image_content;
}
- bool pass () override;
void seek (dcpomatic::ContentTime, bool) override;
private:
+ bool do_pass () override;
std::shared_ptr<const ImageContent> _image_content;
std::shared_ptr<ImageProxy> _image;
}
+/** @param frame Frame position in the content, as if there were no trim */
void
Piece::audio (shared_ptr<const Content> content, AudioStreamPtr stream_ptr, shared_ptr<const AudioBuffers> audio, Frame frame)
{
auto film = _film.lock ();
DCPOMATIC_ASSERT (film);
+ /* Here we have the frame index into the content, and shortly we're going to lose that information and
+ * start thinking about frame indices into the piece. Here's the last chance for us to apply this content's
+ * trim, so we'll take it.
+ */
+ auto const start_trim = content->trim_start().frames_round(stream_ptr->frame_rate());
+ auto const remove_from_start = std::max(Frame(0), start_trim - frame);
+ if (remove_from_start > 0) {
+ auto trimmed = make_shared<AudioBuffers>(audio);
+ trimmed->trim_start (remove_from_start);
+ frame += remove_from_start;
+ }
+
+ auto const end_trim = content->trim_send().frames_round(stream_ptr->frame_rate());
+
+
auto content_streams = content->audio->streams();
auto content_stream_iter = std::find(content_streams.begin(), content_streams.end(), stream_ptr);
DCPOMATIC_ASSERT (content_stream_iter != content_streams.end());
stream.position = frame;
}
+ std::cout << "piece sends frame " << stream.position << " " << to_string(resampled_audio_to_dcp(stream.position) << "\n";
Audio (PieceAudio(index, audio, resampled_audio_to_dcp(stream.position), stream_ptr->mapping()));
stream.position += audio->frames();
}
auto film = _film.lock ();
DCPOMATIC_ASSERT (film);
- for (auto const& i: _content) {
- auto t = content_time_to_dcp(i.content, std::max(i.decoder->position(), i.content->trim_start()));
- DCPOMATIC_ASSERT (t);
- if (*t < i.content->end(film)) {
- LOG_DEBUG_PLAYER ("Calling pass() on %1", i.content->path(0));
- i.decoder->pass();
- return;
+ for (auto& i: _content) {
+ if (i.decoder->done()) {
+ continue;
}
+ LOG_DEBUG_PLAYER ("Calling pass() on %1", i.content->path(0));
+ i.decoder->pass();
+ return;
}
}
i.position = 0;
}
- for (auto const& i: _content) {
+ for (auto& i: _content) {
if (time < i.content->position()) {
/* Before; seek to the start of the content. Even if this request is for an inaccurate seek
we must seek this (following) content accurately, otherwise when we come to the end of the current
DCPOMATIC_ASSERT (film);
for (auto const& i: _content) {
- auto t = content_time_to_dcp(i.content, std::max(i.decoder->position(), i.content->trim_start()));
- DCPOMATIC_ASSERT (t);
- if (*t < i.content->end(film)) {
+ if (!i.decoder->done()) {
+ auto t = content_time_to_dcp(i.content, std::max(i.decoder->position(), i.content->trim_start()));
+ DCPOMATIC_ASSERT (t);
/* This is the first unfinished decoder we have, so we'll make a decision with it.
Given two choices at the same time, pick the one with texts so we see it before
the video.
{
int index = 0;
for (auto& i: _audio_streams) {
- auto ro = i.resampler->flush ();
- if (ro->frames() > 0) {
- Audio (PieceAudio(index, ro, resampled_audio_to_dcp(i.position), i.mapping));
- i.position += ro->frames();
+ if (i.resampler) {
+ auto ro = i.resampler->flush ();
+ if (ro->frames() > 0) {
+ Audio (PieceAudio(index, ro, resampled_audio_to_dcp(i.position), i.mapping));
+ i.position += ro->frames();
+ }
}
++index;
}
bool
Piece::done () const
{
- auto film = _film.lock();
- DCPOMATIC_ASSERT (film);
- auto const& last = _content.back();
- return content_time_to_dcp(last.content, std::max(last.decoder->position(), last.content->trim_start())) > last.content->end(film);
+ for (auto const& i: _content) {
+ if (!i.decoder->done()) {
+ return false;
+ }
+ }
+
+ return true;
}
{
public:
struct Pair {
- Pair () {}
-
Pair (std::shared_ptr<Content> c, std::shared_ptr<Decoder> d)
: content(c)
, decoder(d)
/* The end of this block in the DCP */
int const rfr = piece->resampled_audio_frame_rate ();
auto end = audio.time + DCPTime::from_frames(audio.audio->frames(), rfr);
+ std::cout << "Player gets " << to_string(audio.time) << "\n";
/* Remove anything that comes before the start or after the end of the content */
if (audio.time < piece->position()) {
}
bool
-StringTextFileDecoder::pass ()
+StringTextFileDecoder::do_pass ()
{
if (_next >= _subtitles.size ()) {
return true;
StringTextFileDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const StringTextFileContent>);
void seek (dcpomatic::ContentTime time, bool accurate);
- bool pass ();
std::vector<dcpomatic::FontData> fonts () const;
private:
+ bool do_pass ();
+
dcpomatic::ContentTimePeriod content_time_period (sub::Subtitle s) const;
size_t _next;
bool
-VideoMXFDecoder::pass ()
+VideoMXFDecoder::do_pass ()
{
auto const vfr = _content->active_video_frame_rate (film());
auto const frame = _next.frames_round (vfr);
public:
VideoMXFDecoder (std::shared_ptr<const Film> film, std::shared_ptr<const VideoMXFContent>);
- bool pass () override;
void seek (dcpomatic::ContentTime t, bool accurate) override;
private:
+ bool do_pass () override;
std::shared_ptr<const VideoMXFContent> _content;
/** Time of next thing to return from pass */
i->Stop.connect (bind(&TextView::data_stop, this, _1));
}
}
- while (!decoder->pass ()) {}
+ while (!decoder->done()) {
+ decoder->pass();
+ }
SetSizerAndFit (sizer);
}
decoder->only_text()->StringStart.connect (bind(store, _1, _2));
stored.clear();
- while (!decoder->pass() && stored.empty()) {}
+ while (!decoder->done() && stored.empty()) {
+ decoder->pass();
+ }
BOOST_REQUIRE_EQUAL (stored.size(), 2U);
BOOST_CHECK_EQUAL (stored.front().text(), "Noch mal.");
decoder->only_text()->StringStart.connect (bind(store, _1, _2));
stored.clear();
- while (!decoder->pass()) {
+ while (!decoder->done()) {
+ decoder->pass();
if (!stored.empty() && stored_time == ContentTime(0)) {
BOOST_CHECK_EQUAL (stored.front().text(), "<b>Hello world!</b>");
}
auto decoder = make_shared<DCPSubtitleDecoder>(film, content);
stored.clear();
- while (!decoder->pass ()) {
+ while (!decoder->done()) {
+ decoder->pass();
decoder->only_text()->StringStart.connect (bind (store, _1, _2));
if (!stored.empty() && stored_time == ContentTime::from_seconds(0.08)) {
auto i = stored.begin ();
check (shared_ptr<FFmpegDecoder> decoder, int frame)
{
BOOST_REQUIRE (decoder->ffmpeg_content()->video_frame_rate ());
- decoder->seek (ContentTime::from_frames (frame, decoder->ffmpeg_content()->video_frame_rate().get()), true);
+ decoder->seek (ContentTime::from_frames(frame, decoder->ffmpeg_content()->video_frame_rate().get()), true);
stored_frame = {};
- while (!decoder->pass() && !stored_frame) {}
+ while (!decoder->done() && !stored_frame) {
+ decoder->pass();
+ }
BOOST_CHECK (*stored_frame <= frame);
}
static void
test (boost::filesystem::path file, vector<int> frames)
{
- boost::filesystem::path path = TestPaths::private_data() / file;
+ auto path = TestPaths::private_data() / file;
BOOST_REQUIRE (boost::filesystem::exists (path));
- shared_ptr<Film> film = new_test_film ("ffmpeg_decoder_seek_test_" + file.string());
+ auto film = new_test_film ("ffmpeg_decoder_seek_test_" + file.string());
auto content = make_shared<FFmpegContent>(path);
film->examine_and_add_content (content);
BOOST_REQUIRE (!wait_for_jobs());
auto decoder = make_shared<FFmpegDecoder>(film, content);
decoder->video->Data.connect (bind (&store, _2));
- for (vector<int>::const_iterator i = frames.begin(); i != frames.end(); ++i) {
- check (decoder, *i);
+ for (auto i: frames) {
+ check (decoder, i);
}
}
BOOST_AUTO_TEST_CASE (ffmpeg_decoder_seek_test)
{
- vector<int> frames;
-
- frames.clear ();
- frames.push_back (0);
- frames.push_back (42);
- frames.push_back (999);
- frames.push_back (0);
-
+ vector<int> frames = { 0, 42, 999, 0 };
test ("boon_telly.mkv", frames);
test ("Sintel_Trailer1.480p.DivX_Plus_HD.mkv", frames);
- frames.clear ();
- frames.push_back (15);
- frames.push_back (42);
- frames.push_back (999);
- frames.push_back (15);
-
+ frames = { 15, 42, 999, 15 };
test ("prophet_long_clip.mkv", frames);
}
decoder->video->Data.connect (bind(&video_handler, _1));
decoded_image = {};
while (!decoded_image) {
- BOOST_REQUIRE (!decoder->pass());
+ decoder->pass();
+ BOOST_REQUIRE (!decoder->done());
}
return pixel_range (decoded_image->image().image);
decoder->video->Data.connect (bind(&video_handler, _1));
decoded_image = {};
while (!decoded_image) {
- BOOST_REQUIRE (!decoder->pass());
+ decoder->pass();
+ BOOST_REQUIRE (!decoder->done());
}
return pixel_range (decoded_image->image().image);