using boost::shared_ptr;
using boost::optional;
-void
-ActiveText::add (DCPTimePeriod period, list<PlayerText>& pc, list<Period> p) const
-{
- BOOST_FOREACH (Period i, p) {
- DCPTimePeriod test (i.from, i.to.get_value_or(DCPTime::max()));
- optional<DCPTimePeriod> overlap = period.overlap (test);
- if (overlap && overlap->duration() > DCPTime(period.duration().get() / 2)) {
- pc.push_back (i.subs);
- }
- }
-}
-
-list<PlayerText>
-ActiveText::get (DCPTimePeriod period) const
-{
- boost::mutex::scoped_lock lm (_mutex);
-
- list<PlayerText> ps;
-
- for (Map::const_iterator i = _data.begin(); i != _data.end(); ++i) {
-
- shared_ptr<const TextContent> caption = i->first.lock ();
- if (!caption || !caption->use()) {
- continue;
- }
-
- add (period, ps, i->second);
- }
-
- return ps;
-}
-
/** Get the open captions that should be burnt into a given period.
* @param period Period of interest.
* @param always_burn_captions Always burn captions even if their content is not set to burn.
continue;
}
- add (period, ps, i->second);
+ BOOST_FOREACH (Period j, i->second) {
+ DCPTimePeriod test (j.from, j.to.get_value_or(DCPTime::max()));
+ optional<DCPTimePeriod> overlap = period.overlap (test);
+ if (overlap && overlap->duration() > DCPTime(period.duration().get() / 2)) {
+ ps.push_back (j.subs);
+ }
+ }
}
return ps;
class ActiveText : public boost::noncopyable
{
public:
- std::list<PlayerText> get (DCPTimePeriod period) const;
std::list<PlayerText> get_burnt (DCPTimePeriod period, bool always_burn_captions) const;
void clear_before (DCPTime time);
void clear ();
typedef std::map<boost::weak_ptr<const TextContent>, std::list<Period> > Map;
- void add (DCPTimePeriod period, std::list<PlayerText>& pc, std::list<Period> p) const;
-
mutable boost::mutex _mutex;
Map _data;
};
{
_player_video_connection = _player->Video.connect (bind (&Butler::video, this, _1, _2));
_player_audio_connection = _player->Audio.connect (bind (&Butler::audio, this, _1, _2));
+ _player_text_connection = _player->Text.connect (bind (&Butler::text, this, _1, _2, _3));
_player_changed_connection = _player->Changed.connect (bind (&Butler::player_changed, this));
_thread = new boost::thread (bind (&Butler::thread, this));
#ifdef DCPOMATIC_LINUX
return r;
}
+optional<pair<PlayerText, DCPTimePeriod> >
+Butler::get_closed_caption ()
+{
+ boost::mutex::scoped_lock lm (_mutex);
+ return _closed_caption.get ();
+}
+
void
Butler::seek (DCPTime position, bool accurate)
{
}
{
- boost::mutex::scoped_lock lm (_video_audio_mutex);
+ boost::mutex::scoped_lock lm (_buffers_mutex);
_video.clear ();
_audio.clear ();
+ _closed_caption.clear ();
}
_finished = false;
_prepare_service.post (bind (&Butler::prepare, this, weak_ptr<PlayerVideo>(video)));
- boost::mutex::scoped_lock lm2 (_video_audio_mutex);
+ boost::mutex::scoped_lock lm2 (_buffers_mutex);
_video.put (video, time);
}
}
}
- boost::mutex::scoped_lock lm2 (_video_audio_mutex);
+ boost::mutex::scoped_lock lm2 (_buffers_mutex);
_audio.put (remap (audio, _audio_channels, _audio_mapping), time);
}
}
{
- boost::mutex::scoped_lock lm (_video_audio_mutex);
+ boost::mutex::scoped_lock lm (_buffers_mutex);
_video.clear ();
_audio.clear ();
+ _closed_caption.clear ();
}
_finished = false;
seek_unlocked (seek_to, true);
_awaiting = seek_to;
}
+
+void
+Butler::text (PlayerText pt, TextType type, DCPTimePeriod period)
+{
+ if (type != TEXT_CLOSED_CAPTION) {
+ return;
+ }
+
+ boost::mutex::scoped_lock lm2 (_buffers_mutex);
+ _closed_caption.put (make_pair(pt, period));
+}
#include "video_ring_buffers.h"
#include "audio_ring_buffers.h"
+#include "text_ring_buffers.h"
#include "audio_mapping.h"
#include "exception_store.h"
#include <boost/shared_ptr.hpp>
void seek (DCPTime position, bool accurate);
std::pair<boost::shared_ptr<PlayerVideo>, DCPTime> get_video ();
boost::optional<DCPTime> get_audio (float* out, Frame frames);
+ boost::optional<std::pair<PlayerText, DCPTimePeriod> > get_closed_caption ();
void disable_audio ();
void thread ();
void video (boost::shared_ptr<PlayerVideo> video, DCPTime time);
void audio (boost::shared_ptr<AudioBuffers> audio, DCPTime time);
+ void text (PlayerText pt, TextType type, DCPTimePeriod period);
bool should_run () const;
void prepare (boost::weak_ptr<PlayerVideo> video) const;
void player_changed ();
boost::shared_ptr<Log> _log;
boost::thread* _thread;
- /** mutex to protect _video and _audio for when we are clearing them and they both need to be
- cleared together without any data being inserted in the interim.
+ /** mutex to protect _video, _audio and _closed_caption for when we are clearing them and they all need to be
+ cleared together without any data being inserted in the interim;
+ XXX: is this necessary now that all butler output data is timestamped? Perhaps the locked clear-out
+ is only required if we guarantee that get_video() and get_audio() calls are in sync.
*/
- boost::mutex _video_audio_mutex;
+ boost::mutex _buffers_mutex;
VideoRingBuffers _video;
AudioRingBuffers _audio;
+ TextRingBuffers _closed_caption;
boost::thread_group _prepare_pool;
boost::asio::io_service _prepare_service;
boost::signals2::scoped_connection _player_video_connection;
boost::signals2::scoped_connection _player_audio_connection;
+ boost::signals2::scoped_connection _player_text_connection;
boost::signals2::scoped_connection _player_changed_connection;
};
return done;
}
-list<PlayerText>
-Player::closed_captions_for_frame (DCPTime time) const
-{
- boost::mutex::scoped_lock _lm (_mutex);
- return _active_texts[TEXT_CLOSED_CAPTION].get (
- DCPTimePeriod(time, time + DCPTime::from_frames(1, _film->video_frame_rate()))
- );
-}
-
/** @return Open subtitles for the frame at the given time, converted to images */
optional<PositionImage>
Player::open_subtitles_for_frame (DCPTime time) const
DCPTime content_time_to_dcp (boost::shared_ptr<Content> content, ContentTime t);
- std::list<PlayerText> closed_captions_for_frame (DCPTime time) const;
-
/** Emitted when something has changed such that if we went back and emitted
* the last frame again it would look different. This is not emitted after
* a seek.
string_text_file.cc
string_text_file_content.cc
string_text_file_decoder.cc
+ text_ring_buffers.cc
timer.cc
transcode_job.cc
types.cc
#include "closed_captions_dialog.h"
#include "lib/string_text.h"
+#include "lib/butler.h"
#include <boost/bind.hpp>
using std::list;
using std::max;
using std::cout;
+using std::pair;
using std::make_pair;
using boost::shared_ptr;
using boost::weak_ptr;
+using boost::optional;
ClosedCaptionsDialog::ClosedCaptionsDialog (wxWindow* parent)
: wxDialog (parent, wxID_ANY, _("Closed captions"), wxDefaultPosition, wxDefaultSize,
wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxFRAME_FLOAT_ON_PARENT
#endif
)
-
+ , _current_in_lines (false)
{
_lines.resize (CLOSED_CAPTION_LINES);
Bind (wxEVT_PAINT, boost::bind (&ClosedCaptionsDialog::paint, this));
void
ClosedCaptionsDialog::update (DCPTime time)
{
- shared_ptr<Player> player = _player.lock ();
- DCPOMATIC_ASSERT (player);
- list<StringText> to_show;
- BOOST_FOREACH (PlayerText i, player->closed_captions_for_frame(time)) {
- BOOST_FOREACH (StringText j, i.string) {
- to_show.push_back (j);
+ if (_current_in_lines && _current->second.to > time) {
+ /* Current one is fine */
+ return;
+ }
+
+ if (_current && _current->second.to < time) {
+ /* Current one has finished; clear out */
+ for (int j = 0; j < CLOSED_CAPTION_LINES; ++j) {
+ _lines[j] = "";
}
+ Refresh ();
+ _current = optional<pair<PlayerText, DCPTimePeriod> >();
}
- for (int j = 0; j < CLOSED_CAPTION_LINES; ++j) {
- _lines[j] = "";
+ if (!_current) {
+ /* We have no current one: get another */
+ shared_ptr<Butler> butler = _butler.lock ();
+ DCPOMATIC_ASSERT (butler);
+ _current = butler->get_closed_caption ();
+ _current_in_lines = false;
}
- to_show.sort (ClosedCaptionSorter());
+ if (_current && _current->second.contains(time)) {
+ /* We need to set this new one up */
- list<StringText>::const_iterator j = to_show.begin();
- int k = 0;
- while (j != to_show.end() && k < CLOSED_CAPTION_LINES) {
- _lines[k] = j->text();
- ++j;
- ++k;
- }
+ list<StringText> to_show = _current->first.string;
- Refresh ();
+ for (int j = 0; j < CLOSED_CAPTION_LINES; ++j) {
+ _lines[j] = "";
+ }
+
+ to_show.sort (ClosedCaptionSorter());
+
+ list<StringText>::const_iterator j = to_show.begin();
+ int k = 0;
+ while (j != to_show.end() && k < CLOSED_CAPTION_LINES) {
+ _lines[k] = j->text();
+ ++j;
+ ++k;
+ }
+
+ Refresh ();
+ _current_in_lines = true;
+ }
}
void
ClosedCaptionsDialog::clear ()
{
+ _current = optional<pair<PlayerText, DCPTimePeriod> >();
+ _current_in_lines = false;
Refresh ();
}
void
-ClosedCaptionsDialog::set_player (weak_ptr<Player> player)
+ClosedCaptionsDialog::set_butler (weak_ptr<Butler> butler)
{
- _player = player;
+ _butler = butler;
}
#include "lib/player.h"
#include <wx/wx.h>
-class Player;
+class Butler;
class ClosedCaptionsDialog : public wxDialog
{
void update (DCPTime);
void clear ();
- void set_player (boost::weak_ptr<Player>);
+ void set_butler (boost::weak_ptr<Butler>);
private:
void paint ();
+ boost::optional<std::pair<PlayerText, DCPTimePeriod> > _current;
+ bool _current_in_lines;
std::vector<wxString> _lines;
- boost::weak_ptr<Player> _player;
+ boost::weak_ptr<Butler> _butler;
};
if (!_film) {
_player.reset ();
- _closed_captions_dialog->set_player (_player);
recreate_butler ();
_frame.reset ();
refresh_panel ();
return;
}
- _closed_captions_dialog->set_player (_player);
-
_player->set_always_burn_open_subtitles ();
_player->set_play_referenced ();
_butler->disable_audio ();
}
+ _closed_captions_dialog->set_butler (_butler);
+
if (was_running) {
start ();
}