diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-07-15 16:36:49 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-07-15 16:36:49 +0100 |
| commit | 1bb4bd728a445de0728c897211bf079c714d4f41 (patch) | |
| tree | b4d606dafac8a3fc99d8ce362238c0e14bc9459b | |
| parent | 49c7639efbd0c7e014e9ddf3380b6d7f1fed9285 (diff) | |
Add some player tests. Fix seek with content at non-DCP frame rate. A few other small fixes.
| -rw-r--r-- | src/lib/player.cc | 24 | ||||
| -rw-r--r-- | src/lib/player.h | 5 | ||||
| -rw-r--r-- | src/lib/playlist.cc | 40 | ||||
| -rw-r--r-- | src/lib/playlist.h | 1 | ||||
| -rw-r--r-- | test/play_test.cc | 123 |
5 files changed, 174 insertions, 19 deletions
diff --git a/src/lib/player.cc b/src/lib/player.cc index c8c206424..6ee8c5029 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -138,7 +138,7 @@ Player::pass () continue; } - if (dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) { + if (_video && dynamic_pointer_cast<VideoDecoder> ((*i)->decoder)) { if ((*i)->video_position < earliest_t) { earliest_t = (*i)->video_position; earliest = *i; @@ -146,7 +146,7 @@ Player::pass () } } - if (dynamic_pointer_cast<AudioDecoder> ((*i)->decoder)) { + if (_audio && dynamic_pointer_cast<AudioDecoder> ((*i)->decoder)) { if ((*i)->audio_position < earliest_t) { earliest_t = (*i)->audio_position; earliest = *i; @@ -238,6 +238,10 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image work_image = im; } +#ifdef DCPOMATIC_DEBUG + _last_video = piece->content; +#endif + Video (work_image, same, time); time += TIME_HZ / _film->dcp_video_frame_rate(); @@ -348,7 +352,10 @@ Player::flush () } -/** @return true on error */ +/** Seek so that the next pass() will yield (approximately) the requested frame. + * Pass accurate = true to try harder to get close to the request. + * @return true on error + */ void Player::seek (Time t, bool accurate) { @@ -374,11 +381,16 @@ Player::seek (Time t, bool accurate) (*i)->video_position = (*i)->audio_position = vc->start() + s; FrameRateConversion frc (vc->video_frame_rate(), _film->dcp_video_frame_rate()); - VideoContent::Frame f = s * vc->video_frame_rate() / (frc.factor() * TIME_HZ); + /* Here we are converting from time (in the DCP) to a frame number in the content. + Hence we need to use the DCP's frame rate and the double/skip correction, not + the source's rate. + */ + VideoContent::Frame f = s * _film->dcp_video_frame_rate() / (frc.factor() * TIME_HZ); dynamic_pointer_cast<VideoDecoder>((*i)->decoder)->seek (f, accurate); } _video_position = _audio_position = t; + /* XXX: don't seek audio because we don't need to... */ } @@ -501,6 +513,10 @@ Player::resampler (shared_ptr<AudioContent> c) void Player::emit_black () { +#ifdef DCPOMATIC_DEBUG + _last_video.reset (); +#endif + /* XXX: use same here */ Video (_black_frame, false, _video_position); _video_position += _film->video_frames_to_time (1); diff --git a/src/lib/player.h b/src/lib/player.h index b3eadd7c0..92a358043 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -75,6 +75,7 @@ public: boost::signals2::signal<void ()> Changed; private: + friend class PlayerWrapper; void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, bool, VideoContent::Frame); void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame); @@ -126,6 +127,10 @@ private: Time from; Time to; } _out_subtitle; + +#ifdef DCPOMATIC_DEBUG + boost::shared_ptr<Content> _last_video; +#endif }; #endif diff --git a/src/lib/playlist.cc b/src/lib/playlist.cc index 703a14663..0d6462f6c 100644 --- a/src/lib/playlist.cc +++ b/src/lib/playlist.cc @@ -72,25 +72,35 @@ Playlist::~Playlist () void Playlist::content_changed (weak_ptr<Content> c, int p) { - if (p == ContentProperty::LENGTH && _sequence_video && !_sequencing_video) { - _sequencing_video = true; - - ContentList cl = _content; - sort (cl.begin(), cl.end(), ContentSorter ()); - Time last = 0; - for (ContentList::iterator i = cl.begin(); i != cl.end(); ++i) { - if (!dynamic_pointer_cast<VideoContent> (*i)) { - continue; - } + if (p == ContentProperty::LENGTH) { + maybe_sequence_video (); + } - (*i)->set_start (last); - last = (*i)->end (); - } + ContentChanged (c, p); +} - _sequencing_video = false; +void +Playlist::maybe_sequence_video () +{ + if (!_sequence_video || _sequencing_video) { + return; } - ContentChanged (c, p); + _sequencing_video = true; + + ContentList cl = _content; + sort (cl.begin(), cl.end(), ContentSorter ()); + Time last = 0; + for (ContentList::iterator i = cl.begin(); i != cl.end(); ++i) { + if (!dynamic_pointer_cast<VideoContent> (*i)) { + continue; + } + + (*i)->set_start (last); + last = (*i)->end (); + } + + _sequencing_video = false; } string diff --git a/src/lib/playlist.h b/src/lib/playlist.h index cf0f09b31..805df4d70 100644 --- a/src/lib/playlist.h +++ b/src/lib/playlist.h @@ -86,6 +86,7 @@ public: Time video_end () const; void set_sequence_video (bool); + void maybe_sequence_video (); mutable boost::signals2::signal<void ()> Changed; mutable boost::signals2::signal<void (boost::weak_ptr<Content>, int)> ContentChanged; diff --git a/test/play_test.cc b/test/play_test.cc new file mode 100644 index 000000000..12f80a282 --- /dev/null +++ b/test/play_test.cc @@ -0,0 +1,123 @@ +/* + Copyright (C) 2013 Carl Hetherington <cth@carlh.net> + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include "player.h" + +/* This test needs stuff in Player that is only included in debug mode */ +#ifdef DCPOMATIC_DEBUG + +using boost::optional; + +struct Video +{ + boost::shared_ptr<Content> content; + boost::shared_ptr<const Image> image; + Time time; +}; + +class PlayerWrapper +{ +public: + PlayerWrapper (shared_ptr<Player> p) + : _player (p) + { + _player->Video.connect (bind (&PlayerWrapper::process_video, this, _1, _2, _3)); + } + + void process_video (shared_ptr<const Image> i, bool, Time t) + { + Video v; + v.content = _player->_last_video; + v.image = i; + v.time = t; + _queue.push_front (v); + } + + optional<Video> get_video () + { + while (_queue.empty() && !_player->pass ()) {} + if (_queue.empty ()) { + return optional<Video> (); + } + + Video v = _queue.back (); + _queue.pop_back (); + return v; + } + + void seek (Time t, bool ac) + { + _player->seek (t, ac); + _queue.clear (); + } + +private: + shared_ptr<Player> _player; + std::list<Video> _queue; +}; + +BOOST_AUTO_TEST_CASE (play_test) +{ + shared_ptr<Film> film = new_test_film ("play_test"); + film->set_dcp_content_type (DCPContentType::from_dci_name ("FTR")); + film->set_container (Ratio::from_id ("185")); + film->set_name ("play_test"); + + shared_ptr<FFmpegContent> A (new FFmpegContent (film, "test/data/red_24.mp4")); + film->examine_and_add_content (A); + wait_for_jobs (); + + BOOST_CHECK_EQUAL (A->video_length(), 16); + + shared_ptr<FFmpegContent> B (new FFmpegContent (film, "test/data/red_30.mp4")); + film->examine_and_add_content (B); + wait_for_jobs (); + + BOOST_CHECK_EQUAL (B->video_length(), 16); + + /* Film should have been set to 25fps */ + BOOST_CHECK_EQUAL (film->dcp_video_frame_rate(), 25); + + BOOST_CHECK_EQUAL (A->start(), 0); + /* A is 16 frames long at 25 fps */ + BOOST_CHECK_EQUAL (B->start(), 16 * TIME_HZ / 25); + + shared_ptr<Player> player = film->player (); + PlayerWrapper wrap (player); + /* Seek and audio don't get on at the moment */ + player->disable_audio (); + + for (int i = 0; i < 32; ++i) { + optional<Video> v = wrap.get_video (); + BOOST_CHECK (v); + if (i < 16) { + BOOST_CHECK (v.get().content == A); + } else { + BOOST_CHECK (v.get().content == B); + } + } + + player->seek (10 * TIME_HZ / 25, true); + optional<Video> v = wrap.get_video (); + BOOST_CHECK (v); + cout << (v.get().time * 25 / TIME_HZ) << "\n"; + BOOST_CHECK_EQUAL (v.get().time, 10 * TIME_HZ / 25); +} + +#endif |
