summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2026-01-01 00:42:05 +0100
committerCarl Hetherington <cth@carlh.net>2026-01-01 00:42:05 +0100
commitcf2b75373d6908f97bd9c25ce24ffdd9685545f5 (patch)
treeec9015e18ab461ee2ab694c090d3ecbd0b61ac7e /src
parent2567be677682e96d5cb7de31dbdcea80111ac8f4 (diff)
Allow export of Interop/SMPTE format subtitles (#3025).
Diffstat (limited to 'src')
-rw-r--r--src/lib/subtitle_film_encoder.cc39
-rw-r--r--src/lib/subtitle_film_encoder.h11
-rw-r--r--src/tools/dcpomatic.cc12
-rw-r--r--src/wx/export_subtitles_dialog.cc64
-rw-r--r--src/wx/export_subtitles_dialog.h8
5 files changed, 107 insertions, 27 deletions
diff --git a/src/lib/subtitle_film_encoder.cc b/src/lib/subtitle_film_encoder.cc
index 6130ce194..4a29ebcfa 100644
--- a/src/lib/subtitle_film_encoder.cc
+++ b/src/lib/subtitle_film_encoder.cc
@@ -49,19 +49,28 @@ using namespace boost::placeholders;
* @param initial_name Hint that may be used to create filenames, if @ref output is a directory.
* @param include_font true to refer to and export any font file (for Interop; ignored for SMPTE).
*/
-SubtitleFilmEncoder::SubtitleFilmEncoder(shared_ptr<const Film> film, shared_ptr<Job> job, boost::filesystem::path output, string initial_name, bool split_reels, bool include_font)
+SubtitleFilmEncoder::SubtitleFilmEncoder(
+ shared_ptr<const Film> film,
+ shared_ptr<Job> job,
+ boost::filesystem::path output,
+ string initial_name,
+ bool split_reels,
+ bool include_font,
+ dcp::Standard standard
+ )
: FilmEncoder(film, job)
, _split_reels(split_reels)
, _include_font(include_font)
, _reel_index(0)
, _length(film->length())
+ , _standard(standard)
{
_player.set_play_referenced();
_player.set_ignore_video();
_player.set_ignore_audio();
_player.Text.connect(boost::bind(&SubtitleFilmEncoder::text, this, _1, _2, _3, _4));
- string const extension = film->interop() ? ".xml" : ".mxf";
+ string const extension = standard == dcp::Standard::INTEROP ? ".xml" : ".mxf";
int const files = split_reels ? film->reels().size() : 1;
for (int i = 0; i < files; ++i) {
@@ -105,20 +114,27 @@ SubtitleFilmEncoder::go()
for (auto& i: _assets) {
if (!i.first) {
/* No subtitles arrived for this asset; make an empty one so we write something to the output */
- if (_film->interop()) {
+ switch (_standard) {
+ case dcp::Standard::INTEROP:
+ {
auto s = make_shared<dcp::InteropTextAsset>();
s->set_movie_title(_film->name());
s->set_reel_number(fmt::to_string(reel + 1));
i.first = s;
- } else {
+ break;
+ }
+ case dcp::Standard::SMPTE:
+ {
auto s = make_shared<dcp::SMPTETextAsset>();
s->set_content_title_text(_film->name());
s->set_reel_number(reel + 1);
i.first = s;
+ break;
+ }
}
}
- if (!_film->interop() || _include_font) {
+ if (_standard == dcp::Standard::SMPTE || _include_font) {
for (auto j: _player.get_subtitle_fonts()) {
i.first->add_font(j->id(), j->data().get_value_or(_default_font));
}
@@ -140,7 +156,9 @@ SubtitleFilmEncoder::text(PlayerText subs, TextType type, optional<DCPTextTrack>
if (!_assets[_reel_index].first) {
shared_ptr<dcp::TextAsset> asset;
auto const lang = _film->open_text_languages();
- if (_film->interop()) {
+ switch (_standard) {
+ case dcp::Standard::INTEROP:
+ {
auto s = make_shared<dcp::InteropTextAsset>();
s->set_movie_title(_film->name());
if (lang.first) {
@@ -148,7 +166,10 @@ SubtitleFilmEncoder::text(PlayerText subs, TextType type, optional<DCPTextTrack>
}
s->set_reel_number(fmt::to_string(_reel_index + 1));
_assets[_reel_index].first = s;
- } else {
+ break;
+ }
+ case dcp::Standard::SMPTE:
+ {
auto s = make_shared<dcp::SMPTETextAsset>();
s->set_content_title_text(_film->name());
if (lang.first) {
@@ -164,6 +185,8 @@ SubtitleFilmEncoder::text(PlayerText subs, TextType type, optional<DCPTextTrack>
s->set_key(_film->key());
}
_assets[_reel_index].first = s;
+ break;
+ }
}
}
@@ -171,7 +194,7 @@ SubtitleFilmEncoder::text(PlayerText subs, TextType type, optional<DCPTextTrack>
/* XXX: couldn't / shouldn't we use period here rather than getting time from the subtitle? */
i.set_in (i.in());
i.set_out(i.out());
- if (_film->interop() && !_include_font) {
+ if (_standard == dcp::Standard::INTEROP && !_include_font) {
i.unset_font();
}
_assets[_reel_index].first->add(make_shared<dcp::TextString>(i));
diff --git a/src/lib/subtitle_film_encoder.h b/src/lib/subtitle_film_encoder.h
index e2b6dd208..0fbb830c3 100644
--- a/src/lib/subtitle_film_encoder.h
+++ b/src/lib/subtitle_film_encoder.h
@@ -39,7 +39,15 @@ class Film;
class SubtitleFilmEncoder : public FilmEncoder
{
public:
- SubtitleFilmEncoder(std::shared_ptr<const Film> film, std::shared_ptr<Job> job, boost::filesystem::path output, std::string initial_name, bool split_reels, bool include_font);
+ SubtitleFilmEncoder(
+ std::shared_ptr<const Film> film,
+ std::shared_ptr<Job> job,
+ boost::filesystem::path output,
+ std::string initial_name,
+ bool split_reels,
+ bool include_font,
+ dcp::Standard standard
+ );
void go() override;
@@ -60,5 +68,6 @@ private:
int _reel_index;
boost::optional<dcpomatic::DCPTime> _last;
dcpomatic::DCPTime _length;
+ dcp::Standard _standard;
dcp::ArrayData _default_font;
};
diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc
index aaf1040d4..f41a7edc4 100644
--- a/src/tools/dcpomatic.cc
+++ b/src/tools/dcpomatic.cc
@@ -1061,8 +1061,16 @@ private:
}
auto job = make_shared<TranscodeJob>(_film, TranscodeJob::ChangedBehaviour::EXAMINE_THEN_STOP);
job->set_encoder(
- make_shared<SubtitleFilmEncoder>(_film, job, dialog.path(), _film->isdcf_name(true), dialog.split_reels(), dialog.include_font())
- );
+ make_shared<SubtitleFilmEncoder>(
+ _film,
+ job,
+ dialog.path(),
+ _film->isdcf_name(true),
+ dialog.split_reels(),
+ dialog.include_font(),
+ dialog.standard()
+ )
+ );
JobManager::instance()->add(job);
}
diff --git a/src/wx/export_subtitles_dialog.cc b/src/wx/export_subtitles_dialog.cc
index 73ddd9d6c..92cac77cc 100644
--- a/src/wx/export_subtitles_dialog.cc
+++ b/src/wx/export_subtitles_dialog.cc
@@ -20,6 +20,7 @@
#include "check_box.h"
+#include "dcpomatic_choice.h"
#include "export_subtitles_dialog.h"
#include "file_picker_ctrl.h"
#include "wx_util.h"
@@ -36,11 +37,19 @@ using boost::bind;
ExportSubtitlesDialog::ExportSubtitlesDialog(wxWindow* parent, int reels, bool interop)
: wxDialog(parent, wxID_ANY, _("Export subtitles"))
- , _interop(interop)
{
auto sizer = new wxGridBagSizer(DCPOMATIC_SIZER_GAP, DCPOMATIC_SIZER_GAP);
int r = 0;
+
+ add_label_to_sizer(sizer, this, _("Format"), true, wxGBPosition(r, 0));
+ _format = new Choice(this);
+ _format->add_entry(_("XML (Interop)"));
+ _format->add_entry(_("MXF (SMPTE)"));
+ _format->set(interop ? 0 : 1);
+ sizer->Add(_format, wxGBPosition(r, 1));
+ ++r;
+
_split_reels = new CheckBox(this, _("Write reels into separate files"));
sizer->Add(_split_reels, wxGBPosition(r, 0), wxGBSpan(1, 2));
++r;
@@ -53,23 +62,16 @@ ExportSubtitlesDialog::ExportSubtitlesDialog(wxWindow* parent, int reels, bool i
sizer->Add(_include_font, wxGBPosition(r, 0), wxGBSpan(1, 2));
++r;
- if (!_interop) {
- _include_font->Enable(false);
- }
-
- wxString const wildcard = _interop ? _("Subtitle files (.xml)|*.xml") : _("Subtitle files (.mxf)|*.mxf");
-
- _file_label = new wxStaticText(this, wxID_ANY, _("Output file"));
- sizer->Add(_file_label, wxGBPosition(r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
- _file = new FilePickerCtrl(this, _("Select output file"), wildcard, false, true, "ExportSubtitlesPath");
+ _file_label = add_label_to_sizer(sizer, this, _("Output file"), true, wxGBPosition(r, 0));
+ _file = new FilePickerCtrl(this, _("Select output file"), wxT(""), false, true, "ExportSubtitlesPath");
sizer->Add(_file, wxGBPosition(r, 1));
++r;
- _dir_label = new wxStaticText(this, wxID_ANY, (_("Output folder")));
- sizer->Add(_dir_label, wxGBPosition(r, 0), wxDefaultSpan, wxALIGN_CENTER_VERTICAL);
+ _dir_label = add_label_to_sizer(sizer, this, _("Output folder"), true, wxGBPosition(r, 0));
_dir = new DirPickerCtrl(this);
sizer->Add(_dir, wxGBPosition(r, 1));
+ _format->bind(&ExportSubtitlesDialog::format_changed, this);
_split_reels->bind(&ExportSubtitlesDialog::setup_sensitivity, this);
_include_font->bind(&ExportSubtitlesDialog::setup_sensitivity, this);
_file->Bind(wxEVT_FILEPICKER_CHANGED, bind(&ExportSubtitlesDialog::setup_sensitivity, this));
@@ -85,13 +87,45 @@ ExportSubtitlesDialog::ExportSubtitlesDialog(wxWindow* parent, int reels, bool i
SetSizerAndFit(overall_sizer);
setup_sensitivity();
+ setup_wildcard();
+}
+
+
+void
+ExportSubtitlesDialog::format_changed()
+{
+ setup_sensitivity();
+ setup_wildcard();
+}
+
+
+void
+ExportSubtitlesDialog::setup_wildcard()
+{
+ _file->set_wildcard(standard() == dcp::Standard::INTEROP ? _("Subtitle files (.xml)|*.xml") : _("Subtitle files (.mxf)|*.mxf"));
+}
+
+
+dcp::Standard
+ExportSubtitlesDialog::standard() const
+{
+ switch (_format->get().get_value_or(0)) {
+ case 0:
+ return dcp::Standard::INTEROP;
+ case 1:
+ return dcp::Standard::SMPTE;
+ }
+
+ DCPOMATIC_ASSERT(false);
+ return dcp::Standard::SMPTE;
}
void
ExportSubtitlesDialog::setup_sensitivity()
{
- bool const multi = split_reels() || (_interop && _include_font->GetValue());
+ bool const multi = split_reels() || (standard() == dcp::Standard::INTEROP && _include_font->GetValue());
+ _include_font->Enable(standard() == dcp::Standard::INTEROP);
_file_label->Enable(!multi);
_file->Enable(!multi);
_dir_label->Enable(multi);
@@ -112,7 +146,7 @@ ExportSubtitlesDialog::path() const
if (_file->IsEnabled()) {
if (auto path = _file->path()) {
wxFileName fn(std_to_wx(path->string()));
- fn.SetExt(char_to_wx(_interop ? "xml" : "mxf"));
+ fn.SetExt(char_to_wx(standard() == dcp::Standard::INTEROP ? "xml" : "mxf"));
return wx_to_std(fn.GetFullPath());
}
}
@@ -131,6 +165,6 @@ ExportSubtitlesDialog::split_reels() const
bool
ExportSubtitlesDialog::include_font() const
{
- return !_interop || _include_font->GetValue();
+ return standard() == dcp::Standard::SMPTE || _include_font->GetValue();
}
diff --git a/src/wx/export_subtitles_dialog.h b/src/wx/export_subtitles_dialog.h
index 282c8884e..6c18555c0 100644
--- a/src/wx/export_subtitles_dialog.h
+++ b/src/wx/export_subtitles_dialog.h
@@ -20,6 +20,7 @@
#include "dir_picker_ctrl.h"
+#include <dcp/types.h>
#include <dcp/warnings.h>
LIBDCP_DISABLE_WARNINGS
#include <wx/wx.h>
@@ -28,22 +29,27 @@ LIBDCP_ENABLE_WARNINGS
class CheckBox;
+class Choice;
class FilePickerCtrl;
class ExportSubtitlesDialog : public wxDialog
{
public:
+ /** @param interop true to default to XML export, otherwise MXF */
ExportSubtitlesDialog(wxWindow* parent, int reels, bool interop);
boost::filesystem::path path() const;
bool split_reels() const;
bool include_font() const;
+ dcp::Standard standard() const;
private:
+ void format_changed();
void setup_sensitivity();
+ void setup_wildcard();
- bool _interop;
+ Choice* _format;
CheckBox* _split_reels;
CheckBox* _include_font = nullptr;
wxStaticText* _file_label;