Basics of setting and storing SMPTE CPL markers.
authorCarl Hetherington <cth@carlh.net>
Fri, 15 Mar 2019 00:52:56 +0000 (00:52 +0000)
committerCarl Hetherington <cth@carlh.net>
Fri, 10 May 2019 22:43:42 +0000 (23:43 +0100)
src/lib/film.cc
src/lib/film.h
src/wx/dcp_panel.cc
src/wx/dcp_panel.h
src/wx/film_editor.cc
src/wx/markers_dialog.cc [new file with mode: 0644]
src/wx/markers_dialog.h [new file with mode: 0644]
src/wx/wscript

index aa16fdad8b485b24125ff7dd0cd9da688493a99d..b40b439f528628e577484fb6d40eab4d4322559a 100644 (file)
@@ -403,6 +403,11 @@ Film::metadata (bool with_content_paths) const
        root->add_child("UploadAfterMakeDCP")->add_child_text (_upload_after_make_dcp ? "1" : "0");
        root->add_child("ReencodeJ2K")->add_child_text (_reencode_j2k ? "1" : "0");
        root->add_child("UserExplicitVideoFrameRate")->add_child_text(_user_explicit_video_frame_rate ? "1" : "0");
+       for (map<dcp::Marker, DCPTime>::const_iterator i = _markers.begin(); i != _markers.end(); ++i) {
+               xmlpp::Element* m = root->add_child("Marker");
+               m->set_attribute("Type", dcp::marker_to_string(i->first));
+               m->add_child_text(raw_convert<string>(i->second.get()));
+       }
        _playlist->as_xml (root->add_child ("Playlist"), with_content_paths);
 
        return doc;
@@ -531,7 +536,12 @@ Film::read_metadata (optional<boost::filesystem::path> path)
        _reencode_j2k = f.optional_bool_child("ReencodeJ2K").get_value_or(false);
        _user_explicit_video_frame_rate = f.optional_bool_child("UserExplicitVideoFrameRate").get_value_or(false);
 
+       BOOST_FOREACH (cxml::ConstNodePtr i, f.node_children("Marker")) {
+               _markers[dcp::marker_from_string(i->string_attribute("Type"))] = DCPTime(dcp::raw_convert<DCPTime::Type>(i->content()));
+       }
+
        list<string> notes;
+       /* This method is the only one that can return notes (so far) */
        _playlist->set_from_xml (shared_from_this(), f.node_child ("Playlist"), _state_version, notes);
 
        /* Write backtraces to this film's directory, until another film is loaded */
@@ -1685,3 +1695,27 @@ Film::closed_caption_tracks () const
 
        return tt;
 }
+
+void
+Film::set_marker (dcp::Marker type, DCPTime time)
+{
+       ChangeSignaller<Film> ch (this, MARKERS);
+       _markers[type] = time;
+}
+
+void
+Film::unset_marker (dcp::Marker type)
+{
+       ChangeSignaller<Film> ch (this, MARKERS);
+       _markers.erase (type);
+}
+
+optional<DCPTime>
+Film::marker (dcp::Marker type) const
+{
+       map<dcp::Marker, DCPTime>::const_iterator i = _markers.find (type);
+       if (i == _markers.end()) {
+               return optional<DCPTime>();
+       }
+       return i->second;
+}
index b77ce7a7651099dfd4334381002613b824207af4..dd69c9e68b3b6aaef1bd41a7bc1ab8756160b334 100644 (file)
@@ -200,7 +200,8 @@ public:
                REEL_TYPE,
                REEL_LENGTH,
                UPLOAD_AFTER_MAKE_DCP,
-               REENCODE_J2K
+               REENCODE_J2K,
+               MARKERS
        };
 
 
@@ -296,6 +297,7 @@ public:
                return _reencode_j2k;
        }
 
+       boost::optional<DCPTime> marker (dcp::Marker type) const;
 
        /* SET */
 
@@ -327,6 +329,8 @@ public:
        void set_reel_length (int64_t);
        void set_upload_after_make_dcp (bool);
        void set_reencode_j2k (bool);
+       void set_marker (dcp::Marker type, DCPTime time);
+       void unset_marker (dcp::Marker type);
 
        /** Emitted when some property has of the Film is about to change or has changed */
        mutable boost::signals2::signal<void (ChangeType, Property)> Change;
@@ -406,6 +410,7 @@ private:
        bool _reencode_j2k;
        /** true if the user has ever explicitly set the video frame rate of this film */
        bool _user_explicit_video_frame_rate;
+       std::map<dcp::Marker, DCPTime> _markers;
 
        int _state_version;
 
