From 1db0293ad36605da9ca8daa8736ef581f4f6a34e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 28 Aug 2018 23:34:56 +0100 Subject: [PATCH] Basics of splitting CCAP streams into different assets. --- src/lib/butler.cc | 10 ++++--- src/lib/butler.h | 4 +-- src/lib/dcp_encoder.cc | 9 +++--- src/lib/dcp_encoder.h | 3 +- src/lib/dcp_text_track.cc | 10 +++++++ src/lib/dcp_text_track.h | 2 ++ src/lib/hints.cc | 4 +-- src/lib/hints.h | 3 +- src/lib/player.cc | 2 +- src/lib/player.h | 2 +- src/lib/reel_writer.cc | 47 +++++++++++++++++++++++++------- src/lib/reel_writer.h | 6 ++-- src/lib/text_ring_buffers.cc | 10 +++---- src/lib/text_ring_buffers.h | 26 ++++++++++++++++-- src/lib/writer.cc | 33 ++++++++++++++++------ src/lib/writer.h | 6 ++-- src/wx/closed_captions_dialog.cc | 12 ++++---- src/wx/closed_captions_dialog.h | 3 +- test/closed_caption_test.cc | 1 + test/player_test.cc | 6 ++-- 20 files changed, 143 insertions(+), 56 deletions(-) diff --git a/src/lib/butler.cc b/src/lib/butler.cc index de9ed4ed0..a127ee9bd 100644 --- a/src/lib/butler.cc +++ b/src/lib/butler.cc @@ -64,7 +64,7 @@ Butler::Butler (shared_ptr player, shared_ptr log, AudioMapping aud { _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_text_connection = _player->Text.connect (bind (&Butler::text, this, _1, _2, _3, _4)); /* The butler must here about things first, otherwise it might not sort out suspensions in time for get_video() to be called in response to this signal. */ @@ -208,7 +208,7 @@ Butler::get_video () return r; } -optional > +optional Butler::get_closed_caption () { boost::mutex::scoped_lock lm (_mutex); @@ -349,12 +349,14 @@ Butler::player_change (ChangeType type, bool frequent) } void -Butler::text (PlayerText pt, TextType type, DCPTimePeriod period) +Butler::text (PlayerText pt, TextType type, optional track, DCPTimePeriod period) { if (type != TEXT_CLOSED_CAPTION) { return; } + DCPOMATIC_ASSERT (track); + boost::mutex::scoped_lock lm2 (_buffers_mutex); - _closed_caption.put (make_pair(pt, period)); + _closed_caption.put (pt, *track, period); } diff --git a/src/lib/butler.h b/src/lib/butler.h index b10c93e79..4322c401d 100644 --- a/src/lib/butler.h +++ b/src/lib/butler.h @@ -43,7 +43,7 @@ public: void seek (DCPTime position, bool accurate); std::pair, DCPTime> get_video (); boost::optional get_audio (float* out, Frame frames); - boost::optional > get_closed_caption (); + boost::optional get_closed_caption (); void disable_audio (); @@ -53,7 +53,7 @@ private: void thread (); void video (boost::shared_ptr video, DCPTime time); void audio (boost::shared_ptr audio, DCPTime time); - void text (PlayerText pt, TextType type, DCPTimePeriod period); + void text (PlayerText pt, TextType type, boost::optional track, DCPTimePeriod period); bool should_run () const; void prepare (boost::weak_ptr video) const; void player_change (ChangeType type, bool frequent); diff --git a/src/lib/dcp_encoder.cc b/src/lib/dcp_encoder.cc index 7fbbb2c63..50a8fd927 100644 --- a/src/lib/dcp_encoder.cc +++ b/src/lib/dcp_encoder.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2017 Carl Hetherington + Copyright (C) 2012-2018 Carl Hetherington This file is part of DCP-o-matic. @@ -49,6 +49,7 @@ using std::list; using boost::shared_ptr; using boost::weak_ptr; using boost::dynamic_pointer_cast; +using boost::optional; /** Construct a DCP encoder. * @param film Film that we are encoding. @@ -61,7 +62,7 @@ DCPEncoder::DCPEncoder (shared_ptr film, weak_ptr job) { _player_video_connection = _player->Video.connect (bind (&DCPEncoder::video, this, _1, _2)); _player_audio_connection = _player->Audio.connect (bind (&DCPEncoder::audio, this, _1, _2)); - _player_text_connection = _player->Text.connect (bind (&DCPEncoder::text, this, _1, _2, _3)); + _player_text_connection = _player->Text.connect (bind (&DCPEncoder::text, this, _1, _2, _3, _4)); BOOST_FOREACH (shared_ptr c, film->content ()) { BOOST_FOREACH (shared_ptr i, c->text) { @@ -143,10 +144,10 @@ DCPEncoder::audio (shared_ptr data, DCPTime time) } void -DCPEncoder::text (PlayerText data, TextType type, DCPTimePeriod period) +DCPEncoder::text (PlayerText data, TextType type, optional track, DCPTimePeriod period) { if (type == TEXT_CLOSED_CAPTION || _non_burnt_subtitles) { - _writer->write (data, type, period); + _writer->write (data, type, track, period); } } diff --git a/src/lib/dcp_encoder.h b/src/lib/dcp_encoder.h index 8a2ad947d..3ccd5695e 100644 --- a/src/lib/dcp_encoder.h +++ b/src/lib/dcp_encoder.h @@ -20,6 +20,7 @@ #include "types.h" #include "player_text.h" +#include "dcp_text_track.h" #include "encoder.h" #include @@ -52,7 +53,7 @@ private: void video (boost::shared_ptr, DCPTime); void audio (boost::shared_ptr, DCPTime); - void text (PlayerText, TextType, DCPTimePeriod); + void text (PlayerText, TextType, boost::optional, DCPTimePeriod); boost::shared_ptr _writer; boost::shared_ptr _j2k_encoder; diff --git a/src/lib/dcp_text_track.cc b/src/lib/dcp_text_track.cc index 2d3d9fe10..1c73870aa 100644 --- a/src/lib/dcp_text_track.cc +++ b/src/lib/dcp_text_track.cc @@ -61,3 +61,13 @@ operator!= (DCPTextTrack const & a, DCPTextTrack const & b) { return !(a == b); } + +bool +operator< (DCPTextTrack const & a, DCPTextTrack const & b) +{ + if (a.name != b.name) { + return a.name < b.name; + } + + return a.language < b.language; +} diff --git a/src/lib/dcp_text_track.h b/src/lib/dcp_text_track.h index d69e6dae9..913e77fa5 100644 --- a/src/lib/dcp_text_track.h +++ b/src/lib/dcp_text_track.h @@ -27,6 +27,7 @@ class DCPTextTrack { public: + DCPTextTrack () {} DCPTextTrack (cxml::ConstNodePtr node); DCPTextTrack (std::string name_, std::string language_); @@ -39,5 +40,6 @@ public: bool operator== (DCPTextTrack const & a, DCPTextTrack const & b); bool operator!= (DCPTextTrack const & a, DCPTextTrack const & b); +bool operator< (DCPTextTrack const & a, DCPTextTrack const & b); #endif diff --git a/src/lib/hints.cc b/src/lib/hints.cc index 8979b6b4c..f2e13f503 100644 --- a/src/lib/hints.cc +++ b/src/lib/hints.cc @@ -266,7 +266,7 @@ Hints::thread () shared_ptr player (new Player (film, film->playlist ())); player->set_ignore_video (); player->set_ignore_audio (); - player->Text.connect (bind(&Hints::text, this, _1, _2, _3)); + player->Text.connect (bind(&Hints::text, this, _1, _2, _3, _4)); while (!player->pass ()) { bind (boost::ref(Pulse)); } @@ -281,7 +281,7 @@ Hints::hint (string h) } void -Hints::text (PlayerText text, TextType type, DCPTimePeriod period) +Hints::text (PlayerText text, TextType type, optional track, DCPTimePeriod period) { if (type != TEXT_CLOSED_CAPTION) { return; diff --git a/src/lib/hints.h b/src/lib/hints.h index 67cada89f..9da061286 100644 --- a/src/lib/hints.h +++ b/src/lib/hints.h @@ -21,6 +21,7 @@ #include "signaller.h" #include "player_text.h" #include "types.h" +#include "dcp_text_track.h" #include "dcpomatic_time.h" #include #include @@ -46,7 +47,7 @@ private: void thread (); void stop_thread (); void hint (std::string h); - void text (PlayerText text, TextType type, DCPTimePeriod period); + void text (PlayerText text, TextType type, boost::optional track, DCPTimePeriod period); boost::weak_ptr _film; boost::thread* _thread; diff --git a/src/lib/player.cc b/src/lib/player.cc index cf9bc2e63..5202bbbe0 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -991,7 +991,7 @@ Player::subtitle_stop (weak_ptr wp, weak_ptr wc, Conte bool const always = (text->type() == TEXT_OPEN_SUBTITLE && _always_burn_open_subtitles); if (text->use() && !always && !text->burn()) { - Text (from.first, text->type(), DCPTimePeriod (from.second, dcp_to)); + Text (from.first, text->type(), text->dcp_track().get_value_or(DCPTextTrack()), DCPTimePeriod (from.second, dcp_to)); } } diff --git a/src/lib/player.h b/src/lib/player.h index b4f41f6da..70a2e4ae3 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -99,7 +99,7 @@ public: /** Emitted when a text is ready. This signal may be emitted considerably * after the corresponding Video. */ - boost::signals2::signal Text; + boost::signals2::signal, DCPTimePeriod)> Text; private: friend class PlayerWrapper; diff --git a/src/lib/reel_writer.cc b/src/lib/reel_writer.cc index 3d5264060..7b0233d21 100644 --- a/src/lib/reel_writer.cc +++ b/src/lib/reel_writer.cc @@ -57,6 +57,7 @@ using std::list; using std::string; using std::cout; +using std::map; using boost::shared_ptr; using boost::optional; using boost::dynamic_pointer_cast; @@ -350,7 +351,6 @@ maybe_add_text ( shared_ptr reel_asset; if (asset) { - boost::filesystem::path liberation_normal; try { liberation_normal = shared_path() / "LiberationSans-Regular.ttf"; @@ -376,7 +376,6 @@ maybe_add_text ( boost::filesystem::create_directories (directory); asset->write (directory / ("sub_" + asset->id() + ".xml")); } else { - /* All our assets should be the same length; use the picture asset length here as a reference to set the subtitle one. We'll use the duration rather than the intrinsic duration; we don't care if the picture asset has been trimmed, we're @@ -508,8 +507,10 @@ ReelWriter::create_reel (list const & refs, listadd (reel_sound_asset); - maybe_add_text (_text_asset[TEXT_OPEN_SUBTITLE], reel_picture_asset->duration(), reel, refs, fonts, _film, _period); - maybe_add_text (_text_asset[TEXT_CLOSED_CAPTION], reel_picture_asset->duration(), reel, refs, fonts, _film, _period); + maybe_add_text (_subtitle_asset, reel_picture_asset->duration(), reel, refs, fonts, _film, _period); + for (map >::const_iterator i = _closed_caption_assets.begin(); i != _closed_caption_assets.end(); ++i) { + maybe_add_text (i->second, reel_picture_asset->duration(), reel, refs, fonts, _film, _period); + } return reel; } @@ -545,9 +546,23 @@ ReelWriter::write (shared_ptr audio) } void -ReelWriter::write (PlayerText subs, TextType type, DCPTimePeriod period) +ReelWriter::write (PlayerText subs, TextType type, optional track, DCPTimePeriod period) { - if (!_text_asset[type]) { + shared_ptr asset; + + switch (type) { + case TEXT_OPEN_SUBTITLE: + asset = _subtitle_asset; + break; + case TEXT_CLOSED_CAPTION: + DCPOMATIC_ASSERT (track); + asset = _closed_caption_assets[*track]; + break; + default: + DCPOMATIC_ASSERT (false); + } + + if (!asset) { string lang = _film->subtitle_language (); if (lang.empty ()) { lang = "Unknown"; @@ -557,7 +572,7 @@ ReelWriter::write (PlayerText subs, TextType type, DCPTimePeriod period) s->set_movie_title (_film->name ()); s->set_language (lang); s->set_reel_number (raw_convert (_reel_index + 1)); - _text_asset[type] = s; + asset = s; } else { shared_ptr s (new dcp::SMPTESubtitleAsset ()); s->set_content_title_text (_film->name ()); @@ -569,19 +584,31 @@ ReelWriter::write (PlayerText subs, TextType type, DCPTimePeriod period) if (_film->encrypted ()) { s->set_key (_film->key ()); } - _text_asset[type] = s; + asset = s; } } + switch (type) { + case TEXT_OPEN_SUBTITLE: + _subtitle_asset = asset; + break; + case TEXT_CLOSED_CAPTION: + DCPOMATIC_ASSERT (track); + _closed_caption_assets[*track] = asset; + break; + default: + DCPOMATIC_ASSERT (false); + } + BOOST_FOREACH (StringText i, subs.string) { /* XXX: couldn't / shouldn't we use period here rather than getting time from the subtitle? */ i.set_in (i.in() - dcp::Time (_period.from.seconds(), i.in().tcr)); i.set_out (i.out() - dcp::Time (_period.from.seconds(), i.out().tcr)); - _text_asset[type]->add (shared_ptr(new dcp::SubtitleString(i))); + asset->add (shared_ptr(new dcp::SubtitleString(i))); } BOOST_FOREACH (BitmapText i, subs.bitmap) { - _text_asset[type]->add ( + asset->add ( shared_ptr( new dcp::SubtitleImage( i.image->as_png(), diff --git a/src/lib/reel_writer.h b/src/lib/reel_writer.h index 2e5ad8975..ae64c3ac7 100644 --- a/src/lib/reel_writer.h +++ b/src/lib/reel_writer.h @@ -22,6 +22,7 @@ #include "dcpomatic_time.h" #include "referenced_reel_asset.h" #include "player_text.h" +#include "dcp_text_track.h" #include #include @@ -60,7 +61,7 @@ public: void fake_write (Frame frame, Eyes eyes, int size); void repeat_write (Frame frame, Eyes eyes); void write (boost::shared_ptr audio); - void write (PlayerText text, TextType type, DCPTimePeriod period); + void write (PlayerText text, TextType type, boost::optional track, DCPTimePeriod period); void finish (); boost::shared_ptr create_reel (std::list const & refs, std::list > const & fonts); @@ -113,7 +114,8 @@ private: boost::shared_ptr _picture_asset_writer; boost::shared_ptr _sound_asset; boost::shared_ptr _sound_asset_writer; - boost::shared_ptr _text_asset[TEXT_COUNT]; + boost::shared_ptr _subtitle_asset; + std::map > _closed_caption_assets; static int const _info_size; }; diff --git a/src/lib/text_ring_buffers.cc b/src/lib/text_ring_buffers.cc index 3586ab648..cc5357804 100644 --- a/src/lib/text_ring_buffers.cc +++ b/src/lib/text_ring_buffers.cc @@ -24,21 +24,21 @@ using std::pair; using boost::optional; void -TextRingBuffers::put (pair text) +TextRingBuffers::put (PlayerText text, DCPTextTrack track, DCPTimePeriod period) { boost::mutex::scoped_lock lm (_mutex); - _data.push_back (text); + _data.push_back (Data(text, track, period)); } -optional > +optional TextRingBuffers::get () { boost::mutex::scoped_lock lm (_mutex); if (_data.empty ()) { - return pair(); + return optional(); } - pair r = _data.front (); + Data r = _data.front (); _data.pop_front (); return r; } diff --git a/src/lib/text_ring_buffers.h b/src/lib/text_ring_buffers.h index e33d9be3b..289a38149 100644 --- a/src/lib/text_ring_buffers.h +++ b/src/lib/text_ring_buffers.h @@ -18,18 +18,38 @@ */ +#ifndef DCPOMATIC_TEXT_RING_BUFFERS_H +#define DCPOMATIC_TEXT_RING_BUFFERS_H + #include "player_text.h" +#include "dcp_text_track.h" #include #include class TextRingBuffers { public: - void put (std::pair text); - boost::optional > get (); + void put (PlayerText text, DCPTextTrack track, DCPTimePeriod period); + + struct Data { + Data (PlayerText text_, DCPTextTrack track_, DCPTimePeriod period_) + : text (text_) + , track (track_) + , period (period_) + {} + + PlayerText text; + DCPTextTrack track; + DCPTimePeriod period; + }; + + boost::optional get (); void clear (); private: boost::mutex _mutex; - std::list > _data; + + std::list _data; }; + +#endif diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 4b5c5a102..8e2079aeb 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2012-2017 Carl Hetherington + Copyright (C) 2012-2018 Carl Hetherington This file is part of DCP-o-matic. @@ -63,9 +63,11 @@ using std::cout; using std::map; using std::min; using std::max; +using std::vector; using boost::shared_ptr; using boost::weak_ptr; using boost::dynamic_pointer_cast; +using boost::optional; using dcp::Data; Writer::Writer (shared_ptr film, weak_ptr j) @@ -95,8 +97,9 @@ Writer::Writer (shared_ptr film, weak_ptr j) and captions arrive to the Writer in sequence. This is not so for video. */ _audio_reel = _reels.begin (); - for (int i = 0; i < TEXT_COUNT; ++i) { - _text_reel[i] = _reels.begin (); + _subtitle_reel = _reels.begin (); + BOOST_FOREACH (DCPTextTrack i, _film->closed_caption_tracks()) { + _caption_reels[i] = _reels.begin (); } /* Check that the signer is OK if we need one */ @@ -664,17 +667,29 @@ Writer::can_fake_write (Frame frame) const return (frame != 0 && frame < reel.first_nonexistant_frame()); } +/** @param track Closed caption track if type == TEXT_CLOSED_CAPTION */ void -Writer::write (PlayerText text, TextType type, DCPTimePeriod period) +Writer::write (PlayerText text, TextType type, optional track, DCPTimePeriod period) { - while (_text_reel[type]->period().to <= period.from) { - ++_text_reel[type]; - DCPOMATIC_ASSERT (_text_reel[type] != _reels.end()); + vector::iterator* reel = 0; + + switch (type) { + case TEXT_OPEN_SUBTITLE: + reel = &_subtitle_reel; + break; + case TEXT_CLOSED_CAPTION: + reel = &_caption_reels[*track]; + break; + default: + DCPOMATIC_ASSERT (false); } - DCPOMATIC_ASSERT (_text_reel[type] != _reels.end()); + while ((*reel)->period().to <= period.from) { + ++(*reel); + DCPOMATIC_ASSERT (*reel != _reels.end()); + } - _text_reel[type]->write (text, type, period); + (*reel)->write (text, type, track, period); } void diff --git a/src/lib/writer.h b/src/lib/writer.h index 484ca1285..e08e9f28d 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -25,6 +25,7 @@ #include "types.h" #include "player_text.h" #include "exception_store.h" +#include "dcp_text_track.h" #include #include #include @@ -104,7 +105,7 @@ public: bool can_repeat (Frame) const; void repeat (Frame, Eyes); void write (boost::shared_ptr, DCPTime time); - void write (PlayerText text, TextType type, DCPTimePeriod period); + void write (PlayerText text, TextType type, boost::optional, DCPTimePeriod period); void write (std::list > fonts); void write (ReferencedReelAsset asset); void finish (); @@ -124,7 +125,8 @@ private: boost::weak_ptr _job; std::vector _reels; std::vector::iterator _audio_reel; - std::vector::iterator _text_reel[TEXT_COUNT]; + std::vector::iterator _subtitle_reel; + std::map::iterator> _caption_reels; /** our thread, or 0 */ boost::thread* _thread; diff --git a/src/wx/closed_captions_dialog.cc b/src/wx/closed_captions_dialog.cc index 3f444f9a4..b81414bed 100644 --- a/src/wx/closed_captions_dialog.cc +++ b/src/wx/closed_captions_dialog.cc @@ -104,18 +104,18 @@ private: void ClosedCaptionsDialog::update (DCPTime time) { - if (_current_in_lines && _current->second.to > time) { + if (_current_in_lines && _current->period.to > time) { /* Current one is fine */ return; } - if (_current && _current->second.to < time) { + if (_current && _current->period.to < time) { /* Current one has finished; clear out */ for (int j = 0; j < CLOSED_CAPTION_LINES; ++j) { _lines[j] = ""; } Refresh (); - _current = optional >(); + _current = optional(); } if (!_current) { @@ -126,10 +126,10 @@ ClosedCaptionsDialog::update (DCPTime time) _current_in_lines = false; } - if (_current && _current->second.contains(time)) { + if (_current && _current->period.contains(time)) { /* We need to set this new one up */ - list to_show = _current->first.string; + list to_show = _current->text.string; for (int j = 0; j < CLOSED_CAPTION_LINES; ++j) { _lines[j] = ""; @@ -153,7 +153,7 @@ ClosedCaptionsDialog::update (DCPTime time) void ClosedCaptionsDialog::clear () { - _current = optional >(); + _current = optional(); _current_in_lines = false; Refresh (); } diff --git a/src/wx/closed_captions_dialog.h b/src/wx/closed_captions_dialog.h index 3da7f6522..3818e3812 100644 --- a/src/wx/closed_captions_dialog.h +++ b/src/wx/closed_captions_dialog.h @@ -20,6 +20,7 @@ #include "lib/dcpomatic_time.h" #include "lib/player.h" +#include "lib/text_ring_buffers.h" #include class Butler; @@ -36,7 +37,7 @@ public: private: void paint (); - boost::optional > _current; + boost::optional _current; bool _current_in_lines; std::vector _lines; boost::weak_ptr _butler; diff --git a/test/closed_caption_test.cc b/test/closed_caption_test.cc index 4074f8371..35abf1b02 100644 --- a/test/closed_caption_test.cc +++ b/test/closed_caption_test.cc @@ -81,5 +81,6 @@ BOOST_AUTO_TEST_CASE (closed_caption_test2) BOOST_REQUIRE_EQUAL (check.cpls().size(), 1); BOOST_REQUIRE_EQUAL (check.cpls().front()->reels().size(), 1); + std::cout << !check.cpls().front()->reels().front()->closed_captions().size() << "\n"; BOOST_REQUIRE_EQUAL (!check.cpls().front()->reels().front()->closed_captions().size(), 3); } diff --git a/test/player_test.cc b/test/player_test.cc index 605f3bddd..0fe1d953b 100644 --- a/test/player_test.cc +++ b/test/player_test.cc @@ -286,15 +286,17 @@ BOOST_AUTO_TEST_CASE (player_trim_test) struct Sub { PlayerText text; TextType type; + optional track; DCPTimePeriod period; }; static void -store (list* out, PlayerText text, TextType type, DCPTimePeriod period) +store (list* out, PlayerText text, TextType type, optional track, DCPTimePeriod period) { Sub s; s.text = text; s.type = type; + s.track = track; s.period = period; out->push_back (s); } @@ -316,7 +318,7 @@ BOOST_AUTO_TEST_CASE (player_ignore_video_and_audio_test) player->set_ignore_audio (); list out; - player->Text.connect (bind (&store, &out, _1, _2, _3)); + player->Text.connect (bind (&store, &out, _1, _2, _3, _4)); while (!player->pass ()) {} BOOST_CHECK_EQUAL (out.size(), 6); -- 2.30.2