From: Carl Hetherington Date: Sat, 21 Jul 2018 20:47:25 +0000 (+0100) Subject: Revert "Remove join function; the code is long and I don't think anybody" X-Git-Tag: v2.13.37~10 X-Git-Url: https://git.carlh.net/gitweb/?p=dcpomatic.git;a=commitdiff_plain;h=6199e090dc1e32d3753ba7e7d7ea8c5f0d79f046 Revert "Remove join function; the code is long and I don't think anybody" It turns out Carsten uses it :) This reverts commit bd5e8b83a3a18787241982efdae809d4db21f65d. --- diff --git a/ChangeLog b/ChangeLog index ec5c163d4..58269eed3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,5 @@ 2018-07-20 Carl Hetherington - * Remove Join function. - * Advanced configuration option to allow any container ratio. * Support closed-caption creation (#725). diff --git a/src/lib/audio_content.cc b/src/lib/audio_content.cc index d4ce6c243..a252e4b5a 100644 --- a/src/lib/audio_content.cc +++ b/src/lib/audio_content.cc @@ -91,6 +91,27 @@ AudioContent::AudioContent (Content* parent, cxml::ConstNodePtr node) } } +AudioContent::AudioContent (Content* parent, vector > c) + : ContentPart (parent) +{ + shared_ptr ref = c[0]->audio; + DCPOMATIC_ASSERT (ref); + + for (size_t i = 1; i < c.size(); ++i) { + if (c[i]->audio->gain() != ref->gain()) { + throw JoinError (_("Content to be joined must have the same audio gain.")); + } + + if (c[i]->audio->delay() != ref->delay()) { + throw JoinError (_("Content to be joined must have the same audio delay.")); + } + } + + _gain = ref->gain (); + _delay = ref->delay (); + _streams = ref->streams (); +} + void AudioContent::as_xml (xmlpp::Node* node) const { diff --git a/src/lib/audio_content.h b/src/lib/audio_content.h index d73c73735..a1f5ba8a0 100644 --- a/src/lib/audio_content.h +++ b/src/lib/audio_content.h @@ -44,6 +44,7 @@ class AudioContent : public ContentPart { public: explicit AudioContent (Content* parent); + AudioContent (Content* parent, std::vector >); void as_xml (xmlpp::Node *) const; std::string technical_summary () const; diff --git a/src/lib/caption_content.cc b/src/lib/caption_content.cc index d44fb55c5..0d13568e2 100644 --- a/src/lib/caption_content.cc +++ b/src/lib/caption_content.cc @@ -228,6 +228,83 @@ CaptionContent::CaptionContent (Content* parent, cxml::ConstNodePtr node, int ve _original_type = string_to_caption_type (node->optional_string_child("Type").get_value_or("open")); } +CaptionContent::CaptionContent (Content* parent, vector > c) + : ContentPart (parent) +{ + shared_ptr ref = c[0]->caption; + DCPOMATIC_ASSERT (ref); + list > ref_fonts = ref->fonts (); + + for (size_t i = 1; i < c.size(); ++i) { + + if (c[i]->caption->use() != ref->use()) { + throw JoinError (_("Content to be joined must have the same 'use subtitles' setting.")); + } + + if (c[i]->caption->burn() != ref->burn()) { + throw JoinError (_("Content to be joined must have the same 'burn subtitles' setting.")); + } + + if (c[i]->caption->x_offset() != ref->x_offset()) { + throw JoinError (_("Content to be joined must have the same subtitle X offset.")); + } + + if (c[i]->caption->y_offset() != ref->y_offset()) { + throw JoinError (_("Content to be joined must have the same subtitle Y offset.")); + } + + if (c[i]->caption->x_scale() != ref->x_scale()) { + throw JoinError (_("Content to be joined must have the same subtitle X scale.")); + } + + if (c[i]->caption->y_scale() != ref->y_scale()) { + throw JoinError (_("Content to be joined must have the same subtitle Y scale.")); + } + + if (c[i]->caption->line_spacing() != ref->line_spacing()) { + throw JoinError (_("Content to be joined must have the same subtitle line spacing.")); + } + + if ((c[i]->caption->fade_in() != ref->fade_in()) || (c[i]->caption->fade_out() != ref->fade_out())) { + throw JoinError (_("Content to be joined must have the same subtitle fades.")); + } + + if ((c[i]->caption->outline_width() != ref->outline_width())) { + throw JoinError (_("Content to be joined must have the same outline width.")); + } + + list > fonts = c[i]->caption->fonts (); + if (fonts.size() != ref_fonts.size()) { + throw JoinError (_("Content to be joined must use the same fonts.")); + } + + list >::const_iterator j = ref_fonts.begin (); + list >::const_iterator k = fonts.begin (); + + while (j != ref_fonts.end ()) { + if (**j != **k) { + throw JoinError (_("Content to be joined must use the same fonts.")); + } + ++j; + ++k; + } + } + + _use = ref->use (); + _burn = ref->burn (); + _x_offset = ref->x_offset (); + _y_offset = ref->y_offset (); + _x_scale = ref->x_scale (); + _y_scale = ref->y_scale (); + _language = ref->language (); + _fonts = ref_fonts; + _line_spacing = ref->line_spacing (); + _fade_in = ref->fade_in (); + _fade_out = ref->fade_out (); + _outline_width = ref->outline_width (); + + connect_to_fonts (); +} /** _mutex must not be held on entry */ void diff --git a/src/lib/caption_content.h b/src/lib/caption_content.h index 4152dc533..767fc7234 100644 --- a/src/lib/caption_content.h +++ b/src/lib/caption_content.h @@ -59,6 +59,7 @@ class CaptionContent : public ContentPart { public: explicit CaptionContent (Content* parent); + CaptionContent (Content* parent, std::vector >); void as_xml (xmlpp::Node *) const; std::string identifier () const; diff --git a/src/lib/content.cc b/src/lib/content.cc index 13c5794fe..629672b73 100644 --- a/src/lib/content.cc +++ b/src/lib/content.cc @@ -104,6 +104,40 @@ Content::Content (shared_ptr film, cxml::ConstNodePtr node) _video_frame_rate = node->optional_number_child ("VideoFrameRate"); } +Content::Content (shared_ptr film, vector > c) + : _film (film) + , _position (c.front()->position ()) + , _trim_start (c.front()->trim_start ()) + , _trim_end (c.back()->trim_end ()) + , _video_frame_rate (c.front()->video_frame_rate()) + , _change_signals_frequent (false) +{ + for (size_t i = 0; i < c.size(); ++i) { + if (i > 0 && c[i]->trim_start() > ContentTime ()) { + throw JoinError (_("Only the first piece of content to be joined can have a start trim.")); + } + + if (i < (c.size() - 1) && c[i]->trim_end () > ContentTime ()) { + throw JoinError (_("Only the last piece of content to be joined can have an end trim.")); + } + + if ( + (_video_frame_rate && !c[i]->_video_frame_rate) || + (!_video_frame_rate && c[i]->_video_frame_rate) + ) { + throw JoinError (_("Content to be joined must have the same video frame rate")); + } + + if (_video_frame_rate && fabs (_video_frame_rate.get() - c[i]->_video_frame_rate.get()) > VIDEO_FRAME_RATE_EPSILON) { + throw JoinError (_("Content to be joined must have the same video frame rate")); + } + + for (size_t j = 0; j < c[i]->number_of_paths(); ++j) { + _paths.push_back (c[i]->path (j)); + } + } +} + void Content::as_xml (xmlpp::Node* node, bool with_paths) const { diff --git a/src/lib/content.h b/src/lib/content.h index 2a249011a..d84f636fb 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -67,6 +67,7 @@ public: Content (boost::shared_ptr, DCPTime); Content (boost::shared_ptr, boost::filesystem::path); Content (boost::shared_ptr, cxml::ConstNodePtr); + Content (boost::shared_ptr, std::vector >); virtual ~Content () {} /** Examine the content to establish digest, frame rates and any other diff --git a/src/lib/exceptions.h b/src/lib/exceptions.h index 769857fd3..eceafa105 100644 --- a/src/lib/exceptions.h +++ b/src/lib/exceptions.h @@ -81,6 +81,14 @@ private: boost::filesystem::path _file; }; +class JoinError : public std::runtime_error +{ +public: + explicit JoinError (std::string s) + : std::runtime_error (s) + {} +}; + /** @class OpenFileError. * @brief Indicates that some error occurred when trying to open a file. */ diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index ddf4548b4..db999df20 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -128,6 +128,67 @@ FFmpegContent::FFmpegContent (shared_ptr film, cxml::ConstNodePtr no } +FFmpegContent::FFmpegContent (shared_ptr film, vector > c) + : Content (film, c) +{ + vector >::const_iterator i = c.begin (); + + bool need_video = false; + bool need_audio = false; + bool need_caption = false; + + if (i != c.end ()) { + need_video = static_cast ((*i)->video); + need_audio = static_cast ((*i)->audio); + need_caption = static_cast ((*i)->caption); + } + + while (i != c.end ()) { + if (need_video != static_cast ((*i)->video)) { + throw JoinError (_("Content to be joined must all have or not have video")); + } + if (need_audio != static_cast ((*i)->audio)) { + throw JoinError (_("Content to be joined must all have or not have audio")); + } + if (need_caption != static_cast ((*i)->caption)) { + throw JoinError (_("Content to be joined must all have or not have captions")); + } + ++i; + } + + if (need_video) { + video.reset (new VideoContent (this, c)); + } + if (need_audio) { + audio.reset (new AudioContent (this, c)); + } + if (need_caption) { + caption.reset (new CaptionContent (this, c)); + } + + shared_ptr ref = dynamic_pointer_cast (c[0]); + DCPOMATIC_ASSERT (ref); + + for (size_t i = 0; i < c.size(); ++i) { + shared_ptr fc = dynamic_pointer_cast (c[i]); + if (fc->caption && fc->caption->use() && *(fc->_subtitle_stream.get()) != *(ref->_subtitle_stream.get())) { + throw JoinError (_("Content to be joined must use the same subtitle stream.")); + } + } + + /* XXX: should probably check that more of the stuff below is the same in *this and ref */ + + _subtitle_streams = ref->subtitle_streams (); + _subtitle_stream = ref->subtitle_stream (); + _first_video = ref->_first_video; + _filters = ref->_filters; + _color_range = ref->_color_range; + _color_primaries = ref->_color_primaries; + _color_trc = ref->_color_trc; + _colorspace = ref->_colorspace; + _bits_per_pixel = ref->_bits_per_pixel; +} + void FFmpegContent::as_xml (xmlpp::Node* node, bool with_paths) const { diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h index 21f5d4680..64f6f9ff8 100644 --- a/src/lib/ffmpeg_content.h +++ b/src/lib/ffmpeg_content.h @@ -48,6 +48,7 @@ class FFmpegContent : public Content public: FFmpegContent (boost::shared_ptr, boost::filesystem::path); FFmpegContent (boost::shared_ptr, cxml::ConstNodePtr, int version, std::list &); + FFmpegContent (boost::shared_ptr, std::vector >); boost::shared_ptr shared_from_this () { return boost::dynamic_pointer_cast (Content::shared_from_this ()); diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index 0dba55525..d3ba6c1ab 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Carl Hetherington + Copyright (C) 2013-2016 Carl Hetherington This file is part of DCP-o-matic. @@ -154,6 +154,56 @@ VideoContent::VideoContent (Content* parent, cxml::ConstNodePtr node, int versio } } +VideoContent::VideoContent (Content* parent, vector > c) + : ContentPart (parent) + , _length (0) + , _yuv (false) +{ + shared_ptr ref = c[0]->video; + DCPOMATIC_ASSERT (ref); + + for (size_t i = 1; i < c.size(); ++i) { + + if (c[i]->video->size() != ref->size()) { + throw JoinError (_("Content to be joined must have the same picture size.")); + } + + if (c[i]->video->frame_type() != ref->frame_type()) { + throw JoinError (_("Content to be joined must have the same video frame type.")); + } + + if (c[i]->video->crop() != ref->crop()) { + throw JoinError (_("Content to be joined must have the same crop.")); + } + + if (c[i]->video->scale() != ref->scale()) { + throw JoinError (_("Content to be joined must have the same scale setting.")); + } + + if (c[i]->video->colour_conversion() != ref->colour_conversion()) { + throw JoinError (_("Content to be joined must have the same colour conversion.")); + } + + if (c[i]->video->fade_in() != ref->fade_in() || c[i]->video->fade_out() != ref->fade_out()) { + throw JoinError (_("Content to be joined must have the same fades.")); + } + + _length += c[i]->video->length (); + + if (c[i]->video->yuv ()) { + _yuv = true; + } + } + + _size = ref->size (); + _frame_type = ref->frame_type (); + _crop = ref->crop (); + _scale = ref->scale (); + _colour_conversion = ref->colour_conversion (); + _fade_in = ref->fade_in (); + _fade_out = ref->fade_out (); +} + void VideoContent::as_xml (xmlpp::Node* node) const { diff --git a/src/lib/video_content.h b/src/lib/video_content.h index 1fc87bb9b..774210c13 100644 --- a/src/lib/video_content.h +++ b/src/lib/video_content.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2018 Carl Hetherington + Copyright (C) 2013-2016 Carl Hetherington This file is part of DCP-o-matic. @@ -52,6 +52,7 @@ class VideoContent : public ContentPart, public boost::enable_shared_from_this >); void as_xml (xmlpp::Node *) const; std::string technical_summary () const; diff --git a/src/wx/content_menu.cc b/src/wx/content_menu.cc index 07ccaded2..36187e00f 100644 --- a/src/wx/content_menu.cc +++ b/src/wx/content_menu.cc @@ -54,6 +54,7 @@ using boost::dynamic_pointer_cast; enum { /* Start at 256 so we can have IDs on _cpl_menu from 1 to 255 */ ID_repeat = 256, + ID_join, ID_find_missing, ID_properties, ID_re_examine, @@ -69,6 +70,7 @@ ContentMenu::ContentMenu (wxWindow* p) , _pop_up_open (false) { _repeat = _menu->Append (ID_repeat, _("Repeat...")); + _join = _menu->Append (ID_join, _("Join")); _find_missing = _menu->Append (ID_find_missing, _("Find missing...")); _properties = _menu->Append (ID_properties, _("Properties...")); _re_examine = _menu->Append (ID_re_examine, _("Re-examine...")); @@ -81,6 +83,7 @@ ContentMenu::ContentMenu (wxWindow* p) _remove = _menu->Append (ID_remove, _("Remove")); _parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::repeat, this), ID_repeat); + _parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::join, this), ID_join); _parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::find_missing, this), ID_find_missing); _parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::properties, this), ID_properties); _parent->Bind (wxEVT_MENU, boost::bind (&ContentMenu::re_examine, this), ID_re_examine); @@ -111,6 +114,8 @@ ContentMenu::popup (weak_ptr film, ContentList c, TimelineContentViewList } } + _join->Enable (n > 1); + _find_missing->Enable (_content.size() == 1 && !_content.front()->paths_valid ()); _properties->Enable (_content.size() == 1); _re_examine->Enable (!_content.empty ()); @@ -185,6 +190,33 @@ ContentMenu::repeat () _views.clear (); } +void +ContentMenu::join () +{ + vector > fc; + BOOST_FOREACH (shared_ptr i, _content) { + shared_ptr f = dynamic_pointer_cast (i); + if (f) { + fc.push_back (f); + } + } + + DCPOMATIC_ASSERT (fc.size() > 1); + + shared_ptr film = _film.lock (); + if (!film) { + return; + } + + try { + shared_ptr joined (new FFmpegContent (film, fc)); + film->remove_content (_content); + film->examine_and_add_content (joined); + } catch (JoinError& e) { + error_dialog (_parent, std_to_wx (e.what ())); + } +} + void ContentMenu::remove () { diff --git a/src/wx/content_menu.h b/src/wx/content_menu.h index 1faf16de6..a38109b07 100644 --- a/src/wx/content_menu.h +++ b/src/wx/content_menu.h @@ -40,6 +40,7 @@ public: private: void repeat (); + void join (); void find_missing (); void properties (); void re_examine (); @@ -58,6 +59,7 @@ private: ContentList _content; TimelineContentViewList _views; wxMenuItem* _repeat; + wxMenuItem* _join; wxMenuItem* _find_missing; wxMenuItem* _properties; wxMenuItem* _re_examine;