@@ -414,7 +419,6 @@ private:
        /** film being used as a template, or 0 */
        boost::shared_ptr<Film> _template_film;
 
-
        boost::signals2::scoped_connection _playlist_change_connection;
        boost::signals2::scoped_connection _playlist_order_changed_connection;
        boost::signals2::scoped_connection _playlist_content_change_connection;
index 3defe75e4822b938f5dac5d6b40699fc54e2e937..85ed81e465029ad099246a7cb0241a5c8237daf6 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -28,6 +28,7 @@
 #include "static_text.h"
 #include "check_box.h"
 #include "dcpomatic_button.h"
+#include "markers_dialog.h"
 #include "lib/ratio.h"
 #include "lib/config.h"
 #include "lib/dcp_content_type.h"
@@ -58,11 +59,14 @@ using std::max;
 using std::make_pair;
 using boost::lexical_cast;
 using boost::shared_ptr;
+using boost::weak_ptr;
 using dcp::locale_convert;
 
-DCPPanel::DCPPanel (wxNotebook* n, boost::shared_ptr<Film> film)
+DCPPanel::DCPPanel (wxNotebook* n, shared_ptr<Film> film, weak_ptr<FilmViewer> viewer)
        : _audio_dialog (0)
+       , _markers_dialog (0)
        , _film (film)
+       , _viewer (viewer)
        , _generally_sensitive (true)
 {
        _panel = new wxPanel (n);
@@ -114,6 +118,8 @@ DCPPanel::DCPPanel (wxNotebook* n, boost::shared_ptr<Film> film)
 
        _upload_after_make_dcp = new CheckBox (_panel, _("Upload DCP to TMS after it is made"));
 
+       _markers = new Button (_panel, _("Edit markers..."));
+
        _notebook = new wxNotebook (_panel, wxID_ANY);
        _sizer->Add (_notebook, 1, wxEXPAND | wxTOP, 6);
 
@@ -132,6 +138,7 @@ DCPPanel::DCPPanel (wxNotebook* n, boost::shared_ptr<Film> film)
        _reel_length->Bind           (wxEVT_SPINCTRL, boost::bind (&DCPPanel::reel_length_changed, this));
        _standard->Bind              (wxEVT_CHOICE,   boost::bind (&DCPPanel::standard_changed, this));
        _upload_after_make_dcp->Bind (wxEVT_CHECKBOX, boost::bind (&DCPPanel::upload_after_make_dcp_changed, this));
+       _markers->Bind               (wxEVT_BUTTON,   boost::bind (&DCPPanel::markers_clicked, this));
 
        BOOST_FOREACH (DCPContentType const * i, DCPContentType::all()) {
                _dcp_content_type->Append (std_to_wx (i->pretty_name ()));
@@ -213,6 +220,7 @@ DCPPanel::add_to_grid ()
        _standard_label->Show (full);
        _standard->Show (full);
        _upload_after_make_dcp->Show (full);
+       _markers->Show (full);
        _reencode_j2k->Show (full);
        _encrypted->Show (full);
 
@@ -245,6 +253,9 @@ DCPPanel::add_to_grid ()
 
                _grid->Add (_upload_after_make_dcp, wxGBPosition (r, 0), wxGBSpan (1, 2));
                ++r;
+
+               _grid->Add (_markers, wxGBPosition(r, 0), wxGBSpan(1, 2));
+               ++r;
        }
 }
 
@@ -365,6 +376,18 @@ DCPPanel::upload_after_make_dcp_changed ()
        _film->set_upload_after_make_dcp (_upload_after_make_dcp->GetValue ());
 }
 
