-/* -*- c-basic-offset: 8; default-tab-width: 8; -*- */
-
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
#include "film.h"
-#include "container.h"
#include "job.h"
-#include "filter.h"
#include "util.h"
#include "job_manager.h"
-#include "ab_transcode_job.h"
#include "transcode_job.h"
#include "scp_dcp_job.h"
#include "log.h"
#include "config.h"
#include "version.h"
#include "ui_signaller.h"
-#include "analyse_audio_job.h"
#include "playlist.h"
#include "player.h"
-#include "ffmpeg_content.h"
-#include "imagemagick_content.h"
-#include "sndfile_content.h"
#include "dcp_content_type.h"
+#include "ratio.h"
+#include "cross.h"
#include "i18n.h"
using std::cout;
using std::list;
using boost::shared_ptr;
+using boost::weak_ptr;
using boost::lexical_cast;
+using boost::dynamic_pointer_cast;
using boost::to_upper_copy;
using boost::ends_with;
using boost::starts_with;
, _use_dci_name (true)
, _dcp_content_type (Config::instance()->default_dcp_content_type ())
, _container (Config::instance()->default_container ())
+ , _resolution (RESOLUTION_2K)
, _scaler (Scaler::from_id ("bicubic"))
- , _ab (false)
, _with_subtitles (false)
- , _subtitle_offset (0)
- , _subtitle_scale (1)
- , _colour_lut (0)
- , _j2k_bandwidth (200000000)
+ , _j2k_bandwidth (Config::instance()->default_j2k_bandwidth ())
, _dci_metadata (Config::instance()->default_dci_metadata ())
- , _dcp_video_frame_rate (0)
+ , _dcp_video_frame_rate (24)
, _dcp_audio_channels (MAX_AUDIO_CHANNELS)
, _dirty (false)
{
}
set_directory (result.string ());
- _log.reset (new FileLog (file ("log")));
-}
-
-Film::Film (Film const & o)
- : boost::enable_shared_from_this<Film> (o)
- /* note: the copied film shares the original's log */
- , _log (o._log)
- , _playlist (new Playlist (o._playlist))
- , _directory (o._directory)
- , _name (o._name)
- , _use_dci_name (o._use_dci_name)
- , _dcp_content_type (o._dcp_content_type)
- , _container (o._container)
- , _filters (o._filters)
- , _scaler (o._scaler)
- , _ab (o._ab)
- , _with_subtitles (o._with_subtitles)
- , _subtitle_offset (o._subtitle_offset)
- , _subtitle_scale (o._subtitle_scale)
- , _colour_lut (o._colour_lut)
- , _j2k_bandwidth (o._j2k_bandwidth)
- , _dci_metadata (o._dci_metadata)
- , _dcp_video_frame_rate (o._dcp_video_frame_rate)
- , _dci_date (o._dci_date)
- , _dirty (o._dirty)
-{
- _playlist->ContentChanged.connect (bind (&Film::playlist_content_changed, this, _1, _2));
+ _log.reset (new FileLog ("log"));
}
string
-Film::video_state_identifier () const
+Film::video_identifier () const
{
assert (container ());
LocaleGuard lg;
- pair<string, string> f = Filter::ffmpeg_strings (filters());
-
stringstream s;
s << container()->id()
- << "_" << _playlist->video_digest()
+ << "_" << resolution_to_string (_resolution)
+ << "_" << _playlist->video_identifier()
<< "_" << _dcp_video_frame_rate
- << "_" << f.first << "_" << f.second
<< "_" << scaler()->id()
- << "_" << j2k_bandwidth()
- << "_" << lexical_cast<int> (colour_lut());
-
- if (ab()) {
- pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
- s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
- }
+ << "_" << j2k_bandwidth();
return s.str ();
}
{
boost::filesystem::path p;
p /= "info";
- p /= video_state_identifier ();
+ p /= video_identifier ();
return dir (p.string());
}
string
Film::internal_video_mxf_dir () const
{
- boost::filesystem::path p;
return dir ("video");
}
string
Film::internal_video_mxf_filename () const
{
- return video_state_identifier() + ".mxf";
+ return video_identifier() + ".mxf";
}
string
return o;
}
-string
-Film::audio_analysis_path () const
+boost::filesystem::path
+Film::audio_analysis_path (shared_ptr<const AudioContent> c) const
{
- boost::filesystem::path p;
- p /= "analysis";
- p /= _playlist->audio_digest();
- return file (p.string ());
+ boost::filesystem::path p = dir ("analysis");
+ p /= c->digest();
+ return p;
}
/** Add suitable Jobs to the JobManager to create a DCP for this Film */
#endif
pair<string, int> const c = cpu_info ();
log()->log (String::compose ("CPU: %1, %2 processors", c.first, c.second));
+ list<pair<string, string> > const m = mount_info ();
+ for (list<pair<string, string> >::const_iterator i = m.begin(); i != m.end(); ++i) {
+ log()->log (String::compose ("Mount: %1 %2", i->first, i->second));
+ }
if (container() == 0) {
throw MissingSettingError (_("container"));
}
- if (_playlist->content().empty ()) {
+ if (content().empty()) {
throw StringError (_("You must add some content to the DCP before creating it"));
}
throw MissingSettingError (_("name"));
}
- shared_ptr<Job> r;
-
- if (ab()) {
- r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this())));
- } else {
- r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
- }
-}
-
-/** Start a job to analyse the audio in our Playlist */
-void
-Film::analyse_audio ()
-{
- if (_analyse_audio_job) {
- return;
- }
-
- _analyse_audio_job.reset (new AnalyseAudioJob (shared_from_this()));
- _analyse_audio_job->Finished.connect (bind (&Film::analyse_audio_finished, this));
- JobManager::instance()->add (_analyse_audio_job);
-}
-
-/** Start a job to examine a piece of content */
-void
-Film::examine_content (shared_ptr<Content> c)
-{
- shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
- JobManager::instance()->add (j);
-}
-
-void
-Film::analyse_audio_finished ()
-{
- ensure_ui_thread ();
-
- if (_analyse_audio_job->finished_ok ()) {
- AudioAnalysisSucceeded ();
- }
-
- _analyse_audio_job.reset ();
+ JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this())));
}
/** Start a job to send our DCP to the configured TMS */
root->add_child("Container")->add_child_text (_container->id ());
}
- for (vector<Filter const *>::const_iterator i = _filters.begin(); i != _filters.end(); ++i) {
- root->add_child("Filter")->add_child_text ((*i)->id ());
- }
-
+ root->add_child("Resolution")->add_child_text (resolution_to_string (_resolution));
root->add_child("Scaler")->add_child_text (_scaler->id ());
- root->add_child("AB")->add_child_text (_ab ? "1" : "0");
root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0");
- root->add_child("SubtitleOffset")->add_child_text (lexical_cast<string> (_subtitle_offset));
- root->add_child("SubtitleScale")->add_child_text (lexical_cast<string> (_subtitle_scale));
- root->add_child("ColourLUT")->add_child_text (lexical_cast<string> (_colour_lut));
root->add_child("J2KBandwidth")->add_child_text (lexical_cast<string> (_j2k_bandwidth));
_dci_metadata.as_xml (root->add_child ("DCIMetadata"));
root->add_child("DCPVideoFrameRate")->add_child_text (lexical_cast<string> (_dcp_video_frame_rate));
{
optional<string> c = f.optional_string_child ("Container");
if (c) {
- _container = Container::from_id (c.get ());
- }
- }
-
- {
- list<shared_ptr<cxml::Node> > c = f.node_children ("Filter");
- for (list<shared_ptr<cxml::Node> >::iterator i = c.begin(); i != c.end(); ++i) {
- _filters.push_back (Filter::from_id ((*i)->content ()));
+ _container = Ratio::from_id (c.get ());
}
}
+ _resolution = string_to_resolution (f.string_child ("Resolution"));
_scaler = Scaler::from_id (f.string_child ("Scaler"));
- _ab = f.bool_child ("AB");
_with_subtitles = f.bool_child ("WithSubtitles");
- _subtitle_offset = f.number_child<float> ("SubtitleOffset");
- _subtitle_scale = f.number_child<float> ("SubtitleScale");
- _colour_lut = f.number_child<int> ("ColourLUT");
_j2k_bandwidth = f.number_child<int> ("J2KBandwidth");
_dci_metadata = DCIMetadata (f.node_child ("DCIMetadata"));
_dcp_video_frame_rate = f.number_child<int> ("DCPVideoFrameRate");
if (!dm.audio_language.empty ()) {
d << "_" << dm.audio_language;
- if (!dm.subtitle_language.empty()) {
+ if (!dm.subtitle_language.empty() && with_subtitles()) {
d << "-" << dm.subtitle_language;
} else {
d << "-XX";
}
}
- d << "_51_2K";
+ switch (dcp_audio_channels ()) {
+ case 1:
+ d << "_10";
+ break;
+ case 2:
+ d << "_20";
+ break;
+ case 3:
+ d << "_30";
+ break;
+ case 4:
+ d << "_40";
+ break;
+ case 5:
+ d << "_50";
+ break;
+ case 6:
+ d << "_51";
+ break;
+ }
+
+ d << "_" << resolution_to_string (_resolution);
if (!dm.studio.empty ()) {
d << "_" << dm.studio;
}
void
-Film::set_container (Container const * c)
+Film::set_container (Ratio const * c)
{
{
boost::mutex::scoped_lock lm (_state_mutex);
}
void
-Film::set_filters (vector<Filter const *> f)
+Film::set_resolution (Resolution r)
{
{
boost::mutex::scoped_lock lm (_state_mutex);
- _filters = f;
+ _resolution = r;
}
- signal_changed (FILTERS);
+ signal_changed (RESOLUTION);
}
void
signal_changed (SCALER);
}
-void
-Film::set_ab (bool a)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _ab = a;
- }
- signal_changed (AB);
-}
-
void
Film::set_with_subtitles (bool w)
{
signal_changed (WITH_SUBTITLES);
}
-void
-Film::set_subtitle_offset (int o)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_offset = o;
- }
- signal_changed (SUBTITLE_OFFSET);
-}
-
-void
-Film::set_subtitle_scale (float s)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_scale = s;
- }
- signal_changed (SUBTITLE_SCALE);
-}
-
-void
-Film::set_colour_lut (int i)
-{
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _colour_lut = i;
- }
- signal_changed (COLOUR_LUT);
-}
-
void
Film::set_j2k_bandwidth (int b)
{
signal_changed (DCI_METADATA);
}
-
void
Film::set_dcp_video_frame_rate (int f)
{
signal_changed (DCP_VIDEO_FRAME_RATE);
}
+void
+Film::set_dcp_audio_channels (int c)
+{
+ {
+ boost::mutex::scoped_lock lm (_state_mutex);
+ _dcp_audio_channels = c;
+ }
+ signal_changed (DCP_AUDIO_CHANNELS);
+}
+
void
Film::signal_changed (Property p)
{
case Film::CONTENT:
set_dcp_video_frame_rate (_playlist->best_dcp_frame_rate ());
break;
+ case Film::DCP_VIDEO_FRAME_RATE:
+ _playlist->maybe_sequence_video ();
+ break;
default:
break;
}
{
boost::filesystem::path p;
p /= "j2c";
- p /= video_state_identifier ();
+ p /= video_identifier ();
stringstream s;
s.width (8);
}
shared_ptr<Player>
-Film::player () const
+Film::make_player () const
{
- boost::mutex::scoped_lock lm (_state_mutex);
return shared_ptr<Player> (new Player (shared_from_this (), _playlist));
}
return _playlist;
}
-Playlist::ContentList
+ContentList
Film::content () const
{
return _playlist->content ();
}
+void
+Film::examine_and_add_content (shared_ptr<Content> c)
+{
+ shared_ptr<Job> j (new ExamineContentJob (shared_from_this(), c));
+ j->Finished.connect (bind (&Film::add_content_weak, this, boost::weak_ptr<Content> (c)));
+ JobManager::instance()->add (j);
+}
+
+void
+Film::add_content_weak (weak_ptr<Content> c)
+{
+ shared_ptr<Content> content = c.lock ();
+ if (content) {
+ add_content (content);
+ }
+}
+
void
Film::add_content (shared_ptr<Content> c)
{
+ /* Add video content after any existing content */
+ if (dynamic_pointer_cast<VideoContent> (c)) {
+ c->set_start (_playlist->video_end ());
+ }
+
_playlist->add (c);
- examine_content (c);
}
void
signal_changed (CONTENT);
}
-int
-Film::loop () const
-{
- return _playlist->loop ();
-}
-
-void
-Film::set_loop (int c)
-{
- _playlist->set_loop (c);
-}
-
OutputAudioFrame
Film::time_to_audio_frames (Time t) const
{
/* XXX */
return 48000;
}
+
+void
+Film::set_sequence_video (bool s)
+{
+ _playlist->set_sequence_video (s);
+}
+
+libdcp::Size
+Film::full_frame () const
+{
+ switch (_resolution) {
+ case RESOLUTION_2K:
+ return libdcp::Size (2048, 1080);
+ case RESOLUTION_4K:
+ return libdcp::Size (4096, 2160);
+ }
+
+ assert (false);
+ return libdcp::Size ();
+}