diff options
| author | Carl Hetherington <cth@carlh.net> | 2015-09-16 00:52:50 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2015-09-16 00:52:50 +0100 |
| commit | dd59755d86cdb1892bb0ba4d69af520cee4aa964 (patch) | |
| tree | d0773389ed1a5512c4ebdd14baac6aceee0e77c7 /src | |
| parent | c4bf7a7fca387c7fbbea612d7abbb0d5732b8c5c (diff) | |
Back-end for very basic and hacky VF support for a DCP imported as content.
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/content.cc | 4 | ||||
| -rw-r--r-- | src/lib/dcp_content.cc | 13 | ||||
| -rw-r--r-- | src/lib/dcp_content.h | 27 | ||||
| -rw-r--r-- | src/lib/dcp_decoder.cc | 6 | ||||
| -rw-r--r-- | src/lib/dcp_decoder.h | 4 | ||||
| -rw-r--r-- | src/lib/player.cc | 76 | ||||
| -rw-r--r-- | src/lib/player.h | 8 | ||||
| -rw-r--r-- | src/lib/transcoder.cc | 12 | ||||
| -rw-r--r-- | src/lib/writer.cc | 103 | ||||
| -rw-r--r-- | src/lib/writer.h | 3 | ||||
| -rw-r--r-- | src/wx/film_viewer.cc | 1 |
11 files changed, 218 insertions, 39 deletions
diff --git a/src/lib/content.cc b/src/lib/content.cc index 8b611f6b6..aa596f75a 100644 --- a/src/lib/content.cc +++ b/src/lib/content.cc @@ -228,8 +228,8 @@ Content::length_after_trim () const return max (DCPTime (), full_length() - DCPTime (trim_start() + trim_end(), film->active_frame_rate_change (position ()))); } -/** @return string which includes everything about how this content affects - * its playlist. +/** @return string which changes when something about this content changes which affects + * the appearance of its video. */ string Content::identifier () const diff --git a/src/lib/dcp_content.cc b/src/lib/dcp_content.cc index 549515400..15bcd0b56 100644 --- a/src/lib/dcp_content.cc +++ b/src/lib/dcp_content.cc @@ -49,6 +49,9 @@ DCPContent::DCPContent (shared_ptr<const Film> film, boost::filesystem::path p) , _has_subtitles (false) , _encrypted (false) , _kdm_valid (false) + , _reference_video (false) + , _reference_audio (false) + , _reference_subtitle (false) { read_directory (p); } @@ -66,6 +69,9 @@ DCPContent::DCPContent (shared_ptr<const Film> film, cxml::ConstNodePtr node, in _kdm = dcp::EncryptedKDM (node->string_child ("KDM")); } _kdm_valid = node->bool_child ("KDMValid"); + _reference_video = node->optional_bool_child ("ReferenceVideo").get_value_or (false); + _reference_audio = node->optional_bool_child ("ReferenceAudio").get_value_or (false); + _reference_subtitle = node->optional_bool_child ("ReferenceSubtitle").get_value_or (false); } void @@ -138,6 +144,9 @@ DCPContent::as_xml (xmlpp::Node* node) const node->add_child("KDM")->add_child_text (_kdm->as_xml ()); } node->add_child("KDMValid")->add_child_text (_kdm_valid ? "1" : "0"); + node->add_child("ReferenceVideo")->add_child_text (_reference_video ? "1" : "0"); + node->add_child("ReferenceAudio")->add_child_text (_reference_audio ? "1" : "0"); + node->add_child("ReferenceSubtitle")->add_child_text (_reference_subtitle ? "1" : "0"); } DCPTime @@ -153,7 +162,9 @@ string DCPContent::identifier () const { SafeStringStream s; - s << VideoContent::identifier() << "_" << SubtitleContent::identifier (); + s << VideoContent::identifier() << "_" << SubtitleContent::identifier () << " " + << (_reference_video ? "1" : "0") + << (_reference_subtitle ? "1" : "0"); return s.str (); } diff --git a/src/lib/dcp_content.h b/src/lib/dcp_content.h index 410953eb7..5a0559df2 100644 --- a/src/lib/dcp_content.h +++ b/src/lib/dcp_content.h @@ -85,6 +85,21 @@ public: bool can_be_played () const; + bool reference_video () const { + boost::mutex::scoped_lock lm (_mutex); + return _reference_video; + } + + bool reference_audio () const { + boost::mutex::scoped_lock lm (_mutex); + return _reference_audio; + } + + bool reference_subtitle () const { + boost::mutex::scoped_lock lm (_mutex); + return _reference_subtitle; + } + protected: void add_properties (std::list<std::pair<std::string, std::string> >& p) const; @@ -98,6 +113,18 @@ private: boost::optional<dcp::EncryptedKDM> _kdm; /** true if _kdm successfully decrypts the first frame of our DCP */ bool _kdm_valid; + /** true if the video in this DCP should be included in the output by reference + * rather than by rewrapping. + */ + bool _reference_video; + /** true if the audio in this DCP should be included in the output by reference + * rather than by rewrapping. + */ + bool _reference_audio; + /** true if the subtitle in this DCP should be included in the output by reference + * rather than by rewrapping. + */ + bool _reference_subtitle; }; #endif diff --git a/src/lib/dcp_decoder.cc b/src/lib/dcp_decoder.cc index 1c3285d2d..5060b9a7f 100644 --- a/src/lib/dcp_decoder.cc +++ b/src/lib/dcp_decoder.cc @@ -68,7 +68,7 @@ DCPDecoder::pass () double const vfr = _dcp_content->video_frame_rate (); int64_t const frame = _next.frames_round (vfr); - if ((*_reel)->main_picture ()) { + if ((*_reel)->main_picture () && !_dcp_content->reference_video ()) { shared_ptr<dcp::PictureAsset> asset = (*_reel)->main_picture()->asset (); shared_ptr<dcp::MonoPictureAsset> mono = dynamic_pointer_cast<dcp::MonoPictureAsset> (asset); shared_ptr<dcp::StereoPictureAsset> stereo = dynamic_pointer_cast<dcp::StereoPictureAsset> (asset); @@ -88,7 +88,7 @@ DCPDecoder::pass () } } - if ((*_reel)->main_sound ()) { + if ((*_reel)->main_sound () && !_dcp_content->reference_audio ()) { int64_t const entry_point = (*_reel)->main_sound()->entry_point (); shared_ptr<const dcp::SoundFrame> sf = (*_reel)->main_sound()->asset()->get_frame (entry_point + frame); uint8_t const * from = sf->data (); @@ -106,7 +106,7 @@ DCPDecoder::pass () audio (_dcp_content->audio_stream(), data, _next); } - if ((*_reel)->main_subtitle ()) { + if ((*_reel)->main_subtitle () && !_dcp_content->reference_subtitle ()) { int64_t const entry_point = (*_reel)->main_subtitle()->entry_point (); list<dcp::SubtitleString> subs = (*_reel)->main_subtitle()->subtitle_asset()->subtitles_during ( dcp::Time (entry_point + frame, vfr, vfr), diff --git a/src/lib/dcp_decoder.h b/src/lib/dcp_decoder.h index 15220810d..6fdbd946a 100644 --- a/src/lib/dcp_decoder.h +++ b/src/lib/dcp_decoder.h @@ -37,6 +37,10 @@ class DCPDecoder : public VideoDecoder, public AudioDecoder, public SubtitleDeco public: DCPDecoder (boost::shared_ptr<const DCPContent>, bool fast); + std::list<boost::shared_ptr<dcp::Reel> > reels () const { + return _reels; + } + private: friend struct dcp_subtitle_within_dcp_test; diff --git a/src/lib/player.cc b/src/lib/player.cc index f5d851c96..64fcfc93e 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -46,6 +46,10 @@ #include "dcp_subtitle_decoder.h" #include "audio_processor.h" #include "playlist.h" +#include <dcp/reel.h> +#include <dcp/reel_sound_asset.h> +#include <dcp/reel_subtitle_asset.h> +#include <dcp/reel_picture_asset.h> #include <boost/foreach.hpp> #include <stdint.h> #include <algorithm> @@ -78,6 +82,7 @@ Player::Player (shared_ptr<const Film> film, shared_ptr<const Playlist> playlist , _ignore_audio (false) , _always_burn_subtitles (false) , _fast (false) + , _play_referenced (false) { _film_changed_connection = _film->Changed.connect (bind (&Player::film_changed, this, _1)); _playlist_changed_connection = _playlist->Changed.connect (bind (&Player::playlist_changed, this)); @@ -329,7 +334,10 @@ Player::black_player_video_frame (DCPTime time) const ); } -/** @return All PlayerVideos at the given time (there may be two frames for 3D) */ +/** @return All PlayerVideos at the given time. There may be none if the content + * at `time' is a DCP which we are passing through (i.e. referring to by reference) + * or 2 if we have 3D. + */ list<shared_ptr<PlayerVideo> > Player::get_video (DCPTime time, bool accurate) { @@ -383,6 +391,11 @@ Player::get_video (DCPTime time, bool accurate) shared_ptr<VideoContent> video_content = dynamic_pointer_cast<VideoContent> (piece->content); DCPOMATIC_ASSERT (video_content); + shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (video_content); + if (dcp_content && dcp_content->reference_video () && !_play_referenced) { + continue; + } + bool const use = /* always use the last video */ piece == last || @@ -432,6 +445,7 @@ Player::get_video (DCPTime time, bool accurate) return pvf; } +/** @return Audio data or 0 if the only audio data here is referenced DCP data */ shared_ptr<AudioBuffers> Player::get_audio (DCPTime time, DCPTime length, bool accurate) { @@ -449,11 +463,25 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate) return audio; } - for (list<shared_ptr<Piece> >::iterator i = ov.begin(); i != ov.end(); ++i) { + bool all_referenced = true; + BOOST_FOREACH (shared_ptr<Piece> i, ov) { + shared_ptr<AudioContent> audio_content = dynamic_pointer_cast<AudioContent> (i->content); + shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (i->content); + if (audio_content && (!dcp_content || !dcp_content->reference_audio ())) { + /* There is audio content which is not from a DCP or not set to be referenced */ + all_referenced = false; + } + } + + if (all_referenced && !_play_referenced) { + return shared_ptr<AudioBuffers> (); + } - shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> ((*i)->content); + BOOST_FOREACH (shared_ptr<Piece> i, ov) { + + shared_ptr<AudioContent> content = dynamic_pointer_cast<AudioContent> (i->content); DCPOMATIC_ASSERT (content); - shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> ((*i)->decoder); + shared_ptr<AudioDecoder> decoder = dynamic_pointer_cast<AudioDecoder> (i->decoder); DCPOMATIC_ASSERT (decoder); /* The time that we should request from the content */ @@ -472,7 +500,7 @@ Player::get_audio (DCPTime time, DCPTime length, bool accurate) request = DCPTime (); } - Frame const content_frame = dcp_to_resampled_audio (*i, request); + Frame const content_frame = dcp_to_resampled_audio (i, request); BOOST_FOREACH (AudioStreamPtr j, content->audio_streams ()) { @@ -587,6 +615,11 @@ Player::get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt) continue; } + shared_ptr<DCPContent> dcp_content = dynamic_pointer_cast<DCPContent> (subtitle_content); + if (dcp_content && dcp_content->reference_subtitle () && !_play_referenced) { + continue; + } + shared_ptr<SubtitleDecoder> subtitle_decoder = dynamic_pointer_cast<SubtitleDecoder> ((*j)->decoder); ContentTime const from = dcp_to_content_subtitle (*j, time); /* XXX: this video_frame_rate() should be the rate that the subtitle content has been prepared for */ @@ -683,3 +716,36 @@ Player::set_fast () _fast = true; _have_valid_pieces = false; } + +void +Player::set_play_referenced () +{ + _play_referenced = true; + _have_valid_pieces = false; +} + +list<shared_ptr<dcp::ReelAsset> > +Player::get_reel_assets () +{ + list<shared_ptr<dcp::ReelAsset> > a; + + BOOST_FOREACH (shared_ptr<Content> i, _playlist->content ()) { + shared_ptr<DCPContent> j = dynamic_pointer_cast<DCPContent> (i); + if (!j) { + continue; + } + /* XXX: hack hack hack */ + DCPDecoder decoder (j, false); + if (j->reference_video ()) { + a.push_back (decoder.reels().front()->main_picture ()); + } + if (j->reference_audio ()) { + a.push_back (decoder.reels().front()->main_sound ()); + } + if (j->reference_subtitle ()) { + a.push_back (decoder.reels().front()->main_subtitle ()); + } + } + + return a; +} diff --git a/src/lib/player.h b/src/lib/player.h index 842c2283c..e52ad59c2 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -29,6 +29,10 @@ #include <boost/enable_shared_from_this.hpp> #include <list> +namespace dcp { + class ReelAsset; +} + class PlayerVideo; class Playlist; class Font; @@ -46,6 +50,7 @@ public: boost::shared_ptr<AudioBuffers> get_audio (DCPTime time, DCPTime length, bool accurate); PlayerSubtitles get_subtitles (DCPTime time, DCPTime length, bool starting, bool burnt); std::list<boost::shared_ptr<Font> > get_subtitle_fonts (); + std::list<boost::shared_ptr<dcp::ReelAsset> > get_reel_assets (); void set_video_container_size (dcp::Size); void set_ignore_video (); @@ -53,6 +58,7 @@ public: void set_enable_subtitles (bool enable); void set_always_burn_subtitles (bool burn); void set_fast (); + void set_play_referenced (); /** 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 @@ -127,6 +133,8 @@ private: bool _always_burn_subtitles; /** true if we should try to be fast rather than high quality */ bool _fast; + /** true if we should `play' (i.e output) referenced DCP data (e.g. for preview) */ + bool _play_referenced; boost::shared_ptr<AudioProcessor> _audio_processor; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index 4e8932268..49742f98d 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -90,12 +90,22 @@ Transcoder::go () for (list<shared_ptr<PlayerVideo> >::const_iterator i = v.begin(); i != v.end(); ++i) { _encoder->enqueue (*i); } - _writer->write (_player->get_audio (t, frame, true)); + + shared_ptr<AudioBuffers> audio = _player->get_audio (t, frame, true); + if (audio) { + _writer->write (audio); + } + if (non_burnt_subtitles) { _writer->write (_player->get_subtitles (t, frame, true, false)); } } + /* XXX: we should be passing through details of positions, at least... */ + BOOST_FOREACH (shared_ptr<dcp::ReelAsset> i, _player->get_reel_assets ()) { + _writer->write (i); + } + _finishing = true; _encoder->end (); _writer->finish (); diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 1cc8ad3cc..1318cc20f 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -495,38 +495,46 @@ Writer::finish () terminate_thread (true); - _picture_asset_writer->finalize (); - if (_sound_asset_writer) { - _sound_asset_writer->finalize (); + if (!_picture_asset_writer->finalize ()) { + /* Nothing was written to the picture asset */ + _picture_asset.reset (); } - /* Hard-link the video asset into the DCP */ - boost::filesystem::path video_from = _picture_asset->file (); + if (_sound_asset_writer) { + if (!_sound_asset_writer->finalize ()) { + /* Nothing was written to the sound asset */ + _sound_asset.reset (); + } + } - boost::filesystem::path video_to; - video_to /= _film->dir (_film->dcp_name()); - video_to /= video_asset_filename (_picture_asset); + /* Hard-link any video asset file into the DCP */ + if (_picture_asset) { + boost::filesystem::path video_from = _picture_asset->file (); + boost::filesystem::path video_to; + video_to /= _film->dir (_film->dcp_name()); + video_to /= video_asset_filename (_picture_asset); - boost::system::error_code ec; - boost::filesystem::create_hard_link (video_from, video_to, ec); - if (ec) { - LOG_WARNING_NC ("Hard-link failed; copying instead"); - boost::filesystem::copy_file (video_from, video_to, ec); + boost::system::error_code ec; + boost::filesystem::create_hard_link (video_from, video_to, ec); if (ec) { - LOG_ERROR ("Failed to copy video file from %1 to %2 (%3)", video_from.string(), video_to.string(), ec.message ()); - throw FileError (ec.message(), video_from); + LOG_WARNING_NC ("Hard-link failed; copying instead"); + boost::filesystem::copy_file (video_from, video_to, ec); + if (ec) { + LOG_ERROR ("Failed to copy video file from %1 to %2 (%3)", video_from.string(), video_to.string(), ec.message ()); + throw FileError (ec.message(), video_from); + } } - } - _picture_asset->set_file (video_to); + _picture_asset->set_file (video_to); + } /* Move the audio asset into the DCP */ - if (_sound_asset) { boost::filesystem::path audio_to; audio_to /= _film->dir (_film->dcp_name ()); audio_to /= audio_asset_filename (_sound_asset); + boost::system::error_code ec; boost::filesystem::rename (_film->file (audio_asset_filename (_sound_asset)), audio_to, ec); if (ec) { throw FileError ( @@ -550,18 +558,43 @@ Writer::finish () shared_ptr<dcp::Reel> reel (new dcp::Reel ()); - shared_ptr<dcp::MonoPictureAsset> mono = dynamic_pointer_cast<dcp::MonoPictureAsset> (_picture_asset); - if (mono) { - reel->add (shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelMonoPictureAsset (mono, 0))); - } + shared_ptr<dcp::ReelPictureAsset> reel_picture_asset; + + if (_picture_asset) { + /* We have made a picture asset of our own. Put it into the reel */ + shared_ptr<dcp::MonoPictureAsset> mono = dynamic_pointer_cast<dcp::MonoPictureAsset> (_picture_asset); + if (mono) { + reel_picture_asset.reset (new dcp::ReelMonoPictureAsset (mono, 0)); + } - shared_ptr<dcp::StereoPictureAsset> stereo = dynamic_pointer_cast<dcp::StereoPictureAsset> (_picture_asset); - if (stereo) { - reel->add (shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelStereoPictureAsset (stereo, 0))); + shared_ptr<dcp::StereoPictureAsset> stereo = dynamic_pointer_cast<dcp::StereoPictureAsset> (_picture_asset); + if (stereo && boost::filesystem::exists (stereo->file ())) { + reel_picture_asset.reset (new dcp::ReelStereoPictureAsset (stereo, 0)); + } + } else { + /* We don't have a picture asset of our own; maybe we need to reference one */ + /* XXX: this is all a hack */ + BOOST_FOREACH (shared_ptr<dcp::ReelAsset> i, _reel_assets) { + shared_ptr<dcp::ReelPictureAsset> j = dynamic_pointer_cast<dcp::ReelPictureAsset> (i); + if (j) { + reel_picture_asset = j; + } + } } + reel->add (reel_picture_asset); + if (_sound_asset) { + /* We have made a sound asset of our own. Put it into the reel */ reel->add (shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (_sound_asset, 0))); + } else { + /* We don't have a sound asset of our own; maybe we need to reference one */ + /* XXX: this is all a hack */ + BOOST_FOREACH (shared_ptr<dcp::ReelAsset> i, _reel_assets) { + if (dynamic_pointer_cast<dcp::ReelSoundAsset> (i)) { + reel->add (i); + } + } } if (_subtitle_asset) { @@ -592,10 +625,18 @@ Writer::finish () new dcp::ReelSubtitleAsset ( _subtitle_asset, dcp::Fraction (_film->video_frame_rate(), 1), - _picture_asset->intrinsic_duration (), + reel_picture_asset->intrinsic_duration (), 0 ) )); + } else { + /* We don't have a subtitle asset of our own; maybe we need to reference one */ + /* XXX: this is all a hack */ + BOOST_FOREACH (shared_ptr<dcp::ReelAsset> i, _reel_assets) { + if (dynamic_pointer_cast<dcp::ReelSubtitleAsset> (i)) { + reel->add (i); + } + } } cpl->add (reel); @@ -604,7 +645,9 @@ Writer::finish () DCPOMATIC_ASSERT (job); job->sub (_("Computing image digest")); - _picture_asset->hash (boost::bind (&Job::set_progress, job.get(), _1, false)); + if (_picture_asset) { + _picture_asset->hash (boost::bind (&Job::set_progress, job.get(), _1, false)); + } if (_sound_asset) { job->sub (_("Computing audio digest")); @@ -817,3 +860,9 @@ Writer::read_frame_info (FILE* file, int frame, Eyes eyes) const return info; } + +void +Writer::write (shared_ptr<dcp::ReelAsset> asset) +{ + _reel_assets.push_back (asset); +} diff --git a/src/lib/writer.h b/src/lib/writer.h index 120e1f338..09e4f4798 100644 --- a/src/lib/writer.h +++ b/src/lib/writer.h @@ -48,6 +48,7 @@ namespace dcp { class SoundAsset; class SoundAssetWriter; class SubtitleAsset; + class ReelAsset; } struct QueueItem @@ -107,6 +108,7 @@ public: void write (boost::shared_ptr<const AudioBuffers>); void write (PlayerSubtitles subs); void write (std::list<boost::shared_ptr<Font> > fonts); + void write (boost::shared_ptr<dcp::ReelAsset> reel_asset); void finish (); void set_encoder_threads (int threads); @@ -166,6 +168,7 @@ private: boost::shared_ptr<dcp::SoundAsset> _sound_asset; boost::shared_ptr<dcp::SoundAssetWriter> _sound_asset_writer; boost::shared_ptr<dcp::SubtitleAsset> _subtitle_asset; + std::list<boost::shared_ptr<dcp::ReelAsset> > _reel_assets; std::list<boost::shared_ptr<Font> > _fonts; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 924172970..27c8d23b6 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -151,6 +151,7 @@ FilmViewer::set_film (shared_ptr<Film> film) */ _player->set_always_burn_subtitles (true); _player->set_ignore_audio (); + _player->set_play_referenced (); _film_connection = _film->Changed.connect (boost::bind (&FilmViewer::film_changed, this, _1)); |
