From b9f6e9512017dc1ecd3a42aa1ef3c6058608cef5 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Tue, 21 May 2013 23:19:31 +0100 Subject: [PATCH] Give Film a container; move crop into video content; other bits. --- branch-notes | 1 + src/lib/config.cc | 16 ++--- src/lib/config.h | 12 ++-- src/lib/container.cc | 83 ++++++++++++++++++++++ src/lib/container.h | 78 +++++++++++++++++++++ src/lib/encoder.cc | 4 +- src/lib/ffmpeg_decoder.cc | 3 +- src/lib/ffmpeg_decoder.h | 4 ++ src/lib/film.cc | 122 +++++---------------------------- src/lib/film.h | 28 ++------ src/lib/filter_graph.cc | 6 +- src/lib/imagemagick_decoder.cc | 4 +- src/lib/util.cc | 2 + src/lib/video_content.cc | 81 ++++++++++++++++++++++ src/lib/video_content.h | 13 ++++ src/lib/video_decoder.cc | 5 +- src/lib/video_decoder.h | 4 +- src/lib/writer.cc | 4 +- src/lib/wscript | 1 + src/wx/config_dialog.cc | 26 +++---- src/wx/config_dialog.h | 4 +- src/wx/film_editor.cc | 106 ++++++++++++++++++---------- src/wx/film_editor.h | 7 +- src/wx/film_viewer.cc | 23 ++++--- test/test.cc | 19 ++--- 25 files changed, 428 insertions(+), 228 deletions(-) create mode 100644 src/lib/container.cc create mode 100644 src/lib/container.h diff --git a/branch-notes b/branch-notes index 134883135..3695ecfd5 100644 --- a/branch-notes +++ b/branch-notes @@ -6,3 +6,4 @@ things to put back overall length? trim method (trim in general) A/B + diff --git a/src/lib/config.cc b/src/lib/config.cc index 3beb0aea6..c5245bfb4 100644 --- a/src/lib/config.cc +++ b/src/lib/config.cc @@ -27,7 +27,7 @@ #include "server.h" #include "scaler.h" #include "filter.h" -#include "format.h" +#include "container.h" #include "dcp_content_type.h" #include "sound_processor.h" @@ -52,7 +52,7 @@ Config::Config () , _tms_path (N_(".")) , _sound_processor (SoundProcessor::from_id (N_("dolby_cp750"))) , _default_still_length (10) - , _default_format (0) + , _default_container (0) , _default_dcp_content_type (0) { _allowed_dcp_frame_rates.push_back (24); @@ -104,9 +104,9 @@ Config::read () _language = f.optional_string_child ("Language"); - c = f.optional_string_child ("DefaultFormat"); + c = f.optional_string_child ("DefaultContainer"); if (c) { - _default_format = Format::from_id (c.get ()); + _default_container = Container::from_id (c.get ()); } c = f.optional_string_child ("DefaultDCPContentType"); @@ -167,8 +167,8 @@ Config::read_old_metadata () _sound_processor = SoundProcessor::from_id (v); } else if (k == "language") { _language = v; - } else if (k == "default_format") { - _default_format = Format::from_id (v); + } else if (k == "default_container") { + _default_container = Container::from_id (v); } else if (k == "default_dcp_content_type") { _default_dcp_content_type = DCPContentType::from_dci_name (v); } else if (k == "dcp_metadata_issuer") { @@ -247,8 +247,8 @@ Config::write () const if (_language) { root->add_child("Language")->add_child_text (_language.get()); } - if (_default_format) { - root->add_child("DefaultFormat")->add_child_text (_default_format->id ()); + if (_default_container) { + root->add_child("DefaultContainer")->add_child_text (_default_container->id ()); } if (_default_dcp_content_type) { root->add_child("DefaultDCPContentType")->add_child_text (_default_dcp_content_type->dci_name ()); diff --git a/src/lib/config.h b/src/lib/config.h index 96aa4e449..c43a4f079 100644 --- a/src/lib/config.h +++ b/src/lib/config.h @@ -34,7 +34,7 @@ class ServerDescription; class Scaler; class Filter; class SoundProcessor; -class Format; +class Container; class DCPContentType; /** @class Config @@ -114,8 +114,8 @@ public: return _default_still_length; } - Format const * default_format () const { - return _default_format; + Container const * default_container () const { + return _default_container; } DCPContentType const * default_dcp_content_type () const { @@ -193,8 +193,8 @@ public: _default_still_length = s; } - void set_default_format (Format const * f) { - _default_format = f; + void set_default_container (Container const * f) { + _default_container = f; } void set_default_dcp_content_type (DCPContentType const * t) { @@ -244,7 +244,7 @@ private: DCIMetadata _default_dci_metadata; boost::optional _language; int _default_still_length; - Format const * _default_format; + Container const * _default_container; DCPContentType const * _default_dcp_content_type; libdcp::XMLMetadata _dcp_metadata; diff --git a/src/lib/container.cc b/src/lib/container.cc new file mode 100644 index 000000000..6eaea7300 --- /dev/null +++ b/src/lib/container.cc @@ -0,0 +1,83 @@ +/* + Copyright (C) 2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include +#include "container.h" + +#include "i18n.h" + +using std::string; +using std::stringstream; +using std::vector; + +vector Container::_containers; + +void +Container::setup_containers () +{ + _containers.push_back (new Container (libdcp::Size (1285, 1080), "119", _("1.19"), "F")); + _containers.push_back (new Container (libdcp::Size (1436, 1080), "133", _("4:3"), "F")); + _containers.push_back (new Container (libdcp::Size (1480, 1080), "137", _("Academy"), "F")); + _containers.push_back (new Container (libdcp::Size (1485, 1080), "138", _("1.375"), "F")); + _containers.push_back (new Container (libdcp::Size (1793, 1080), "166", _("1.66"), "F")); + _containers.push_back (new Container (libdcp::Size (1920, 1080), "178", _("16:9"), "F")); + _containers.push_back (new Container (libdcp::Size (1998, 1080), "185", _("Flat"), "F")); + _containers.push_back (new Container (libdcp::Size (2048, 858), "239", _("Scope"), "S")); + _containers.push_back (new Container (libdcp::Size (2048, 1080), "full-frame", _("Full frame"), "C")); +} + +/** @return A name to be presented to the user */ +string +Container::name () const +{ + stringstream s; + if (!_nickname.empty ()) { + s << _nickname << " ("; + } + + s << _dcp_size.width << "x" << _dcp_size.height; + + if (!_nickname.empty ()) { + s << ")"; + } + + return s.str (); +} + +Container const * +Container::from_id (string i) +{ + vector::iterator j = _containers.begin (); + while (j != _containers.end() && (*j)->id() != i) { + ++j; + } + + if (j == _containers.end ()) { + return 0; + } + + return *j; +} + +float +Container::ratio () const +{ + return static_cast (_dcp_size.width) / _dcp_size.height; +} diff --git a/src/lib/container.h b/src/lib/container.h new file mode 100644 index 000000000..fc3c72792 --- /dev/null +++ b/src/lib/container.h @@ -0,0 +1,78 @@ +/* + Copyright (C) 2013 Carl Hetherington + + This program 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. + + This program 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 this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +*/ + +#include +#include + +class Container +{ +public: + Container (libdcp::Size dcp, std::string id, std::string n, std::string d) + : _dcp_size (dcp) + , _id (id) + , _nickname (n) + , _dci_name (d) + {} + + /** @return size in pixels of the images that we should + * put in a DCP for this ratio. This size will not correspond + * to the ratio when we are doing things like 16:9 in a Flat frame. + */ + libdcp::Size dcp_size () const { + return _dcp_size; + } + + std::string id () const { + return _id; + } + + /** @return Full name to present to the user */ + std::string name () const; + + /** @return Nickname (e.g. Flat, Scope) */ + std::string nickname () const { + return _nickname; + } + + std::string dci_name () const { + return _dci_name; + } + + float ratio () const; + + static void setup_containers (); + static Container const * from_id (std::string i); + static std::vector all () { + return _containers; + } + +private: + /** libdcp::Size in pixels of the images that we should + * put in a DCP for this container. + */ + libdcp::Size _dcp_size; + /** id for use in metadata */ + std::string _id; + /** nickname (e.g. Flat, Scope) */ + std::string _nickname; + std::string _dci_name; + + /** all available containers */ + static std::vector _containers; +}; diff --git a/src/lib/encoder.cc b/src/lib/encoder.cc index 95e98ab76..52927c5d3 100644 --- a/src/lib/encoder.cc +++ b/src/lib/encoder.cc @@ -39,6 +39,7 @@ #include "writer.h" #include "player.h" #include "audio_mapping.h" +#include "container.h" #include "i18n.h" @@ -208,9 +209,10 @@ Encoder::process_video (shared_ptr image, bool same, shared_ptr const s = Filter::ffmpeg_strings (_film->filters()); TIMING ("adding to queue of %1", _queue.size ()); + /* XXX: padding */ _queue.push_back (shared_ptr ( new DCPVideoFrame ( - image, sub, _film->format()->dcp_size(), _film->format()->dcp_padding (_film), + image, sub, _film->container()->dcp_size(), 0, _film->subtitle_offset(), _film->subtitle_scale(), _film->scaler(), _video_frames_out, _film->dcp_video_frame_rate(), s.second, _film->colour_lut(), _film->j2k_bandwidth(), diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index a637160ae..fcb2e82ba 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -68,7 +68,7 @@ boost::mutex FFmpegDecoder::_mutex; FFmpegDecoder::FFmpegDecoder (shared_ptr f, shared_ptr c, bool video, bool audio, bool subtitles) : Decoder (f) - , VideoDecoder (f) + , VideoDecoder (f, c) , AudioDecoder (f, c) , _ffmpeg_content (c) , _format_context (0) @@ -540,7 +540,6 @@ void FFmpegDecoder::film_changed (Film::Property p) { switch (p) { - case Film::CROP: case Film::FILTERS: { boost::mutex::scoped_lock lm (_filter_graphs_mutex); diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index f3f6b126b..7aadef105 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -83,6 +83,10 @@ public: bool seek_back (); bool pass (); + boost::shared_ptr ffmpeg_content () const { + return _ffmpeg_content; + } + private: /* No copy construction */ diff --git a/src/lib/film.cc b/src/lib/film.cc index ef67a2704..f05362d56 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -34,7 +34,7 @@ #include #include #include "film.h" -#include "format.h" +#include "container.h" #include "job.h" #include "filter.h" #include "util.h" @@ -95,7 +95,7 @@ Film::Film (string d, bool must_exist) : _playlist (new Playlist) , _use_dci_name (true) , _dcp_content_type (Config::instance()->default_dcp_content_type ()) - , _format (Config::instance()->default_format ()) + , _container (Config::instance()->default_container ()) , _scaler (Scaler::from_id ("bicubic")) , _ab (false) , _audio_gain (0) @@ -160,8 +160,7 @@ Film::Film (Film const & o) , _name (o._name) , _use_dci_name (o._use_dci_name) , _dcp_content_type (o._dcp_content_type) - , _format (o._format) - , _crop (o._crop) + , _container (o._container) , _filters (o._filters) , _scaler (o._scaler) , _ab (o._ab) @@ -183,15 +182,14 @@ Film::Film (Film const & o) string Film::video_state_identifier () const { - assert (format ()); + assert (container ()); LocaleGuard lg; pair f = Filter::ffmpeg_strings (filters()); stringstream s; - s << format()->id() + s << container()->id() << "_" << _playlist->video_digest() - << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom << "_" << _dcp_video_frame_rate << "_" << f.first << "_" << f.second << "_" << scaler()->id() @@ -305,8 +303,8 @@ Film::make_dcp () pair const c = cpu_info (); log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second)); - if (format() == 0) { - throw MissingSettingError (_("format")); + if (container() == 0) { + throw MissingSettingError (_("container")); } if (_playlist->content().empty ()) { @@ -377,7 +375,7 @@ Film::send_dcp_to_tms () int Film::encoded_frames () const { - if (format() == 0) { + if (container() == 0) { return 0; } @@ -410,15 +408,10 @@ Film::write_metadata () const root->add_child("DCPContentType")->add_child_text (_dcp_content_type->dci_name ()); } - if (_format) { - root->add_child("Format")->add_child_text (_format->id ()); + if (_container) { + root->add_child("Container")->add_child_text (_container->id ()); } - root->add_child("LeftCrop")->add_child_text (boost::lexical_cast (_crop.left)); - root->add_child("RightCrop")->add_child_text (boost::lexical_cast (_crop.right)); - root->add_child("TopCrop")->add_child_text (boost::lexical_cast (_crop.top)); - root->add_child("BottomCrop")->add_child_text (boost::lexical_cast (_crop.bottom)); - for (vector::const_iterator i = _filters.begin(); i != _filters.end(); ++i) { root->add_child("Filter")->add_child_text ((*i)->id ()); } @@ -466,17 +459,12 @@ Film::read_metadata () } { - optional c = f.optional_string_child ("Format"); + optional c = f.optional_string_child ("Container"); if (c) { - _format = Format::from_id (c.get ()); + _container = Container::from_id (c.get ()); } } - _crop.left = f.number_child ("LeftCrop"); - _crop.right = f.number_child ("RightCrop"); - _crop.top = f.number_child ("TopCrop"); - _crop.bottom = f.number_child ("BottomCrop"); - { list > c = f.node_children ("Filter"); for (list >::iterator i = c.begin(); i != c.end(); ++i) { @@ -502,15 +490,6 @@ Film::read_metadata () _dirty = false; } -libdcp::Size -Film::cropped_size (libdcp::Size s) const -{ - boost::mutex::scoped_lock lm (_state_mutex); - s.width -= _crop.left + _crop.right; - s.height -= _crop.top + _crop.bottom; - return s; -} - /** Given a directory name, return its full path within the Film's directory. * The directory (and its parents) will be created if they do not exist. */ @@ -570,8 +549,8 @@ Film::dci_name (bool if_created_now) const d << "_" << dcp_content_type()->dci_name(); } - if (format()) { - d << "_" << format()->dci_name(); + if (container()) { + d << "_" << container()->dci_name(); } DCIMetadata const dm = dci_metadata (); @@ -666,80 +645,13 @@ Film::set_dcp_content_type (DCPContentType const * t) } void -Film::set_format (Format const * f) +Film::set_container (Container const * c) { { boost::mutex::scoped_lock lm (_state_mutex); - _format = f; - } - signal_changed (FORMAT); -} - -void -Film::set_crop (Crop c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _crop = c; - } - signal_changed (CROP); -} - -void -Film::set_left_crop (int c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - - if (_crop.left == c) { - return; - } - - _crop.left = c; - } - signal_changed (CROP); -} - -void -Film::set_right_crop (int c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - if (_crop.right == c) { - return; - } - - _crop.right = c; - } - signal_changed (CROP); -} - -void -Film::set_top_crop (int c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - if (_crop.top == c) { - return; - } - - _crop.top = c; - } - signal_changed (CROP); -} - -void -Film::set_bottom_crop (int c) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - if (_crop.bottom == c) { - return; - } - - _crop.bottom = c; + _container = c; } - signal_changed (CROP); + signal_changed (CONTAINER); } void diff --git a/src/lib/film.h b/src/lib/film.h index 6dd2b446d..9b01dc604 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -39,7 +39,7 @@ #include "playlist.h" class DCPContentType; -class Format; +class Container; class Job; class Filter; class Log; @@ -88,7 +88,6 @@ public: void write_metadata () const; - libdcp::Size cropped_size (libdcp::Size) const; std::string dci_name (bool if_created_now) const; std::string dcp_name (bool if_created_now = false) const; @@ -131,8 +130,7 @@ public: CONTENT, LOOP, DCP_CONTENT_TYPE, - FORMAT, - CROP, + CONTAINER, FILTERS, SCALER, AB, @@ -170,14 +168,9 @@ public: return _dcp_content_type; } - Format const * format () const { + Container const * container () const { boost::mutex::scoped_lock lm (_state_mutex); - return _format; - } - - Crop crop () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _crop; + return _container; } std::vector filters () const { @@ -249,12 +242,7 @@ public: void add_content (boost::shared_ptr); void remove_content (boost::shared_ptr); void set_dcp_content_type (DCPContentType const *); - void set_format (Format const *); - void set_crop (Crop); - void set_left_crop (int); - void set_right_crop (int); - void set_top_crop (int); - void set_bottom_crop (int); + void set_container (Container const *); void set_filters (std::vector); void set_scaler (Scaler const *); void set_ab (bool); @@ -309,10 +297,8 @@ private: bool _use_dci_name; /** The type of content that this Film represents (feature, trailer etc.) */ DCPContentType const * _dcp_content_type; - /** The format to present this Film in (flat, scope, etc.) */ - Format const * _format; - /** The crop to apply to the source */ - Crop _crop; + /** The container to put this Film in (flat, scope, etc.) */ + Container const * _container; /** Video filters that should be used when generating DCPs */ std::vector _filters; /** Scaler algorithm to use */ diff --git a/src/lib/filter_graph.cc b/src/lib/filter_graph.cc index a52c030fe..09bbf5dcd 100644 --- a/src/lib/filter_graph.cc +++ b/src/lib/filter_graph.cc @@ -68,7 +68,11 @@ FilterGraph::FilterGraph (shared_ptr film, FFmpegDecoder* decoder, l filters += N_(","); } - filters += crop_string (Position (film->crop().left, film->crop().top), film->cropped_size (decoder->native_size())); + Crop crop = decoder->ffmpeg_content()->crop (); + libdcp::Size cropped_size = decoder->native_size (); + cropped_size.width -= crop.left + crop.right; + cropped_size.height -= crop.top + crop.bottom; + filters += crop_string (Position (crop.left, crop.top), cropped_size); AVFilterGraph* graph = avfilter_graph_alloc(); if (graph == 0) { diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index dd18ad64e..c6b19b742 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -34,7 +34,7 @@ using libdcp::Size; ImageMagickDecoder::ImageMagickDecoder (shared_ptr f, shared_ptr c) : Decoder (f) - , VideoDecoder (f) + , VideoDecoder (f, c) , _imagemagick_content (c) , _position (0) { @@ -92,7 +92,7 @@ ImageMagickDecoder::pass () delete magick_image; - _image = _image->crop (_film->crop(), true); + _image = _image->crop (_imagemagick_content->crop(), true); emit_video (_image, false, double (_position) / 24); ++_position; diff --git a/src/lib/util.cc b/src/lib/util.cc index de5fbf8d1..e1bc560c6 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -61,6 +61,7 @@ extern "C" { #include "filter.h" #include "sound_processor.h" #include "config.h" +#include "container.h" #ifdef DVDOMATIC_WINDOWS #include "stack.hpp" #endif @@ -285,6 +286,7 @@ dcpomatic_setup () avfilter_register_all (); Format::setup_formats (); + Container::setup_containers (); DCPContentType::setup_dcp_content_types (); Scaler::setup_scalers (); Filter::setup_filters (); diff --git a/src/lib/video_content.cc b/src/lib/video_content.cc index 9fb2b9bce..cb31b6476 100644 --- a/src/lib/video_content.cc +++ b/src/lib/video_content.cc @@ -26,6 +26,7 @@ int const VideoContentProperty::VIDEO_LENGTH = 0; int const VideoContentProperty::VIDEO_SIZE = 1; int const VideoContentProperty::VIDEO_FRAME_RATE = 2; +int const VideoContentProperty::VIDEO_CROP = 3; using std::string; using std::stringstream; @@ -47,6 +48,10 @@ VideoContent::VideoContent (shared_ptr node) _video_size.width = node->number_child ("VideoWidth"); _video_size.height = node->number_child ("VideoHeight"); _video_frame_rate = node->number_child ("VideoFrameRate"); + _crop.left = node->number_child ("LeftCrop"); + _crop.right = node->number_child ("RightCrop"); + _crop.top = node->number_child ("TopCrop"); + _crop.bottom = node->number_child ("BottomCrop"); } VideoContent::VideoContent (VideoContent const & o) @@ -66,6 +71,10 @@ VideoContent::as_xml (xmlpp::Node* node) const node->add_child("VideoWidth")->add_child_text (lexical_cast (_video_size.width)); node->add_child("VideoHeight")->add_child_text (lexical_cast (_video_size.height)); node->add_child("VideoFrameRate")->add_child_text (lexical_cast (_video_frame_rate)); + node->add_child("LeftCrop")->add_child_text (boost::lexical_cast (_crop.left)); + node->add_child("RightCrop")->add_child_text (boost::lexical_cast (_crop.right)); + node->add_child("TopCrop")->add_child_text (boost::lexical_cast (_crop.top)); + node->add_child("BottomCrop")->add_child_text (boost::lexical_cast (_crop.bottom)); } void @@ -104,3 +113,75 @@ VideoContent::information () const return s.str (); } + +void +VideoContent::set_crop (Crop c) +{ + { + boost::mutex::scoped_lock lm (_mutex); + _crop = c; + } + signal_changed (VideoContentProperty::VIDEO_CROP); +} + +void +VideoContent::set_left_crop (int c) +{ + { + boost::mutex::scoped_lock lm (_mutex); + + if (_crop.left == c) { + return; + } + + _crop.left = c; + } + + signal_changed (VideoContentProperty::VIDEO_CROP); +} + +void +VideoContent::set_right_crop (int c) +{ + { + boost::mutex::scoped_lock lm (_mutex); + if (_crop.right == c) { + return; + } + + _crop.right = c; + } + + signal_changed (VideoContentProperty::VIDEO_CROP); +} + +void +VideoContent::set_top_crop (int c) +{ + { + boost::mutex::scoped_lock lm (_mutex); + if (_crop.top == c) { + return; + } + + _crop.top = c; + } + + signal_changed (VideoContentProperty::VIDEO_CROP); +} + +void +VideoContent::set_bottom_crop (int c) +{ + { + boost::mutex::scoped_lock lm (_mutex); + if (_crop.bottom == c) { + return; + } + + _crop.bottom = c; + } + + signal_changed (VideoContentProperty::VIDEO_CROP); +} + diff --git a/src/lib/video_content.h b/src/lib/video_content.h index 75e507d4d..67217e10c 100644 --- a/src/lib/video_content.h +++ b/src/lib/video_content.h @@ -31,6 +31,7 @@ public: static int const VIDEO_LENGTH; static int const VIDEO_SIZE; static int const VIDEO_FRAME_RATE; + static int const VIDEO_CROP; }; class VideoContent : public virtual Content @@ -58,6 +59,17 @@ public: return _video_frame_rate; } + void set_crop (Crop); + void set_left_crop (int); + void set_right_crop (int); + void set_top_crop (int); + void set_bottom_crop (int); + + Crop crop () const { + boost::mutex::scoped_lock lm (_mutex); + return _crop; + } + protected: void take_from_video_decoder (boost::shared_ptr); @@ -66,6 +78,7 @@ protected: private: libdcp::Size _video_size; float _video_frame_rate; + Crop _crop; }; #endif diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 533fdcf1a..0c8cda294 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -32,8 +32,9 @@ using std::cout; using boost::shared_ptr; using boost::optional; -VideoDecoder::VideoDecoder (shared_ptr f) +VideoDecoder::VideoDecoder (shared_ptr f, shared_ptr c) : Decoder (f) + , _video_content (c) , _video_frame (0) , _last_content_time (0) { @@ -72,7 +73,7 @@ VideoDecoder::emit_subtitle (shared_ptr s) if (_timed_subtitle) { Position const p = _timed_subtitle->subtitle()->position (); - _timed_subtitle->subtitle()->set_position (Position (p.x - _film->crop().left, p.y - _film->crop().top)); + _timed_subtitle->subtitle()->set_position (Position (p.x - _video_content->crop().left, p.y - _video_content->crop().top)); } } diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index a73c7d11e..286a0d6e4 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -28,7 +28,7 @@ class VideoContent; class VideoDecoder : public VideoSource, public virtual Decoder { public: - VideoDecoder (boost::shared_ptr); + VideoDecoder (boost::shared_ptr, boost::shared_ptr); /** @return video frame rate second, or 0 if unknown */ virtual float video_frame_rate () const = 0; @@ -60,9 +60,9 @@ protected: void emit_subtitle (boost::shared_ptr); private: + boost::shared_ptr _video_content; int _video_frame; double _last_content_time; - boost::shared_ptr _timed_subtitle; }; diff --git a/src/lib/writer.cc b/src/lib/writer.cc index 96c797c5a..aef7230dd 100644 --- a/src/lib/writer.cc +++ b/src/lib/writer.cc @@ -27,7 +27,7 @@ #include "writer.h" #include "compose.hpp" #include "film.h" -#include "format.h" +#include "container.h" #include "log.h" #include "dcp_video_frame.h" #include "dcp_content_type.h" @@ -76,7 +76,7 @@ Writer::Writer (shared_ptr f, shared_ptr j) _film->internal_video_mxf_dir (), _film->internal_video_mxf_filename (), _film->dcp_video_frame_rate (), - _film->format()->dcp_size () + _film->container()->dcp_size () ) ); diff --git a/src/lib/wscript b/src/lib/wscript index d8cc8261d..1da245484 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -13,6 +13,7 @@ sources = """ audio_source.cc config.cc combiner.cc + container.cc content.cc cross.cc dci_metadata.cc diff --git a/src/wx/config_dialog.cc b/src/wx/config_dialog.cc index 50b8990b1..ae5bed723 100644 --- a/src/wx/config_dialog.cc +++ b/src/wx/config_dialog.cc @@ -28,7 +28,7 @@ #include #include "lib/config.h" #include "lib/server.h" -#include "lib/format.h" +#include "lib/container.h" #include "lib/scaler.h" #include "lib/filter.h" #include "lib/dcp_content_type.h" @@ -127,9 +127,9 @@ ConfigDialog::make_misc_panel () table->Add (_default_dci_metadata_button); table->AddSpacer (1); - add_label_to_sizer (table, _misc_panel, _("Default format")); - _default_format = new wxChoice (_misc_panel, wxID_ANY); - table->Add (_default_format); + add_label_to_sizer (table, _misc_panel, _("Default container")); + _default_container = new wxChoice (_misc_panel, wxID_ANY); + table->Add (_default_container); table->AddSpacer (1); add_label_to_sizer (table, _misc_panel, _("Default content type")); @@ -171,17 +171,17 @@ ConfigDialog::make_misc_panel () _default_dci_metadata_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (ConfigDialog::edit_default_dci_metadata_clicked), 0, this); - vector fmt = Format::all (); + vector fmt = Container::all (); int n = 0; - for (vector::iterator i = fmt.begin(); i != fmt.end(); ++i) { - _default_format->Append (std_to_wx ((*i)->name ())); - if (*i == config->default_format ()) { - _default_format->SetSelection (n); + for (vector::iterator i = fmt.begin(); i != fmt.end(); ++i) { + _default_container->Append (std_to_wx ((*i)->name ())); + if (*i == config->default_container ()) { + _default_container->SetSelection (n); } ++n; } - _default_format->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (ConfigDialog::default_format_changed), 0, this); + _default_container->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (ConfigDialog::default_container_changed), 0, this); vector const ct = DCPContentType::all (); n = 0; @@ -533,10 +533,10 @@ ConfigDialog::default_still_length_changed (wxCommandEvent &) } void -ConfigDialog::default_format_changed (wxCommandEvent &) +ConfigDialog::default_container_changed (wxCommandEvent &) { - vector fmt = Format::all (); - Config::instance()->set_default_format (fmt[_default_format->GetSelection()]); + vector fmt = Container::all (); + Config::instance()->set_default_container (fmt[_default_container->GetSelection()]); } void diff --git a/src/wx/config_dialog.h b/src/wx/config_dialog.h index 5df1a634f..dda846b7d 100644 --- a/src/wx/config_dialog.h +++ b/src/wx/config_dialog.h @@ -57,7 +57,7 @@ private: void edit_server_clicked (wxCommandEvent &); void remove_server_clicked (wxCommandEvent &); void server_selection_changed (wxListEvent &); - void default_format_changed (wxCommandEvent &); + void default_container_changed (wxCommandEvent &); void default_dcp_content_type_changed (wxCommandEvent &); void issuer_changed (wxCommandEvent &); void creator_changed (wxCommandEvent &); @@ -79,7 +79,7 @@ private: wxPanel* _metadata_panel; wxCheckBox* _set_language; wxChoice* _language; - wxChoice* _default_format; + wxChoice* _default_container; wxChoice* _default_dcp_content_type; wxTextCtrl* _tms_ip; wxTextCtrl* _tms_path; diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 4955d9f3d..3c5e1e0a7 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -54,6 +54,7 @@ #include "imagemagick_content_dialog.h" #include "timeline_dialog.h" #include "audio_mapping_view.h" +#include "container.h" using std::string; using std::cout; @@ -126,6 +127,11 @@ FilmEditor::make_dcp_panel () grid->Add (_edit_dci_button, wxGBPosition (r, 1), wxDefaultSpan); ++r; + add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Container"), wxGBPosition (r, 0)); + _container = new wxChoice (_dcp_panel, wxID_ANY); + grid->Add (_container, wxGBPosition (r, 1)); + ++r; + add_label_to_grid_bag_sizer (grid, _dcp_panel, _("Content Type"), wxGBPosition (r, 0)); _dcp_content_type = new wxChoice (_dcp_panel, wxID_ANY); grid->Add (_dcp_content_type, wxGBPosition (r, 1)); @@ -137,11 +143,16 @@ FilmEditor::make_dcp_panel () _dcp_frame_rate = new wxChoice (_dcp_panel, wxID_ANY); s->Add (_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL); _best_dcp_frame_rate = new wxButton (_dcp_panel, wxID_ANY, _("Use best")); - s->Add (_best_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxALL | wxEXPAND, 6); + s->Add (_best_dcp_frame_rate, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND); grid->Add (s, wxGBPosition (r, 1)); } ++r; + vector const co = Container::all (); + for (vector::const_iterator i = co.begin(); i != co.end(); ++i) { + _container->Append (std_to_wx ((*i)->name ())); + } + vector const ct = DCPContentType::all (); for (vector::const_iterator i = ct.begin(); i != ct.end(); ++i) { _dcp_content_type->Append (std_to_wx ((*i)->pretty_name ())); @@ -159,7 +170,8 @@ FilmEditor::connect_to_widgets () _name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this); _use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this); _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this); - _format->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this); + _container->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::container_changed), 0, this); +// _format->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this); _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_SELECTED, wxListEventHandler (FilmEditor::content_selection_changed), 0, this); _content->Connect (wxID_ANY, wxEVT_COMMAND_LIST_ITEM_DESELECTED, wxListEventHandler (FilmEditor::content_selection_changed), 0, this); _content_add->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::content_add_clicked), 0, this); @@ -434,44 +446,48 @@ FilmEditor::make_subtitle_panel () void FilmEditor::left_crop_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr c = selected_video_content (); + if (!c) { return; } - _film->set_left_crop (_left_crop->GetValue ()); + c->set_left_crop (_left_crop->GetValue ()); } /** Called when the right crop widget has been changed */ void FilmEditor::right_crop_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr c = selected_video_content (); + if (!c) { return; } - _film->set_right_crop (_right_crop->GetValue ()); + c->set_right_crop (_right_crop->GetValue ()); } /** Called when the top crop widget has been changed */ void FilmEditor::top_crop_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr c = selected_video_content (); + if (!c) { return; } - _film->set_top_crop (_top_crop->GetValue ()); + c->set_top_crop (_top_crop->GetValue ()); } /** Called when the bottom crop value has been changed */ void FilmEditor::bottom_crop_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr c = selected_video_content (); + if (!c) { return; } - _film->set_bottom_crop (_bottom_crop->GetValue ()); + c->set_bottom_crop (_bottom_crop->GetValue ()); } /** Called when the name widget has been changed */ @@ -561,7 +577,7 @@ FilmEditor::film_changed (Film::Property p) case Film::CONTENT: setup_content (); setup_formats (); - setup_format (); +// setup_format (); setup_subtitle_control_sensitivity (); setup_show_audio_sensitivity (); break; @@ -570,15 +586,8 @@ FilmEditor::film_changed (Film::Property p) checked_set (_loop_count, _film->loop()); setup_loop_sensitivity (); break; - case Film::FORMAT: - setup_format (); - break; - case Film::CROP: - checked_set (_left_crop, _film->crop().left); - checked_set (_right_crop, _film->crop().right); - checked_set (_top_crop, _film->crop().top); - checked_set (_bottom_crop, _film->crop().bottom); - setup_scaling_description (); + case Film::CONTAINER: + setup_container (); break; case Film::FILTERS: { @@ -655,7 +664,7 @@ FilmEditor::film_changed (Film::Property p) } void -FilmEditor::film_content_changed (weak_ptr content, int property) +FilmEditor::film_content_changed (weak_ptr weak_content, int property) { if (!_film) { /* We call this method ourselves (as well as using it as a signal handler) @@ -663,8 +672,20 @@ FilmEditor::film_content_changed (weak_ptr content, int property) */ return; } - - if (property == FFmpegContentProperty::SUBTITLE_STREAMS) { + + shared_ptr content = weak_content.lock (); + shared_ptr video_content; + if (content) { + video_content = dynamic_pointer_cast (content); + } + + if (property == VideoContentProperty::VIDEO_CROP) { + checked_set (_left_crop, video_content ? video_content->crop().left : 0); + checked_set (_right_crop, video_content ? video_content->crop().right : 0); + checked_set (_top_crop, video_content ? video_content->crop().top : 0); + checked_set (_bottom_crop, video_content ? video_content->crop().bottom : 0); + setup_scaling_description (); + } else if (property == FFmpegContentProperty::SUBTITLE_STREAMS) { setup_subtitle_control_sensitivity (); } else if (property == FFmpegContentProperty::AUDIO_STREAMS) { setup_show_audio_sensitivity (); @@ -675,37 +696,39 @@ FilmEditor::film_content_changed (weak_ptr content, int property) } void -FilmEditor::setup_format () +FilmEditor::setup_container () { int n = 0; - vector::iterator i = _formats.begin (); - while (i != _formats.end() && *i != _film->format ()) { + vector containers = Container::all (); + vector::iterator i = containers.begin (); + while (i != containers.end() && *i != _film->container ()) { ++i; ++n; } - if (i == _formats.end()) { - checked_set (_format, -1); + if (i == containers.end()) { + checked_set (_container, -1); } else { - checked_set (_format, n); + checked_set (_container, n); } setup_dcp_name (); setup_scaling_description (); } -/** Called when the format widget has been changed */ +/** Called when the container widget has been changed */ void -FilmEditor::format_changed (wxCommandEvent &) +FilmEditor::container_changed (wxCommandEvent &) { if (!_film) { return; } - int const n = _format->GetSelection (); + int const n = _container->GetSelection (); if (n >= 0) { - assert (n < int (_formats.size())); - _film->set_format (_formats[n]); + vector containers = Container::all (); + assert (n < int (containers.size())); + _film->set_container (containers[n]); } } @@ -755,8 +778,7 @@ FilmEditor::set_film (shared_ptr f) film_changed (Film::CONTENT); film_changed (Film::LOOP); film_changed (Film::DCP_CONTENT_TYPE); - film_changed (Film::FORMAT); - film_changed (Film::CROP); + film_changed (Film::CONTAINER); film_changed (Film::FILTERS); film_changed (Film::SCALER); film_changed (Film::AUDIO_GAIN); @@ -769,6 +791,7 @@ FilmEditor::set_film (shared_ptr f) film_changed (Film::DCI_METADATA); film_changed (Film::DCP_VIDEO_FRAME_RATE); + film_content_changed (boost::shared_ptr (), VideoContentProperty::VIDEO_CROP); film_content_changed (boost::shared_ptr (), FFmpegContentProperty::SUBTITLE_STREAMS); film_content_changed (boost::shared_ptr (), FFmpegContentProperty::SUBTITLE_STREAM); film_content_changed (boost::shared_ptr (), FFmpegContentProperty::AUDIO_STREAMS); @@ -1102,6 +1125,17 @@ FilmEditor::selected_content () return c[s]; } +shared_ptr +FilmEditor::selected_video_content () +{ + shared_ptr c = selected_content (); + if (!c) { + return shared_ptr (); + } + + return dynamic_pointer_cast (c); +} + void FilmEditor::setup_scaling_description () { diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index 9123dfdb4..3f7264296 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -35,6 +35,7 @@ class Film; class AudioDialog; class TimelineDialog; class AudioMappingView; +class Format; /** @class FilmEditor * @brief A wx widget to edit a film's metadata, and perform various functions. @@ -69,7 +70,7 @@ private: void content_add_clicked (wxCommandEvent &); void content_remove_clicked (wxCommandEvent &); void imagemagick_video_length_changed (wxCommandEvent &); - void format_changed (wxCommandEvent &); + void container_changed (wxCommandEvent &); void dcp_content_type_changed (wxCommandEvent &); void scaler_changed (wxCommandEvent &); void audio_gain_changed (wxCommandEvent &); @@ -103,13 +104,14 @@ private: void setup_scaling_description (); void setup_main_notebook_size (); void setup_content (); - void setup_format (); + void setup_container (); void setup_content_button_sensitivity (); void setup_loop_sensitivity (); void setup_content_properties (); void active_jobs_changed (bool); boost::shared_ptr selected_content (); + boost::shared_ptr selected_video_content (); wxNotebook* _main_notebook; wxNotebook* _content_notebook; @@ -129,6 +131,7 @@ private: wxTextCtrl* _name; wxStaticText* _dcp_name; wxCheckBox* _use_dci_name; + wxChoice* _container; wxListCtrl* _content; wxButton* _content_add; wxButton* _content_remove; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 2f69235c1..6a40e88f7 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -27,6 +27,7 @@ #include #include #include "lib/film.h" +#include "lib/container.h" #include "lib/format.h" #include "lib/util.h" #include "lib/job_manager.h" @@ -115,7 +116,7 @@ void FilmViewer::film_changed (Film::Property p) { switch (p) { - case Film::FORMAT: + case Film::CONTAINER: calculate_sizes (); update_from_raw (); break; @@ -135,7 +136,6 @@ FilmViewer::film_changed (Film::Property p) break; case Film::SCALER: case Film::FILTERS: - case Film::CROP: update_from_decoder (); break; default: @@ -173,7 +173,7 @@ FilmViewer::set_film (shared_ptr f) _film->ContentChanged.connect (boost::bind (&FilmViewer::film_content_changed, this, _1, _2)); film_changed (Film::CONTENT); - film_changed (Film::FORMAT); + film_changed (Film::CONTAINER); film_changed (Film::WITH_SUBTITLES); film_changed (Film::SUBTITLE_OFFSET); film_changed (Film::SUBTITLE_SCALE); @@ -314,7 +314,8 @@ FilmViewer::raw_to_display () when working out the scale that we are applying. */ - Size const cropped_size = _film->cropped_size (_raw_frame->size ()); + /* XXX */ + Size const cropped_size = _raw_frame->size ();//_film->cropped_size (_raw_frame->size ()); Rect tx = subtitle_transformed_area ( float (_film_size.width) / cropped_size.width, @@ -337,10 +338,10 @@ FilmViewer::calculate_sizes () return; } - Format const * format = _film->format (); + Container const * container = _film->container (); float const panel_ratio = static_cast (_panel_size.width) / _panel_size.height; - float const film_ratio = format ? format->container_ratio () : 1.78; + float const film_ratio = container ? container->ratio () : 1.78; if (panel_ratio < film_ratio) { /* panel is less widscreen than the film; clamp width */ @@ -356,9 +357,9 @@ FilmViewer::calculate_sizes () of our _display_frame. */ _display_frame_x = 0; - if (format) { - _display_frame_x = static_cast (format->dcp_padding (_film)) * _out_size.width / format->dcp_size().width; - } +// if (format) { +// _display_frame_x = static_cast (format->dcp_padding (_film)) * _out_size.width / format->dcp_size().width; +// } _film_size = _out_size; _film_size.width -= _display_frame_x * 2; @@ -472,7 +473,9 @@ FilmViewer::film_content_changed (weak_ptr, int p) /* Force an update to our frame */ wxScrollEvent ev; slider_moved (ev); - } + } else if (p == VideoContentProperty::VIDEO_CROP) { + update_from_decoder (); + } } void diff --git a/test/test.cc b/test/test.cc index eaf210370..68d8611c1 100644 --- a/test/test.cc +++ b/test/test.cc @@ -40,6 +40,7 @@ #include "ffmpeg_decoder.h" #include "sndfile_decoder.h" #include "dcp_content_type.h" +#include "container.h" #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE dcpomatic_test #include @@ -137,18 +138,14 @@ BOOST_AUTO_TEST_CASE (film_metadata_test) shared_ptr f (new Film (test_film, false)); f->_dci_date = boost::gregorian::from_undelimited_string ("20130211"); - BOOST_CHECK (f->format() == 0); + BOOST_CHECK (f->container() == 0); BOOST_CHECK (f->dcp_content_type() == 0); BOOST_CHECK (f->filters ().empty()); f->set_name ("fred"); // BOOST_CHECK_THROW (f->set_content ("jim"), OpenFileError); f->set_dcp_content_type (DCPContentType::from_pretty_name ("Short")); - f->set_format (Format::from_nickname ("Flat")); - f->set_left_crop (1); - f->set_right_crop (2); - f->set_top_crop (3); - f->set_bottom_crop (4); + f->set_container (Container::from_id ("185")); vector f_filters; f_filters.push_back (Filter::from_id ("pphb")); f_filters.push_back (Filter::from_id ("unsharp")); @@ -164,11 +161,7 @@ BOOST_AUTO_TEST_CASE (film_metadata_test) BOOST_CHECK_EQUAL (g->name(), "fred"); BOOST_CHECK_EQUAL (g->dcp_content_type(), DCPContentType::from_pretty_name ("Short")); - BOOST_CHECK_EQUAL (g->format(), Format::from_nickname ("Flat")); - BOOST_CHECK_EQUAL (g->crop().left, 1); - BOOST_CHECK_EQUAL (g->crop().right, 2); - BOOST_CHECK_EQUAL (g->crop().top, 3); - BOOST_CHECK_EQUAL (g->crop().bottom, 4); + BOOST_CHECK_EQUAL (g->container(), Container::from_id ("185")); vector g_filters = g->filters (); BOOST_CHECK_EQUAL (g_filters.size(), 2); BOOST_CHECK_EQUAL (g_filters.front(), Filter::from_id ("pphb")); @@ -317,7 +310,7 @@ BOOST_AUTO_TEST_CASE (make_dcp_test) shared_ptr film = new_test_film ("make_dcp_test"); film->set_name ("test_film2"); // film->set_content ("../../../test/test.mp4"); - film->set_format (Format::from_nickname ("Flat")); + film->set_container (Container::from_id ("185")); film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test")); film->make_dcp (); film->write_metadata (); @@ -348,7 +341,7 @@ BOOST_AUTO_TEST_CASE (make_dcp_with_range_test) film->set_name ("test_film3"); // film->set_content ("../../../test/test.mp4"); // film->examine_content (); - film->set_format (Format::from_nickname ("Flat")); + film->set_container (Container::from_id ("185")); film->set_dcp_content_type (DCPContentType::from_pretty_name ("Test")); film->make_dcp (); -- 2.30.2