--- /dev/null
+/*
+ Copyright (C) 2016-2017 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 "audio_ring_buffers.h"
+#include "dcpomatic_assert.h"
+#include "exceptions.h"
+#include <boost/foreach.hpp>
+
+using std::min;
+using std::cout;
+using boost::shared_ptr;
+
+AudioRingBuffers::AudioRingBuffers ()
+ : _used_in_head (0)
+{
+
+}
+
+void
+AudioRingBuffers::put (shared_ptr<const AudioBuffers> data, DCPTime time)
+{
+ boost::mutex::scoped_lock lm (_mutex);
+
+ if (!_buffers.empty ()) {
+ DCPOMATIC_ASSERT (_buffers.front()->channels() == data->channels());
+ }
+
+ _buffers.push_back (data);
+ _latest = time;
+}
+
+void
+AudioRingBuffers::get (float* out, int channels, int frames)
+{
+ boost::mutex::scoped_lock lm (_mutex);
+
+ while (frames > 0) {
+ if (_buffers.empty ()) {
+ for (int i = 0; i < frames; ++i) {
+ for (int j = 0; j < channels; ++j) {
+ *out++ = 0;
+ }
+ }
+ cout << "audio underrun; missing " << frames << "!\n";
+ return;
+ }
+
+ int const to_do = min (frames, _buffers.front()->frames() - _used_in_head);
+ float** p = _buffers.front()->data();
+ int const c = min (_buffers.front()->channels(), channels);
+ for (int i = 0; i < to_do; ++i) {
+ for (int j = 0; j < c; ++j) {
+ *out++ = p[j][i + _used_in_head];
+ }
+ for (int j = c; j < channels; ++j) {
+ *out++ = 0;
+ }
+ }
+ _used_in_head += to_do;
+ frames -= to_do;
+
+ if (_used_in_head == _buffers.front()->frames()) {
+ _buffers.pop_front ();
+ _used_in_head = 0;
+ }
+ }
+}
+
+void
+AudioRingBuffers::clear ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ _buffers.clear ();
+ _used_in_head = 0;
+ _latest = DCPTime ();
+}
+
+Frame
+AudioRingBuffers::size ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ Frame s = 0;
+ BOOST_FOREACH (shared_ptr<const AudioBuffers> i, _buffers) {
+ s += i->frames ();
+ }
+ return s - _used_in_head;
+}
--- /dev/null
+/*
+ Copyright (C) 2016-2017 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/>.
+
+*/
+
+#ifndef DCPOMATIC_AUDIO_RING_BUFFERS_H
+#define DCPOMATIC_AUDIO_RING_BUFFERS_H
+
+#include "audio_buffers.h"
+#include "types.h"
+#include "dcpomatic_time.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/thread.hpp>
+#include <list>
+
+class AudioRingBuffers : public boost::noncopyable
+{
+public:
+ AudioRingBuffers ();
+
+ void put (boost::shared_ptr<const AudioBuffers> data, DCPTime time);
+ void get (float* out, int channels, int frames);
+
+ void clear ();
+ Frame size ();
+
+ DCPTime latest () const {
+ boost::mutex::scoped_lock lm (_mutex);
+ return _latest;
+ }
+
+private:
+ mutable boost::mutex _mutex;
+ std::list<boost::shared_ptr<const AudioBuffers> > _buffers;
+ DCPTime _latest;
+ int _used_in_head;
+};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2016-2017 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 "butler.h"
+#include "player.h"
+#include <boost/weak_ptr.hpp>
+#include <boost/shared_ptr.hpp>
+
+using std::cout;
+using std::pair;
+using std::make_pair;
+using boost::weak_ptr;
+using boost::shared_ptr;
+using boost::bind;
+using boost::optional;
+
+/** Video readahead in frames */
+#define VIDEO_READAHEAD 10
+
+Butler::Butler (weak_ptr<const Film> film, shared_ptr<Player> player)
+ : _film (film)
+ , _player (player)
+ , _pending_seek_accurate (false)
+ , _finished (false)
+{
+ _player->Video.connect (bind (&VideoRingBuffers::put, &_video, _1, _2));
+ _thread = new boost::thread (bind (&Butler::thread, this));
+}
+
+Butler::~Butler ()
+{
+ _thread->interrupt ();
+ try {
+ _thread->join ();
+ } catch (boost::thread_interrupted& e) {
+ /* No problem */
+ }
+ delete _thread;
+}
+
+void
+Butler::thread ()
+{
+ while (true) {
+ boost::mutex::scoped_lock lm (_mutex);
+
+ while (_video.size() > VIDEO_READAHEAD && !_pending_seek_position) {
+ _summon.wait (lm);
+ }
+
+ if (_pending_seek_position) {
+ _player->seek (*_pending_seek_position, _pending_seek_accurate);
+ _pending_seek_position = optional<DCPTime> ();
+ }
+
+ while (_video.size() < VIDEO_READAHEAD) {
+ _arrived.notify_all ();
+ if (_player->pass ()) {
+ _finished = true;
+ break;
+ }
+ }
+ }
+}
+
+pair<shared_ptr<PlayerVideo>, DCPTime>
+Butler::get_video ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ while (_video.size() == 0 && !_finished) {
+ _arrived.wait (lm);
+ }
+
+ if (_finished) {
+ return make_pair (shared_ptr<PlayerVideo>(), DCPTime());
+ }
+
+ return _video.get ();
+}
+
+void
+Butler::seek (DCPTime position, bool accurate)
+{
+ _video.clear ();
+
+ {
+ boost::mutex::scoped_lock lm (_mutex);
+ _finished = false;
+ _pending_seek_position = position;
+ _pending_seek_accurate = accurate;
+ }
+
+ _summon.notify_all ();
+}
--- /dev/null
+/*
+ Copyright (C) 2016-2017 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 "video_ring_buffers.h"
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <boost/thread.hpp>
+#include <boost/thread/condition.hpp>
+#include <boost/signals2.hpp>
+
+class Film;
+class Player;
+class PlayerVideo;
+
+class Butler : public boost::noncopyable
+{
+public:
+ Butler (boost::weak_ptr<const Film> film, boost::shared_ptr<Player> player);
+ ~Butler ();
+
+ void seek (DCPTime position, bool accurate);
+ std::pair<boost::shared_ptr<PlayerVideo>, DCPTime> get_video ();
+
+private:
+ void thread ();
+ void video (boost::shared_ptr<PlayerVideo> video, DCPTime time);
+
+ boost::weak_ptr<const Film> _film;
+ boost::shared_ptr<Player> _player;
+ boost::thread* _thread;
+
+ VideoRingBuffers _video;
+
+ boost::mutex _mutex;
+ boost::condition _summon;
+ boost::condition _arrived;
+ boost::optional<DCPTime> _pending_seek_position;
+ bool _pending_seek_accurate;
+
+ bool _finished;
+
+ boost::signals2::scoped_connection _player_video_connection;
+};
*/
+#ifndef DCPOMATIC_PLAYER_VIDEO_H
+#define DCPOMATIC_PLAYER_VIDEO_H
+
#include "types.h"
#include "position.h"
#include "dcpomatic_time.h"
boost::optional<ColourConversion> _colour_conversion;
boost::optional<PositionImage> _subtitle;
};
+
+#endif
--- /dev/null
+/*
+ Copyright (C) 2016-2017 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 "video_ring_buffers.h"
+#include "player_video.h"
+#include <boost/foreach.hpp>
+#include <list>
+#include <iostream>
+
+using std::list;
+using std::make_pair;
+using std::cout;
+using std::pair;
+using boost::shared_ptr;
+using boost::optional;
+
+void
+VideoRingBuffers::put (shared_ptr<PlayerVideo> frame, DCPTime time)
+{
+ cout << "put " << to_string(time) << "\n";
+ boost::mutex::scoped_lock lm (_mutex);
+ _data.push_back (make_pair (frame, time));
+}
+
+pair<shared_ptr<PlayerVideo>, DCPTime>
+VideoRingBuffers::get ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ if (_data.empty ()) {
+ cout << "get: no data.\n";
+ return make_pair(shared_ptr<PlayerVideo>(), DCPTime());
+ }
+ pair<shared_ptr<PlayerVideo>, DCPTime> const r = _data.front ();
+ cout << "get: here we go! " << to_string(r.second) << "\n";
+ _data.pop_front ();
+ return r;
+}
+
+Frame
+VideoRingBuffers::size () const
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ return _data.size ();
+}
+
+void
+VideoRingBuffers::clear ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ _data.clear ();
+}
+
+optional<DCPTime>
+VideoRingBuffers::latest () const
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ if (_data.empty ()) {
+ return optional<DCPTime> ();
+ }
+
+ return _data.back().second;
+}
--- /dev/null
+/*
+ Copyright (C) 2016-2017 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 "dcpomatic_time.h"
+#include "player_video.h"
+#include "types.h"
+#include <boost/noncopyable.hpp>
+#include <boost/thread/mutex.hpp>
+#include <boost/shared_ptr.hpp>
+#include <utility>
+
+class PlayerVideo;
+
+class VideoRingBuffers : public boost::noncopyable
+{
+public:
+ void put (boost::shared_ptr<PlayerVideo> frame, DCPTime time);
+ std::pair<boost::shared_ptr<PlayerVideo>, DCPTime> get ();
+
+ void clear ();
+ Frame size () const;
+
+ boost::optional<DCPTime> latest () const;
+
+private:
+ mutable boost::mutex _mutex;
+ std::list<std::pair<boost::shared_ptr<PlayerVideo>, DCPTime> > _data;
+};
audio_point.cc
audio_processor.cc
audio_stream.cc
+ butler.cc
case_insensitive_sorter.cc
cinema.cc
cinema_kdms.cc
video_mxf_content.cc
video_mxf_decoder.cc
video_mxf_examiner.cc
+ video_ring_buffers.cc
writer.cc
"""
#include "lib/video_content.h"
#include "lib/video_decoder.h"
#include "lib/timer.h"
+#include "lib/butler.h"
#include "lib/log.h"
#include "lib/config.h"
extern "C" {
_player->set_play_referenced ();
_film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1));
-
_player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1));
- _player->Video.connect (boost::bind (&FilmViewer::video, this, _1, _2));
+
+ recreate_butler ();
calculate_sizes ();
refresh ();
setup_sensitivity ();
}
+void
+FilmViewer::recreate_butler ()
+{
+ _butler.reset ();
+
+ if (!_film) {
+ return;
+ }
+
+ _butler.reset (new Butler (_film, _player));
+}
+
void
FilmViewer::refresh_panel ()
{
}
void
-FilmViewer::video (shared_ptr<PlayerVideo> pv, DCPTime time)
+FilmViewer::get ()
{
- if (!_player) {
- return;
- }
+ pair<shared_ptr<PlayerVideo>, DCPTime> video;
+ do {
+ video = _butler->get_video ();
+ } while (_film->three_d() && ((_left_eye->GetValue() && video.first->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && video.first->eyes() == EYES_LEFT)));
- if (_film->three_d ()) {
- if ((_left_eye->GetValue() && pv->eyes() == EYES_RIGHT) || (_right_eye->GetValue() && pv->eyes() == EYES_LEFT)) {
- return;
- }
+ if (!video.first) {
+ return;
}
/* In an ideal world, what we would do here is:
* image and convert it (from whatever the user has said it is) to RGB.
*/
- _frame = pv->image (
+ _frame = video.first->image (
bind (&Log::dcp_log, _film->log().get(), _1, _2),
bind (&PlayerVideo::always_rgb, _1),
false, true
);
- ImageChanged (pv);
+ ImageChanged (video.first);
- _position = time;
- _inter_position = pv->inter_position ();
- _inter_size = pv->inter_size ();
+ _position = video.second;
+ _inter_position = video.first->inter_position ();
+ _inter_size = video.first->inter_size ();
refresh_panel ();
}
-void
-FilmViewer::get ()
-{
- Image const * current = _frame.get ();
- while (!_player->pass() && _frame.get() == current) {}
-}
-
void
FilmViewer::timer ()
{
void
FilmViewer::calculate_sizes ()
{
- if (!_film || !_player) {
+ if (!_film) {
return;
}
void
FilmViewer::seek (DCPTime t, bool accurate)
{
- if (!_player) {
+ if (!_butler) {
return;
}
- _player->seek (t, accurate);
+ _butler->seek (t, accurate);
_last_seek_accurate = accurate;
get ();
}
* @brief A wx widget to view `thumbnails' of a Film.
*/
-#include <wx/wx.h>
#include "lib/film.h"
+#include <wx/wx.h>
class wxToggleButton;
class FFmpegPlayer;
class RGBPlusAlphaImage;
class PlayerVideo;
class Player;
+class Butler;
/** @class FilmViewer
* @brief A wx widget to view a preview of a Film.
void player_changed (bool);
void update_position_label ();
void update_position_slider ();
- void video (boost::shared_ptr<PlayerVideo>, DCPTime time);
void get ();
void seek (DCPTime t, bool accurate);
void refresh_panel ();
void frame_number_clicked ();
void go_to (DCPTime t);
void jump_to_selected_clicked ();
+ void recreate_butler ();
boost::shared_ptr<Film> _film;
boost::shared_ptr<Player> _player;
* can get the same one that we got last time.
*/
bool _last_seek_accurate;
+
+ boost::shared_ptr<Butler> _butler;
};