+void
+DCPPanel::markers_clicked ()
+{
+       if (_markers_dialog) {
+               _markers_dialog->Destroy ();
+               _markers_dialog = 0;
+       }
+
+       _markers_dialog = new MarkersDialog (_panel, _film, _viewer);
+       _markers_dialog->Show();
+}
+
 void
 DCPPanel::film_changed (int p)
 {
@@ -458,6 +481,7 @@ DCPPanel::film_changed (int p)
        case Film::INTEROP:
                checked_set (_standard, _film->interop() ? 1 : 0);
                setup_dcp_name ();
+               _markers->Enable (!_film->interop());
                break;
        case Film::AUDIO_PROCESSOR:
                if (_film->audio_processor ()) {
@@ -558,11 +582,15 @@ DCPPanel::dcp_content_type_changed ()
 void
 DCPPanel::set_film (shared_ptr<Film> film)
 {
-       /* We are changing film, so destroy any audio dialog for the old one */
+       /* We are changing film, so destroy any dialogs for the old one */
        if (_audio_dialog) {
                _audio_dialog->Destroy ();
                _audio_dialog = 0;
        }
+       if (_markers_dialog) {
+               _markers_dialog->Destroy ();
+               _markers_dialog = 0;
+       }
 
        _film = film;
 
@@ -626,6 +654,7 @@ DCPPanel::setup_sensitivity ()
        _reel_type->Enable              (_generally_sensitive && _film && !_film->references_dcp_video() && !_film->references_dcp_audio());
        _reel_length->Enable            (_generally_sensitive && _film && _film->reel_type() == REELTYPE_BY_LENGTH);
        _upload_after_make_dcp->Enable  (_generally_sensitive);
+       _markers->Enable                (_generally_sensitive);
        _frame_rate_choice->Enable      (_generally_sensitive && _film && !_film->references_dcp_video());
        _frame_rate_spin->Enable        (_generally_sensitive && _film && !_film->references_dcp_video());
        _audio_channels->Enable         (_generally_sensitive && _film && !_film->references_dcp_audio());
index c43311429a3172e32a39fb9979dcaa1942212f8b..27049962cba87db5626bbeeb2e9c588493949343 100644 (file)
@@ -1,5 +1,5 @@
 /*
-    Copyright (C) 2012-2018 Carl Hetherington <cth@carlh.net>
+    Copyright (C) 2012-2019 Carl Hetherington <cth@carlh.net>
 
     This file is part of DCP-o-matic.
 
@@ -35,13 +35,15 @@ class wxSizer;
 class wxGridBagSizer;
 
 class AudioDialog;
+class MarkersDialog;
 class Film;
+class FilmViewer;
 class Ratio;
 
 class DCPPanel : public boost::noncopyable
 {
 public:
-       DCPPanel (wxNotebook *, boost::shared_ptr<Film>);
+       DCPPanel (wxNotebook *, boost::shared_ptr<Film>, boost::weak_ptr<FilmViewer> viewer);
 
        void set_film (boost::shared_ptr<Film>);
        void set_general_sensitivity (bool);
@@ -77,6 +79,7 @@ private:
        void reel_type_changed ();
        void reel_length_changed ();
        void upload_after_make_dcp_changed ();
+       void markers_clicked ();
        void reencode_j2k_changed ();
 
        void setup_frame_rate_widget ();
@@ -144,9 +147,12 @@ private:
        wxStaticText* _reel_length_gb_label;
        wxSpinCtrl* _reel_length;
        wxCheckBox* _upload_after_make_dcp;
+       wxButton* _markers;
 
        AudioDialog* _audio_dialog;
+       MarkersDialog* _markers_dialog;
 
        boost::shared_ptr<Film> _film;
+       boost::weak_ptr<FilmViewer> _viewer;
        bool _generally_sensitive;
 };
index a017175f522106d4663f1b98d37087d9e2304d19..ae0868f27f55f01bf2543e6307efda9639ace184 100644 (file)
@@ -52,7 +52,7 @@ FilmEditor::FilmEditor (wxWindow* parent, weak_ptr<FilmViewer> viewer)
 
        _content_panel = new ContentPanel (_main_notebook, _film, viewer);
        _main_notebook->AddPage (_content_panel->window(), _("Content"), true);
-       _dcp_panel = new DCPPanel (_main_notebook, _film);
+       _dcp_panel = new DCPPanel (_main_notebook, _film, viewer);
        _main_notebook->AddPage (_dcp_panel->panel (), _("DCP"), false);
 
        JobManager::instance()->ActiveJobsChanged.connect (
diff --git a/src/wx/markers_dialog.cc b/src/wx/markers_dialog.cc
new file mode 100644 (file)
index 0000000..724cb7d
--- /dev/null
@@ -0,0 +1,127 @@
+/*
+    Copyright (C) 2019 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 "markers_dialog.h"
+#include "wx_util.h"
+#include "timecode.h"
+#include "static_text.h"
+#include "dcpomatic_button.h"
+#include "check_box.h"
+#include "film_viewer.h"
+#include "lib/film.h"
+#include <dcp/types.h>
+#include <wx/gbsizer.h>
+#include <boost/bind.hpp>
+#include <iostream>
+
+using std::cout;
+using boost::bind;
+using boost::shared_ptr;
+using boost::weak_ptr;
+using boost::optional;
+
+class Marker
+{
+public:
+       Marker (wxWindow* parent, wxGridBagSizer* grid, int row, weak_ptr<Film> film_, weak_ptr<FilmViewer> viewer_, wxString name, dcp::Marker type_)
+               : film (film_)
+               , viewer (viewer_)
+               , type (type_)
+       {
+               checkbox = new CheckBox(parent, name);
+               grid->Add (checkbox, wxGBPosition(row, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+               timecode = new Timecode<DCPTime> (parent);
+               grid->Add (timecode, wxGBPosition(row, 1));
+               set_button = new Button (parent, _("Set from current position"));
+               grid->Add (set_button, wxGBPosition(row, 2));
+
+               shared_ptr<Film> f = film.lock ();
+               DCPOMATIC_ASSERT (f);
+
+               optional<DCPTime> t = f->marker (type);
+               checkbox->SetValue (static_cast<bool>(t));
+               if (t) {
+                       timecode->set (*t, f->video_frame_rate());
+               }
+
+               set_sensitivity ();
+
+               set_button->Bind (wxEVT_BUTTON, bind(&Marker::set, this));
+               checkbox->Bind (wxEVT_CHECKBOX, bind(&Marker::set_sensitivity, this));
+               timecode->Changed.connect (bind(&Marker::changed, this));
+       }
+
+private:
+       void set_sensitivity ()
+       {
+               timecode->Enable (checkbox->GetValue());
+               set_button->Enable (checkbox->GetValue());
+       }
+
+       void set ()
+       {
+               shared_ptr<Film> f = film.lock ();
+               DCPOMATIC_ASSERT (f);
+               shared_ptr<FilmViewer> v = viewer.lock ();
+               DCPOMATIC_ASSERT (v);
+               timecode->set (v->position(), f->video_frame_rate());
+       }
+
+       void changed ()
+       {
+               shared_ptr<Film> f = film.lock ();
+               DCPOMATIC_ASSERT (f);
+               if (checkbox->GetValue()) {
+                       f->set_marker (type, timecode->get(f->video_frame_rate()));
+               } else {
+                       f->unset_marker (type);
+               }
+       }
+
+       weak_ptr<Film> film;
+       weak_ptr<FilmViewer> viewer;
+       dcp::Marker type;
+       CheckBox* checkbox;
+       Timecode<DCPTime>* timecode;
+       Button* set_button;
+};
+
+MarkersDialog::MarkersDialog (wxWindow* parent, weak_ptr<Film> film, weak_ptr<FilmViewer> viewer)
+       : wxDialog (parent, wxID_ANY, _("Markers"))
+       , _film (film)
+{
+       wxSizer* sizer = new wxBoxSizer (wxVERTICAL);
+       wxGridBagSizer* grid = new wxGridBagSizer (DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+
+       int r = 0;
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("First frame of composition"), dcp::FFOC)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("Last frame of composition"), dcp::LFOC)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("First frame of title credits"), dcp::FFTC)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("Last frame of title credits"), dcp::LFTC)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("First frame of intermission"), dcp::FFOI)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("Last frame of intermission"), dcp::LFOI)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("First frame of end credits"), dcp::FFEC)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("Last frame of end credits"), dcp::LFEC)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("First frame of moving credits"), dcp::FFMC)));
+       _markers.push_back (shared_ptr<Marker>(new Marker(this, grid, r++, film, viewer, _("Last frame of moving credits"), dcp::LFMC)));
+
+       sizer->Add (grid, 0, wxALL, 8);
+       SetSizerAndFit (sizer);
+}
diff --git a/src/wx/markers_dialog.h b/src/wx/markers_dialog.h
new file mode 100644 (file)
index 0000000..fbbaa1a
--- /dev/null
@@ -0,0 +1,38 @@
+/*
+    Copyright (C) 2019 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 <wx/wx.h>
+#include <boost/shared_ptr.hpp>
+#include <boost/weak_ptr.hpp>
+#include <list>
+
+class Marker;
+class Film;
+class FilmViewer;
+
+class MarkersDialog : public wxDialog
+{
+public:
+       MarkersDialog (wxWindow* parent, boost::weak_ptr<Film> film, boost::weak_ptr<FilmViewer> viewer);
+
+private:
+       std::list<boost::shared_ptr<Marker> > _markers;
+       boost::weak_ptr<Film> _film;
+};
index 538e7da0e6c374c7aee5d75744b6b7bfc1169d6f..349842797cc6fede6077470db3561b39342061ac 100644 (file)
@@ -85,6 +85,7 @@ sources = """
           kdm_timing_panel.cc
           key_dialog.cc
           make_chain_dialog.cc
+          markers_dialog.cc
           message_dialog.cc
           monitor_dialog.cc
           move_to_dialog.cc