Re-work FilmState / Film relationship a bit; Film now inherits from FilmState and...
authorCarl Hetherington <cth@carlh.net>
Wed, 17 Oct 2012 21:40:08 +0000 (22:40 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 17 Oct 2012 21:40:08 +0000 (22:40 +0100)
22 files changed:
src/lib/ab_transcode_job.cc
src/lib/check_hashes_job.cc
src/lib/decoder.cc
src/lib/examine_content_job.cc
src/lib/ffmpeg_decoder.cc
src/lib/film.cc
src/lib/film.h
src/lib/film_state.cc
src/lib/film_state.h
src/lib/imagemagick_encoder.cc
src/lib/j2k_still_encoder.cc
src/lib/j2k_wav_encoder.cc
src/lib/make_dcp_job.cc
src/lib/thumbs_job.cc
src/lib/tiff_decoder.cc
src/lib/transcode_job.cc
src/tools/makedcp.cc
src/wx/film_editor.cc
src/wx/film_editor.h
src/wx/film_viewer.cc
src/wx/film_viewer.h
test/test.cc

index fd8236bf028ec54a2fb10d565bc6e9be433fb2f3..89967bfcac42e7b47d33868fb4bef1a048e7954a 100644 (file)
@@ -39,14 +39,14 @@ ABTranscodeJob::ABTranscodeJob (shared_ptr<const FilmState> s, shared_ptr<const
        : Job (s, o, l, req)
 {
        _fs_b.reset (new FilmState (*_fs));
-       _fs_b->scaler = Config::instance()->reference_scaler ();
-       _fs_b->filters = Config::instance()->reference_filters ();
+       _fs_b->set_scaler (Config::instance()->reference_scaler ());
+       _fs_b->set_filters (Config::instance()->reference_filters ());
 }
 
 string
 ABTranscodeJob::name () const
 {
-       return String::compose ("A/B transcode %1", _fs->name);
+       return String::compose ("A/B transcode %1", _fs->name());
 }
 
 void
index f07a5ab2accaf9d98172bbb2ba9da7cd213b0516..d64ccc866b921412dfc45d99729a35651322781b 100644 (file)
@@ -41,7 +41,7 @@ CheckHashesJob::CheckHashesJob (shared_ptr<const FilmState> s, shared_ptr<const
 string
 CheckHashesJob::name () const
 {
-       return String::compose ("Check hashes of %1", _fs->name);
+       return String::compose ("Check hashes of %1", _fs->name());
 }
 
 void
@@ -66,13 +66,13 @@ CheckHashesJob::run ()
                        ++_bad;
                }
 
-               set_progress (float (i) / _fs->length);
+               set_progress (float (i) / _fs->length());
        }
 
        if (_bad) {
                shared_ptr<Job> tc;
 
-               if (_fs->dcp_ab) {
+               if (_fs->dcp_ab()) {
                        tc.reset (new ABTranscodeJob (_fs, _opt, _log, shared_from_this()));
                } else {
                        tc.reset (new TranscodeJob (_fs, _opt, _log, shared_from_this()));
index ef2ef6fb9715889d1f175a2a1e55db19f96f3d71..03131dac2502a4b12290b1408c00002181a776f3 100644 (file)
@@ -76,7 +76,7 @@ Decoder::Decoder (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const
        , _delay_in_bytes (0)
        , _audio_frames_processed (0)
 {
-       if (_opt->decode_video_frequency != 0 && _fs->length == 0) {
+       if (_opt->decode_video_frequency != 0 && _fs->length() == 0) {
                throw DecodeError ("cannot do a partial decode if length == 0");
        }
 }
@@ -90,7 +90,7 @@ Decoder::~Decoder ()
 void
 Decoder::process_begin ()
 {
-       _delay_in_bytes = _fs->audio_delay * _fs->audio_sample_rate * _fs->audio_channels * _fs->bytes_per_sample() / 1000;
+       _delay_in_bytes = _fs->audio_delay() * _fs->audio_sample_rate() * _fs->audio_channels() * _fs->bytes_per_sample() / 1000;
        delete _delay_line;
        _delay_line = new DelayLine (_delay_in_bytes);
 
@@ -104,7 +104,7 @@ Decoder::process_end ()
        if (_delay_in_bytes < 0) {
                uint8_t remainder[-_delay_in_bytes];
                _delay_line->get_remaining (remainder);
-               _audio_frames_processed += _delay_in_bytes / (_fs->audio_channels * _fs->bytes_per_sample());
+               _audio_frames_processed += _delay_in_bytes / (_fs->audio_channels() * _fs->bytes_per_sample());
                Audio (remainder, _delay_in_bytes);
        }
 
@@ -113,7 +113,7 @@ Decoder::process_end ()
        */
 
        int64_t const audio_short_by_frames =
-               ((int64_t) _fs->dcp_length() * _fs->target_sample_rate() / _fs->frames_per_second)
+               ((int64_t) _fs->dcp_length() * _fs->target_sample_rate() / _fs->frames_per_second())
                - _audio_frames_processed;
 
        if (audio_short_by_frames >= 0) {
@@ -122,7 +122,7 @@ Decoder::process_end ()
                s << "Adding " << audio_short_by_frames << " frames of silence to the end.";
                _log->log (s.str ());
 
-               int64_t bytes = audio_short_by_frames * _fs->audio_channels * _fs->bytes_per_sample();
+               int64_t bytes = audio_short_by_frames * _fs->audio_channels() * _fs->bytes_per_sample();
                
                int64_t const silence_size = 64 * 1024;
                uint8_t silence[silence_size];
@@ -185,10 +185,10 @@ Decoder::process_audio (uint8_t* data, int size)
        int const samples = size / _fs->bytes_per_sample();
 
        /* Maybe apply gain */
-       if (_fs->audio_gain != 0) {
-               float const linear_gain = pow (10, _fs->audio_gain / 20);
+       if (_fs->audio_gain() != 0) {
+               float const linear_gain = pow (10, _fs->audio_gain() / 20);
                uint8_t* p = data;
-               switch (_fs->audio_sample_format) {
+               switch (_fs->audio_sample_format()) {
                case AV_SAMPLE_FMT_S16:
                        for (int i = 0; i < samples; ++i) {
                                /* XXX: assumes little-endian; also we should probably be dithering here */
@@ -217,7 +217,7 @@ Decoder::process_audio (uint8_t* data, int size)
        }
 
        /* Update the number of audio frames we've pushed to the encoder */
-       _audio_frames_processed += size / (_fs->audio_channels * _fs->bytes_per_sample ());
+       _audio_frames_processed += size / (_fs->audio_channels() * _fs->bytes_per_sample());
 
        /* Push into the delay line and then tell the world what we've got */
        int available = _delay_line->feed (data, size);
@@ -240,7 +240,7 @@ Decoder::process_video (AVFrame* frame)
 
        int gap = 0;
        if (_opt->decode_video_frequency != 0) {
-               gap = _fs->length / _opt->decode_video_frequency;
+               gap = _fs->length() / _opt->decode_video_frequency;
        }
 
        if (_opt->decode_video_frequency != 0 && gap != 0 && (_video_frame % gap) != 0) {
@@ -305,7 +305,7 @@ Decoder::process_video (AVFrame* frame)
                        }
 
                        shared_ptr<Subtitle> sub;
-                       if (_timed_subtitle && _timed_subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) {
+                       if (_timed_subtitle && _timed_subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second()))) {
                                sub = _timed_subtitle->subtitle ();
                        }
 
@@ -328,13 +328,13 @@ Decoder::setup_video_filters ()
        
        if (_opt->apply_crop) {
                size_after_crop = _fs->cropped_size (native_size ());
-               fs << crop_string (Position (_fs->crop.left, _fs->crop.top), size_after_crop);
+               fs << crop_string (Position (_fs->crop().left, _fs->crop().top), size_after_crop);
        } else {
                size_after_crop = native_size ();
                fs << crop_string (Position (0, 0), size_after_crop);
        }
 
-       string filters = Filter::ffmpeg_strings (_fs->filters).first;
+       string filters = Filter::ffmpeg_strings (_fs->filters()).first;
        if (!filters.empty ()) {
                filters += ",";
        }
@@ -418,6 +418,6 @@ Decoder::process_subtitle (shared_ptr<TimedSubtitle> s)
        
        if (_opt->apply_crop) {
                Position const p = _timed_subtitle->subtitle()->position ();
-               _timed_subtitle->subtitle()->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top));
+               _timed_subtitle->subtitle()->set_position (Position (p.x - _fs->crop().left, p.y - _fs->crop().top));
        }
 }
index 36b4cbabc95f453e52656dc7a62c7071e42d449b..fd574e2d058281dba42c77115ce0b6afacee7634 100644 (file)
@@ -43,7 +43,7 @@ ExamineContentJob::~ExamineContentJob ()
 string
 ExamineContentJob::name () const
 {
-       return String::compose ("Examine content of %1", _fs->name);
+       return String::compose ("Examine content of %1", _fs->name());
 }
 
 void
index 4cec7ef353858879ebeb4f4e10b044ffad6a6b74..828934604b0db42426567aae5a17863db4d425b0 100644 (file)
@@ -220,7 +220,7 @@ FFmpegDecoder::do_pass ()
                                        0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
                                        );
                                
-                               assert (_audio_codec_context->channels == _fs->audio_channels);
+                               assert (_audio_codec_context->channels == _fs->audio_channels());
                                process_audio (_frame->data[0], data_size);
                        }
                }
@@ -245,7 +245,7 @@ FFmpegDecoder::do_pass ()
                                0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1
                                );
 
-                       assert (_audio_codec_context->channels == _fs->audio_channels);
+                       assert (_audio_codec_context->channels == _fs->audio_channels());
                        process_audio (_frame->data[0], data_size);
                }
 
index e92881dada138c7566235356f4096022dfd22e6e..5b21ed3bc7bde2e96db0f6bca290db0562508d61 100644 (file)
@@ -64,9 +64,8 @@ using namespace boost;
  */
 
 Film::Film (string d, bool must_exist)
-       : _dirty (false)
 {
-       /* Make _state.directory a complete path without ..s (where possible)
+       /* Make state.directory a complete path without ..s (where possible)
           (Code swiped from Adam Bowen on stackoverflow)
        */
        
@@ -84,27 +83,19 @@ Film::Film (string d, bool must_exist)
                }
        }
 
-       _state.directory = result.string ();
+       set_directory (result.string ());
        
-       if (!filesystem::exists (_state.directory)) {
+       if (!filesystem::exists (directory())) {
                if (must_exist) {
-                       throw OpenFileError (_state.directory);
+                       throw OpenFileError (directory());
                } else {
-                       filesystem::create_directory (_state.directory);
+                       filesystem::create_directory (directory());
                }
        }
 
        read_metadata ();
 
-       _log = new FileLog (_state.file ("log"));
-}
-
-/** Copy constructor */
-Film::Film (Film const & other)
-       : _state (other._state)
-       , _dirty (other._dirty)
-{
-
+       _log = new FileLog (file ("log"));
 }
 
 Film::~Film ()
@@ -112,260 +103,17 @@ Film::~Film ()
        delete _log;
 }
          
-/** Read the `metadata' file inside this Film's directory, and fill the
- *  object's data with its content.
- */
-
-void
-Film::read_metadata ()
-{
-       ifstream f (metadata_file().c_str ());
-       multimap<string, string> kv = read_key_value (f);
-       for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
-               _state.read_metadata (i->first, i->second);
-       }
-       _dirty = false;
-}
-
-/** Write our state to a file `metadata' inside the Film's directory */
-void
-Film::write_metadata () const
-{
-       filesystem::create_directories (_state.directory);
-       
-       ofstream f (metadata_file().c_str ());
-       if (!f.good ()) {
-               throw CreateFileError (metadata_file ());
-       }
-
-       _state.write_metadata (f);
-
-       _dirty = false;
-}
-
-/** Set the name by which DVD-o-matic refers to this Film */
-void
-Film::set_name (string n)
-{
-       _state.name = n;
-       signal_changed (NAME);
-}
-
-/** Set the content file for this film.
- *  @param c New content file; if specified as an absolute path, the content should
- *  be within the film's _state.directory; if specified as a relative path, the content
- *  will be assumed to be within the film's _state.directory.
- */
-void
-Film::set_content (string c)
-{
-       string check = _state.directory;
-
-#if BOOST_FILESYSTEM_VERSION == 3
-       filesystem::path slash ("/");
-       string platform_slash = slash.make_preferred().string ();
-#else
-#ifdef DVDOMATIC_WINDOWS
-       string platform_slash = "\\";
-#else
-       string platform_slash = "/";
-#endif
-#endif 
-
-       if (!ends_with (check, platform_slash)) {
-               check += platform_slash;
-       }
-       
-       if (filesystem::path(c).has_root_directory () && starts_with (c, check)) {
-               c = c.substr (_state.directory.length() + 1);
-       }
-
-       if (c == _state.content) {
-               return;
-       }
-       
-       /* Create a temporary decoder so that we can get information
-          about the content.
-       */
-       shared_ptr<FilmState> s = state_copy ();
-       s->content = c;
-       shared_ptr<Options> o (new Options ("", "", ""));
-       o->out_size = Size (1024, 1024);
-
-       shared_ptr<Decoder> d = decoder_factory (s, o, 0, _log);
-       
-       _state.size = d->native_size ();
-       _state.length = d->length_in_frames ();
-       _state.frames_per_second = d->frames_per_second ();
-       _state.audio_channels = d->audio_channels ();
-       _state.audio_sample_rate = d->audio_sample_rate ();
-       _state.audio_sample_format = d->audio_sample_format ();
-       _state.has_subtitles = d->has_subtitles ();
-       _state.audio_streams = d->audio_streams ();
-       _state.subtitle_streams = d->subtitle_streams ();
-       _state.audio_stream = _state.audio_streams.empty() ? -1 : _state.audio_streams.front().id;
-       _state.subtitle_stream = _state.subtitle_streams.empty() ? -1 : _state.subtitle_streams.front().id;
-
-       _state.content_digest = md5_digest (s->content_path ());
-       _state.content = c;
-       
-       signal_changed (SIZE);
-       signal_changed (LENGTH);
-       signal_changed (FRAMES_PER_SECOND);
-       signal_changed (AUDIO_CHANNELS);
-       signal_changed (AUDIO_SAMPLE_RATE);
-       signal_changed (CONTENT);
-       signal_changed (AUDIO_STREAM);
-       signal_changed (SUBTITLE_STREAM);
-}
-
-/** Set the format that this Film should be shown in */
-void
-Film::set_format (Format const * f)
-{
-       _state.format = f;
-       signal_changed (FORMAT);
-}
-
-/** Set the type to specify the DCP as having
- *  (feature, trailer etc.)
- */
-void
-Film::set_dcp_content_type (DCPContentType const * t)
-{
-       _state.dcp_content_type = t;
-       signal_changed (DCP_CONTENT_TYPE);
-}
-
-/** Set the number of pixels by which to crop the left of the source video */
-void
-Film::set_left_crop (int c)
-{
-       if (c == _state.crop.left) {
-               return;
-       }
-       
-       _state.crop.left = c;
-       signal_changed (CROP);
-}
-
-/** Set the number of pixels by which to crop the right of the source video */
-void
-Film::set_right_crop (int c)
-{
-       if (c == _state.crop.right) {
-               return;
-       }
-
-       _state.crop.right = c;
-       signal_changed (CROP);
-}
-
-/** Set the number of pixels by which to crop the top of the source video */
-void
-Film::set_top_crop (int c)
-{
-       if (c == _state.crop.top) {
-               return;
-       }
-       
-       _state.crop.top = c;
-       signal_changed (CROP);
-}
-
-/** Set the number of pixels by which to crop the bottom of the source video */
-void
-Film::set_bottom_crop (int c)
-{
-       if (c == _state.crop.bottom) {
-               return;
-       }
-       
-       _state.crop.bottom = c;
-       signal_changed (CROP);
-}
-
-/** Set the filters to apply to the image when generating thumbnails
- *  or a DCP.
- */
-void
-Film::set_filters (vector<Filter const *> const & f)
-{
-       _state.filters = f;
-       signal_changed (FILTERS);
-}
-
-/** Set the number of frames to put in any generated DCP (from
- *  the start of the film).  0 indicates that all frames should
- *  be used.
- */
-void
-Film::set_dcp_frames (int n)
-{
-       _state.dcp_frames = n;
-       signal_changed (DCP_FRAMES);
-}
-
-void
-Film::set_dcp_trim_action (TrimAction a)
-{
-       _state.dcp_trim_action = a;
-       signal_changed (DCP_TRIM_ACTION);
-}
-
-/** Set whether or not to generate a A/B comparison DCP.
- *  Such a DCP has the left half of its frame as the Film
- *  content without any filtering or post-processing; the
- *  right half is rendered with filters and post-processing.
- */
-void
-Film::set_dcp_ab (bool a)
-{
-       _state.dcp_ab = a;
-       signal_changed (DCP_AB);
-}
-
-void
-Film::set_audio_gain (float g)
-{
-       _state.audio_gain = g;
-       signal_changed (AUDIO_GAIN);
-}
-
-void
-Film::set_audio_delay (int d)
-{
-       _state.audio_delay = d;
-       signal_changed (AUDIO_DELAY);
-}
-
-/** @return path of metadata file */
-string
-Film::metadata_file () const
-{
-       return _state.file ("metadata");
-}
-
-/** @return full path of the content (actual video) file
- *  of this Film.
- */
-string
-Film::content () const
-{
-       return _state.content_path ();
-}
-
 /** The pre-processing GUI part of a thumbs update.
  *  Must be called from the GUI thread.
  */
 void
 Film::update_thumbs_pre_gui ()
 {
-       _state.thumbs.clear ();
-       filesystem::remove_all (_state.dir ("thumbs"));
+       set_thumbs (vector<int> ());
+       filesystem::remove_all (dir ("thumbs"));
 
        /* This call will recreate the directory */
-       _state.dir ("thumbs");
+       dir ("thumbs");
 }
 
 /** The post-processing GUI part of a thumbs update.
@@ -374,7 +122,8 @@ Film::update_thumbs_pre_gui ()
 void
 Film::update_thumbs_post_gui ()
 {
-       string const tdir = _state.dir ("thumbs");
+       string const tdir = dir ("thumbs");
+       vector<int> thumbs;
        
        for (filesystem::directory_iterator i = filesystem::directory_iterator (tdir); i != filesystem::directory_iterator(); ++i) {
 
@@ -387,39 +136,12 @@ Film::update_thumbs_post_gui ()
                
                size_t const d = l.find (".png");
                if (d != string::npos) {
-                       _state.thumbs.push_back (atoi (l.substr (0, d).c_str()));
+                       thumbs.push_back (atoi (l.substr (0, d).c_str()));
                }
        }
 
-       sort (_state.thumbs.begin(), _state.thumbs.end());
-       
-       write_metadata ();
-       signal_changed (THUMBS);
-}
-
-/** @return the number of thumbnail images that we have */
-int
-Film::num_thumbs () const
-{
-       return _state.thumbs.size ();
-}
-
-/** @param n A thumb index.
- *  @return The frame within the Film that it is for.
- */
-int
-Film::thumb_frame (int n) const
-{
-       return _state.thumb_frame (n);
-}
-
-/** @param n A thumb index.
- *  @return The path to the thumb's image file.
- */
-string
-Film::thumb_file (int n) const
-{
-       return _state.thumb_file (n);
+       sort (thumbs.begin(), thumbs.end());
+       set_thumbs (thumbs);
 }
 
 /** @return The path to the directory to write JPEG2000 files to */
