, _tms_path (".")
, _sound_processor (SoundProcessor::from_id ("dolby_cp750"))
{
+ _allowed_dcp_frame_rates.push_back (24);
+ _allowed_dcp_frame_rates.push_back (25);
+ _allowed_dcp_frame_rates.push_back (30);
+
ifstream f (file().c_str ());
string line;
while (getline (f, line)) {
} else if (k == "sound_processor") {
_sound_processor = SoundProcessor::from_id (v);
}
+
+ _default_dci_metadata.read (k, v);
}
}
f << "tms_path " << _tms_path << "\n";
f << "tms_user " << _tms_user << "\n";
f << "tms_password " << _tms_password << "\n";
- f << "sound_processor " << _sound_processor->id ();
+ f << "sound_processor " << _sound_processor->id () << "\n";
+
+ _default_dci_metadata.write (f);
}
string
#include <vector>
#include <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
+ #include "dci_metadata.h"
class ServerDescription;
class Scaler;
return _sound_processor;
}
+ std::list<int> allowed_dcp_frame_rates () const {
+ return _allowed_dcp_frame_rates;
+ }
+
+ DCIMetadata default_dci_metadata () const {
+ return _default_dci_metadata;
+ }
+
/** @param n New number of local encoding threads */
void set_num_local_encoding_threads (int n) {
_num_local_encoding_threads = n;
_tms_password = p;
}
+ void set_allowed_dcp_frame_rates (std::list<int> const & r) {
+ _allowed_dcp_frame_rates = r;
+ }
+
+ void set_default_dci_metadata (DCIMetadata d) {
+ _default_dci_metadata = d;
+ }
+
void write () const;
static Config* instance ();
std::string _tms_password;
/** Our sound processor */
SoundProcessor const * _sound_processor;
+ std::list<int> _allowed_dcp_frame_rates;
+ /** Default DCI metadata for newly-created Films */
+ DCIMetadata _default_dci_metadata;
/** Singleton instance, or 0 */
static Config* _instance;
#include "ab_transcode_job.h"
#include "transcode_job.h"
#include "scp_dcp_job.h"
-#include "make_dcp_job.h"
#include "log.h"
#include "options.h"
#include "exceptions.h"
#include "scaler.h"
#include "decoder_factory.h"
#include "config.h"
-#include "check_hashes_job.h"
#include "version.h"
#include "ui_signaller.h"
#include "video_decoder.h"
using boost::ends_with;
using boost::starts_with;
using boost::optional;
+using libdcp::Size;
-int const Film::state_version = 1;
+int const Film::state_version = 2;
/** Construct a Film object in a given directory, reading any metadata
* file that exists in that directory. An exception will be thrown if
, _dcp_content_type (0)
, _format (0)
, _scaler (Scaler::from_id ("bicubic"))
- , _dcp_trim_start (0)
- , _dcp_trim_end (0)
+ , _trim_start (0)
+ , _trim_end (0)
, _dcp_ab (false)
, _use_content_audio (true)
, _audio_gain (0)
, _subtitle_scale (1)
, _colour_lut (0)
, _j2k_bandwidth (200000000)
+ , _dci_metadata (Config::instance()->default_dci_metadata ())
, _frames_per_second (0)
, _dirty (false)
{
, _crop (o._crop)
, _filters (o._filters)
, _scaler (o._scaler)
- , _dcp_trim_start (o._dcp_trim_start)
- , _dcp_trim_end (o._dcp_trim_end)
+ , _trim_start (o._trim_start)
+ , _trim_end (o._trim_end)
, _dcp_ab (o._dcp_ab)
, _content_audio_stream (o._content_audio_stream)
, _external_audio (o._external_audio)
, _subtitle_scale (o._subtitle_scale)
, _colour_lut (o._colour_lut)
, _j2k_bandwidth (o._j2k_bandwidth)
- , _audio_language (o._audio_language)
- , _subtitle_language (o._subtitle_language)
- , _territory (o._territory)
- , _rating (o._rating)
- , _studio (o._studio)
- , _facility (o._facility)
- , _package_type (o._package_type)
+ , _dci_metadata (o._dci_metadata)
, _size (o._size)
, _length (o._length)
+ , _dcp_intrinsic_duration (o._dcp_intrinsic_duration)
, _content_digest (o._content_digest)
, _content_audio_streams (o._content_audio_streams)
, _external_audio_stream (o._external_audio_stream)
{
delete _log;
}
-
-/** @return The path to the directory to write JPEG2000 files to */
+
string
-Film::j2k_dir () const
+Film::video_state_identifier () const
{
- assert (format());
-
- boost::filesystem::path p;
-
- /* Start with j2c */
- p /= "j2c";
+ assert (format ());
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 << format()->id()
<< "_" << content_digest()
<< "_" << j2k_bandwidth()
<< "_" << boost::lexical_cast<int> (colour_lut());
- p /= s.str ();
-
- /* Similarly for the A/B case */
if (dcp_ab()) {
- stringstream s;
pair<string, string> fa = Filter::ffmpeg_strings (Config::instance()->reference_filters());
s << "ab_" << Config::instance()->reference_scaler()->id() << "_" << fa.first << "_" << fa.second;
- p /= s.str ();
}
-
+
+ return s.str ();
+}
+
+/** @return The path to the directory to write video frame hash files to */
+string
+Film::hash_dir () const
+{
+ boost::filesystem::path p;
+ p /= "hash";
+ p /= video_state_identifier ();
return dir (p.string());
}
+string
+Film::video_mxf_dir () const
+{
+ boost::filesystem::path p;
+ return dir ("video");
+}
+
+string
+Film::video_mxf_filename () const
+{
+ return video_state_identifier() + ".mxf";
+}
+
/** Add suitable Jobs to the JobManager to create a DCP for this Film.
* @param true to transcode, false to use the WAV and J2K files that are already there.
*/
}
log()->log (String::compose ("Content is %1; type %2", content_path(), (content_type() == STILL ? "still" : "video")));
- log()->log (String::compose ("Content length %1", length().get()));
+ if (length()) {
+ log()->log (String::compose ("Content length %1", length().get()));
+ }
log()->log (String::compose ("Content digest %1", content_digest()));
log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
throw MissingSettingError ("name");
}
- shared_ptr<EncodeOptions> oe (new EncodeOptions (j2k_dir(), ".j2c", dir ("wavs")));
- oe->out_size = format()->dcp_size ();
- oe->padding = format()->dcp_padding (shared_from_this ());
- if (dcp_length ()) {
- oe->video_range = make_pair (dcp_trim_start(), dcp_trim_start() + dcp_length().get());
- if (audio_stream()) {
- oe->audio_range = make_pair (
-
- video_frames_to_audio_frames (
- oe->video_range.get().first,
- dcp_audio_sample_rate (audio_stream()->sample_rate()),
- dcp_frame_rate (frames_per_second()).frames_per_second
- ),
-
- video_frames_to_audio_frames (
- oe->video_range.get().second,
- dcp_audio_sample_rate (audio_stream()->sample_rate()),
- dcp_frame_rate (frames_per_second()).frames_per_second
- )
- );
- }
-
- }
-
- oe->video_skip = dcp_frame_rate (frames_per_second()).skip;
-
- shared_ptr<DecodeOptions> od (new DecodeOptions);
- od->decode_subtitles = with_subtitles ();
+ DecodeOptions od;
+ od.decode_subtitles = with_subtitles ();
shared_ptr<Job> r;
if (transcode) {
if (dcp_ab()) {
- r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
+ r = JobManager::instance()->add (shared_ptr<Job> (new ABTranscodeJob (shared_from_this(), od, shared_ptr<Job> ())));
} else {
- r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, oe, shared_ptr<Job> ())));
+ r = JobManager::instance()->add (shared_ptr<Job> (new TranscodeJob (shared_from_this(), od, shared_ptr<Job> ())));
}
}
-
- r = JobManager::instance()->add (shared_ptr<Job> (new CheckHashesJob (shared_from_this(), od, oe, r)));
- JobManager::instance()->add (shared_ptr<Job> (new MakeDCPJob (shared_from_this(), oe, r)));
}
/** Start a job to examine our content file */
}
int N = 0;
- for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (j2k_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
+ for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator (hash_dir ()); i != boost::filesystem::directory_iterator(); ++i) {
++N;
boost::this_thread::interruption_point ();
}
f << "filter " << (*i)->id () << "\n";
}
f << "scaler " << _scaler->id () << "\n";
- f << "dcp_trim_start " << _dcp_trim_start << "\n";
- f << "dcp_trim_end " << _dcp_trim_end << "\n";
+ f << "trim_start " << _trim_start << "\n";
+ f << "trim_end " << _trim_end << "\n";
f << "dcp_ab " << (_dcp_ab ? "1" : "0") << "\n";
if (_content_audio_stream) {
f << "selected_content_audio_stream " << _content_audio_stream->to_string() << "\n";
f << "subtitle_scale " << _subtitle_scale << "\n";
f << "colour_lut " << _colour_lut << "\n";
f << "j2k_bandwidth " << _j2k_bandwidth << "\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";
-
+ _dci_metadata.write (f);
f << "width " << _size.width << "\n";
f << "height " << _size.height << "\n";
f << "length " << _length.get_value_or(0) << "\n";
+ f << "dcp_intrinsic_duration " << _dcp_intrinsic_duration.get_value_or(0) << "\n";
f << "content_digest " << _content_digest << "\n";
for (vector<shared_ptr<AudioStream> >::const_iterator i = _content_audio_streams.begin(); i != _content_audio_streams.end(); ++i) {
_filters.push_back (Filter::from_id (v));
} else if (k == "scaler") {
_scaler = Scaler::from_id (v);
- } else if (k == "dcp_trim_start") {
- _dcp_trim_start = atoi (v.c_str ());
- } else if (k == "dcp_trim_end") {
- _dcp_trim_end = atoi (v.c_str ());
+ } else if ( ((!version || version < 2) && k == "trim_start") || k == "trim_start") {
+ _trim_start = atoi (v.c_str ());
+ } else if ( ((!version || version < 2) && k == "trim_end") || k == "trim_end") {
+ _trim_end = atoi (v.c_str ());
} else if (k == "dcp_ab") {
_dcp_ab = (v == "1");
} else if (k == "selected_content_audio_stream" || (!version && k == "selected_audio_stream")) {
_colour_lut = atoi (v.c_str ());
} else if (k == "j2k_bandwidth") {
_j2k_bandwidth = 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;
}
+
+ _dci_metadata.read (k, v);
/* Cached stuff */
if (k == "width") {
if (vv) {
_length = vv;
}
+ } else if (k == "dcp_intrinsic_duration") {
+ int const vv = atoi (v.c_str ());
+ if (vv) {
+ _dcp_intrinsic_duration = vv;
+ }
} else if (k == "content_digest") {
_content_digest = v;
} else if (k == "content_audio_stream" || (!version && k == "audio_stream")) {
/* Resample to a DCI-approved sample rate */
double t = dcp_audio_sample_rate (audio_stream()->sample_rate());
- DCPFrameRate dfr = dcp_frame_rate (frames_per_second ());
+ DCPFrameRate dfr (frames_per_second ());
- /* Compensate for the fact that video will be rounded to the
- nearest integer number of frames per second.
+ /* Compensate if the DCP is being run at a different frame rate
+ to the source; that is, if the video is run such that it will
+ look different in the DCP compared to the source (slower or faster).
+ skip/repeat doesn't come into effect here.
*/
- if (dfr.run_fast) {
- t *= _frames_per_second * dfr.skip / dfr.frames_per_second;
+
+ if (dfr.change_speed) {
+ t *= _frames_per_second * dfr.factor() / dfr.frames_per_second;
}
return rint (t);
}
-boost::optional<int>
-Film::dcp_length () const
+int
+Film::still_duration_in_frames () const
{
- if (content_type() == STILL) {
- return _still_duration * frames_per_second();
- }
-
- if (!length()) {
- return boost::optional<int> ();
- }
-
- return length().get() - dcp_trim_start() - dcp_trim_end();
+ return still_duration() * frames_per_second();
}
/** @return a DCI-compliant name for a DCP of this film */
d << format()->dci_name() << "_";
}
- if (!audio_language().empty ()) {
- d << audio_language();
- if (!subtitle_language().empty() && with_subtitles()) {
- d << "-" << subtitle_language();
+ DCIMetadata const dm = dci_metadata ();
+
+ if (!dm.audio_language.empty ()) {
+ d << dm.audio_language;
+ if (!dm.subtitle_language.empty() && with_subtitles()) {
+ d << "-" << dm.subtitle_language;
} else {
d << "-XX";
}
d << "_";
}
- if (!territory().empty ()) {
- d << territory();
- if (!rating().empty ()) {
- d << "-" << rating();
+ if (!dm.territory.empty ()) {
+ d << dm.territory;
+ if (!dm.rating.empty ()) {
+ d << "-" << dm.rating;
}
d << "_";
}
d << "2K_";
- if (!studio().empty ()) {
- d << studio() << "_";
+ if (!dm.studio.empty ()) {
+ d << dm.studio << "_";
}
d << boost::gregorian::to_iso_string (_dci_date) << "_";
- if (!facility().empty ()) {
- d << facility() << "_";
+ if (!dm.facility.empty ()) {
+ d << dm.facility << "_";
}
- if (!package_type().empty ()) {
- d << package_type();
+ if (!dm.package_type.empty ()) {
+ d << dm.package_type;
}
return d.str ();
*/
try {
- shared_ptr<DecodeOptions> o (new DecodeOptions);
- Decoders d = decoder_factory (shared_from_this(), o, 0);
+ Decoders d = decoder_factory (shared_from_this(), DecodeOptions(), 0);
set_size (d.video->native_size ());
set_frames_per_second (d.video->frames_per_second ());
}
void
-Film::set_dcp_trim_start (int t)
+Film::set_trim_start (int t)
{
{
boost::mutex::scoped_lock lm (_state_mutex);
- _dcp_trim_start = t;
+ _trim_start = t;
}
- signal_changed (DCP_TRIM_START);
+ signal_changed (TRIM_START);
}
void
-Film::set_dcp_trim_end (int t)
+Film::set_trim_end (int t)
{
{
boost::mutex::scoped_lock lm (_state_mutex);
- _dcp_trim_end = t;
+ _trim_end = t;
}
- signal_changed (DCP_TRIM_END);
+ signal_changed (TRIM_END);
}
void
_external_audio = a;
}
- shared_ptr<DecodeOptions> o (new DecodeOptions);
- shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), o, 0));
+ shared_ptr<ExternalAudioDecoder> decoder (new ExternalAudioDecoder (shared_from_this(), DecodeOptions(), 0));
if (decoder->audio_stream()) {
_external_audio_stream = decoder->audio_stream ();
}
}
void
- Film::set_audio_language (string l)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _audio_language = l;
- }
- signal_changed (DCI_METADATA);
- }
-
- void
- Film::set_subtitle_language (string l)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _subtitle_language = l;
- }
- signal_changed (DCI_METADATA);
- }
-
- void
- Film::set_territory (string t)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _territory = t;
- }
- signal_changed (DCI_METADATA);
- }
-
- void
- Film::set_rating (string r)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _rating = r;
- }
- signal_changed (DCI_METADATA);
- }
-
- void
- Film::set_studio (string s)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _studio = s;
- }
- signal_changed (DCI_METADATA);
- }
-
- void
- Film::set_facility (string f)
- {
- {
- boost::mutex::scoped_lock lm (_state_mutex);
- _facility = f;
- }
- signal_changed (DCI_METADATA);
- }
-
- void
- Film::set_package_type (string p)
+ Film::set_dci_metadata (DCIMetadata m)
{
{
boost::mutex::scoped_lock lm (_state_mutex);
- _package_type = p;
+ _dci_metadata = m;
}
signal_changed (DCI_METADATA);
}
_length = boost::none;
}
signal_changed (LENGTH);
-}
+}
+
+void
+Film::set_dcp_intrinsic_duration (int d)
+{
+ {
+ boost::mutex::scoped_lock lm (_state_mutex);
+ _dcp_intrinsic_duration = d;
+ }
+ signal_changed (DCP_INTRINSIC_DURATION);
+}
void
Film::set_content_digest (string d)
return _external_audio_stream;
}
+
+string
+Film::hash_path (int f) const
+{
+ boost::filesystem::path p;
+ p /= hash_dir ();
+
+ stringstream s;
+ s.width (8);
+ s << setfill('0') << f << ".md5";
+
+ p /= s.str();
+ return p.string ();
+}
+
+string
+Film::j2c_path (int f, bool t) const
+{
+ boost::filesystem::path p;
+ p /= "j2c";
+ p /= video_state_identifier ();
+
+ stringstream s;
+ s.width (8);
+ s << setfill('0') << f << ".j2c";
+
+ if (t) {
+ s << ".tmp";
+ }
+
+ p /= s.str();
+ return p.string ();
+}
+
+
#include "dcp_content_type.h"
#include "util.h"
#include "stream.h"
+ #include "dci_metadata.h"
class Format;
class Job;
Film (Film const &);
~Film ();
- std::string j2k_dir () const;
+ std::string hash_dir () const;
+ std::string j2c_path (int f, bool t) const;
+ std::string hash_path (int f) const;
+ std::string video_mxf_dir () const;
+ std::string video_mxf_filename () const;
void examine_content ();
void send_dcp_to_tms ();
void read_metadata ();
libdcp::Size cropped_size (libdcp::Size) const;
- boost::optional<int> dcp_length () const;
std::string dci_name () const;
std::string dcp_name () const;
+ boost::optional<int> dcp_intrinsic_duration () const {
+ return _dcp_intrinsic_duration;
+ }
+
/** @return true if our state has changed since we last saved it */
bool dirty () const {
return _dirty;
CROP,
FILTERS,
SCALER,
- DCP_TRIM_START,
- DCP_TRIM_END,
+ TRIM_START,
+ TRIM_END,
DCP_AB,
CONTENT_AUDIO_STREAM,
EXTERNAL_AUDIO,
DCI_METADATA,
SIZE,
LENGTH,
+ DCP_INTRINSIC_DURATION,
CONTENT_AUDIO_STREAMS,
SUBTITLE_STREAMS,
FRAMES_PER_SECOND,
return _scaler;
}
- SourceFrame dcp_trim_start () const {
+ int trim_start () const {
boost::mutex::scoped_lock lm (_state_mutex);
- return _dcp_trim_start;
+ return _trim_start;
}
- SourceFrame dcp_trim_end () const {
+ int trim_end () const {
boost::mutex::scoped_lock lm (_state_mutex);
- return _dcp_trim_end;
+ return _trim_end;
}
bool dcp_ab () const {
return _still_duration;
}
+ int still_duration_in_frames () const;
+
boost::shared_ptr<SubtitleStream> subtitle_stream () const {
boost::mutex::scoped_lock lm (_state_mutex);
return _subtitle_stream;
return _j2k_bandwidth;
}
- std::string audio_language () const {
+ DCIMetadata dci_metadata () const {
boost::mutex::scoped_lock lm (_state_mutex);
- return _audio_language;
+ return _dci_metadata;
}
- std::string subtitle_language () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _subtitle_language;
- }
-
- std::string territory () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _territory;
- }
-
- std::string rating () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _rating;
- }
-
- std::string studio () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _studio;
- }
-
- std::string facility () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _facility;
- }
-
- std::string package_type () const {
- boost::mutex::scoped_lock lm (_state_mutex);
- return _package_type;
- }
-
libdcp::Size size () const {
boost::mutex::scoped_lock lm (_state_mutex);
return _size;
void set_bottom_crop (int);
void set_filters (std::vector<Filter const *>);
void set_scaler (Scaler const *);
- void set_dcp_trim_start (int);
- void set_dcp_trim_end (int);
+ void set_trim_start (int);
+ void set_trim_end (int);
void set_dcp_ab (bool);
void set_content_audio_stream (boost::shared_ptr<AudioStream>);
void set_external_audio (std::vector<std::string>);
void set_subtitle_scale (float);
void set_colour_lut (int);
void set_j2k_bandwidth (int);
- 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_dci_metadata (DCIMetadata);
void set_size (libdcp::Size);
void set_length (SourceFrame);
void unset_length ();
+ void set_dcp_intrinsic_duration (int);
void set_content_digest (std::string);
void set_content_audio_streams (std::vector<boost::shared_ptr<AudioStream> >);
void set_subtitle_streams (std::vector<boost::shared_ptr<SubtitleStream> >);
void signal_changed (Property);
void examine_content_finished ();
+ std::string video_state_identifier () const;
/** Complete path to directory containing the film metadata;
* must not be relative.
/** Scaler algorithm to use */
Scaler const * _scaler;
/** Frames to trim off the start of the DCP */
- int _dcp_trim_start;
+ int _trim_start;
/** Frames to trim off the end of the DCP */
- int _dcp_trim_end;
+ int _trim_end;
/** 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.
int _colour_lut;
/** bandwidth for J2K files in bits per second */
int _j2k_bandwidth;
-
- /* 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;
+
+ /** DCI naming stuff */
+ DCIMetadata _dci_metadata;
/* Data which are cached to speed things up */
- /** libdcp::Size, in pixels, of the source (ignoring cropping) */
+ /** Size, in pixels, of the source (ignoring cropping) */
libdcp::Size _size;
/** The length of the source, in video frames (as far as we know) */
boost::optional<SourceFrame> _length;
+ boost::optional<int> _dcp_intrinsic_duration;
/** MD5 digest of our content file */
std::string _content_digest;
/** The audio streams in our content */
using std::stringstream;
using std::vector;
using boost::shared_ptr;
+using libdcp::Size;
vector<Format const *> Format::_formats;
}
+ /** @return Number of pixels (int the DCP image) to pad either side of the film
+ * (so there are dcp_padding() pixels on the left and dcp_padding() on the right)
+ */
int
Format::dcp_padding (shared_ptr<const Film> f) const
{
return p;
}
+ float
+ Format::container_ratio_as_float () const
+ {
+ return static_cast<float> (_dcp_size.width) / _dcp_size.height;
+ }
+
VariableFormat::VariableFormat (libdcp::Size dcp, string id, string n, string d)
: Format (dcp, id, n, d)
{
using namespace std;
using namespace boost;
+using libdcp::Size;
/** Construct a TimedSubtitle. This is a subtitle image, position,
* and a range of time over which it should be shown.
*/
TimedSubtitle::TimedSubtitle (AVSubtitle const & sub)
{
- assert (sub.rects > 0);
+ assert (sub.num_rects > 0);
/* Subtitle PTS in seconds (within the source, not taking into account any of the
source that we may have chopped off for the DCP)
obj.name = 'libdvdomatic'
obj.export_includes = ['.']
- obj.uselib = 'AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE SNDFILE BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2 OPENJPEG POSTPROC TIFF MAGICK SSH DCP GLIB'
+ obj.uselib = 'AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE SNDFILE BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2 OPENJPEG POSTPROC TIFF MAGICK SSH DCP GLIB LZMA'
if bld.env.TARGET_WINDOWS:
obj.uselib += ' WINSOCK2'
obj.source = """
ab_transcoder.cc
audio_decoder.cc
audio_source.cc
- check_hashes_job.cc
config.cc
combiner.cc
cross.cc
+ dci_metadata.cc
dcp_content_type.cc
dcp_video_frame.cc
decoder.cc
job_manager.cc
log.cc
lut.cc
- make_dcp_job.cc
matcher.cc
scp_dcp_job.cc
scaler.cc
version.cc
video_decoder.cc
video_source.cc
+ writer.cc
"""
obj.target = 'dvdomatic'
#include "film_editor.h"
#include "gain_calculator_dialog.h"
#include "sound_processor.h"
- #include "dci_name_dialog.h"
+ #include "dci_metadata_dialog.h"
#include "scaler.h"
using std::string;
_film_sizer->AddSpacer (0);
add_label_to_sizer (_film_sizer, _film_panel, "Content Type");
- _dcp_content_type = new wxComboBox (_film_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
+ _dcp_content_type = new wxChoice (_film_panel, wxID_ANY);
_film_sizer->Add (_dcp_content_type);
video_control (add_label_to_sizer (_film_sizer, _film_panel, "Frames Per Second"));
video_control (add_label_to_sizer (_film_sizer, _film_panel, "Trim frames"));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
video_control (add_label_to_sizer (s, _film_panel, "Start"));
- _dcp_trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (video_control (_dcp_trim_start));
+ _trim_start = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
+ s->Add (video_control (_trim_start));
video_control (add_label_to_sizer (s, _film_panel, "End"));
- _dcp_trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
- s->Add (video_control (_dcp_trim_end));
+ _trim_end = new wxSpinCtrl (_film_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize (64, -1));
+ s->Add (video_control (_trim_end));
_film_sizer->Add (s);
}
_name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this);
_use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this);
_edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this);
- _format->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
+ _format->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
_content->Connect (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::content_changed), 0, this);
_trust_content_header->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::trust_content_header_changed), 0, this);
_left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this);
_top_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::top_crop_changed), 0, this);
_bottom_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::bottom_crop_changed), 0, this);
_filters_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_filters_clicked), 0, this);
- _scaler->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
- _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
+ _scaler->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::scaler_changed), 0, this);
+ _dcp_content_type->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::dcp_content_type_changed), 0, this);
_dcp_ab->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::dcp_ab_toggled), 0, this);
_still_duration->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::still_duration_changed), 0, this);
- _dcp_trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_start_changed), 0, this);
- _dcp_trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::dcp_trim_end_changed), 0, this);
+ _trim_start->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_start_changed), 0, this);
+ _trim_end->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::trim_end_changed), 0, this);
_with_subtitles->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::with_subtitles_toggled), 0, this);
_subtitle_offset->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_offset_changed), 0, this);
_subtitle_scale->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::subtitle_scale_changed), 0, this);
- _colour_lut->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this);
+ _colour_lut->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::colour_lut_changed), 0, this);
_j2k_bandwidth->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::j2k_bandwidth_changed), 0, this);
- _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this);
- _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this);
+ _subtitle_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::subtitle_stream_changed), 0, this);
+ _audio_stream->Connect (wxID_ANY, wxEVT_COMMAND_CHOICE_SELECTED, wxCommandEventHandler (FilmEditor::audio_stream_changed), 0, this);
_audio_gain->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::audio_gain_changed), 0, this);
_audio_gain_calculate_button->Connect (
wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::audio_gain_calculate_button_clicked), 0, this
_video_panel->SetSizer (pad);
add_label_to_sizer (_video_sizer, _video_panel, "Format");
- _format = new wxComboBox (_video_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
+ _format = new wxChoice (_video_panel, wxID_ANY);
_video_sizer->Add (_format);
{
}
video_control (add_label_to_sizer (_video_sizer, _video_panel, "Scaler"));
- _scaler = new wxComboBox (_video_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
+ _scaler = new wxChoice (_video_panel, wxID_ANY);
_video_sizer->Add (video_control (_scaler), 1);
vector<Scaler const *> const sc = Scaler::all ();
}
add_label_to_sizer (_video_sizer, _video_panel, "Colour look-up table");
- _colour_lut = new wxComboBox (_video_panel, wxID_ANY);
+ _colour_lut = new wxChoice (_video_panel, wxID_ANY);
for (int i = 0; i < 2; ++i) {
_colour_lut->Append (std_to_wx (colour_lut_index_to_name (i)));
}
_right_crop->SetRange (0, 1024);
_bottom_crop->SetRange (0, 1024);
_still_duration->SetRange (1, 60 * 60);
- _dcp_trim_start->SetRange (0, 100);
- _dcp_trim_end->SetRange (0, 100);
+ _trim_start->SetRange (0, 100);
+ _trim_end->SetRange (0, 100);
_j2k_bandwidth->SetRange (50, 250);
}
_use_content_audio = new wxRadioButton (_audio_panel, wxID_ANY, _("Use content's audio"), wxDefaultPosition, wxDefaultSize, wxRB_GROUP);
_audio_sizer->Add (video_control (_use_content_audio));
wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
- _audio_stream = new wxComboBox (_audio_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
+ _audio_stream = new wxChoice (_audio_panel, wxID_ANY);
s->Add (video_control (_audio_stream), 1);
_audio = new wxStaticText (_audio_panel, wxID_ANY, wxT (""));
s->Add (video_control (_audio), 1, wxALIGN_CENTER_VERTICAL | wxLEFT, 8);
video_control (_with_subtitles);
_subtitle_sizer->Add (_with_subtitles, 1);
- _subtitle_stream = new wxComboBox (_subtitle_panel, wxID_ANY, wxT (""), wxDefaultPosition, wxDefaultSize, 0, 0, wxCB_READONLY);
+ _subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY);
_subtitle_sizer->Add (video_control (_subtitle_stream));
video_control (add_label_to_sizer (_subtitle_sizer, _subtitle_panel, "Subtitle Offset"));
}
_length->SetLabel (std_to_wx (s.str ()));
if (_film->length()) {
- _dcp_trim_start->SetRange (0, _film->length().get());
- _dcp_trim_end->SetRange (0, _film->length().get());
+ _trim_start->SetRange (0, _film->length().get());
+ _trim_end->SetRange (0, _film->length().get());
}
break;
+ case Film::DCP_INTRINSIC_DURATION:
+ break;
case Film::DCP_CONTENT_TYPE:
checked_set (_dcp_content_type, DCPContentType::as_index (_film->dcp_content_type ()));
_dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
case Film::SCALER:
checked_set (_scaler, Scaler::as_index (_film->scaler ()));
break;
- case Film::DCP_TRIM_START:
- checked_set (_dcp_trim_start, _film->dcp_trim_start());
+ case Film::TRIM_START:
+ checked_set (_trim_start, _film->trim_start());
break;
- case Film::DCP_TRIM_END:
- checked_set (_dcp_trim_end, _film->dcp_trim_end());
+ case Film::TRIM_END:
+ checked_set (_trim_end, _film->trim_end());
break;
case Film::AUDIO_GAIN:
checked_set (_audio_gain, _film->audio_gain ());
film_changed (Film::CROP);
film_changed (Film::FILTERS);
film_changed (Film::SCALER);
- film_changed (Film::DCP_TRIM_START);
- film_changed (Film::DCP_TRIM_END);
+ film_changed (Film::TRIM_START);
+ film_changed (Film::TRIM_END);
film_changed (Film::DCP_AB);
film_changed (Film::CONTENT_AUDIO_STREAM);
film_changed (Film::EXTERNAL_AUDIO);
_scaler->Enable (s);
_audio_stream->Enable (s);
_dcp_content_type->Enable (s);
- _dcp_trim_start->Enable (s);
- _dcp_trim_end->Enable (s);
+ _trim_start->Enable (s);
+ _trim_end->Enable (s);
_dcp_ab->Enable (s);
_colour_lut->Enable (s);
_j2k_bandwidth->Enable (s);
}
void
-FilmEditor::dcp_trim_start_changed (wxCommandEvent &)
+FilmEditor::trim_start_changed (wxCommandEvent &)
{
if (!_film) {
return;
}
- _film->set_dcp_trim_start (_dcp_trim_start->GetValue ());
+ _film->set_trim_start (_trim_start->GetValue ());
}
void
-FilmEditor::dcp_trim_end_changed (wxCommandEvent &)
+FilmEditor::trim_end_changed (wxCommandEvent &)
{
if (!_film) {
return;
}
- _film->set_dcp_trim_end (_dcp_trim_end->GetValue ());
+ _film->set_trim_end (_trim_end->GetValue ());
}
void
return;
}
- DCINameDialog* d = new DCINameDialog (this, _film);
+ DCIMetadataDialog* d = new DCIMetadataDialog (this, _film->dci_metadata ());
d->ShowModal ();
+ _film->set_dci_metadata (d->dci_metadata ());
d->Destroy ();
}
if (_film->subtitle_stream()) {
checked_set (_subtitle_stream, _film->subtitle_stream()->to_string());
} else {
- _subtitle_stream->SetValue (wxT (""));
+ _subtitle_stream->SetSelection (wxNOT_FOUND);
}
}
void content_changed (wxCommandEvent &);
void trust_content_header_changed (wxCommandEvent &);
void format_changed (wxCommandEvent &);
- void dcp_trim_start_changed (wxCommandEvent &);
- void dcp_trim_end_changed (wxCommandEvent &);
+ void trim_start_changed (wxCommandEvent &);
+ void trim_end_changed (wxCommandEvent &);
void dcp_content_type_changed (wxCommandEvent &);
void dcp_ab_toggled (wxCommandEvent &);
void scaler_changed (wxCommandEvent &);
wxCheckBox* _use_dci_name;
wxButton* _edit_dci_button;
/** The Film's format */
- wxComboBox* _format;
+ wxChoice* _format;
/** The Film's content file */
wxFilePickerCtrl* _content;
wxCheckBox* _trust_content_header;
/** Button to open the filters dialogue */
wxButton* _filters_button;
/** The Film's scaler */
- wxComboBox* _scaler;
+ wxChoice* _scaler;
wxRadioButton* _use_content_audio;
- wxComboBox* _audio_stream;
+ wxChoice* _audio_stream;
wxRadioButton* _use_external_audio;
wxFilePickerCtrl* _external_audio[MAX_AUDIO_CHANNELS];
/** The Film's audio gain */
/** The Film's audio delay */
wxSpinCtrl* _audio_delay;
wxCheckBox* _with_subtitles;
- wxComboBox* _subtitle_stream;
+ wxChoice* _subtitle_stream;
wxSpinCtrl* _subtitle_offset;
wxSpinCtrl* _subtitle_scale;
- wxComboBox* _colour_lut;
+ wxChoice* _colour_lut;
wxSpinCtrl* _j2k_bandwidth;
/** The Film's DCP content type */
- wxComboBox* _dcp_content_type;
+ wxChoice* _dcp_content_type;
/** The Film's frames per second */
wxStaticText* _frames_per_second;
/** The Film's original size */
/** The Film's duration for still sources */
wxSpinCtrl* _still_duration;
- wxSpinCtrl* _dcp_trim_start;
- wxSpinCtrl* _dcp_trim_end;
+ wxSpinCtrl* _trim_start;
+ wxSpinCtrl* _trim_end;
/** Selector to generate an A/B comparison DCP */
wxCheckBox* _dcp_ab;
using std::cout;
using std::list;
using boost::shared_ptr;
+using libdcp::Size;
FilmViewer::FilmViewer (shared_ptr<Film> f, wxWindow* p)
: wxPanel (p)
, _panel (new wxPanel (this))
, _slider (new wxSlider (this, wxID_ANY, 0, 0, 4096))
, _play_button (new wxToggleButton (this, wxID_ANY, wxT ("Play")))
+ , _display_frame_x (0)
, _got_frame (false)
- , _out_width (0)
- , _out_height (0)
- , _panel_width (0)
- , _panel_height (0)
, _clear_required (false)
{
_panel->SetDoubleBuffered (true);
break;
case Film::CONTENT:
{
- shared_ptr<DecodeOptions> o (new DecodeOptions);
- o->decode_audio = false;
- o->decode_subtitles = true;
- o->video_sync = false;
+ DecodeOptions o;
+ o.decode_audio = false;
+ o.decode_subtitles = true;
+ o.video_sync = false;
_decoders = decoder_factory (_film, o, 0);
_decoders.video->Video.connect (bind (&FilmViewer::process_video, this, _1, _2, _3));
_decoders.video->OutputChanged.connect (boost::bind (&FilmViewer::decoder_changed, this));
_clear_required = false;
}
- if (!_display_frame || !_film || !_out_width || !_out_height) {
+ if (!_display_frame || !_film || !_out_size.width || !_out_size.height) {
dc.Clear ();
return;
}
- wxImage frame (_out_width, _out_height, _display_frame->data()[0], true);
+ if (_display_frame_x) {
+ dc.SetPen(*wxBLACK_PEN);
+ dc.SetBrush(*wxBLACK_BRUSH);
+ dc.DrawRectangle (0, 0, _display_frame_x, _film_size.height);
+ dc.DrawRectangle (_display_frame_x + _film_size.width, 0, _display_frame_x * 2 + _film_size.width, _film_size.height);
+ }
+
+ wxImage frame (_film_size.width, _film_size.height, _display_frame->data()[0], true);
wxBitmap frame_bitmap (frame);
- dc.DrawBitmap (frame_bitmap, 0, 0);
+ dc.DrawBitmap (frame_bitmap, _display_frame_x, 0);
if (_film->with_subtitles() && _display_sub) {
wxImage sub (_display_sub->size().width, _display_sub->size().height, _display_sub->data()[0], _display_sub->alpha(), true);
void
FilmViewer::panel_sized (wxSizeEvent& ev)
{
- _panel_width = ev.GetSize().GetWidth();
- _panel_height = ev.GetSize().GetHeight();
+ _panel_size.width = ev.GetSize().GetWidth();
+ _panel_size.height = ev.GetSize().GetHeight();
calculate_sizes ();
update_from_raw ();
}
void
FilmViewer::raw_to_display ()
{
- if (!_raw_frame || _out_width < 64 || _out_height < 64 || !_film) {
+ if (!_raw_frame || _out_size.width < 64 || _out_size.height < 64 || !_film) {
return;
}
}
/* Get a compacted image as we have to feed it to wxWidgets */
- _display_frame = _raw_frame->scale_and_convert_to_rgb (libdcp::Size (_out_width, _out_height), 0, _film->scaler(), false);
+ _display_frame = _raw_frame->scale_and_convert_to_rgb (_film_size, 0, _film->scaler(), false);
if (old_size != _display_frame->size()) {
_clear_required = true;
if (_raw_sub) {
Rect tx = subtitle_transformed_area (
- float (_out_width) / _film->size().width,
- float (_out_height) / _film->size().height,
+ float (_film_size.width) / _film->size().width,
+ float (_film_size.height) / _film->size().height,
_raw_sub->area(), _film->subtitle_offset(), _film->subtitle_scale()
);
_display_sub.reset (new RGBPlusAlphaImage (_raw_sub->image()->scale (tx.size(), _film->scaler(), false)));
_display_sub_position = tx.position();
+ _display_sub_position.x += _display_frame_x;
} else {
_display_sub.reset ();
}
if (!_film) {
return;
}
+
+ Format const * format = _film->format ();
- float const panel_ratio = static_cast<float> (_panel_width) / _panel_height;
- float const film_ratio = _film->format() ? _film->format()->ratio_as_float(_film) : 1.78;
+ float const panel_ratio = static_cast<float> (_panel_size.width) / _panel_size.height;
+ float const film_ratio = format ? format->container_ratio_as_float () : 1.78;
+
if (panel_ratio < film_ratio) {
/* panel is less widscreen than the film; clamp width */
- _out_width = _panel_width;
- _out_height = _out_width / film_ratio;
+ _out_size.width = _panel_size.width;
+ _out_size.height = _out_size.width / film_ratio;
} else {
- /* panel is more widescreen than the film; clamp heignt */
- _out_height = _panel_height;
- _out_width = _out_height * film_ratio;
+ /* panel is more widescreen than the film; clamp height */
+ _out_size.height = _panel_size.height;
+ _out_size.width = _out_size.height * film_ratio;
+ }
+
+ /* Work out how much padding there is in terms of our display; this will be the x position
+ of our _display_frame.
+ */
+ _display_frame_x = 0;
+ if (format) {
+ _display_frame_x = static_cast<float> (format->dcp_padding (_film)) * _out_size.width / format->dcp_size().width;
+ }
+
+ _film_size = _out_size;
+ _film_size.width -= _display_frame_x * 2;
+
+ /* Catch silly values */
+ if (_out_size.width < 64) {
+ _out_size.width = 64;
}
}