ID_jobs_send_dcp_to_tms,
ID_jobs_show_dcp,
ID_jobs_open_dcp_in_player,
- ID_tools_video_waveform,
+ ID_view_closed_captions,
+ ID_view_video_waveform,
ID_tools_hints,
ID_tools_encoding_servers,
ID_tools_manage_templates,
Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_send_dcp_to_tms, this), ID_jobs_send_dcp_to_tms);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_show_dcp, this), ID_jobs_show_dcp);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::jobs_open_dcp_in_player, this), ID_jobs_open_dcp_in_player);
- Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_video_waveform, this), ID_tools_video_waveform);
+ Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_closed_captions, this), ID_view_closed_captions);
+ Bind (wxEVT_MENU, boost::bind (&DOMFrame::view_video_waveform, this), ID_view_video_waveform);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_hints, this), ID_tools_hints);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_encoding_servers, this), ID_tools_encoding_servers);
Bind (wxEVT_MENU, boost::bind (&DOMFrame::tools_manage_templates, this), ID_tools_manage_templates);
_film = film;
_film_viewer->set_film (_film);
_film_editor->set_film (_film);
- delete _video_waveform_dialog;
- _video_waveform_dialog = 0;
+ if (_video_waveform_dialog) {
+ _video_waveform_dialog->Destroy ();
+ _video_waveform_dialog = 0;
+ }
set_menu_sensitivity ();
if (_film->directory()) {
Config::instance()->add_to_history (_film->directory().get());
#endif
}
- void tools_video_waveform ()
+ void view_closed_captions ()
+ {
+ _film_viewer->show_closed_captions ();
+ }
+
+ void view_video_waveform ()
{
if (!_video_waveform_dialog) {
_video_waveform_dialog = new VideoWaveformDialog (this, _film, _film_viewer);
add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL);
add_item (jobs_menu, _("Open DCP in &player"), ID_jobs_open_dcp_in_player, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL);
+ wxMenu* view = new wxMenu;
+ add_item (view, _("Closed captions..."), ID_view_closed_captions, NEEDS_FILM);
+ add_item (view, _("Video waveform..."), ID_view_video_waveform, NEEDS_FILM);
+
wxMenu* tools = new wxMenu;
- add_item (tools, _("Video waveform..."), ID_tools_video_waveform, NEEDS_FILM);
add_item (tools, _("Hints..."), ID_tools_hints, 0);
add_item (tools, _("Encoding servers..."), ID_tools_encoding_servers, 0);
add_item (tools, _("Manage templates..."), ID_tools_manage_templates, 0);
m->Append (edit, _("&Edit"));
m->Append (content, _("&Content"));
m->Append (jobs_menu, _("&Jobs"));
+ m->Append (view, _("&View"));
m->Append (tools, _("&Tools"));
m->Append (help, _("&Help"));
}
using std::vector;
using std::string;
using std::list;
+using std::cout;
using boost::shared_ptr;
using boost::dynamic_pointer_cast;
--- /dev/null
+/*
+ Copyright (C) 2018 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 "closed_captions_view.h"
+#include <boost/bind.hpp>
+
+using std::list;
+using std::cout;
+using std::make_pair;
+
+int const ClosedCaptionsDialog::_num_lines = 3;
+int const ClosedCaptionsDialog::_num_chars_per_line = 30;
+
+ClosedCaptionsDialog::ClosedCaptionsDialog (wxWindow* parent)
+ : wxDialog (parent, wxID_ANY, _("Closed captions"), wxDefaultPosition, wxDefaultSize,
+#ifdef DCPOMATIC_OSX
+ /* I can't get wxFRAME_FLOAT_ON_PARENT to work on OS X, and although wxSTAY_ON_TOP keeps
+ the window above all others (and not just our own) it's better than nothing for now.
+ */
+ wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxSTAY_ON_TOP
+#else
+ wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxFRAME_FLOAT_ON_PARENT
+#endif
+ )
+
+{
+ _lines.resize (_num_lines);
+ Bind (wxEVT_PAINT, boost::bind (&ClosedCaptionsDialog::paint, this));
+}
+
+void
+ClosedCaptionsDialog::paint ()
+{
+ wxPaintDC dc (this);
+ dc.SetBackground (*wxBLACK_BRUSH);
+ dc.Clear ();
+ dc.SetTextForeground (*wxWHITE);
+
+ /* Choose a font which fits vertically */
+ int const line_height = dc.GetSize().GetHeight() / _num_lines;
+ wxFont font (*wxNORMAL_FONT);
+ font.SetPixelSize (wxSize (0, line_height * 0.8));
+ dc.SetFont (font);
+
+ for (int i = 0; i < _num_lines; ++i) {
+ if (_lines[i].IsEmpty()) {
+ dc.DrawText (wxString::Format("Line %d", i + 1), 8, line_height * i);
+ } else {
+ dc.DrawText (_lines[i], 8, line_height * i);
+ }
+ }
+}
+
+class ClosedCaptionSorter
+{
+public:
+ bool operator() (TextCaption const & a, TextCaption const & b)
+ {
+ return from_top(a) < from_top(b);
+ }
+
+private:
+ float from_top (TextCaption const & c) const
+ {
+ switch (c.v_align()) {
+ case dcp::VALIGN_TOP:
+ return c.v_position();
+ case dcp::VALIGN_CENTER:
+ return c.v_position() + 0.5;
+ case dcp::VALIGN_BOTTOM:
+ return 1.0 - c.v_position();
+ }
+ DCPOMATIC_ASSERT (false);
+ return 0;
+ }
+};
+
+void
+ClosedCaptionsDialog::refresh (DCPTime time)
+{
+ list<TextCaption> to_show;
+ list<Caption>::iterator i = _captions.begin ();
+ while (i != _captions.end ()) {
+ if (time > i->second.to) {
+ list<Caption>::iterator tmp = i;
+ ++i;
+ _captions.erase (tmp);
+ } else if (i->second.contains (time)) {
+ BOOST_FOREACH (TextCaption j, i->first.text) {
+ to_show.push_back (j);
+ }
+ ++i;
+ } else {
+ ++i;
+ }
+ }
+
+ for (int j = 0; j < _num_lines; ++j) {
+ _lines[j] = "";
+ }
+
+ to_show.sort (ClosedCaptionSorter());
+
+ list<TextCaption>::const_iterator j = to_show.begin();
+ int k = 0;
+ while (j != to_show.end() && k < _num_lines) {
+ _lines[k] = j->text();
+ ++j;
+ ++k;
+ }
+
+ Refresh ();
+}
+
+void
+ClosedCaptionsDialog::caption (PlayerCaption caption, DCPTimePeriod period)
+{
+ _captions.push_back (make_pair (caption, period));
+}
+
+void
+ClosedCaptionsDialog::clear ()
+{
+ _captions.clear ();
+ Refresh ();
+}
--- /dev/null
+/*
+ Copyright (C) 2018 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 "lib/dcpomatic_time.h"
+#include "lib/player_caption.h"
+#include <wx/wx.h>
+
+class ClosedCaptionsView : public wxDialog
+{
+public:
+ ClosedCaptionsView (wxWindow* parent);
+
+ void refresh (DCPTime);
+ void caption (PlayerCaption, DCPTimePeriod);
+ void clear ();
+
+private:
+ void paint ();
+
+ typedef std::pair<PlayerCaption, DCPTimePeriod> Caption;
+ std::list<Caption> _captions;
+ std::vector<wxString> _lines;
+ static int const _num_lines;
+ static int const _num_chars_per_line;
+};
--- /dev/null
+/*
+ Copyright (C) 2018 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 "closed_captions_view.h"
+#include <boost/bind.hpp>
+
+using std::list;
+using std::cout;
+using std::make_pair;
+
+int const ClosedCaptionsDialog::_num_lines = 3;
+int const ClosedCaptionsDialog::_num_chars_per_line = 30;
+
+ClosedCaptionsDialog::ClosedCaptionsDialog (wxWindow* parent)
+ : wxDialog (parent, wxID_ANY, _("Closed captions"), wxDefaultPosition, wxDefaultSize,
+#ifdef DCPOMATIC_OSX
+ /* I can't get wxFRAME_FLOAT_ON_PARENT to work on OS X, and although wxSTAY_ON_TOP keeps
+ the window above all others (and not just our own) it's better than nothing for now.
+ */
+ wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxSTAY_ON_TOP
+#else
+ wxDEFAULT_FRAME_STYLE | wxRESIZE_BORDER | wxFULL_REPAINT_ON_RESIZE | wxFRAME_FLOAT_ON_PARENT
+#endif
+ )
+
+{
+ _lines.resize (_num_lines);
+ Bind (wxEVT_PAINT, boost::bind (&ClosedCaptionsDialog::paint, this));
+}
+
+void
+ClosedCaptionsDialog::paint ()
+{
+ wxPaintDC dc (this);
+ dc.SetBackground (*wxBLACK_BRUSH);
+ dc.Clear ();
+ dc.SetTextForeground (*wxWHITE);
+
+ /* Choose a font which fits vertically */
+ int const line_height = dc.GetSize().GetHeight() / _num_lines;
+ wxFont font (*wxNORMAL_FONT);
+ font.SetPixelSize (wxSize (0, line_height * 0.8));
+ dc.SetFont (font);
+
+ for (int i = 0; i < _num_lines; ++i) {
+ if (_lines[i].IsEmpty()) {
+ dc.DrawText (wxString::Format("Line %d", i + 1), 8, line_height * i);
+ } else {
+ dc.DrawText (_lines[i], 8, line_height * i);
+ }
+ }
+}
+
+class ClosedCaptionSorter
+{
+public:
+ bool operator() (TextCaption const & a, TextCaption const & b)
+ {
+ return from_top(a) < from_top(b);
+ }
+
+private:
+ float from_top (TextCaption const & c) const
+ {
+ switch (c.v_align()) {
+ case dcp::VALIGN_TOP:
+ return c.v_position();
+ case dcp::VALIGN_CENTER:
+ return c.v_position() + 0.5;
+ case dcp::VALIGN_BOTTOM:
+ return 1.0 - c.v_position();
+ }
+ DCPOMATIC_ASSERT (false);
+ return 0;
+ }
+};
+
+void
+ClosedCaptionsDialog::refresh (DCPTime time)
+{
+ list<TextCaption> to_show;
+ list<Caption>::iterator i = _captions.begin ();
+ while (i != _captions.end ()) {
+ if (time > i->second.to) {
+ list<Caption>::iterator tmp = i;
+ ++i;
+ _captions.erase (tmp);
+ } else if (i->second.contains (time)) {
+ BOOST_FOREACH (TextCaption j, i->first.text) {
+ to_show.push_back (j);
+ }
+ ++i;
+ } else {
+ ++i;
+ }
+ }
+
+ for (int j = 0; j < _num_lines; ++j) {
+ _lines[j] = "";
+ }
+
+ to_show.sort (ClosedCaptionSorter());
+
+ list<TextCaption>::const_iterator j = to_show.begin();
+ int k = 0;
+ while (j != to_show.end() && k < _num_lines) {
+ _lines[k] = j->text();
+ ++j;
+ ++k;
+ }
+
+ Refresh ();
+}
+
+void
+ClosedCaptionsDialog::caption (PlayerCaption caption, DCPTimePeriod period)
+{
+ _captions.push_back (make_pair (caption, period));
+}
+
+void
+ClosedCaptionsDialog::clear ()
+{
+ _captions.clear ();
+ Refresh ();
+}
--- /dev/null
+/*
+ Copyright (C) 2018 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 "lib/dcpomatic_time.h"
+#include "lib/player_caption.h"
+#include <wx/wx.h>
+
+class ClosedCaptionsDialog : public wxDialog
+{
+public:
+ ClosedCaptionsDialog (wxWindow* parent);
+
+ void refresh (DCPTime);
+ void caption (PlayerCaption, DCPTimePeriod);
+ void clear ();
+
+private:
+ void paint ();
+
+ typedef std::pair<PlayerCaption, DCPTimePeriod> Caption;
+ std::list<Caption> _captions;
+ std::vector<wxString> _lines;
+ static int const _num_lines;
+ static int const _num_chars_per_line;
+};
#include "playhead_to_timecode_dialog.h"
#include "playhead_to_frame_dialog.h"
#include "wx_util.h"
+#include "closed_captions_view.h"
#include "lib/film.h"
#include "lib/ratio.h"
#include "lib/util.h"
, _playing (false)
, _latency_history_count (0)
, _dropped (0)
+ , _closed_captions_dialog (new ClosedCaptionsDialog(GetParent()))
{
#ifndef __WXOSX__
_panel->SetDoubleBuffered (true);
_film = film;
_frame.reset ();
+ _closed_captions_dialog->clear ();
update_position_slider ();
update_position_label ();
_film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1));
_player->Changed.connect (boost::bind (&FilmViewer::player_changed, this, _1, _2));
+ _player->Caption.connect (boost::bind (&FilmViewer::caption, this, _1, _2, _3));
/* Keep about 1 second's worth of history samples */
_latency_history_count = _film->audio_frame_rate() / _audio_block_size;
_inter_size = _player_video.first->inter_size ();
refresh_panel ();
+
+ _closed_captions_dialog->refresh (time());
}
void
bool const was_running = stop ();
+ _closed_captions_dialog->clear ();
_butler->seek (t, accurate);
get ();
{
return DCPTime::from_frames (1, _film->video_frame_rate());
}
+
+void
+FilmViewer::show_closed_captions ()
+{
+ _closed_captions_dialog->Show();
+}
+
+void
+FilmViewer::caption (PlayerCaption c, CaptionType t, DCPTimePeriod p)
+{
+ if (t == CAPTION_CLOSED) {
+ _closed_captions_dialog->caption (c, p);
+ }
+}
/*
- Copyright (C) 2012-2017 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
This file is part of DCP-o-matic.
#include "lib/film.h"
#include "lib/config.h"
+#include "lib/player_caption.h"
#include <RtAudio.h>
#include <wx/wx.h>
class PlayerVideo;
class Player;
class Butler;
+class ClosedCaptionsDialog;
/** @class FilmViewer
* @brief A wx widget to view a preview of a Film.
int audio_callback (void* out, unsigned int frames);
+ void show_closed_captions ();
+
boost::signals2::signal<void (boost::weak_ptr<PlayerVideo>)> ImageChanged;
private:
DCPTime time () const;
Frame average_latency () const;
DCPTime one_video_frame () const;
+ void caption (PlayerCaption caption, CaptionType type, DCPTimePeriod period);
boost::shared_ptr<Film> _film;
boost::shared_ptr<Player> _player;
int _dropped;
boost::optional<int> _dcp_decode_reduction;
+ ClosedCaptionsDialog* _closed_captions_dialog;
+
boost::signals2::scoped_connection _config_changed_connection;
};
content_panel.cc
content_properties_dialog.cc
content_sub_panel.cc
+ closed_captions_dialog.cc
dcp_panel.cc
email_dialog.cc
image_sequence_dialog.cc