@@ -433,18 +155,18 @@ Film::j2k_dir () const
        /* Start with j2c */
        p /= "j2c";
 
-       pair<string, string> f = Filter::ffmpeg_strings (filters ());
+       pair<string, string> f = Filter::ffmpeg_strings (filters());
 
        /* Write stuff to specify the filter / post-processing settings that are in use,
           so that we don't get confused about J2K files generated using different
           settings.
        */
        stringstream s;
-       s << _state.format->id()
-         << "_" << _state.content_digest
+       s << format()->id()
+         << "_" << content_digest()
          << "_" << crop().left << "_" << crop().right << "_" << crop().top << "_" << crop().bottom
          << "_" << f.first << "_" << f.second
-         << "_" << _state.scaler->id();
+         << "_" << scaler()->id();
 
        p /= s.str ();
 
@@ -456,15 +178,7 @@ Film::j2k_dir () const
                p /= s.str ();
        }
        
-       return _state.dir (p.string ());
-}
-
-/** Handle a change to the Film's metadata */
-void
-Film::signal_changed (Property p)
-{
-       _dirty = true;
-       Changed (p);
+       return dir (p.string());
 }
 
 /** Add suitable Jobs to the JobManager to create a DCP for this Film.
@@ -502,7 +216,7 @@ Film::make_dcp (bool transcode, int freq)
        }
 
        shared_ptr<const FilmState> fs = state_copy ();
-       shared_ptr<Options> o (new Options (j2k_dir(), ".j2c", _state.dir ("wavs")));
+       shared_ptr<Options> o (new Options (j2k_dir(), ".j2c", dir ("wavs")));
        o->out_size = format()->dcp_size ();
        if (dcp_frames() == 0) {
                /* Decode the whole film, no blacking */
@@ -527,7 +241,7 @@ Film::make_dcp (bool transcode, int freq)
        shared_ptr<Job> r;
 
        if (transcode) {
-               if (_state.dcp_ab) {
+               if (dcp_ab()) {
                        r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (fs, o, log(), shared_ptr<Job> ())));
                } else {
                        r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (fs, o, log(), shared_ptr<Job> ())));
@@ -538,16 +252,10 @@ Film::make_dcp (bool transcode, int freq)
        JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (fs, o, log(), r)));
 }
 
-shared_ptr<FilmState>
-Film::state_copy () const
-{
-       return shared_ptr<FilmState> (new FilmState (_state));
-}
-
 void
 Film::copy_from_dvd_post_gui ()
 {
-       const string dvd_dir = _state.dir ("dvd");
+       const string dvd_dir = dir ("dvd");
 
        string largest_file;
        uintmax_t largest_size = 0;
@@ -582,44 +290,23 @@ Film::examine_content ()
 void
 Film::examine_content_post_gui ()
 {
-       _state.length = _examine_content_job->last_video_frame ();
-       signal_changed (LENGTH);
-       
+       set_length (_examine_content_job->last_video_frame ());
        _examine_content_job.reset ();
 }
 
-void
-Film::set_scaler (Scaler const * s)
-{
-       _state.scaler = s;
-       signal_changed (SCALER);
-}
 
 /** @return full paths to any audio files that this Film has */
 vector<string>
 Film::audio_files () const
 {
        vector<string> f;
-       for (filesystem::directory_iterator i = filesystem::directory_iterator (_state.dir("wavs")); i != filesystem::directory_iterator(); ++i) {
+       for (filesystem::directory_iterator i = filesystem::directory_iterator (dir("wavs")); i != filesystem::directory_iterator(); ++i) {
                f.push_back (i->path().string ());
        }
 
        return f;
 }
 
-ContentType
-Film::content_type () const
-{
-       return _state.content_type ();
-}
-
-void
-Film::set_still_duration (int d)
-{
-       _state.still_duration = d;
-       signal_changed (STILL_DURATION);
-}
-
 void
 Film::send_dcp_to_tms ()
 {
@@ -651,31 +338,10 @@ Film::encoded_frames () const
        return N;
 }
 
-void
-Film::set_with_subtitles (bool w)
-{
-       _state.with_subtitles = w;
-       signal_changed (WITH_SUBTITLES);
-}
-
-void
-Film::set_subtitle_offset (int o)
-{
-       _state.subtitle_offset = o;
-       signal_changed (SUBTITLE_OFFSET);
-}
-
-void
-Film::set_subtitle_scale (float s)
-{
-       _state.subtitle_scale = s;
-       signal_changed (SUBTITLE_SCALE);
-}
-
 pair<Position, string>
 Film::thumb_subtitle (int n) const
 {
-       string sub_file = _state.thumb_base(n) + ".sub";
+       string sub_file = thumb_base(n) + ".sub";
        if (!filesystem::exists (sub_file)) {
                return pair<Position, string> ();
        }
@@ -689,79 +355,9 @@ Film::thumb_subtitle (int n) const
                        sub.first.x = lexical_cast<int> (i->second);
                } else if (i->first == "y") {
                        sub.first.y = lexical_cast<int> (i->second);
-                       sub.second = String::compose ("%1.sub.png", _state.thumb_base(n));
+                       sub.second = String::compose ("%1.sub.png", thumb_base(n));
                }
        }
        
        return sub;
 }
-
-void
-Film::set_audio_language (string v)
-{
-       _state.audio_language = v;
-       signal_changed (DCI_METADATA);
-}
-
-void
-Film::set_subtitle_language (string v)
-{
-       _state.subtitle_language = v;
-       signal_changed (DCI_METADATA);
-}
-
-void
-Film::set_territory (string v)
-{
-       _state.territory = v;
-       signal_changed (DCI_METADATA);
-}
-
-void
-Film::set_rating (string v)
-{
-       _state.rating = v;
-       signal_changed (DCI_METADATA);
-}
-
-void
-Film::set_studio (string v)
-{
-       _state.studio = v;
-       signal_changed (DCI_METADATA);
-}
-
-void
-Film::set_facility (string v)
-{
-       _state.facility = v;
-       signal_changed (DCI_METADATA);
-}
-
-void
-Film::set_package_type (string v)
-{
-       _state.package_type = v;
-       signal_changed (DCI_METADATA);
-}
-
-void
-Film::set_use_dci_name (bool v)
-{
-       _state.use_dci_name = v;
-       signal_changed (USE_DCI_NAME);
-}
-
-void
-Film::set_audio_stream (int id)
-{
-       _state.audio_stream = id;
-       signal_changed (AUDIO_STREAM);
-}
-
-void
-Film::set_subtitle_stream (int id)
-{
-       _state.subtitle_stream = id;
-       signal_changed (SUBTITLE_STREAM);
-}
index 14731046a1ca01e7787adfa2f3e3b5d1175a17c2..e96c60308c01a13c61b0ee1d40d6be88c7d32ecd 100644 (file)
@@ -30,7 +30,6 @@
 #include <inttypes.h>
 #include <boost/thread/mutex.hpp>
 #include <boost/thread.hpp>
-#include <sigc++/signal.h>
 extern "C" {
 #include <libavcodec/avcodec.h>
 }
@@ -49,235 +48,18 @@ class ExamineContentJob;
  *  A representation of a piece of video (with sound), including naming,
  *  the source content file, and how it should be presented in a DCP.
  */
-class Film
+class Film : public FilmState
 {
 public:
        Film (std::string d, bool must_exist = true);
-       Film (Film const &);
        ~Film ();
 
-       void write_metadata () const;
-
-       /** @return complete path to directory containing the film metadata */
-       std::string directory () const {
-               return _state.directory;
-       }
-
-       std::string content () const;
-       ContentType content_type () const;
-
-       /** @return name for DVD-o-matic */
-       std::string name () const {
-               return _state.name;
-       }
-
-       /** @return name to give the DCP */
-       std::string dcp_name () const {
-               return _state.dcp_name ();
-       }
-
-       /** @return true to use a DCI-spec name for the DCP */
-       bool use_dci_name () const {
-               return _state.use_dci_name;
-       }
-
-       /** @return number of pixels to crop from the sides of the original picture */
-       Crop crop () const {
-               return _state.crop;
-       }
-
-       /** @return the format to present this film in (flat, scope, etc.) */
-       Format const * format () const {
-               return _state.format;
-       }
-
-       /** @return video filters that should be used when generating DCPs */
-       std::vector<Filter const *> filters () const {
-               return _state.filters;
-       }
-
-       /** @return scaler algorithm to use */
-       Scaler const * scaler () const {
-               return _state.scaler;
-       }
-
-       /** @return number of frames to put in the DCP, or 0 for all */
-       int dcp_frames () const {
-               return _state.dcp_frames;
-       }
-
-       /** @return what to do with the end of an encode when trimming */
-       TrimAction dcp_trim_action () const {
-               return _state.dcp_trim_action;
-       }
-
-       /** @return true to create an A/B comparison DCP, where the left half of the image
-        *  is the video without any filters or post-processing, and the right half
-        *  has the specified filters and post-processing.
-        */
-       bool dcp_ab () const {
-               return _state.dcp_ab;
-       }
-
-       /** @return gain that should be applied to the audio when making a DCP
-           (in dB).
-       */
-       float audio_gain () const {
-               return _state.audio_gain;
-       }
-
-       /** @return delay to apply to audio (positive moves audio later) in milliseconds */
-       int audio_delay () const {
-               return _state.audio_delay;
-       }
-
-       /** @return duration to make still-sourced films (in seconds) */
-       int still_duration () const {
-               return _state.still_duration;
-       }
-
-       /** @return true to encode DCP with subtitles, if they are available */
-       bool with_subtitles () const {
-               return _state.with_subtitles;
-       }
-
-       /** @return offset to move subtitles by, in source pixels; +ve moves
-           them down the image, -ve moves them up.
-       */
-       int subtitle_offset () const {
-               return _state.subtitle_offset;
-       }
-
-       /** @return scaling factor to apply to subtitle images */
-       float subtitle_scale () const {
-               return _state.subtitle_scale;
-       }
-       
-       void set_filters (std::vector<Filter const *> const &);
-
-       void set_scaler (Scaler const *);
-
-       /** @return the type of content that this Film represents (feature, trailer etc.) */
-       DCPContentType const * dcp_content_type () {
-               return _state.dcp_content_type;
-       }
-
-       std::vector<Stream> audio_streams () const {
-               return _state.audio_streams;
-       }
-
-       int audio_stream () const {
-               return _state.audio_stream;
-       }
-
-       std::vector<Stream> subtitle_streams () const {
-               return _state.subtitle_streams;
-       }
-
-       int subtitle_stream () const {
-               return _state.subtitle_stream;
-       }
-       
-       void set_dcp_frames (int);
-       void set_dcp_trim_action (TrimAction);
-       void set_dcp_ab (bool);
-       
-       void set_name (std::string);
-       void set_use_dci_name (bool);
-       void set_content (std::string);
-       void set_top_crop (int);
-       void set_bottom_crop (int);
-       void set_left_crop (int);
-       void set_right_crop (int);
-       void set_format (Format const *);
-       void set_dcp_content_type (DCPContentType const *);
-       void set_audio_gain (float);
-       void set_audio_delay (int);
-       void set_still_duration (int);
-       void set_with_subtitles (bool);
-       void set_subtitle_offset (int);
-       void set_subtitle_scale (float);
-       void set_audio_language (std::string);
-       void set_subtitle_language (std::string);
-       void set_territory (std::string);
-       void set_rating (std::string);
-       void set_studio (std::string);
-       void set_facility (std::string);
-       void set_package_type (std::string);
-       void set_audio_stream (int id);
-       void set_subtitle_stream (int id);
-
-       /** @return size, in pixels, of the source (ignoring cropping) */
-       Size size () const {
-               return _state.size;
-       }
-
-       /** @return length, in video frames */
-       int length () const {
-               return _state.length;
-       }
-
-       /** @return nnumber of video frames per second */
-       float frames_per_second () const {
-               return _state.frames_per_second;
-       }
-
-       /** @return number of audio channels */
-       int audio_channels () const {
-               return _state.audio_channels;
-       }
-
-       /** @return audio sample rate, in Hz */
-       int audio_sample_rate () const {
-               return _state.audio_sample_rate;
-       }
-
-       /** @return format of the audio samples */
-       AVSampleFormat audio_sample_format () const {
-               return _state.audio_sample_format;
-       }
-
-       bool has_subtitles () const {
-               return _state.has_subtitles;
-       }
-
-       std::string audio_language () const {
-               return _state.audio_language;
-       }
-
-       std::string subtitle_language () const {
-               return _state.subtitle_language;
-       }
-       
-       std::string territory () const {
-               return _state.territory;
-       }
-
-       std::string rating () const {
-               return _state.rating;
-       }
-
-       std::string studio () const {
-               return _state.studio;
-       }
-
-       std::string facility () const {
-               return _state.facility;
-       }
-
-       std::string package_type () const {
-               return _state.package_type;
-       }
-
        std::string j2k_dir () const;
 
        std::vector<std::string> audio_files () const;
 
        void update_thumbs_pre_gui ();
        void update_thumbs_post_gui ();
-       int num_thumbs () const;
-       int thumb_frame (int) const;
-       std::string thumb_file (int) const;
        std::pair<Position, std::string> thumb_subtitle (int) const;
 
        void copy_from_dvd_post_gui ();
@@ -286,45 +68,8 @@ public:
        void send_dcp_to_tms ();
        void copy_from_dvd ();
 
-       /** @return true if our metadata has been modified since it was last saved */
-       bool dirty () const {
-               return _dirty;
-       }
-
        void make_dcp (bool, int freq = 0);
 
-       enum Property {
-               NONE,
-               NAME,
-               CONTENT,
-               DCP_CONTENT_TYPE,
-               FORMAT,
-               CROP,
-               FILTERS,
-               SCALER,
-               DCP_FRAMES,
-               DCP_TRIM_ACTION,
-               DCP_AB,
-               AUDIO_STREAM,
-               AUDIO_GAIN,
-               AUDIO_DELAY,
-               THUMBS,
-               SIZE,
-               LENGTH,
-               FRAMES_PER_SECOND,
-               AUDIO_CHANNELS,
-               AUDIO_SAMPLE_RATE,
-               STILL_DURATION,
-               SUBTITLE_STREAM,
-               WITH_SUBTITLES,
-               SUBTITLE_OFFSET,
-               SUBTITLE_SCALE,
-               USE_DCI_NAME,
-               DCI_METADATA
-       };
-
-       boost::shared_ptr<FilmState> state_copy () const;
-
        /** @return Logger.
         *  It is safe to call this from any thread.
         */
@@ -333,25 +78,8 @@ public:
        }
 
        int encoded_frames () const;
-
-       /** Emitted when some metadata property has changed */
-       mutable sigc::signal1<void, Property> Changed;
        
 private:
-       void read_metadata ();
-       std::string metadata_file () const;
-       void update_dimensions ();
-       void signal_changed (Property);
-
-       /** The majority of our state.  Kept in a separate object
-        *  so that it can easily be copied for passing onto long-running
-        *  jobs (which then have an unchanging set of parameters).
-        */
-       FilmState _state;
-
-       /** true if our metadata has changed since it was last written to disk */
-       mutable bool _dirty;
-
        /** Log to write to */
        Log* _log;
 
index 2717d55eea7e0c62e342fb626b5a13736b37a38c..f5eb24f869326461234d480630731b5fb19233c1 100644 (file)
@@ -30,6 +30,7 @@
 #include <sstream>
 #include <boost/filesystem.hpp>
 #include <boost/date_time.hpp>
+#include <boost/algorithm/string.hpp>
 #include "film_state.h"
 #include "scaler.h"
 #include "filter.h"
@@ -37,6 +38,9 @@
 #include "dcp_content_type.h"
 #include "util.h"
 #include "exceptions.h"
+#include "options.h"
+#include "decoder.h"
+#include "decoder_factory.h"
 
 using namespace std;
 using namespace boost;
@@ -45,31 +49,41 @@ using namespace boost;
  *  @param f Stream to write to.
  */
 void
-FilmState::write_metadata (ofstream& f) const
+FilmState::write_metadata () const
 {
+       filesystem::create_directories (directory());
+
+       string const m = file ("metadata");
+       ofstream f (m.c_str ());
+       if (!f.good ()) {
+               throw CreateFileError (m);
+       }
+
+       /* XXX: reorder this */
+       
        /* User stuff */
-       f << "name " << name << "\n";
-       f << "use_dci_name " << use_dci_name << "\n";
-       f << "content " << content << "\n";
-       if (dcp_content_type) {
-               f << "dcp_content_type " << dcp_content_type->pretty_name () << "\n";
-       }
-       f << "frames_per_second " << frames_per_second << "\n";
-       if (format) {
-               f << "format " << format->as_metadata () << "\n";
-       }
-       f << "left_crop " << crop.left << "\n";
-       f << "right_crop " << crop.right << "\n";
-       f << "top_crop " << crop.top << "\n";
-       f << "bottom_crop " << crop.bottom << "\n";
-       for (vector<Filter const *>::const_iterator i = filters.begin(); i != filters.end(); ++i) {
+       f << "name " << _name << "\n";
+       f << "use_dci_name " << _use_dci_name << "\n";
+       f << "content " << _content << "\n";
+       if (_dcp_content_type) {
+               f << "dcp_content_type " << _dcp_content_type->pretty_name () << "\n";
+       }
+       f << "frames_per_second " << _frames_per_second << "\n";
+       if (_format) {
+               f << "format " << _format->as_metadata () << "\n";
+       }
+       f << "left_crop " << _crop.left << "\n";
+       f << "right_crop " << _crop.right << "\n";
+       f << "top_crop " << _crop.top << "\n";
+       f << "bottom_crop " << _crop.bottom << "\n";
+       for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
                f << "filter " << (*i)->id () << "\n";
        }
-       f << "scaler " << scaler->id () << "\n";
-       f << "dcp_frames " << dcp_frames << "\n";
+       f << "scaler " << _scaler->id () << "\n";
+       f << "dcp_frames " << _dcp_frames << "\n";
 
        f << "dcp_trim_action ";
-       switch (dcp_trim_action) {
+       switch (_dcp_trim_action) {
        case CUT:
                f << "cut\n";
                break;
@@ -78,45 +92,47 @@ FilmState::write_metadata (ofstream& f) const
                break;
        }
        
-       f << "dcp_ab " << (dcp_ab ? "1" : "0") << "\n";
-       f << "selected_audio_stream " << audio_stream << "\n";
-       f << "audio_gain " << audio_gain << "\n";
-       f << "audio_delay " << audio_delay << "\n";
-       f << "still_duration " << still_duration << "\n";
-       f << "with_subtitles " << with_subtitles << "\n";
-       f << "subtitle_offset " << subtitle_offset << "\n";
-       f << "subtitle_scale " << subtitle_scale << "\n";
-       f << "audio_language " << audio_language << "\n";
-       f << "subtitle_language " << subtitle_language << "\n";
-       f << "territory " << territory << "\n";
-       f << "rating " << rating << "\n";
-       f << "studio " << studio << "\n";
-       f << "facility " << facility << "\n";
-       f << "package_type " << package_type << "\n";
+       f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
+       f << "selected_audio_stream " << _audio_stream << "\n";
+       f << "audio_gain " << _audio_gain << "\n";
+       f << "audio_delay " << _audio_delay << "\n";
+       f << "still_duration " << _still_duration << "\n";
+       f << "with_subtitles " << _with_subtitles << "\n";
+       f << "subtitle_offset " << _subtitle_offset << "\n";
+       f << "subtitle_scale " << _subtitle_scale << "\n";
+       f << "audio_language " << _audio_language << "\n";
+       f << "subtitle_language " << _subtitle_language << "\n";
+       f << "territory " << _territory << "\n";
+       f << "rating " << _rating << "\n";
+       f << "studio " << _studio << "\n";
+       f << "facility " << _facility << "\n";
+       f << "package_type " << _package_type << "\n";
 
        /* Cached stuff; this is information about our content; we could
           look it up each time, but that's slow.
        */
-       for (vector<int>::const_iterator i = thumbs.begin(); i != thumbs.end(); ++i) {
+       for (vector<int>::const_iterator i = _thumbs.begin(); i != _thumbs.end(); ++i) {
                f << "thumb " << *i << "\n";
        }
-       f << "width " << size.width << "\n";
-       f << "height " << size.height << "\n";
-       f << "length " << length << "\n";
-       f << "audio_channels " << audio_channels << "\n";
-       f << "audio_sample_rate " << audio_sample_rate << "\n";
-       f << "audio_sample_format " << audio_sample_format_to_string (audio_sample_format) << "\n";
-       f << "content_digest " << content_digest << "\n";
-       f << "selected_subtitle_stream " << subtitle_stream << "\n";
-       f << "has_subtitles " << has_subtitles << "\n";
-
-       for (vector<Stream>::const_iterator i = audio_streams.begin(); i != audio_streams.end(); ++i) {
+       f << "width " << _size.width << "\n";
+       f << "height " << _size.height << "\n";
+       f << "length " << _length << "\n";
+       f << "audio_channels " << _audio_channels << "\n";
+       f << "audio_sample_rate " << _audio_sample_rate << "\n";
+       f << "audio_sample_format " << audio_sample_format_to_string (_audio_sample_format) << "\n";
+       f << "content_digest " << _content_digest << "\n";
+       f << "selected_subtitle_stream " << _subtitle_stream << "\n";
+       f << "has_subtitles " << _has_subtitles << "\n";
+
+       for (vector<Stream>::const_iterator i = _audio_streams.begin(); i != _audio_streams.end(); ++i) {
                f << "audio_stream " << i->to_string () << "\n";
        }
 
-       for (vector<Stream>::const_iterator i = subtitle_streams.begin(); i != subtitle_streams.end(); ++i) {
+       for (vector<Stream>::const_iterator i = _subtitle_streams.begin(); i != _subtitle_streams.end(); ++i) {
                f << "subtitle_stream " << i->to_string () << "\n";
        }
+
+       _dirty = false;
 }
 
 /** Read state from a key / value pair.
@@ -124,106 +140,114 @@ FilmState::write_metadata (ofstream& f) const
  *  @param v Value.
  */
 void
-FilmState::read_metadata (string k, string v)
-{
-       /* User-specified stuff */
-       if (k == "name") {
-               name = v;
-       } else if (k == "use_dci_name") {
-               use_dci_name = (v == "1");
-       } else if (k == "content") {
-               content = v;
-       } else if (k == "dcp_content_type") {
-               dcp_content_type = DCPContentType::from_pretty_name (v);
-       } else if (k == "frames_per_second") {
-               frames_per_second = atof (v.c_str ());
-       } else if (k == "format") {
-               format = Format::from_metadata (v);
-       } else if (k == "left_crop") {
-               crop.left = atoi (v.c_str ());
-       } else if (k == "right_crop") {
-               crop.right = atoi (v.c_str ());
-       } else if (k == "top_crop") {
-               crop.top = atoi (v.c_str ());
-       } else if (k == "bottom_crop") {
-               crop.bottom = atoi (v.c_str ());
-       } else if (k == "filter") {
-               filters.push_back (Filter::from_id (v));
-       } else if (k == "scaler") {
-               scaler = Scaler::from_id (v);
-       } else if (k == "dcp_frames") {
-               dcp_frames = atoi (v.c_str ());
-       } else if (k == "dcp_trim_action") {
-               if (v == "cut") {
-                       dcp_trim_action = CUT;
-               } else if (v == "black_out") {
-                       dcp_trim_action = BLACK_OUT;
+FilmState::read_metadata ()
+{
+       ifstream f (file("metadata").c_str());
+       multimap<string, string> kv = read_key_value (f);
+       for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
+               string const k = i->first;
+               string const v = i->second;
+
+               /* User-specified stuff */
+               if (k == "name") {
+                       _name = v;
+               } else if (k == "use_dci_name") {
+                       _use_dci_name = (v == "1");
+               } else if (k == "content") {
+                       _content = v;
+               } else if (k == "dcp_content_type") {
+                       _dcp_content_type = DCPContentType::from_pretty_name (v);
+               } else if (k == "frames_per_second") {
+                       _frames_per_second = atof (v.c_str ());
+               } else if (k == "format") {
+                       _format = Format::from_metadata (v);
+               } else if (k == "left_crop") {
+                       _crop.left = atoi (v.c_str ());
+               } else if (k == "right_crop") {
+                       _crop.right = atoi (v.c_str ());
+               } else if (k == "top_crop") {
+                       _crop.top = atoi (v.c_str ());
+               } else if (k == "bottom_crop") {
+                       _crop.bottom = atoi (v.c_str ());
+               } else if (k == "filter") {
+                       _filters.push_back (Filter::from_id (v));
+               } else if (k == "scaler") {
+                       _scaler = Scaler::from_id (v);
+               } else if (k == "dcp_frames") {
+                       _dcp_frames = atoi (v.c_str ());
+               } else if (k == "dcp_trim_action") {
+                       if (v == "cut") {
+                               _dcp_trim_action = CUT;
+                       } else if (v == "black_out") {
+                               _dcp_trim_action = BLACK_OUT;
+                       }
+               } else if (k == "dcp_ab") {
+                       _dcp_ab = (v == "1");
+               } else if (k == "selected_audio_stream") {
+                       _audio_stream = atoi (v.c_str ());
+               } else if (k == "audio_gain") {
+                       _audio_gain = atof (v.c_str ());
+               } else if (k == "audio_delay") {
+                       _audio_delay = atoi (v.c_str ());
+               } else if (k == "still_duration") {
+                       _still_duration = atoi (v.c_str ());
+               } else if (k == "with_subtitles") {
+                       _with_subtitles = (v == "1");
+               } else if (k == "subtitle_offset") {
+                       _subtitle_offset = atoi (v.c_str ());
+               } else if (k == "subtitle_scale") {
+                       _subtitle_scale = atof (v.c_str ());
+               } else if (k == "selected_subtitle_stream") {
+                       _subtitle_stream = atoi (v.c_str ());
+               } else if (k == "audio_language") {
+                       _audio_language = v;
+               } else if (k == "subtitle_language") {
+                       _subtitle_language = v;
+               } else if (k == "territory") {
+                       _territory = v;
+               } else if (k == "rating") {
+                       _rating = v;
+               } else if (k == "studio") {
+                       _studio = v;
+               } else if (k == "facility") {
+                       _facility = v;
+               } else if (k == "package_type") {
+                       _package_type = v;
                }
-       } else if (k == "dcp_ab") {
-               dcp_ab = (v == "1");
-       } else if (k == "selected_audio_stream") {
-               audio_stream = atoi (v.c_str ());
-       } else if (k == "audio_gain") {
-               audio_gain = atof (v.c_str ());
-       } else if (k == "audio_delay") {
-               audio_delay = atoi (v.c_str ());
-       } else if (k == "still_duration") {
-               still_duration = atoi (v.c_str ());
-       } else if (k == "with_subtitles") {
-               with_subtitles = (v == "1");
-       } else if (k == "subtitle_offset") {
-               subtitle_offset = atoi (v.c_str ());
-       } else if (k == "subtitle_scale") {
-               subtitle_scale = atof (v.c_str ());
-       } else if (k == "selected_subtitle_stream") {
-               subtitle_stream = atoi (v.c_str ());
-       } else if (k == "audio_language") {
-               audio_language = v;
-       } else if (k == "subtitle_language") {
-               subtitle_language = v;
-       } else if (k == "territory") {
-               territory = v;
-       } else if (k == "rating") {
-               rating = v;
-       } else if (k == "studio") {
-               studio = v;
-       } else if (k == "facility") {
-               facility = v;
-       } else if (k == "package_type") {
-               package_type = v;
-       }
-       
-       /* Cached stuff */
-       if (k == "thumb") {
-               int const n = atoi (v.c_str ());
-               /* Only add it to the list if it still exists */
-               if (filesystem::exists (thumb_file_for_frame (n))) {
-                       thumbs.push_back (n);
+               
+               /* Cached stuff */
+               if (k == "thumb") {
+                       int const n = atoi (v.c_str ());
+                       /* Only add it to the list if it still exists */
+                       if (filesystem::exists (thumb_file_for_frame (n))) {
+                               _thumbs.push_back (n);
+                       }
+               } else if (k == "width") {
+                       _size.width = atoi (v.c_str ());
+               } else if (k == "height") {
+                       _size.height = atoi (v.c_str ());
+               } else if (k == "length") {
+                       _length = atof (v.c_str ());
+               } else if (k == "audio_channels") {
+                       _audio_channels = atoi (v.c_str ());
+               } else if (k == "audio_sample_rate") {
+                       _audio_sample_rate = atoi (v.c_str ());
+               } else if (k == "audio_sample_format") {
+                       _audio_sample_format = audio_sample_format_from_string (v);
+               } else if (k == "content_digest") {
+                       _content_digest = v;
+               } else if (k == "has_subtitles") {
+                       _has_subtitles = (v == "1");
+               } else if (k == "audio_stream") {
+                       _audio_streams.push_back (Stream (v));
+               } else if (k == "subtitle_stream") {
+                       _subtitle_streams.push_back (Stream (v));
                }
-       } else if (k == "width") {
-               size.width = atoi (v.c_str ());
-       } else if (k == "height") {
-               size.height = atoi (v.c_str ());
-       } else if (k == "length") {
-               length = atof (v.c_str ());
-       } else if (k == "audio_channels") {
-               audio_channels = atoi (v.c_str ());
-       } else if (k == "audio_sample_rate") {
-               audio_sample_rate = atoi (v.c_str ());
-       } else if (k == "audio_sample_format") {
-               audio_sample_format = audio_sample_format_from_string (v);
-       } else if (k == "content_digest") {
-               content_digest = v;
-       } else if (k == "has_subtitles") {
-               has_subtitles = (v == "1");
-       } else if (k == "audio_stream") {
-               audio_streams.push_back (Stream (v));
-       } else if (k == "subtitle_stream") {
-               subtitle_streams.push_back (Stream (v));
        }
+               
+       _dirty = false;
 }
 
-
 /** @param n A thumb index.
  *  @return The path to the thumb's image file.
  */
@@ -270,15 +294,15 @@ FilmState::thumb_base_for_frame (int n) const
 int
 FilmState::thumb_frame (int n) const
 {
-       assert (n < int (thumbs.size ()));
-       return thumbs[n];
+       assert (n < int (_thumbs.size ()));
+       return _thumbs[n];
 }
 
 Size
 FilmState::cropped_size (Size s) const
 {
-       s.width -= crop.left + crop.right;
-       s.height -= crop.top + crop.bottom;
+       s.width -= _crop.left + _crop.right;
+       s.height -= _crop.top + _crop.bottom;
        return s;
 }
 
@@ -289,7 +313,7 @@ string
 FilmState::dir (string d) const
 {
        filesystem::path p;
-       p /= directory;
+       p /= _directory;
        p /= d;
        filesystem::create_directories (p);
        return p.string ();
@@ -300,28 +324,31 @@ string
 FilmState::file (string f) const
 {
        filesystem::path p;
-       p /= directory;
+       p /= _directory;
        p /= f;
        return p.string ();
 }
 
+/** @return full path of the content (actual video) file
+ *  of the Film.
+ */
 string
 FilmState::content_path () const
 {
-       if (filesystem::path(content).has_root_directory ()) {
-               return content;
+       if (filesystem::path(_content).has_root_directory ()) {
+               return _content;
        }
 
-       return file (content);
+       return file (_content);
 }
 
 ContentType
 FilmState::content_type () const
 {
 #if BOOST_FILESYSTEM_VERSION == 3
-       string ext = filesystem::path(content).extension().string();
+       string ext = filesystem::path(_content).extension().string();
 #else
-       string ext = filesystem::path(content).extension();
+       string ext = filesystem::path(_content).extension();
 #endif
 
        transform (ext.begin(), ext.end(), ext.begin(), ::tolower);
@@ -337,7 +364,7 @@ FilmState::content_type () const
 int
 FilmState::bytes_per_sample () const
 {
-       switch (audio_sample_format) {
+       switch (_audio_sample_format) {
        case AV_SAMPLE_FMT_S16:
                return 2;
        default:
@@ -351,13 +378,13 @@ int
 FilmState::target_sample_rate () const
 {
        /* Resample to a DCI-approved sample rate */
-       double t = dcp_audio_sample_rate (audio_sample_rate);
+       double t = dcp_audio_sample_rate (_audio_sample_rate);
 
        /* Compensate for the fact that video will be rounded to the
           nearest integer number of frames per second.
        */
-       if (rint (frames_per_second) != frames_per_second) {
-               t *= frames_per_second / rint (frames_per_second);
+       if (rint (_frames_per_second) != _frames_per_second) {
+               t *= _frames_per_second / rint (_frames_per_second);
        }
 
        return rint (t);
@@ -366,11 +393,11 @@ FilmState::target_sample_rate () const
 int
 FilmState::dcp_length () const
 {
-       if (dcp_frames) {
-               return dcp_frames;
+       if (_dcp_frames) {
+               return _dcp_frames;
        }
 
-       return length;
+       return _length;
 }
 
 /** @return a DCI-compliant name for a DCP of this film */
@@ -379,7 +406,7 @@ FilmState::dci_name () const
 {
        stringstream d;
 
-       string fixed_name = to_upper_copy (name);
+       string fixed_name = to_upper_copy (_name);
        for (size_t i = 0; i < fixed_name.length(); ++i) {
                if (fixed_name[i] == ' ') {
                        fixed_name[i] = '-';
@@ -393,18 +420,18 @@ FilmState::dci_name () const
 
        d << fixed_name << "_";
 
-       if (dcp_content_type) {
-               d << dcp_content_type->dci_name() << "_";
+       if (_dcp_content_type) {
+               d << _dcp_content_type->dci_name() << "_";
        }
 
-       if (format) {
-               d << format->dci_name() << "_";
+       if (_format) {
+               d << _format->dci_name() << "_";
        }
 
-       if (!audio_language.empty ()) {
-               d << audio_language;
-               if (!subtitle_language.empty() && with_subtitles) {
-                       d << "-" << subtitle_language;
+       if (!_audio_language.empty ()) {
+               d << _audio_language;
+               if (!_subtitle_language.empty() && _with_subtitles) {
+                       d << "-" << _subtitle_language;
                } else {
                        d << "-XX";
                }
@@ -412,15 +439,15 @@ FilmState::dci_name () const
                d << "_";
        }
 
-       if (!territory.empty ()) {
-               d << territory;
-               if (!rating.empty ()) {
-                       d << "-" << rating;
+       if (!_territory.empty ()) {
+               d << _territory;
+               if (!_rating.empty ()) {
+                       d << "-" << _rating;
                }
                d << "_";
        }
 
-       switch (audio_channels) {
+       switch (_audio_channels) {
        case 1:
                d << "10_";
                break;
@@ -434,19 +461,19 @@ FilmState::dci_name () const
 
        d << "2K_";
 
-       if (!studio.empty ()) {
-               d << studio << "_";
+       if (!_studio.empty ()) {
+               d << _studio << "_";
        }
 
        gregorian::date today = gregorian::day_clock::local_day ();
        d << gregorian::to_iso_string (today) << "_";
 
-       if (!facility.empty ()) {
-               d << facility << "_";
+       if (!_facility.empty ()) {
+               d << _facility << "_";
        }
 
-       if (!package_type.empty ()) {
-               d << package_type;
+       if (!_package_type.empty ()) {
+               d << _package_type;
        }
 
        return d.str ();
@@ -456,11 +483,382 @@ FilmState::dci_name () const
 string
 FilmState::dcp_name () const
 {
-       if (use_dci_name) {
+       if (_use_dci_name) {
                return dci_name ();
        }
 
-       return name;
+       return _name;
+}
+
+
+void
+FilmState::set_directory (string d)
+{
+       _directory = d;
+       _dirty = true;
+}
+
+void
+FilmState::set_name (string n)
+{
+       _name = n;
+       signal_changed (NAME);
+}
+
+void
+FilmState::set_use_dci_name (bool u)
+{
+       _use_dci_name = u;
+       signal_changed (USE_DCI_NAME);
 }
 
+void
+FilmState::set_content (string c)
+{
+       string check = _directory;
+
+#if BOOST_FILESYSTEM_VERSION == 3
+       filesystem::path slash ("/");
+       string platform_slash = slash.make_preferred().string ();
+#else
+#ifdef DVDOMATIC_WINDOWS
+       string platform_slash = "\\";
+#else
+       string platform_slash = "/";
+#endif
+#endif 
+
+       if (!ends_with (check, platform_slash)) {
+               check += platform_slash;
+       }
+       
+       if (filesystem::path(c).has_root_directory () && starts_with (c, check)) {
+               c = c.substr (_directory.length() + 1);
+       }
+
+       if (c == _content) {
+               return;
+       }
+
+       /* Create a temporary decoder so that we can get information
+          about the content.
+       */
+
+       shared_ptr<FilmState> s = state_copy ();
+       s->_content = c;
+       shared_ptr<Options> o (new Options ("", "", ""));
+       o->out_size = Size (1024, 1024);
+       
+       shared_ptr<Decoder> d = decoder_factory (s, o, 0, 0);
+       
+       set_size (d->native_size ());
+       set_length (d->length_in_frames ());
+       set_frames_per_second (d->frames_per_second ());
+       set_audio_channels (d->audio_channels ());
+       set_audio_sample_rate (d->audio_sample_rate ());
+       set_audio_sample_format (d->audio_sample_format ());
+       set_has_subtitles (d->has_subtitles ());
+       set_audio_streams (d->audio_streams ());
+       set_subtitle_streams (d->subtitle_streams ());
+       set_audio_stream (audio_streams().empty() ? -1 : audio_streams().front().id);
+       set_subtitle_stream (subtitle_streams().empty() ? -1 : subtitle_streams().front().id);
+       set_content_digest (md5_digest (content_path ()));
+       
+       _content = c;
+       signal_changed (CONTENT);
+}
               
+void
+FilmState::set_dcp_content_type (DCPContentType const * t)
+{
+       _dcp_content_type = t;
+       signal_changed (DCP_CONTENT_TYPE);
+}
+
+void
+FilmState::set_format (Format const * f)
+{
+       _format = f;
+       signal_changed (FORMAT);
+}
+
+void
+FilmState::set_crop (Crop c)
+{
+       _crop = c;
+       signal_changed (CROP);
+}
+
+void
+FilmState::set_left_crop (int c)
+{
+       if (_crop.left == c) {
+               return;
+       }
+
+       _crop.left = c;
+       signal_changed (CROP);
+}
+
+void
+FilmState::set_right_crop (int c)
+{
+       if (_crop.right == c) {
+               return;
+       }
+
+       _crop.right = c;
+       signal_changed (CROP);
+}
+
+void
+FilmState::set_top_crop (int c)
+{
+       if (_crop.top == c) {
+               return;
+       }
+
+       _crop.top = c;
+       signal_changed (CROP);
+}
+
+void
+FilmState::set_bottom_crop (int c)
+{
+       if (_crop.bottom == c) {
+               return;
+       }
+
+       _crop.bottom = c;
+       signal_changed (CROP);
+}
+
+void
+FilmState::set_filters (vector<Filter const *> f)
+{
+       _filters = f;
+       signal_changed (FILTERS);
+}
+
+void
+FilmState::set_scaler (Scaler const * s)
+{
+       _scaler = s;
+       signal_changed (SCALER);
+}
+
+void
+FilmState::set_dcp_frames (int f)
+{
+       _dcp_frames = f;
+       signal_changed (DCP_FRAMES);
+}
+
+void
+FilmState::set_dcp_trim_action (TrimAction a)
+{
+       _dcp_trim_action = a;
+       signal_changed (DCP_TRIM_ACTION);
+}
+
+void
+FilmState::set_dcp_ab (bool a)
+{
+       _dcp_ab = a;
+       signal_changed (DCP_AB);
+}
+
+void
+FilmState::set_audio_stream (int s)
+{
+       _audio_stream = s;
+       signal_changed (AUDIO_STREAM);
+}
+
+void
+FilmState::set_audio_gain (float g)
+{
+       _audio_gain = g;
+       signal_changed (AUDIO_GAIN);
+}
+
+void
+FilmState::set_audio_delay (int d)
+{
+       _audio_delay = d;
+       signal_changed (AUDIO_DELAY);
+}
+
+void
+FilmState::set_still_duration (int d)
+{
+       _still_duration = d;
+       signal_changed (STILL_DURATION);
+}
+
+void
+FilmState::set_subtitle_stream (int s)
+{
+       _subtitle_stream = s;
+       signal_changed (SUBTITLE_STREAM);
+}
+
+void
+FilmState::set_with_subtitles (bool w)
+{
+       _with_subtitles = w;
+       signal_changed (WITH_SUBTITLES);
+}
+
+void
+FilmState::set_subtitle_offset (int o)
+{
+       _subtitle_offset = o;
+       signal_changed (SUBTITLE_OFFSET);
+}
+
+void
+FilmState::set_subtitle_scale (float s)
+{
+       _subtitle_scale = s;
+       signal_changed (SUBTITLE_SCALE);
+}
+
+void
+FilmState::set_audio_language (string l)
+{
+       _audio_language = l;
+       signal_changed (DCI_METADATA);
+}
+
+void
+FilmState::set_subtitle_language (string l)
+{
+       _subtitle_language = l;
+       signal_changed (DCI_METADATA);
+}
+
+void
+FilmState::set_territory (string t)
+{
+       _territory = t;
+       signal_changed (DCI_METADATA);
+}
+
+void
+FilmState::set_rating (string r)
+{
+       _rating = r;
+       signal_changed (DCI_METADATA);
+}
+
+void
+FilmState::set_studio (string s)
+{
+       _studio = s;
+       signal_changed (DCI_METADATA);
+}
+
+void
+FilmState::set_facility (string f)
+{
+       _facility = f;
+       signal_changed (DCI_METADATA);
+}
+
+void
+FilmState::set_package_type (string p)
+{
+       _package_type = p;
+       signal_changed (DCI_METADATA);
+}
+
+void
+FilmState::set_thumbs (vector<int> t)
+{
+       _thumbs = t;
+       signal_changed (THUMBS);
+}
+
+void
+FilmState::set_size (Size s)
+{
+       _size = s;
+       signal_changed (SIZE);
+}
+
+void
+FilmState::set_length (int l)
+{
+       _length = l;
+       signal_changed (LENGTH);
+}
+
+void
+FilmState::set_audio_channels (int c)
+{
+       _audio_channels = c;
+       signal_changed (AUDIO_CHANNELS);
+}
+
+void
+FilmState::set_audio_sample_rate (int r)
+{
+       _audio_sample_rate = r;
+       signal_changed (AUDIO_SAMPLE_RATE);
+}
+
+void
+FilmState::set_audio_sample_format (AVSampleFormat f)
+{
+       _audio_sample_format = f;
+       _dirty = true;
+}
+
+void
+FilmState::set_content_digest (string d)
+{
+       _content_digest = d;
+       _dirty = true;
+}
+
+void
+FilmState::set_has_subtitles (bool s)
+{
+       _has_subtitles = s;
+       signal_changed (HAS_SUBTITLES);
+}
+
+void
+FilmState::set_audio_streams (vector<Stream> s)
+{
+       _audio_streams = s;
+       _dirty = true;
+}
+
+void
+FilmState::set_subtitle_streams (vector<Stream> s)
+{
+       _subtitle_streams = s;
+       _dirty = true;
+}
+
+void
+FilmState::set_frames_per_second (float f)
+{
+       _frames_per_second = f;
+       signal_changed (FRAMES_PER_SECOND);
+}
+       
+void
+FilmState::signal_changed (Property p)
+{
+       _dirty = true;
+       Changed (p);
+}
+
+shared_ptr<FilmState>
+FilmState::state_copy () const
+{
+       return shared_ptr<FilmState> (new FilmState (*this));
+}
index 38176f22405456f328e62b89f8ba884a025119d8..2a00c844a01ab08fe52eebdb2b9a797331082c3b 100644 (file)
@@ -27,6 +27,7 @@
 #ifndef DVDOMATIC_FILM_STATE_H
 #define DVDOMATIC_FILM_STATE_H
 
+#include <sigc++/signal.h>
 extern "C" {
 #include <libavcodec/avcodec.h>
 #include <libswscale/swscale.h>
@@ -53,27 +54,28 @@ class FilmState
 {
 public:
        FilmState ()
-               : use_dci_name (false)
-               , dcp_content_type (0)
-               , frames_per_second (0)
-               , format (0)
-               , scaler (Scaler::from_id ("bicubic"))
-               , dcp_frames (0)
-               , dcp_trim_action (CUT)
-               , dcp_ab (false)
-               , audio_stream (-1)
-               , audio_gain (0)
-               , audio_delay (0)
-               , still_duration (10)
-               , subtitle_stream (-1)
-               , with_subtitles (false)
-               , subtitle_offset (0)
-               , subtitle_scale (1)
-               , length (0)
-               , audio_channels (0)
-               , audio_sample_rate (0)
-               , audio_sample_format (AV_SAMPLE_FMT_NONE)
-               , has_subtitles (false)
+               : _use_dci_name (false)
+               , _dcp_content_type (0)
+               , _format (0)
+               , _scaler (Scaler::from_id ("bicubic"))
+               , _dcp_frames (0)
+               , _dcp_trim_action (CUT)
+               , _dcp_ab (false)
+               , _audio_stream (-1)
+               , _audio_gain (0)
+               , _audio_delay (0)
+               , _still_duration (10)
+               , _subtitle_stream (-1)
+               , _with_subtitles (false)
+               , _subtitle_offset (0)
+               , _subtitle_scale (1)
+               , _length (0)
+               , _audio_channels (0)
+               , _audio_sample_rate (0)
+               , _audio_sample_format (AV_SAMPLE_FMT_NONE)
+               , _has_subtitles (false)
+               , _frames_per_second (0)
+               , _dirty (false)
        {}
 
        std::string file (std::string f) const;
@@ -91,8 +93,8 @@ public:
        int bytes_per_sample () const;
        int target_sample_rate () const;
        
-       void write_metadata (std::ofstream &) const;
-       void read_metadata (std::string, std::string);
+       void write_metadata () const;
+       void read_metadata ();
 
        Size cropped_size (Size) const;
        int dcp_length () const;
@@ -100,89 +102,342 @@ public:
 
        std::string dcp_name () const;
 
+       boost::shared_ptr<FilmState> state_copy () const;
+
+       bool dirty () const {
+               return _dirty;
+       }
+
+       enum Property {
+               NONE,
+               NAME,
+               USE_DCI_NAME,
+               CONTENT,
+               DCP_CONTENT_TYPE,
+               FORMAT,
+               CROP,
+               FILTERS,
+               SCALER,
+               DCP_FRAMES,
+               DCP_TRIM_ACTION,
+               DCP_AB,
+               AUDIO_STREAM,
+               AUDIO_GAIN,
+               AUDIO_DELAY,
+               STILL_DURATION,
+               SUBTITLE_STREAM,
+               WITH_SUBTITLES,
+               SUBTITLE_OFFSET,
+               SUBTITLE_SCALE,
+               DCI_METADATA,
+               THUMBS,
+               SIZE,
+               LENGTH,
+               AUDIO_CHANNELS,
+               AUDIO_SAMPLE_RATE,
+               HAS_SUBTITLES,
+               AUDIO_STREAMS,
+               SUBTITLE_STREAMS,
+               FRAMES_PER_SECOND,
+       };
+
+
+       /* GET */
+
+       std::string directory () const {
+               return _directory;
+       }
+
+       std::string name () const {
+               return _name;
+       }
+
+       bool use_dci_name () const {
+               return _use_dci_name;
+       }
+
+       std::string content () const {
+               return _content;
+       }
+
+       DCPContentType const * dcp_content_type () const {
+               return _dcp_content_type;
+       }
+
+       Format const * format () const {
+               return _format;
+       }
+
+       Crop crop () const {
+               return _crop;
+       }
+
+       std::vector<Filter const *> filters () const {
+               return _filters;
+       }
+
+       Scaler const * scaler () const {
+               return _scaler;
+       }
+
+       int dcp_frames () const {
+               return _dcp_frames;
+       }
+
+       TrimAction dcp_trim_action () const {
+               return _dcp_trim_action;
+       }
+
+       bool dcp_ab () const {
+               return _dcp_ab;
+       }
+
+       int audio_stream () const {
+               return _audio_stream;
+       }
+
+       float audio_gain () const {
+               return _audio_gain;
+       }
+
+       int audio_delay () const {
+               return _audio_delay;
+       }
+
+       int still_duration () const {
+               return _still_duration;
+       }
+
+       int subtitle_stream () const {
+               return _subtitle_stream;
+       }
+
+       bool with_subtitles () const {
+               return _with_subtitles;
+       }
+
+       int subtitle_offset () const {
+               return _subtitle_offset;
+       }
+
+       float subtitle_scale () const {
+               return _subtitle_scale;
+       }
+
+       std::string audio_language () const {
+               return _audio_language;
+       }
+       
+       std::string subtitle_language () const {
+               return _subtitle_language;
+       }
+       
+       std::string territory () const {
+               return _territory;
+       }
+       
+       std::string rating () const {
+               return _rating;
+       }
+       
+       std::string studio () const {
+               return _studio;
+       }
+       
+       std::string facility () const {
+               return _facility;
+       }
+       
+       std::string package_type () const {
+               return _package_type;
+       }
+
+       std::vector<int> thumbs () const {
+               return _thumbs;
+       }
+       
+       Size size () const {
+               return _size;
+       }
+
+       int length () const {
+               return _length;
+       }
+
+       int audio_channels () const {
+               return _audio_channels;
+       }
+       
+       int audio_sample_rate () const {
+               return _audio_sample_rate;
+       }
+       
+       AVSampleFormat audio_sample_format () const {
+               return _audio_sample_format;
+       }
+       
+       std::string content_digest () const {
+               return _content_digest;
+       }
+       
+       bool has_subtitles () const {
+               return _has_subtitles;
+       }
+
+       std::vector<Stream> audio_streams () const {
+               return _audio_streams;
+       }
+
+       std::vector<Stream> subtitle_streams () const {
+               return _subtitle_streams;
+       }
+       
+       float frames_per_second () const {
+               return _frames_per_second;
+       }
+
+       
+       /* SET */
+
+       void set_directory (std::string);
+       void set_name (std::string);
+       void set_use_dci_name (bool);
+       void set_content (std::string);
+       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_filters (std::vector<Filter const *>);
+       void set_scaler (Scaler const *);
+       void set_dcp_frames (int);
+       void set_dcp_trim_action (TrimAction);
+       void set_dcp_ab (bool);
+       void set_audio_stream (int);
+       void set_audio_gain (float);
+       void set_audio_delay (int);
+       void set_still_duration (int);
+       void set_subtitle_stream (int);
+       void set_with_subtitles (bool);
+       void set_subtitle_offset (int);
+       void set_subtitle_scale (float);
+       void set_audio_language (std::string);
+       void set_subtitle_language (std::string);
+       void set_territory (std::string);
+       void set_rating (std::string);
+       void set_studio (std::string);
+       void set_facility (std::string);
+       void set_package_type (std::string);
+       void set_thumbs (std::vector<int>);
+       void set_size (Size);
+       void set_length (int);
+       void set_audio_channels (int);
+       void set_audio_sample_rate (int);
+       void set_audio_sample_format (AVSampleFormat);
+       void set_content_digest (std::string);
+       void set_has_subtitles (bool);
+       void set_audio_streams (std::vector<Stream>);
+       void set_subtitle_streams (std::vector<Stream>);
+       void set_frames_per_second (float);
+
+       /** Emitted when some property has changed */
+       mutable sigc::signal1<void, Property> Changed;
+       
+private:       
+
+       std::string thumb_file_for_frame (int) const;
+       std::string thumb_base_for_frame (int) const;
+       void signal_changed (Property);
+       
        /** Complete path to directory containing the film metadata;
-           must not be relative.
-       */
-       std::string directory;
+        *  must not be relative.
+        */
+       std::string _directory;
        /** Name for DVD-o-matic */
-       std::string name;
+       std::string _name;
        /** True if a auto-generated DCI-compliant name should be used for our DCP */
-       bool use_dci_name;
+       bool _use_dci_name;
        /** File or directory containing content; may be relative to our directory
         *  or an absolute path.
         */
-       std::string content;
+       std::string _content;
        /** The type of content that this Film represents (feature, trailer etc.) */
-       DCPContentType const * dcp_content_type;
-       /** Frames per second of the source */
-       float frames_per_second;
+       DCPContentType const * _dcp_content_type;
        /** The format to present this Film in (flat, scope, etc.) */
-       Format const * format;
-       Crop crop;
+       Format const * _format;
+       /** The crop to apply to the source */
+       Crop _crop;
        /** Video filters that should be used when generating DCPs */
-       std::vector<Filter const *> filters;
+       std::vector<Filter const *> _filters;
        /** Scaler algorithm to use */
-       Scaler const * scaler;
+       Scaler const * _scaler;
        /** Number of frames to put in the DCP, or 0 for all */
-       int dcp_frames;
-
-       TrimAction dcp_trim_action;
-               
+       int _dcp_frames;
+       /** What to do with audio when trimming DCPs */
+       TrimAction _dcp_trim_action;
        /** true to create an A/B comparison DCP, where the left half of the image
            is the video without any filters or post-processing, and the right half
            has the specified filters and post-processing.
        */
-       bool dcp_ab;
-       int audio_stream;
+       bool _dcp_ab;
+       /** The decoder's stream ID to use for audio, or -1 if there is none */
+       int _audio_stream;
        /** Gain to apply to audio in dB */
-       float audio_gain;
+       float _audio_gain;
        /** Delay to apply to audio (positive moves audio later) in milliseconds */
-       int audio_delay;
+       int _audio_delay;
        /** Duration to make still-sourced films (in seconds) */
-       int still_duration;
-       int subtitle_stream;
+       int _still_duration;
+       /** The decoder's stream ID to use for subtitles, or -1 if there are none */
+       int _subtitle_stream;
        /** True if subtitles should be shown for this film */
-       bool with_subtitles;
+       bool _with_subtitles;
        /** y offset for placing subtitles, in source pixels; +ve is further down
            the frame, -ve is further up.
        */
-       int subtitle_offset;
+       int _subtitle_offset;
        /** scale factor to apply to subtitles */
-       float subtitle_scale;
+       float _subtitle_scale;
 
        /* DCI naming stuff */
-       std::string audio_language;
-       std::string subtitle_language;
-       std::string territory;
-       std::string rating;
-       std::string studio;
-       std::string facility;
-       std::string package_type;
+       std::string _audio_language;
+       std::string _subtitle_language;
+       std::string _territory;
+       std::string _rating;
+       std::string _studio;
+       std::string _facility;
+       std::string _package_type;
 
-       /* Data which is cached to speed things up */
+       /* Data which are cached to speed things up */
 
        /** Vector of frame indices for each of our `thumbnails' */
-       std::vector<int> thumbs;
+       std::vector<int> _thumbs;
        /** Size, in pixels, of the source (ignoring cropping) */
-       Size size;
+       Size _size;
        /** Length of the source in frames */
-       int length;
+       int _length;
        /** Number of audio channels */
-       int audio_channels;
+       int _audio_channels;
        /** Sample rate of the source audio, in Hz */
-       int audio_sample_rate;
+       int _audio_sample_rate;
        /** Format of the audio samples */
-       AVSampleFormat audio_sample_format;
+       AVSampleFormat _audio_sample_format;
        /** MD5 digest of our content file */
-       std::string content_digest;
+       std::string _content_digest;
        /** true if the source has subtitles */
-       bool has_subtitles;
-       std::vector<Stream> audio_streams;
-       std::vector<Stream> subtitle_streams;
+       bool _has_subtitles;
+       /** the audio streams that the source has */
+       std::vector<Stream> _audio_streams;
+       /** the subtitle streams that the source has */
+       std::vector<Stream> _subtitle_streams;
+       /** Frames per second of the source */
+       float _frames_per_second;
 
-private:
-       std::string thumb_file_for_frame (int) const;
-       std::string thumb_base_for_frame (int) const;
+       mutable bool _dirty;
+
+       friend class paths_test;
 };
 
 #endif
index 2518f8dc5885987cfe74505c884eca3096c11b67..082fd13e8dd52f4b77f02b9752289d4ed6145173 100644 (file)
@@ -53,7 +53,7 @@ ImageMagickEncoder::ImageMagickEncoder (shared_ptr<const FilmState> s, shared_pt
 void
 ImageMagickEncoder::process_video (shared_ptr<Image> image, int frame, shared_ptr<Subtitle> sub)
 {
-       shared_ptr<Image> scaled = image->scale_and_convert_to_rgb (_opt->out_size, _opt->padding, _fs->scaler);
+       shared_ptr<Image> scaled = image->scale_and_convert_to_rgb (_opt->out_size, _opt->padding, _fs->scaler());
        shared_ptr<Image> compact (new CompactImage (scaled));
 
        string tmp_file = _opt->frame_out_path (frame, true);
@@ -63,8 +63,8 @@ ImageMagickEncoder::process_video (shared_ptr<Image> image, int frame, shared_pt
        filesystem::rename (tmp_file, _opt->frame_out_path (frame, false));
 
        if (sub) {
-               float const x_scale = float (_opt->out_size.width) / _fs->size.width;
-               float const y_scale = float (_opt->out_size.height) / _fs->size.height;
+               float const x_scale = float (_opt->out_size.width) / _fs->size().width;
+               float const y_scale = float (_opt->out_size.height) / _fs->size().height;
 
                string tmp_metadata_file = _opt->frame_out_path (frame, false, ".sub");
                ofstream metadata (tmp_metadata_file.c_str ());
@@ -72,7 +72,7 @@ ImageMagickEncoder::process_video (shared_ptr<Image> image, int frame, shared_pt
                Size new_size = sub->image()->size ();
                new_size.width *= x_scale;
                new_size.height *= y_scale;
-               shared_ptr<Image> scaled = sub->image()->scale (new_size, _fs->scaler);
+               shared_ptr<Image> scaled = sub->image()->scale (new_size, _fs->scaler());
                shared_ptr<Image> compact (new CompactImage (scaled));
                
                string tmp_sub_file = _opt->frame_out_path (frame, true, ".sub.png");
index 2b8aca6492554398427d734a41f3c31c88518675..de2f1eb468ac9a194e29abf0c9191a122e37b1c7 100644 (file)
@@ -50,9 +50,9 @@ J2KStillEncoder::J2KStillEncoder (shared_ptr<const FilmState> s, shared_ptr<cons
 void
 J2KStillEncoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Subtitle> sub)
 {
-       pair<string, string> const s = Filter::ffmpeg_strings (_fs->filters);
+       pair<string, string> const s = Filter::ffmpeg_strings (_fs->filters());
        DCPVideoFrame* f = new DCPVideoFrame (
-               yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset, _fs->subtitle_scale, _fs->scaler, 0, _fs->frames_per_second, s.second,
+               yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset(), _fs->subtitle_scale(), _fs->scaler(), 0, _fs->frames_per_second(), s.second,
                Config::instance()->colour_lut_index(), Config::instance()->j2k_bandwidth(),
                _log
                );
@@ -63,7 +63,7 @@ J2KStillEncoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Sub
        }
 
        string const real = _opt->frame_out_path (0, false);
-       for (int i = 1; i < (_fs->still_duration * ImageMagickDecoder::static_frames_per_second()); ++i) {
+       for (int i = 1; i < (_fs->still_duration() * ImageMagickDecoder::static_frames_per_second()); ++i) {
                if (!boost::filesystem::exists (_opt->frame_out_path (i, false))) {
                        string const link = _opt->frame_out_path (i, false);
 #ifdef DVDOMATIC_POSIX                 
index e2a3a5ed7193dd3b591d0785999aff4c48dfacad..e5d120ad66795d89d725a6101f093be135ae0945 100644 (file)
@@ -56,9 +56,9 @@ J2KWAVEncoder::J2KWAVEncoder (shared_ptr<const FilmState> s, shared_ptr<const Op
        /* Create sound output files with .tmp suffixes; we will rename
           them if and when we complete.
        */
-       for (int i = 0; i < _fs->audio_channels; ++i) {
+       for (int i = 0; i < _fs->audio_channels(); ++i) {
                SF_INFO sf_info;
-               sf_info.samplerate = dcp_audio_sample_rate (_fs->audio_sample_rate);
+               sf_info.samplerate = dcp_audio_sample_rate (_fs->audio_sample_rate());
                /* We write mono files */
                sf_info.channels = 1;
                sf_info.format = SF_FORMAT_WAV | SF_FORMAT_PCM_24;
@@ -122,12 +122,12 @@ J2KWAVEncoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Subti
 
        /* Only do the processing if we don't already have a file for this frame */
        if (!boost::filesystem::exists (_opt->frame_out_path (frame, false))) {
-               pair<string, string> const s = Filter::ffmpeg_strings (_fs->filters);
+               pair<string, string> const s = Filter::ffmpeg_strings (_fs->filters());
                TIMING ("adding to queue of %1", _queue.size ());
                _queue.push_back (boost::shared_ptr<DCPVideoFrame> (
                                          new DCPVideoFrame (
-                                                 yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset, _fs->subtitle_scale,
-                                                 _fs->scaler, frame, _fs->frames_per_second, s.second,
+                                                 yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset(), _fs->subtitle_scale(),
+                                                 _fs->scaler(), frame, _fs->frames_per_second(), s.second,
                                                  Config::instance()->colour_lut_index (), Config::instance()->j2k_bandwidth (),
                                                  _log
                                                  )
@@ -224,11 +224,11 @@ J2KWAVEncoder::encoder_thread (ServerDescription* server)
 void
 J2KWAVEncoder::process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format)
 {
-       if (_fs->audio_sample_rate != _fs->target_sample_rate ()) {
+       if (_fs->audio_sample_rate() != _fs->target_sample_rate()) {
 #ifdef HAVE_SWRESAMPLE
 
                stringstream s;
-               s << "Will resample audio from " << _fs->audio_sample_rate << " to " << _fs->target_sample_rate();
+               s << "Will resample audio from " << _fs->audio_sample_rate() << " to " << _fs->target_sample_rate();
                _log->log (s.str ());
                
                _swr_context = swr_alloc_set_opts (
@@ -238,7 +238,7 @@ J2KWAVEncoder::process_begin (int64_t audio_channel_layout, AVSampleFormat audio
                        _fs->target_sample_rate(),
                        audio_channel_layout,
                        audio_sample_format,
-                       _fs->audio_sample_rate,
+                       _fs->audio_sample_rate(),
                        0, 0
                        );
                
@@ -309,7 +309,7 @@ J2KWAVEncoder::process_end ()
        if (_swr_context) {
 
                while (1) {
-                       uint8_t buffer[256 * _fs->bytes_per_sample() * _fs->audio_channels];
+                       uint8_t buffer[256 * _fs->bytes_per_sample() * _fs->audio_channels()];
                        uint8_t* out[2] = {
                                buffer,
                                0
@@ -325,7 +325,7 @@ J2KWAVEncoder::process_end ()
                                break;
                        }
 
-                       write_audio (buffer, frames * _fs->bytes_per_sample() * _fs->audio_channels);
+                       write_audio (buffer, frames * _fs->bytes_per_sample() * _fs->audio_channels());
                }
 
                swr_free (&_swr_context);
@@ -335,7 +335,7 @@ J2KWAVEncoder::process_end ()
        close_sound_files ();
 
        /* Rename .wav.tmp files to .wav */
-       for (int i = 0; i < _fs->audio_channels; ++i) {
+       for (int i = 0; i < _fs->audio_channels(); ++i) {
                if (boost::filesystem::exists (_opt->multichannel_audio_out_path (i, false))) {
                        boost::filesystem::remove (_opt->multichannel_audio_out_path (i, false));
                }
@@ -366,11 +366,11 @@ J2KWAVEncoder::process_audio (uint8_t* data, int size)
                /* And here's frames (where 1 frame is a collection of samples, 1 for each channel,
                   so for 5.1 a frame would be 6 samples)
                */
-               int const frames = samples / _fs->audio_channels;
+               int const frames = samples / _fs->audio_channels();
 
                /* Compute the resampled frame count and add 32 for luck */
-               int const out_buffer_size_frames = ceil (frames * _fs->target_sample_rate() / _fs->audio_sample_rate) + 32;
-               int const out_buffer_size_bytes = out_buffer_size_frames * _fs->audio_channels * _fs->bytes_per_sample();
+               int const out_buffer_size_frames = ceil (frames * _fs->target_sample_rate() / _fs->audio_sample_rate()) + 32;
+               int const out_buffer_size_bytes = out_buffer_size_frames * _fs->audio_channels() * _fs->bytes_per_sample();
                out_buffer = new uint8_t[out_buffer_size_bytes];
 
                uint8_t* out[2] = {
@@ -386,7 +386,7 @@ J2KWAVEncoder::process_audio (uint8_t* data, int size)
 
                /* And point our variables at the resampled audio */
                data = out_buffer;
-               size = out_frames * _fs->audio_channels * _fs->bytes_per_sample();
+               size = out_frames * _fs->audio_channels() * _fs->bytes_per_sample();
        }
 #endif
 
@@ -403,7 +403,7 @@ J2KWAVEncoder::write_audio (uint8_t* data, int size)
           of the sample size and that size is a multiple of _fs->audio_channels * sample_size.
        */
 
-       assert ((size % (_fs->audio_channels * _fs->bytes_per_sample())) == 0);
+       assert ((size % (_fs->audio_channels() * _fs->bytes_per_sample())) == 0);
        assert ((_deinterleave_buffer_size % _fs->bytes_per_sample()) == 0);
        
        /* XXX: this code is very tricksy and it must be possible to make it simpler ... */
@@ -414,17 +414,17 @@ J2KWAVEncoder::write_audio (uint8_t* data, int size)
        int position = 0;
        while (remaining > 0) {
                /* How many bytes of the deinterleaved data to do this time */
-               int this_time = min (remaining / _fs->audio_channels, _deinterleave_buffer_size);
-               for (int i = 0; i < _fs->audio_channels; ++i) {
+               int this_time = min (remaining / _fs->audio_channels(), _deinterleave_buffer_size);
+               for (int i = 0; i < _fs->audio_channels(); ++i) {
                        for (int j = 0; j < this_time; j += _fs->bytes_per_sample()) {
                                for (int k = 0; k < _fs->bytes_per_sample(); ++k) {
                                        int const to = j + k;
-                                       int const from = position + (i * _fs->bytes_per_sample()) + (j * _fs->audio_channels) + k;
+                                       int const from = position + (i * _fs->bytes_per_sample()) + (j * _fs->audio_channels()) + k;
                                        _deinterleave_buffer[to] = data[from];
                                }
                        }
                        
-                       switch (_fs->audio_sample_format) {
+                       switch (_fs->audio_sample_format()) {
                        case AV_SAMPLE_FMT_S16:
                                sf_write_short (_sound_files[i], (const short *) _deinterleave_buffer, this_time / _fs->bytes_per_sample());
                                break;
@@ -434,7 +434,7 @@ J2KWAVEncoder::write_audio (uint8_t* data, int size)
                }
                
                position += this_time;
-               remaining -= this_time * _fs->audio_channels;
+               remaining -= this_time * _fs->audio_channels();
        }
 }
 
index efd35ba444389f3d175c79ca2f72b7f0e43bece3..83904fd60d2ce2d971132c8335e3e4acb06aad8a 100644 (file)
@@ -52,7 +52,7 @@ MakeDCPJob::MakeDCPJob (shared_ptr<const FilmState> s, shared_ptr<const Options>
 string
 MakeDCPJob::name () const
 {
-       return String::compose ("Make DCP for %1", _fs->name);
+       return String::compose ("Make DCP for %1", _fs->name());
 }
 
 string
@@ -78,17 +78,20 @@ MakeDCPJob::run ()
        int frames = 0;
        switch (_fs->content_type ()) {
        case VIDEO:
-               frames = _fs->dcp_frames ? _fs->dcp_frames : _fs->length;
+               frames = _fs->dcp_length ();
                break;
        case STILL:
-               frames = _fs->still_duration * ImageMagickDecoder::static_frames_per_second ();
+               frames = _fs->still_duration() * ImageMagickDecoder::static_frames_per_second ();
                break;
        }
        
        libdcp::DCP dcp (_fs->dir (_fs->dcp_name()));
        dcp.Progress.connect (sigc::mem_fun (*this, &MakeDCPJob::dcp_progress));
 
-       shared_ptr<libdcp::CPL> cpl (new libdcp::CPL (_fs->dir (_fs->dcp_name()), _fs->dcp_name(), _fs->dcp_content_type->libdcp_kind (), frames, rint (_fs->frames_per_second)));
+       shared_ptr<libdcp::CPL> cpl (
+               new libdcp::CPL (_fs->dir (_fs->dcp_name()), _fs->dcp_name(), _fs->dcp_content_type()->libdcp_kind (), frames, rint (_fs->frames_per_second()))
+               );
+       
        dcp.add_cpl (cpl);
 
        descend (0.9);
@@ -98,7 +101,7 @@ MakeDCPJob::run ()
                        _fs->dir (_fs->dcp_name()),
                        "video.mxf",
                        &dcp.Progress,
-                       rint (_fs->frames_per_second),
+                       rint (_fs->frames_per_second()),
                        frames,
                        _opt->out_size.width,
                        _opt->out_size.height
@@ -109,7 +112,7 @@ MakeDCPJob::run ()
 
        shared_ptr<libdcp::SoundAsset> sa;
 
-       if (_fs->audio_channels > 0) {
+       if (_fs->audio_channels() > 0) {
                descend (0.1);
                sa.reset (
                        new libdcp::SoundAsset (
@@ -117,9 +120,9 @@ MakeDCPJob::run ()
                                _fs->dir (_fs->dcp_name()),
                                "audio.mxf",
                                &dcp.Progress,
-                               rint (_fs->frames_per_second),
+                               rint (_fs->frames_per_second()),
                                frames,
-                               _fs->audio_channels
+                               _fs->audio_channels()
                                )
                        );
                ascend ();
index 16a8a7b016637bc0da96ad3481d74d4ed3071610..845dc967519c95cffa75eaab4f415e1469163735 100644 (file)
@@ -44,7 +44,7 @@ ThumbsJob::ThumbsJob (shared_ptr<const FilmState> s, shared_ptr<const Options> o
 string
 ThumbsJob::name () const
 {
-       return String::compose ("Update thumbs for %1", _fs->name);
+       return String::compose ("Update thumbs for %1", _fs->name());
 }
 
 void
index 101e0c047ee69cd575827fa8e856e71d33a86086..27dd0d69c79203dbceabe3ddfa5a67aed6e8ea60 100644 (file)
@@ -205,7 +205,7 @@ TIFFDecoder::pixel_format () const
 int
 TIFFDecoder::time_base_numerator () const
 {
-       return rint (_fs->frames_per_second);;
+       return rint (_fs->frames_per_second());
 }
 
 
index a53a4b6ad1591acb7227bbc578a37a3e014f613a..3776ea4c1d6404e11f5842daaea9baffc41f10ff 100644 (file)
@@ -48,7 +48,7 @@ TranscodeJob::TranscodeJob (shared_ptr<const FilmState> s, shared_ptr<const Opti
 string
 TranscodeJob::name () const
 {
-       return String::compose ("Transcode %1", _fs->name);
+       return String::compose ("Transcode %1", _fs->name());
 }
 
 void
index d3608059c3c0e47fde5c4a46d9f8d35fad1ca60c..51b45f2d60fb5ac495f9660030ec5c2363eba398 100644 (file)
@@ -143,7 +143,7 @@ main (int argc, char* argv[])
        film->log()->set_level ((Log::Level) log_level);
 
        cout << "\nMaking ";
-       if (film->dcp_ab ()) {
+       if (film->dcp_ab()) {
                cout << "A/B ";
        }
        cout << "DCP for " << film->name() << "\n";
index c687975bae07c660c2cb6ccd8ee563fd8911643a..7ed3a14c7770180ae7536d68e5ea1a4b66303506 100644 (file)
@@ -51,7 +51,7 @@ using namespace boost;
 /** @param f Film to edit */
 FilmEditor::FilmEditor (Film* f, wxWindow* parent)
        : wxPanel (parent)
-       , _ignore_changes (Film::NONE)
+       , _ignore_changes (FilmState::NONE)
        , _film (f)
 {
        _sizer = new wxFlexGridSizer (2, 4, 4);
@@ -273,9 +273,9 @@ FilmEditor::left_crop_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::CROP;
+       _ignore_changes = FilmState::CROP;
        _film->set_left_crop (_left_crop->GetValue ());
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 }
 
 /** Called when the right crop widget has been changed */
@@ -286,9 +286,9 @@ FilmEditor::right_crop_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::CROP;
+       _ignore_changes = FilmState::CROP;
        _film->set_right_crop (_right_crop->GetValue ());
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 }
 
 /** Called when the top crop widget has been changed */
@@ -299,9 +299,9 @@ FilmEditor::top_crop_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::CROP;
+       _ignore_changes = FilmState::CROP;
        _film->set_top_crop (_top_crop->GetValue ());
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 }
 
 /** Called when the bottom crop value has been changed */
@@ -312,9 +312,9 @@ FilmEditor::bottom_crop_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::CROP;
+       _ignore_changes = FilmState::CROP;
        _film->set_bottom_crop (_bottom_crop->GetValue ());
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 }
 
 /** Called when the content filename has been changed */
@@ -325,7 +325,7 @@ FilmEditor::content_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::CONTENT;
+       _ignore_changes = FilmState::CONTENT;
        
        try {
                _film->set_content (wx_to_std (_content->GetPath ()));
@@ -334,7 +334,7 @@ FilmEditor::content_changed (wxCommandEvent &)
                error_dialog (this, String::compose ("Could not set content: %1", e.what ()));
        }
 
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 
        setup_visibility ();
        setup_formats ();
@@ -350,9 +350,9 @@ FilmEditor::dcp_ab_toggled (wxCommandEvent &)
                return;
        }
        
-       _ignore_changes = Film::DCP_AB;
+       _ignore_changes = FilmState::DCP_AB;
        _film->set_dcp_ab (_dcp_ab->GetValue ());
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 }
 
 /** Called when the name widget has been changed */
@@ -363,9 +363,9 @@ FilmEditor::name_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::NAME;
+       _ignore_changes = FilmState::NAME;
        _film->set_name (string (_name->GetValue().mb_str()));
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 
        _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
 }
@@ -377,9 +377,9 @@ FilmEditor::subtitle_offset_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::SUBTITLE_OFFSET;
+       _ignore_changes = FilmState::SUBTITLE_OFFSET;
        _film->set_subtitle_offset (_subtitle_offset->GetValue ());
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 }
 
 void
@@ -389,9 +389,9 @@ FilmEditor::subtitle_scale_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::SUBTITLE_OFFSET;
+       _ignore_changes = FilmState::SUBTITLE_OFFSET;
        _film->set_subtitle_scale (_subtitle_scale->GetValue() / 100.0);
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 }
 
 
@@ -400,7 +400,7 @@ FilmEditor::subtitle_scale_changed (wxCommandEvent &)
  *  @param p Property of the Film that has changed.
  */
 void
-FilmEditor::film_changed (Film::Property p)
+FilmEditor::film_changed (FilmState::Property p)
 {
        if (!_film || _ignore_changes == p) {
                return;
@@ -409,16 +409,24 @@ FilmEditor::film_changed (Film::Property p)
        stringstream s;
                
        switch (p) {
-       case Film::NONE:
+       case FilmState::NONE:
                break;
-       case Film::CONTENT:
+       case FilmState::CONTENT:
                _content->SetPath (std_to_wx (_film->content ()));
                setup_visibility ();
                setup_formats ();
                setup_subtitle_button ();
                setup_streams ();
                break;
-       case Film::FORMAT:
+       case FilmState::HAS_SUBTITLES:
+               setup_subtitle_button ();
+               setup_streams ();
+               break;
+       case FilmState::AUDIO_STREAMS:
+       case FilmState::SUBTITLE_STREAMS:
+               setup_streams ();
+               break;
+       case FilmState::FORMAT:
        {
                int n = 0;
                vector<Format const *>::iterator i = _formats.begin ();
@@ -430,13 +438,13 @@ FilmEditor::film_changed (Film::Property p)
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
                break;
        }
-       case Film::CROP:
+       case FilmState::CROP:
                _left_crop->SetValue (_film->crop().left);
                _right_crop->SetValue (_film->crop().right);
                _top_crop->SetValue (_film->crop().top);
                _bottom_crop->SetValue (_film->crop().bottom);
                break;
-       case Film::FILTERS:
+       case FilmState::FILTERS:
        {
                pair<string, string> p = Filter::ffmpeg_strings (_film->filters ());
                if (p.first.empty () && p.second.empty ()) {
@@ -448,20 +456,20 @@ FilmEditor::film_changed (Film::Property p)
                _sizer->Layout ();
                break;
        }
-       case Film::NAME:
+       case FilmState::NAME:
                _name->ChangeValue (std_to_wx (_film->name ()));
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
                break;
-       case Film::FRAMES_PER_SECOND:
+       case FilmState::FRAMES_PER_SECOND:
        {
                stringstream s;
                s << fixed << setprecision(2) << _film->frames_per_second();
                _frames_per_second->SetLabel (std_to_wx (s.str ()));
                break;
        }
-       case Film::AUDIO_CHANNELS:
+       case FilmState::AUDIO_CHANNELS:
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
-       case Film::AUDIO_SAMPLE_RATE:
+       case FilmState::AUDIO_SAMPLE_RATE:
                if (_film->audio_channels() == 0 && _film->audio_sample_rate() == 0) {
                        _audio->SetLabel (wxT (""));
                } else {
@@ -469,7 +477,7 @@ FilmEditor::film_changed (Film::Property p)
                        _audio->SetLabel (std_to_wx (s.str ()));
                }
                break;
-       case Film::SIZE:
+       case FilmState::SIZE:
                if (_film->size().width == 0 && _film->size().height == 0) {
                        _original_size->SetLabel (wxT (""));
                } else {
@@ -477,7 +485,7 @@ FilmEditor::film_changed (Film::Property p)
                        _original_size->SetLabel (std_to_wx (s.str ()));
                }
                break;
-       case Film::LENGTH:
+       case FilmState::LENGTH:
                if (_film->frames_per_second() > 0 && _film->length() > 0) {
                        s << _film->length() << " frames; " << seconds_to_hms (_film->length() / _film->frames_per_second());
                } else if (_film->length() > 0) {
@@ -485,13 +493,13 @@ FilmEditor::film_changed (Film::Property p)
                } 
                _length->SetLabel (std_to_wx (s.str ()));
                break;
-       case Film::DCP_CONTENT_TYPE:
+       case FilmState::DCP_CONTENT_TYPE:
                _dcp_content_type->SetSelection (DCPContentType::as_index (_film->dcp_content_type ()));
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
                break;
-       case Film::THUMBS:
+       case FilmState::THUMBS:
                break;
-       case Film::DCP_FRAMES:
+       case FilmState::DCP_FRAMES:
                if (_film->dcp_frames() == 0) {
                        _dcp_range->SetLabel (wxT ("Whole film"));
                } else {
@@ -501,46 +509,46 @@ FilmEditor::film_changed (Film::Property p)
                }
                _sizer->Layout ();
                break;
-       case Film::DCP_TRIM_ACTION:
+       case FilmState::DCP_TRIM_ACTION:
                break;
-       case Film::DCP_AB:
+       case FilmState::DCP_AB:
                _dcp_ab->SetValue (_film->dcp_ab ());
                break;
-       case Film::SCALER:
+       case FilmState::SCALER:
                _scaler->SetSelection (Scaler::as_index (_film->scaler ()));
                break;
-       case Film::AUDIO_GAIN:
+       case FilmState::AUDIO_GAIN:
                _audio_gain->SetValue (_film->audio_gain ());
                break;
-       case Film::AUDIO_DELAY:
+       case FilmState::AUDIO_DELAY:
                _audio_delay->SetValue (_film->audio_delay ());
                break;
-       case Film::STILL_DURATION:
+       case FilmState::STILL_DURATION:
                _still_duration->SetValue (_film->still_duration ());
                break;
-       case Film::WITH_SUBTITLES:
+       case FilmState::WITH_SUBTITLES:
                _with_subtitles->SetValue (_film->with_subtitles ());
                _subtitle_scale->Enable (_film->with_subtitles ());
                _subtitle_offset->Enable (_film->with_subtitles ());
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
                break;
-       case Film::SUBTITLE_OFFSET:
+       case FilmState::SUBTITLE_OFFSET:
                _subtitle_offset->SetValue (_film->subtitle_offset ());
                break;
-       case Film::SUBTITLE_SCALE:
+       case FilmState::SUBTITLE_SCALE:
                _subtitle_scale->SetValue (_film->subtitle_scale() * 100);
                break;
-       case Film::USE_DCI_NAME:
+       case FilmState::USE_DCI_NAME:
                _use_dci_name->SetValue (_film->use_dci_name ());
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
                break;
-       case Film::DCI_METADATA:
+       case FilmState::DCI_METADATA:
                _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
                break;
-       case Film::AUDIO_STREAM:
+       case FilmState::AUDIO_STREAM:
                set_selected_stream (_film->audio_streams(), _film->audio_stream(), _audio_stream);
                break;
-       case Film::SUBTITLE_STREAM:
+       case FilmState::SUBTITLE_STREAM:
                set_selected_stream (_film->subtitle_streams(), _film->subtitle_stream(), _subtitle_stream);
                break;
        }
@@ -554,13 +562,13 @@ FilmEditor::format_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::FORMAT;
+       _ignore_changes = FilmState::FORMAT;
        int const n = _format->GetSelection ();
        if (n >= 0) {
                assert (n < int (_formats.size()));
                _film->set_format (_formats[n]);
        }
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 
        _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
 }
@@ -573,12 +581,12 @@ FilmEditor::dcp_content_type_changed (wxCommandEvent &)
                return;
        }
 
-       _ignore_changes = Film::DCP_CONTENT_TYPE;
+       _ignore_changes = FilmState::DCP_CONTENT_TYPE;
        int const n = _dcp_content_type->GetSelection ();
        if (n >= 0) {
                _film->set_dcp_content_type (DCPContentType::from_index (n));
        }
-       _ignore_changes = Film::NONE;
+       _ignore_changes = FilmState::NONE;
 
        _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
 }
@@ -601,29 +609,29 @@ FilmEditor::set_film (Film* f)
                FileChanged ("");
        }
        
-       film_changed (Film::NAME);
-       film_changed (Film::CONTENT);
-       film_changed (Film::DCP_CONTENT_TYPE);
-       film_changed (Film::FORMAT);
-       film_changed (Film::CROP);
-       film_changed (Film::FILTERS);
-       film_changed (Film::DCP_FRAMES);
-       film_changed (Film::DCP_TRIM_ACTION);
-       film_changed (Film::DCP_AB);
-       film_changed (Film::SIZE);
-       film_changed (Film::LENGTH);
-       film_changed (Film::FRAMES_PER_SECOND);
-       film_changed (Film::AUDIO_CHANNELS);
-       film_changed (Film::AUDIO_SAMPLE_RATE);
-       film_changed (Film::SCALER);
-       film_changed (Film::AUDIO_GAIN);
-       film_changed (Film::AUDIO_DELAY);
-       film_changed (Film::STILL_DURATION);
-       film_changed (Film::WITH_SUBTITLES);
-       film_changed (Film::SUBTITLE_OFFSET);
-       film_changed (Film::SUBTITLE_SCALE);
-       film_changed (Film::USE_DCI_NAME);
-       film_changed (Film::DCI_METADATA);
+       film_changed (FilmState::NAME);
+       film_changed (FilmState::CONTENT);
+       film_changed (FilmState::DCP_CONTENT_TYPE);
+       film_changed (FilmState::FORMAT);
+       film_changed (FilmState::CROP);
+       film_changed (FilmState::FILTERS);
+       film_changed (FilmState::DCP_FRAMES);
+       film_changed (FilmState::DCP_TRIM_ACTION);
+       film_changed (FilmState::DCP_AB);
+       film_changed (FilmState::SIZE);
+       film_changed (FilmState::LENGTH);
+       film_changed (FilmState::FRAMES_PER_SECOND);
+       film_changed (FilmState::AUDIO_CHANNELS);
+       film_changed (FilmState::AUDIO_SAMPLE_RATE);
+       film_changed (FilmState::SCALER);
+       film_changed (FilmState::AUDIO_GAIN);
+       film_changed (FilmState::AUDIO_DELAY);
+       film_changed (FilmState::STILL_DURATION);
+       film_changed (FilmState::WITH_SUBTITLES);
+       film_changed (FilmState::SUBTITLE_OFFSET);
+       film_changed (FilmState::SUBTITLE_SCALE);
+       film_changed (FilmState::USE_DCI_NAME);
+       film_changed (FilmState::DCI_METADATA);
 }
 
 /** Updates the sensitivity of lots of widgets to a given value.
@@ -661,7 +669,7 @@ FilmEditor::set_things_sensitive (bool s)
 void
 FilmEditor::edit_filters_clicked (wxCommandEvent &)
 {
-       FilterDialog* d = new FilterDialog (this, _film->filters ());
+       FilterDialog* d = new FilterDialog (this, _film->filters());
        d->ActiveChanged.connect (sigc::mem_fun (*_film, &Film::set_filters));
        d->ShowModal ();
        d->Destroy ();
index b204385bc34a868d054f5fd93b6895c64062576c..b50bfd1f9816e4dc87ac5751f17cefc5f1e9c737 100644 (file)
@@ -69,7 +69,7 @@ private:
        void subtitle_stream_changed (wxCommandEvent &);
 
        /* Handle changes to the model */
-       void film_changed (Film::Property);
+       void film_changed (FilmState::Property);
 
        /* Button clicks */
        void edit_filters_clicked (wxCommandEvent &);
@@ -84,7 +84,7 @@ private:
        wxControl* video_control (wxControl *);
        wxControl* still_control (wxControl *);
 
-       Film::Property _ignore_changes;
+       FilmState::Property _ignore_changes;
 
        /** The film we are editing */
        Film* _film;
index e2ab1db5dc74bb4a2773537ccb670a61810db1b6..614dd794b8a336ebeb200cb023d703d96cbec293 100644 (file)
@@ -50,7 +50,7 @@ public:
        /** Handle a paint event */
        void paint_event (wxPaintEvent& ev)
        {
-               if (!_film || _film->num_thumbs() == 0) {
+               if (!_film || _film->thumbs().size() == 0) {
                        wxPaintDC dc (this);
                        return;
                }
@@ -143,12 +143,14 @@ private:
                int vw, vh;
                GetSize (&vw, &vh);
 
+               Crop const fc = _film->crop ();
+
                /* Cropped rectangle */
                Rect cropped_area (
-                       _film->crop().left,
-                       _film->crop().top,
-                       _image->GetWidth() - (_film->crop().left + _film->crop().right),
-                       _image->GetHeight() - (_film->crop().top + _film->crop().bottom)
+                       fc.left,
+                       fc.top,
+                       _image->GetWidth() - (fc.left + fc.right),
+                       _image->GetHeight() - (fc.top + fc.bottom)
                        );
 
                /* Target ratio */
@@ -232,7 +234,7 @@ FilmViewer::FilmViewer (Film* f, wxWindow* p)
        _thumb_panel = new ThumbPanel (this, f);
        _sizer->Add (_thumb_panel, 1, wxEXPAND);
 
-       int const m = max (1, f ? f->num_thumbs() - 1 : 0);
+       int const m = max (1LU, f ? f->thumbs().size() - 1 : 0);
        _slider = new wxSlider (this, wxID_ANY, 0, 0, m);
        _sizer->Add (_slider, 0, wxEXPAND | wxLEFT | wxRIGHT);
        set_thumbnail (0);
@@ -245,7 +247,7 @@ FilmViewer::FilmViewer (Film* f, wxWindow* p)
 void
 FilmViewer::set_thumbnail (int n)
 {
-       if (_film == 0 || _film->num_thumbs() <= n) {
+       if (_film == 0 || int (_film->thumbs().size()) <= n) {
                return;
        }
 
@@ -259,12 +261,12 @@ FilmViewer::slider_changed (wxCommandEvent &)
 }
 
 void
-FilmViewer::film_changed (Film::Property p)
+FilmViewer::film_changed (FilmState::Property p)
 {
        switch (p) {
-       case Film::THUMBS:
-               if (_film && _film->num_thumbs() > 1) {
-                       _slider->SetRange (0, _film->num_thumbs () - 1);
+       case FilmState::THUMBS:
+               if (_film && _film->thumbs().size() > 1) {
+                       _slider->SetRange (0, _film->thumbs().size() - 1);
                } else {
                        _thumb_panel->clear ();
                        _slider->SetRange (0, 1);
@@ -273,16 +275,16 @@ FilmViewer::film_changed (Film::Property p)
                _slider->SetValue (0);
                set_thumbnail (0);
                break;
-       case Film::CONTENT:
+       case FilmState::CONTENT:
                setup_visibility ();
                _film->examine_content ();
                update_thumbs ();
                break;
-       case Film::CROP:
-       case Film::FORMAT:
-       case Film::WITH_SUBTITLES:
-       case Film::SUBTITLE_OFFSET:
-       case Film::SUBTITLE_SCALE:
+       case FilmState::CROP:
+       case FilmState::FORMAT:
+       case FilmState::WITH_SUBTITLES:
+       case FilmState::SUBTITLE_OFFSET:
+       case FilmState::SUBTITLE_SCALE:
                _thumb_panel->recompose ();
                break;
        default:
index 58474c6ba9b76c8d396cc46e9ef6169adb850e89..d2e597637fcfac6fd9a2ad6f2ed9b92be93b4626 100644 (file)
@@ -41,7 +41,7 @@ private:
        void slider_changed (wxCommandEvent &);
        void update_thumbs ();
        void set_thumbnail (int);
-       void film_changed (Film::Property);
+       void film_changed (FilmState::Property);
 
        Film* _film;
        wxBoxSizer* _sizer;
index 730aa8d3e2a21dca5e0cf691a5f3f352f2787a92..7ca6d0cd357d65e585eefa84986d8792790d5843 100644 (file)
@@ -258,13 +258,15 @@ BOOST_AUTO_TEST_CASE (md5_digest_test)
 BOOST_AUTO_TEST_CASE (paths_test)
 {
        FilmState s;
-       s.directory = "build/test/a/b/c/d/e";
-       s.thumbs.push_back (42);
+       s.set_directory ("build/test/a/b/c/d/e");
+       vector<int> thumbs;
+       thumbs.push_back (42);
+       s.set_thumbs (thumbs);
        BOOST_CHECK_EQUAL (s.thumb_file (0), "build/test/a/b/c/d/e/thumbs/00000042.png");
 
-       s.content = "/foo/bar/baz";
+       s._content = "/foo/bar/baz";
        BOOST_CHECK_EQUAL (s.content_path(), "/foo/bar/baz");
-       s.content = "foo/bar/baz";
+       s._content = "foo/bar/baz";
        BOOST_CHECK_EQUAL (s.content_path(), "build/test/a/b/c/d/e/foo/bar/baz");
 }
 
@@ -401,23 +403,23 @@ BOOST_AUTO_TEST_CASE (make_dcp_with_range_test)
 BOOST_AUTO_TEST_CASE (audio_sampling_rate_test)
 {
        FilmState fs;
-       fs.frames_per_second = 24;
+       fs.set_frames_per_second (24);
 
-       fs.audio_sample_rate = 48000;
+       fs.set_audio_sample_rate (48000);
        BOOST_CHECK_EQUAL (fs.target_sample_rate(), 48000);
 
-       fs.audio_sample_rate = 44100;
+       fs.set_audio_sample_rate (44100);
        BOOST_CHECK_EQUAL (fs.target_sample_rate(), 48000);
 
-       fs.audio_sample_rate = 80000;
+       fs.set_audio_sample_rate (80000);
        BOOST_CHECK_EQUAL (fs.target_sample_rate(), 96000);
 
-       fs.frames_per_second = 23.976;
-       fs.audio_sample_rate = 48000;
+       fs.set_frames_per_second (23.976);
+       fs.set_audio_sample_rate (48000);
        BOOST_CHECK_EQUAL (fs.target_sample_rate(), 47952);
 
-       fs.frames_per_second = 29.97;
-       fs.audio_sample_rate = 48000;
+       fs.set_frames_per_second (29.97);
+       fs.set_audio_sample_rate (48000);
        BOOST_CHECK_EQUAL (fs.target_sample_rate(), 47952);
 }