+2014-03-07 Carl Hetherington <cth@carlh.net>
+
+ * Add subtitle view.
+
+ 2014-04-01 Carl Hetherington <cth@carlh.net>
+
+ * Basic support for separate left/right-eye files or directories
+ for 3D.
+
+ 2014-03-30 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.66.9 released.
+
+ 2014-03-30 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.66.8 released.
+
+ * nl_NL translation from Theo Kooijmans.
+
+ 2014-03-27 Carl Hetherington <cth@carlh.net>
+
+ * Auto-save film metadata before starting DCP encode.
+
+ 2014-03-25 Carl Hetherington <cth@carlh.net>
+
+ * Add support for downloading Doremi server certificates.
+
+ 2014-03-24 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.66.7 released.
+
+ 2014-03-24 Carl Hetherington <cth@carlh.net>
+
+ * Fix error on creating DCPs without audio.
+
+ 2014-03-23 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.66.6 released.
+
+ 2014-03-23 Carl Hetherington <cth@carlh.net>
+
+ * Attempt to fix format string specifier error on Windows.
+
+ * Version 1.66.5 released.
+
+ 2014-03-22 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.66.4 released.
+
+ 2014-03-22 Carl Hetherington <cth@carlh.net>
+
+ * Allow specification of the video frame rate that a sound file
+ was prepared for.
+
+ * Another attempt to fix colour conversion dialog strange behaviour
+ on OS X.
++>>>>>>> master
+
2014-03-18 Carl Hetherington <cth@carlh.net>
* Version 1.66.3 released.
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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 <glib.h>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
-#include <libdcp/colour_matrix.h>
+#include <dcp/colour_matrix.h>
#include <libcxml/cxml.h>
#include "config.h"
#include "server.h"
)
, _check_for_updates (false)
, _check_for_test_updates (false)
+ , _maximum_j2k_bandwidth (250000000)
{
_allowed_dcp_frame_rates.push_back (24);
_allowed_dcp_frame_rates.push_back (25);
_allowed_dcp_frame_rates.push_back (50);
_allowed_dcp_frame_rates.push_back (60);
- _colour_conversions.push_back (PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6));
- _colour_conversions.push_back (PresetColourConversion (_("sRGB non-linearised"), 2.4, false, libdcp::colour_matrix::srgb_to_xyz, 2.6));
- _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, libdcp::colour_matrix::rec709_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("sRGB non-linearised"), 2.4, false, dcp::colour_matrix::srgb_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, dcp::colour_matrix::rec709_to_xyz, 2.6));
}
void
/* Loading version 0 (before Rec. 709 was added as a preset).
Add it in.
*/
- _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, libdcp::colour_matrix::rec709_to_xyz, 2.6));
+ _colour_conversions.push_back (PresetColourConversion (_("Rec. 709"), 2.2, false, dcp::colour_matrix::rec709_to_xyz, 2.6));
}
list<cxml::NodePtr> cin = f.node_children ("Cinema");
_check_for_updates = f.optional_bool_child("CheckForUpdates").get_value_or (false);
_check_for_test_updates = f.optional_bool_child("CheckForTestUpdates").get_value_or (false);
+
+ _maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
}
void
root->add_child("CheckForUpdates")->add_child_text (_check_for_updates ? "1" : "0");
root->add_child("CheckForTestUpdates")->add_child_text (_check_for_test_updates ? "1" : "0");
+ root->add_child("MaximumJ2KBandwidth")->add_child_text (lexical_cast<string> (_maximum_j2k_bandwidth));
+
doc.write_to_file_formatted (file(false).string ());
}
delete _instance;
_instance = 0;
}
+
+ void
+ Config::changed ()
+ {
+ write ();
+ Changed ();
+ }
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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 <boost/shared_ptr.hpp>
#include <boost/signals2.hpp>
#include <boost/filesystem.hpp>
-#include <libdcp/metadata.h>
+#include <dcp/metadata.h>
#include "dci_metadata.h"
#include "colour_conversion.h"
#include "server.h"
void set_use_any_servers (bool u) {
_use_any_servers = u;
- write ();
+ changed ();
}
bool use_any_servers () const {
/** @param s New list of servers */
void set_servers (std::vector<std::string> s) {
_servers = s;
- write ();
+ changed ();
}
/** @return Host names / IP addresses of J2K encoding servers that should definitely be used */
return _tms_ip;
}
- /** @return The path on a TMS that we should write DCPs to */
+ /** @return The path on a TMS that we should changed DCPs to */
std::string tms_path () const {
return _tms_path;
}
return _default_dcp_content_type;
}
- libdcp::XMLMetadata dcp_metadata () const {
+ dcp::XMLMetadata dcp_metadata () const {
return _dcp_metadata;
}
bool check_for_test_updates () const {
return _check_for_test_updates;
}
+
+ int maximum_j2k_bandwidth () const {
+ return _maximum_j2k_bandwidth;
+ }
/** @param n New number of local encoding threads */
void set_num_local_encoding_threads (int n) {
_num_local_encoding_threads = n;
- write ();
+ changed ();
}
void set_default_directory (boost::filesystem::path d) {
_default_directory = d;
- write ();
+ changed ();
}
/** @param p New server port */
void set_server_port_base (int p) {
_server_port_base = p;
- write ();
+ changed ();
}
/** @param i IP address of a TMS that we can copy DCPs to */
void set_tms_ip (std::string i) {
_tms_ip = i;
- write ();
+ changed ();
}
- /** @param p Path on a TMS that we should write DCPs to */
+ /** @param p Path on a TMS that we should changed DCPs to */
void set_tms_path (std::string p) {
_tms_path = p;
- write ();
+ changed ();
}
/** @param u User name to log into the TMS with */
void set_tms_user (std::string u) {
_tms_user = u;
- write ();
+ changed ();
}
/** @param p Password to log into the TMS with */
void set_tms_password (std::string p) {
_tms_password = p;
- write ();
+ changed ();
}
void add_cinema (boost::shared_ptr<Cinema> c) {
_cinemas.push_back (c);
+ changed ();
}
void remove_cinema (boost::shared_ptr<Cinema> c) {
_cinemas.remove (c);
+ changed ();
}
void set_allowed_dcp_frame_rates (std::list<int> const & r) {
_allowed_dcp_frame_rates = r;
- write ();
+ changed ();
}
void set_default_dci_metadata (DCIMetadata d) {
_default_dci_metadata = d;
- write ();
+ changed ();
}
void set_language (std::string l) {
_language = l;
- write ();
+ changed ();
}
void unset_language () {
_language = boost::none;
- write ();
+ changed ();
}
void set_default_still_length (int s) {
_default_still_length = s;
- write ();
+ changed ();
}
void set_default_container (Ratio const * c) {
_default_container = c;
- write ();
+ changed ();
}
void set_default_dcp_content_type (DCPContentType const * t) {
_default_dcp_content_type = t;
- write ();
+ changed ();
}
- void set_dcp_metadata (libdcp::XMLMetadata m) {
+ void set_dcp_metadata (dcp::XMLMetadata m) {
_dcp_metadata = m;
- write ();
+ changed ();
}
void set_default_j2k_bandwidth (int b) {
_default_j2k_bandwidth = b;
- write ();
+ changed ();
}
void set_default_audio_delay (int d) {
_default_audio_delay = d;
- write ();
+ changed ();
}
void set_colour_conversions (std::vector<PresetColourConversion> const & c) {
_colour_conversions = c;
- write ();
+ changed ();
}
void set_mail_server (std::string s) {
_mail_server = s;
- write ();
+ changed ();
}
void set_mail_user (std::string u) {
_mail_user = u;
- write ();
+ changed ();
}
void set_mail_password (std::string p) {
_mail_password = p;
- write ();
+ changed ();
}
void set_kdm_from (std::string f) {
_kdm_from = f;
- write ();
+ changed ();
}
void set_kdm_email (std::string e) {
_kdm_email = e;
- write ();
+ changed ();
}
void set_check_for_updates (bool c) {
_check_for_updates = c;
- write ();
+ changed ();
}
void set_check_for_test_updates (bool c) {
_check_for_test_updates = c;
- write ();
+ changed ();
}
-
- void write () const;
+ void set_maximum_j2k_bandwidth (int b) {
+ _maximum_j2k_bandwidth = b;
+ changed ();
+ }
+
boost::filesystem::path signer_chain_directory () const;
+ void changed ();
+ boost::signals2::signal<void ()> Changed;
+
static Config* instance ();
static void drop ();
boost::filesystem::path file (bool) const;
void read ();
void read_old_metadata ();
+ void write () const;
/** number of threads to use for J2K encoding on the local machine */
int _num_local_encoding_threads;
int _default_still_length;
Ratio const * _default_container;
DCPContentType const * _default_dcp_content_type;
- libdcp::XMLMetadata _dcp_metadata;
+ dcp::XMLMetadata _dcp_metadata;
int _default_j2k_bandwidth;
int _default_audio_delay;
std::vector<PresetColourConversion> _colour_conversions;
/** true to check for updates on startup */
bool _check_for_updates;
bool _check_for_test_updates;
+ /** maximum allowed J2K bandwidth in bits per second */
+ int _maximum_j2k_bandwidth;
/** Singleton instance, or 0 */
static Config* _instance;
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
Taken from code Copyright (C) 2010-2011 Terrence Meiczinger
This program is free software; you can redistribute it and/or modify
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
-#include <libdcp/rec709_linearised_gamma_lut.h>
-#include <libdcp/srgb_linearised_gamma_lut.h>
-#include <libdcp/gamma_lut.h>
-#include <libdcp/xyz_frame.h>
-#include <libdcp/rgb_xyz.h>
-#include <libdcp/colour_matrix.h>
+#include <dcp/gamma_lut.h>
+#include <dcp/xyz_frame.h>
+#include <dcp/rgb_xyz.h>
+#include <dcp/colour_matrix.h>
#include <libcxml/cxml.h>
#include "film.h"
#include "dcp_video_frame.h"
using std::cout;
using boost::shared_ptr;
using boost::lexical_cast;
-using libdcp::Size;
+using dcp::Size;
#define DCI_COEFFICENT (48.0 / 52.37)
shared_ptr<EncodedData>
DCPVideoFrame::encode_locally ()
{
- shared_ptr<libdcp::LUT> in_lut;
- if (_conversion.input_gamma_linearised) {
- in_lut = libdcp::SRGBLinearisedGammaLUT::cache.get (12, _conversion.input_gamma);
- } else {
- in_lut = libdcp::GammaLUT::cache.get (12, _conversion.input_gamma);
- }
+ shared_ptr<dcp::GammaLUT> in_lut;
+ in_lut = dcp::GammaLUT::cache.get (12, _conversion.input_gamma, _conversion.input_gamma_linearised);
/* XXX: libdcp should probably use boost */
}
}
- shared_ptr<libdcp::XYZFrame> xyz = libdcp::rgb_to_xyz (
+ shared_ptr<dcp::XYZFrame> xyz = dcp::rgb_to_xyz (
_image,
in_lut,
- libdcp::GammaLUT::cache.get (16, 1 / _conversion.output_gamma),
+ dcp::GammaLUT::cache.get (16, 1 / _conversion.output_gamma, false),
matrix
);
stringstream xml;
doc.write_to_stream (xml, "UTF-8");
- _log->log (String::compose (
- N_("Sending to remote; pixel format %1, components %2, lines (%3,%4,%5), line sizes (%6,%7,%8)"),
- _image->pixel_format(), _image->components(),
- _image->lines(0), _image->lines(1), _image->lines(2),
- _image->line_size()[0], _image->line_size()[1], _image->line_size()[2]
- ));
+ _log->log (String::compose (N_("Sending frame %1 to remote"), _frame));
socket->write (xml.str().length() + 1);
socket->write ((uint8_t *) xml.str().c_str(), xml.str().length() + 1);
}
void
-EncodedData::write_info (shared_ptr<const Film> film, int frame, Eyes eyes, libdcp::FrameInfo fin) const
+EncodedData::write_info (shared_ptr<const Film> film, int frame, Eyes eyes, dcp::FrameInfo fin) const
{
boost::filesystem::path const info = film->info_path (frame, eyes);
FILE* h = fopen_boost (info, "w");
#include "writer.h"
#include "server_finder.h"
#include "player.h"
+#include "dcp_video.h"
#include "i18n.h"
, _video_frames_out (0)
, _terminate (false)
{
- _have_a_real_frame[EYES_BOTH] = false;
- _have_a_real_frame[EYES_LEFT] = false;
- _have_a_real_frame[EYES_RIGHT] = false;
+
}
Encoder::~Encoder ()
void
Encoder::add_worker_threads (ServerDescription d)
{
+ _film->log()->log (String::compose (N_("Adding %1 worker threads for remote %2"), d.host_name ()));
for (int i = 0; i < d.threads(); ++i) {
_threads.push_back (new boost::thread (boost::bind (&Encoder::encoder_thread, this, d)));
}
}
void
-Encoder::process_video (shared_ptr<PlayerImage> image, Eyes eyes, ColourConversion conversion, bool same)
+Encoder::process_video (shared_ptr<DCPVideo> frame)
{
_waker.nudge ();
rethrow ();
if (_writer->can_fake_write (_video_frames_out)) {
- _writer->fake_write (_video_frames_out, eyes);
- _have_a_real_frame[eyes] = false;
- frame_done ();
- } else if (same && _have_a_real_frame[eyes]) {
- /* Use the last frame that we encoded. */
- _writer->repeat (_video_frames_out, eyes);
+ _writer->fake_write (_video_frames_out, frame->eyes ());
frame_done ();
} else {
/* Queue this new frame for encoding */
TIMING ("adding to queue of %1", _queue.size ());
_queue.push_back (shared_ptr<DCPVideoFrame> (
new DCPVideoFrame (
- image->image(), _video_frames_out, eyes, conversion, _film->video_frame_rate(),
- _film->j2k_bandwidth(), _film->resolution(), _film->log()
+ frame->image(PIX_FMT_RGB24, false),
+ _video_frames_out,
+ frame->eyes(),
+ frame->conversion(),
+ _film->video_frame_rate(),
+ _film->j2k_bandwidth(),
+ _film->resolution(),
+ _film->log()
)
));
_condition.notify_all ();
- _have_a_real_frame[eyes] = true;
}
- if (eyes != EYES_LEFT) {
+ if (frame->eyes() != EYES_LEFT) {
++_video_frames_out;
}
}
/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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 <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
-#include <boost/date_time.hpp>
#include <libxml++/libxml++.h>
#include <libcxml/cxml.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/cpl.h>
-#include <libdcp/signer.h>
-#include <libdcp/util.h>
-#include <libdcp/kdm.h>
+#include <dcp/signer_chain.h>
+#include <dcp/cpl.h>
+#include <dcp/signer.h>
+#include <dcp/util.h>
+#include <dcp/local_time.h>
#include "film.h"
#include "job.h"
#include "util.h"
using boost::ends_with;
using boost::starts_with;
using boost::optional;
-using libdcp::Size;
-using libdcp::Signer;
+using dcp::Size;
+using dcp::Signer;
/* 5 -> 6
* AudioMapping XML changed.
if (dcp_name().find ("/") != string::npos) {
throw BadSettingError (_("name"), _("cannot contain slashes"));
}
-
+
+ /* It seems to make sense to auto-save metadata here, since the make DCP may last
+ a long time, and crashes/power failures are moderately likely.
+ */
+ write_metadata ();
+
log()->log (String::compose ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary()));
{
_sequence_video = f.bool_child ("SequenceVideo");
_three_d = f.bool_child ("ThreeD");
_interop = f.bool_child ("Interop");
- _key = libdcp::Key (f.string_child ("Key"));
+ _key = dcp::Key (f.string_child ("Key"));
list<string> notes;
/* This method is the only one that can return notes (so far) */
return file (p);
}
-/** @return List of subdirectories (not full paths) containing DCPs that can be successfully libdcp::DCP::read() */
+/** @return List of subdirectories (not full paths) containing DCPs that can be successfully dcp::DCP::read() */
list<boost::filesystem::path>
Film::dcps () const
{
) {
try {
- libdcp::DCP dcp (*i);
+ dcp::DCP dcp (*i);
dcp.read ();
out.push_back (i->path().leaf ());
} catch (...) {
_playlist->move_later (c);
}
-Time
+DCPTime
Film::length () const
{
return _playlist->length ();
return _playlist->has_subtitles ();
}
-OutputVideoFrame
+int
Film::best_video_frame_rate () const
{
return _playlist->best_dcp_frame_rate ();
}
+FrameRateChange
+Film::active_frame_rate_change (DCPTime t) const
+{
+ return _playlist->active_frame_rate_change (t, video_frame_rate ());
+}
+
void
Film::playlist_content_changed (boost::weak_ptr<Content> c, int p)
{
signal_changed (CONTENT);
}
-OutputAudioFrame
-Film::time_to_audio_frames (Time t) const
-{
- return divide_with_round (t * audio_frame_rate (), TIME_HZ);
-}
-
-OutputVideoFrame
-Film::time_to_video_frames (Time t) const
-{
- return divide_with_round (t * video_frame_rate (), TIME_HZ);
-}
-
-Time
-Film::audio_frames_to_time (OutputAudioFrame f) const
-{
- return divide_with_round (f * TIME_HZ, audio_frame_rate ());
-}
-
-Time
-Film::video_frames_to_time (OutputVideoFrame f) const
-{
- return divide_with_round (f * TIME_HZ, video_frame_rate ());
-}
-
-OutputAudioFrame
+int
Film::audio_frame_rate () const
{
/* XXX */
}
/** @return Size of the largest possible image in whatever resolution we are using */
-libdcp::Size
+dcp::Size
Film::full_frame () const
{
switch (_resolution) {
case RESOLUTION_2K:
- return libdcp::Size (2048, 1080);
+ return dcp::Size (2048, 1080);
case RESOLUTION_4K:
- return libdcp::Size (4096, 2160);
+ return dcp::Size (4096, 2160);
}
assert (false);
- return libdcp::Size ();
+ return dcp::Size ();
}
/** @return Size of the frame */
-libdcp::Size
+dcp::Size
Film::frame_size () const
{
return fit_ratio_within (container()->ratio(), full_frame ());
}
-libdcp::KDM
+dcp::EncryptedKDM
Film::make_kdm (
- shared_ptr<libdcp::Certificate> target,
+ shared_ptr<dcp::Certificate> target,
boost::filesystem::path dcp_dir,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until
+ dcp::LocalTime from,
+ dcp::LocalTime until
) const
{
shared_ptr<const Signer> signer = make_signer ();
- libdcp::DCP dcp (dir (dcp_dir.string ()));
+ dcp::DCP dcp (dir (dcp_dir.string ()));
try {
dcp.read ();
throw KDMError (_("Could not read DCP to make KDM for"));
}
- time_t now = time (0);
- struct tm* tm = localtime (&now);
- string const issue_date = libdcp::tm_to_string (tm);
-
dcp.cpls().front()->set_mxf_keys (key ());
- return libdcp::KDM (dcp.cpls().front(), signer, target, from, until, "DCP-o-matic", issue_date);
+ return dcp::DecryptedKDM (
+ dcp.cpls().front(), from, until, "DCP-o-matic", dcp.cpls().front()->content_title_text(), dcp::LocalTime().as_string()
+ ).encrypt (signer, target);
}
-list<libdcp::KDM>
+list<dcp::EncryptedKDM>
Film::make_kdms (
list<shared_ptr<Screen> > screens,
boost::filesystem::path dcp,
- boost::posix_time::ptime from,
- boost::posix_time::ptime until
+ dcp::LocalTime from,
+ dcp::LocalTime until
) const
{
- list<libdcp::KDM> kdms;
+ list<dcp::EncryptedKDM> kdms;
for (list<shared_ptr<Screen> >::iterator i = screens.begin(); i != screens.end(); ++i) {
kdms.push_back (make_kdm ((*i)->certificate, dcp, from, until));
uint64_t
Film::required_disk_space () const
{
- return uint64_t (j2k_bandwidth() / 8) * length() / TIME_HZ;
+ return uint64_t (j2k_bandwidth() / 8) * length().seconds();
}
/** This method checks the disk that the Film is on and tries to decide whether or not
void
Playlist::content_changed (weak_ptr<Content> content, int property, bool frequent)
{
- if (property == ContentProperty::LENGTH) {
+ if (property == ContentProperty::LENGTH || property == VideoContentProperty::VIDEO_FRAME_TYPE) {
maybe_sequence_video ();
}
_sequencing_video = true;
ContentList cl = _content;
- DCPTime next;
- Time next_left = 0;
- Time next_right = 0;
++ DCPTime next_left;
++ DCPTime next_right;
for (ContentList::iterator i = _content.begin(); i != _content.end(); ++i) {
- if (!dynamic_pointer_cast<VideoContent> (*i)) {
+ shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (*i);
+ if (!vc) {
continue;
}
-
+
- (*i)->set_position (next);
- next = (*i)->end() + DCPTime::delta ();
+ if (vc->video_frame_type() == VIDEO_FRAME_TYPE_3D_RIGHT) {
+ vc->set_position (next_right);
- next_right = vc->end() + 1;
++ next_right = vc->end() + DCPTime::delta ();
+ } else {
+ vc->set_position (next_left);
- next_left = vc->end() + 1;
++ next_left = vc->end() + DCPTime::delta ();
+ }
}
/* This won't change order, so it does not need a sort */
return best->dcp;
}
-Time
+DCPTime
Playlist::length () const
{
- Time len = 0;
+ DCPTime len;
for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
- len = max (len, (*i)->end() + 1);
+ len = max (len, (*i)->end() + DCPTime::delta ());
}
return len;
}
}
-Time
+DCPTime
Playlist::video_end () const
{
- Time end = 0;
+ DCPTime end;
for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
if (dynamic_pointer_cast<const VideoContent> (*i)) {
end = max (end, (*i)->end ());
return end;
}
+FrameRateChange
+Playlist::active_frame_rate_change (DCPTime t, int dcp_video_frame_rate) const
+{
+ for (ContentList::const_iterator i = _content.begin(); i != _content.end(); ++i) {
+ shared_ptr<const VideoContent> vc = dynamic_pointer_cast<const VideoContent> (*i);
+ if (!vc) {
+ break;
+ }
+
+ if (vc->position() >= t && t < vc->end()) {
+ return FrameRateChange (vc->video_frame_rate(), dcp_video_frame_rate);
+ }
+ }
+
+ return FrameRateChange (dcp_video_frame_rate, dcp_video_frame_rate);
+}
+
void
Playlist::set_sequence_video (bool s)
{
void
Playlist::repeat (ContentList c, int n)
{
- pair<Time, Time> range (TIME_MAX, 0);
+ pair<DCPTime, DCPTime> range (DCPTime::max (), DCPTime ());
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
range.first = min (range.first, (*i)->position ());
range.second = max (range.second, (*i)->position ());
range.second = max (range.second, (*i)->end ());
}
- Time pos = range.second;
+ DCPTime pos = range.second;
for (int i = 0; i < n; ++i) {
for (ContentList::iterator i = c.begin(); i != c.end(); ++i) {
shared_ptr<Content> copy = (*i)->clone ();
return;
}
- Time const p = (*previous)->position ();
+ DCPTime const p = (*previous)->position ();
(*previous)->set_position (p + c->length_after_trim ());
c->set_position (p);
sort (_content.begin(), _content.end(), ContentSorter ());
return;
}
- Time const p = (*next)->position ();
+ DCPTime const p = (*next)->position ();
(*next)->set_position (c->position ());
c->set_position (p + c->length_after_trim ());
sort (_content.begin(), _content.end(), ContentSorter ());
/*
-- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
++ Copyright (C) 2013-2014 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
using boost::shared_ptr;
using boost::lexical_cast;
-int const SndfileContentProperty::VIDEO_FRAME_RATE = 600;
-
SndfileContent::SndfileContent (shared_ptr<const Film> f, boost::filesystem::path p)
: Content (f, p)
, AudioContent (f, p)
, _audio_mapping (node->node_child ("AudioMapping"), version)
{
_audio_channels = node->number_child<int> ("AudioChannels");
- _audio_length = node->number_child<AudioContent::Frame> ("AudioLength");
+ _audio_length = ContentTime (node->number_child<int64_t> ("AudioLength"));
_audio_frame_rate = node->number_child<int> ("AudioFrameRate");
}
_("%1 channels, %2kHz, %3 samples"),
audio_channels(),
content_audio_frame_rate() / 1000.0,
- audio_length()
+ audio_length().frames (content_audio_frame_rate ())
);
return s.str ();
job->set_progress_unknown ();
Content::examine (job);
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
-
- SndfileDecoder dec (film, shared_from_this());
+ SndfileDecoder dec (shared_from_this());
{
boost::mutex::scoped_lock lm (_mutex);
AudioContent::as_xml (node);
node->add_child("AudioChannels")->add_child_text (lexical_cast<string> (audio_channels ()));
- node->add_child("AudioLength")->add_child_text (lexical_cast<string> (audio_length ()));
+ node->add_child("AudioLength")->add_child_text (lexical_cast<string> (audio_length().get ()));
node->add_child("AudioFrameRate")->add_child_text (lexical_cast<string> (content_audio_frame_rate ()));
_audio_mapping.as_xml (node->add_child("AudioMapping"));
}
-Time
+DCPTime
SndfileContent::full_length () const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- float const rate = _video_frame_rate.get_value_or (film->video_frame_rate ());
- OutputAudioFrame const len = divide_with_round (
- audio_length() * output_audio_frame_rate() * rate,
- content_audio_frame_rate() * film->video_frame_rate()
- );
-
- return film->audio_frames_to_time (len);
+ return DCPTime (audio_length(), film->active_frame_rate_change (position ()));
}
int
signal_changed (AudioContentProperty::AUDIO_MAPPING);
}
-float
-SndfileContent::video_frame_rate () const
-{
- {
- boost::mutex::scoped_lock lm (_mutex);
- if (_video_frame_rate) {
- return _video_frame_rate.get ();
- }
- }
-
- shared_ptr<const Film> film = _film.lock ();
- assert (film);
- return film->video_frame_rate ();
-}
+
#include <vector>
#include <stdint.h>
#include <boost/shared_ptr.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
+#include "dcpomatic_time.h"
+#include "position.h"
class Content;
class VideoContent;
*/
#define SERVER_LINK_VERSION 1
-typedef int64_t Time;
-#define TIME_MAX INT64_MAX
-#define TIME_HZ ((Time) 96000)
-typedef int64_t OutputAudioFrame;
-typedef int OutputVideoFrame;
typedef std::vector<boost::shared_ptr<Content> > ContentList;
typedef std::vector<boost::shared_ptr<VideoContent> > VideoContentList;
typedef std::vector<boost::shared_ptr<AudioContent> > AudioContentList;
typedef std::vector<boost::shared_ptr<SubtitleContent> > SubtitleContentList;
typedef std::vector<boost::shared_ptr<FFmpegContent> > FFmpegContentList;
-template<class T>
+typedef int64_t VideoFrame;
+typedef int64_t AudioFrame;
+
+/* XXX -> DCPAudio */
struct TimedAudioBuffers
{
TimedAudioBuffers ()
: time (0)
{}
- TimedAudioBuffers (boost::shared_ptr<AudioBuffers> a, T t)
+ TimedAudioBuffers (boost::shared_ptr<AudioBuffers> a, DCPTime t)
: audio (a)
, time (t)
{}
boost::shared_ptr<AudioBuffers> audio;
- T time;
+ DCPTime time;
};
enum VideoFrameType
VIDEO_FRAME_TYPE_2D,
VIDEO_FRAME_TYPE_3D_LEFT_RIGHT,
VIDEO_FRAME_TYPE_3D_TOP_BOTTOM,
- VIDEO_FRAME_TYPE_3D_ALTERNATE
+ VIDEO_FRAME_TYPE_3D_ALTERNATE,
+ /** This content is all the left frames of some 3D */
+ VIDEO_FRAME_TYPE_3D_LEFT,
+ /** This content is all the right frames of some 3D */
+ VIDEO_FRAME_TYPE_3D_RIGHT
};
enum Eyes
/** Number of pixels to remove from the bottom */
int bottom;
- libdcp::Size apply (libdcp::Size s, int minimum = 4) const {
+ dcp::Size apply (dcp::Size s, int minimum = 4) const {
s.width -= left + right;
s.height -= top + bottom;
/*
-- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
++ Copyright (C) 2012-2014 Carl Hetherington <cth@carlh.net>
Copyright (C) 2000-2007 Paul Davis
This program is free software; you can redistribute it and/or modify
#include <glib.h>
#include <openjpeg.h>
#include <openssl/md5.h>
+#include <pangomm/init.h>
#include <magick/MagickCore.h>
#include <magick/version.h>
-#include <libdcp/version.h>
-#include <libdcp/util.h>
-#include <libdcp/signer_chain.h>
-#include <libdcp/signer.h>
+#include <dcp/version.h>
+#include <dcp/util.h>
+#include <dcp/signer_chain.h>
+#include <dcp/signer.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include "job.h"
#include "cross.h"
#include "video_content.h"
+#include "rect.h"
#ifdef DCPOMATIC_WINDOWS
#include "stack.hpp"
#endif
using boost::thread;
using boost::lexical_cast;
using boost::optional;
-using libdcp::Size;
+using dcp::Size;
static boost::thread::id ui_thread;
static boost::filesystem::path backtrace_file;
return s.str ();
}
-/** Return a user-readable string summarising the versions of our dependencies */
-string
-dependency_version_summary ()
-{
- stringstream s;
- s << N_("libopenjpeg ") << opj_version () << N_(", ")
- << N_("libavcodec ") << ffmpeg_version_to_string (avcodec_version()) << N_(", ")
- << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ")
- << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ")
- << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ")
- << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
- << MagickVersion << N_(", ")
- << N_("libssh ") << ssh_version (0) << N_(", ")
- << N_("libdcp ") << libdcp::version << N_(" git ") << libdcp::git_commit;
-
- return s.str ();
-}
-
double
seconds (struct timeval t)
{
set_terminate (terminate);
- libdcp::init ();
+ Pango::init ();
+ dcp::init ();
Ratio::setup_ratios ();
VideoContentScale::setup_scales ();
return s.str ();
}
-static bool
-about_equal (float a, float b)
-{
- /* A film of F seconds at f FPS will be Ff frames;
- Consider some delta FPS d, so if we run the same
- film at (f + d) FPS it will last F(f + d) seconds.
-
- Hence the difference in length over the length of the film will
- be F(f + d) - Ff frames
- = Ff + Fd - Ff frames
- = Fd frames
- = Fd/f seconds
-
- So if we accept a difference of 1 frame, ie 1/f seconds, we can
- say that
-
- 1/f = Fd/f
- ie 1 = Fd
- ie d = 1/F
-
- So for a 3hr film, ie F = 3 * 60 * 60 = 10800, the acceptable
- FPS error is 1/F ~= 0.0001 ~= 10-e4
- */
-
- return (fabs (a - b) < 1e-4);
-}
-
/** @param An arbitrary audio frame rate.
* @return The appropriate DCP-approved frame rate (48kHz or 96kHz).
*/
assert (boost::this_thread::get_id() == ui_thread);
}
-/** @param v Content video frame.
- * @param audio_sample_rate Source audio sample rate.
- * @param frames_per_second Number of video frames per second.
- * @return Equivalent number of audio frames for `v'.
- */
-int64_t
-video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second)
-{
- return ((int64_t) v * audio_sample_rate / frames_per_second);
-}
-
string
audio_channel_name (int c)
{
return channels[c];
}
-FrameRateConversion::FrameRateConversion (float source, int dcp)
- : skip (false)
- , repeat (1)
- , change_speed (false)
-{
- if (fabs (source / 2.0 - dcp) < fabs (source - dcp)) {
- /* The difference between source and DCP frame rate will be lower
- (i.e. better) if we skip.
- */
- skip = true;
- } else if (fabs (source * 2 - dcp) < fabs (source - dcp)) {
- /* The difference between source and DCP frame rate would be better
- if we repeated each frame once; it may be better still if we
- repeated more than once. Work out the required repeat.
- */
- repeat = round (dcp / source);
- }
-
- change_speed = !about_equal (source * factor(), dcp);
-
- if (!skip && repeat == 1 && !change_speed) {
- description = _("Content and DCP have the same rate.\n");
- } else {
- if (skip) {
- description = _("DCP will use every other frame of the content.\n");
- } else if (repeat == 2) {
- description = _("Each content frame will be doubled in the DCP.\n");
- } else if (repeat > 2) {
- description = String::compose (_("Each content frame will be repeated %1 more times in the DCP.\n"), repeat - 1);
- }
-
- if (change_speed) {
- float const pc = dcp * 100 / (source * factor());
- description += String::compose (_("DCP will run at %1%% of the content speed.\n"), pc);
- }
- }
-}
-
LocaleGuard::LocaleGuard ()
: _old (0)
{
return t;
}
-shared_ptr<const libdcp::Signer>
+shared_ptr<const dcp::Signer>
make_signer ()
{
boost::filesystem::path const sd = Config::instance()->signer_chain_directory ();
if (!boost::filesystem::exists (p)) {
boost::filesystem::remove_all (sd);
boost::filesystem::create_directories (sd);
- libdcp::make_signer_chain (sd, openssl_path ());
+ dcp::make_signer_chain (sd, openssl_path ());
break;
}
++i;
}
- libdcp::CertificateChain chain;
+ dcp::CertificateChain chain;
{
boost::filesystem::path p (sd);
p /= "ca.self-signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
}
{
boost::filesystem::path p (sd);
p /= "intermediate.signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
}
{
boost::filesystem::path p (sd);
p /= "leaf.signed.pem";
- chain.add (shared_ptr<libdcp::Certificate> (new libdcp::Certificate (p)));
+ chain.add (shared_ptr<dcp::Certificate> (new dcp::Certificate (p)));
}
boost::filesystem::path signer_key (sd);
signer_key /= "leaf.key";
- return shared_ptr<const libdcp::Signer> (new libdcp::Signer (chain, signer_key));
+ return shared_ptr<const dcp::Signer> (new dcp::Signer (chain, signer_key));
}
map<string, string>
return r;
}
-libdcp::Size
-fit_ratio_within (float ratio, libdcp::Size full_frame)
+dcp::Size
+fit_ratio_within (float ratio, dcp::Size full_frame)
{
if (ratio < full_frame.ratio ()) {
- return libdcp::Size (rint (full_frame.height * ratio), full_frame.height);
+ return dcp::Size (rint (full_frame.height * ratio), full_frame.height);
}
- return libdcp::Size (full_frame.width, rint (full_frame.width / ratio));
+ return dcp::Size (full_frame.width, rint (full_frame.width / ratio));
}
void *
}
}
+/** Return a user-readable string summarising the versions of our dependencies */
+string
+dependency_version_summary ()
+{
+ stringstream s;
+ s << N_("libopenjpeg ") << opj_version () << N_(", ")
+ << N_("libavcodec ") << ffmpeg_version_to_string (avcodec_version()) << N_(", ")
+ << N_("libavfilter ") << ffmpeg_version_to_string (avfilter_version()) << N_(", ")
+ << N_("libavformat ") << ffmpeg_version_to_string (avformat_version()) << N_(", ")
+ << N_("libavutil ") << ffmpeg_version_to_string (avutil_version()) << N_(", ")
+ << N_("libswscale ") << ffmpeg_version_to_string (swscale_version()) << N_(", ")
+ << MagickVersion << N_(", ")
+ << N_("libssh ") << ssh_version (0) << N_(", ")
+ << N_("libdcp ") << dcp::version << N_(" git ") << dcp::git_commit;
+
+ return s.str ();
+}
++
+ ScopedTemporary::ScopedTemporary ()
+ : _open (0)
+ {
+ _file = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path ();
+ }
+
+ ScopedTemporary::~ScopedTemporary ()
+ {
+ close ();
+ boost::system::error_code ec;
+ boost::filesystem::remove (_file, ec);
+ }
+
+ char const *
+ ScopedTemporary::c_str () const
+ {
+ return _file.string().c_str ();
+ }
+
+ FILE*
+ ScopedTemporary::open (char const * params)
+ {
+ _open = fopen (c_str(), params);
+ return _open;
+ }
+
+ void
+ ScopedTemporary::close ()
+ {
+ if (_open) {
+ fclose (_open);
+ _open = 0;
+ }
+ }
#include <boost/asio.hpp>
#include <boost/optional.hpp>
#include <boost/filesystem.hpp>
-#include <libdcp/util.h>
+#include <dcp/util.h>
+#include <dcp/signer.h>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavfilter/avfilter.h>
extern boost::filesystem::path mo_path ();
#endif
extern std::string tidy_for_filename (std::string);
-extern boost::shared_ptr<const libdcp::Signer> make_signer ();
-extern libdcp::Size fit_ratio_within (float ratio, libdcp::Size);
+extern boost::shared_ptr<const dcp::Signer> make_signer ();
+extern dcp::Size fit_ratio_within (float ratio, dcp::Size);
extern std::string entities_to_text (std::string e);
extern std::map<std::string, std::string> split_get_request (std::string url);
-
-struct FrameRateConversion
-{
- FrameRateConversion (float, int);
-
- /** @return factor by which to multiply a source frame rate
- to get the effective rate after any skip or repeat has happened.
- */
- float factor () const {
- if (skip) {
- return 0.5;
- }
-
- return repeat;
- }
-
- /** true to skip every other frame */
- bool skip;
- /** number of times to use each frame (e.g. 1 is normal, 2 means repeat each frame once, and so on) */
- int repeat;
- /** true if this DCP will run its video faster or slower than the source
- * without taking into account `repeat' nor `skip'.
- * (e.g. change_speed will be true if
- * source is 29.97fps, DCP is 30fps
- * source is 14.50fps, DCP is 30fps
- * but not if
- * source is 15.00fps, DCP is 30fps
- * source is 12.50fps, DCP is 25fps)
- */
- bool change_speed;
-
- std::string description;
-};
-
extern int dcp_audio_frame_rate (int);
extern int stride_round_up (int, int const *, int);
extern std::multimap<std::string, std::string> read_key_value (std::istream& s);
int _timeout;
};
-extern int64_t video_frames_to_audio_frames (VideoContent::Frame v, float audio_sample_rate, float frames_per_second);
-
class LocaleGuard
{
public:
char* _old;
};
+ class ScopedTemporary
+ {
+ public:
+ ScopedTemporary ();
+ ~ScopedTemporary ();
+
+ boost::filesystem::path file () const {
+ return _file;
+ }
+
+ char const * c_str () const;
+ FILE* open (char const *);
+ void close ();
+
+ private:
+ boost::filesystem::path _file;
+ FILE* _open;
+ };
#endif
#include <iomanip>
#include <libcxml/cxml.h>
-#include <libdcp/colour_matrix.h>
+#include <dcp/colour_matrix.h>
#include "video_content.h"
#include "video_examiner.h"
#include "compose.hpp"
setup_default_colour_conversion ();
}
-VideoContent::VideoContent (shared_ptr<const Film> f, Time s, VideoContent::Frame len)
+VideoContent::VideoContent (shared_ptr<const Film> f, DCPTime s, ContentTime len)
: Content (f, s)
, _video_length (len)
, _video_frame_rate (0)
VideoContent::VideoContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node, int version)
: Content (f, node)
{
- _video_length = node->number_child<VideoContent::Frame> ("VideoLength");
+ _video_length = ContentTime (node->number_child<int64_t> ("VideoLength"));
_video_size.width = node->number_child<int> ("VideoWidth");
_video_size.height = node->number_child<int> ("VideoHeight");
_video_frame_rate = node->number_child<float> ("VideoFrameRate");
VideoContent::as_xml (xmlpp::Node* node) const
{
boost::mutex::scoped_lock lm (_mutex);
- node->add_child("VideoLength")->add_child_text (lexical_cast<string> (_video_length));
+ node->add_child("VideoLength")->add_child_text (lexical_cast<string> (_video_length.get ()));
node->add_child("VideoWidth")->add_child_text (lexical_cast<string> (_video_size.width));
node->add_child("VideoHeight")->add_child_text (lexical_cast<string> (_video_size.height));
node->add_child("VideoFrameRate")->add_child_text (lexical_cast<string> (_video_frame_rate));
void
VideoContent::setup_default_colour_conversion ()
{
- _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, libdcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
+ _colour_conversion = PresetColourConversion (_("sRGB"), 2.4, true, dcp::colour_matrix::srgb_to_xyz, 2.6).conversion;
}
void
VideoContent::take_from_video_examiner (shared_ptr<VideoExaminer> d)
{
/* These examiner calls could call other content methods which take a lock on the mutex */
- libdcp::Size const vs = d->video_size ();
+ dcp::Size const vs = d->video_size ();
float const vfr = d->video_frame_rate ();
{
{
return String::compose (
"video: length %1, size %2x%3, rate %4",
- video_length_after_3d_combine(), video_size().width, video_size().height, video_frame_rate()
+ video_length_after_3d_combine().seconds(),
+ video_size().width,
+ video_size().height,
+ video_frame_rate()
);
}
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_3d_split () const
{
- libdcp::Size const s = video_size ();
+ dcp::Size const s = video_size ();
switch (video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
case VIDEO_FRAME_TYPE_3D_ALTERNATE:
+ case VIDEO_FRAME_TYPE_3D_LEFT:
+ case VIDEO_FRAME_TYPE_3D_RIGHT:
return s;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
- return libdcp::Size (s.width / 2, s.height);
+ return dcp::Size (s.width / 2, s.height);
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
- return libdcp::Size (s.width, s.height / 2);
+ return dcp::Size (s.width, s.height / 2);
}
assert (false);
}
/** @return Video size after 3D split and crop */
-libdcp::Size
+dcp::Size
VideoContent::video_size_after_crop () const
{
return crop().apply (video_size_after_3d_split ());
}
/** @param t A time offset from the start of this piece of content.
- * @return Corresponding frame index.
+ * @return Corresponding time with respect to the content.
*/
-VideoContent::Frame
-VideoContent::time_to_content_video_frames (Time t) const
+ContentTime
+VideoContent::dcp_time_to_content_time (DCPTime t) const
{
shared_ptr<const Film> film = _film.lock ();
assert (film);
-
- FrameRateConversion frc (video_frame_rate(), film->video_frame_rate());
-
- /* Here we are converting from time (in the DCP) to a frame number in the content.
- Hence we need to use the DCP's frame rate and the double/skip correction, not
- the source's rate.
- */
- return t * film->video_frame_rate() / (frc.factor() * TIME_HZ);
+ return ContentTime (t, FrameRateChange (video_frame_rate(), film->video_frame_rate()));
}
VideoContentScale::VideoContentScale (Ratio const * r)
/** @param display_container Size of the container that we are displaying this content in.
* @param film_container The size of the film's image.
*/
-libdcp::Size
-VideoContentScale::size (shared_ptr<const VideoContent> c, libdcp::Size display_container, libdcp::Size film_container) const
+dcp::Size
+VideoContentScale::size (shared_ptr<const VideoContent> c, dcp::Size display_container, dcp::Size film_container) const
{
if (_ratio) {
return fit_ratio_within (_ratio->ratio (), display_container);
}
- libdcp::Size const ac = c->video_size_after_crop ();
+ dcp::Size const ac = c->video_size_after_crop ();
/* Force scale if the film_container is smaller than the content's image */
if (_scale || film_container.width < ac.width || film_container.height < ac.height) {
/* Scale the image so that it will be in the right place in film_container, even if display_container is a
different size.
*/
- return libdcp::Size (
+ return dcp::Size (
c->video_size().width * float(display_container.width) / film_container.width,
c->video_size().height * float(display_container.height) / film_container.height
);
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2014 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
VideoContentScale (bool);
VideoContentScale (boost::shared_ptr<cxml::Node>);
- libdcp::Size size (boost::shared_ptr<const VideoContent>, libdcp::Size, libdcp::Size) const;
+ dcp::Size size (boost::shared_ptr<const VideoContent>, dcp::Size, dcp::Size) const;
std::string id () const;
std::string name () const;
void as_xml (xmlpp::Node *) const;
typedef int Frame;
VideoContent (boost::shared_ptr<const Film>);
- VideoContent (boost::shared_ptr<const Film>, Time, VideoContent::Frame);
+ VideoContent (boost::shared_ptr<const Film>, DCPTime, ContentTime);
VideoContent (boost::shared_ptr<const Film>, boost::filesystem::path);
VideoContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>, int);
VideoContent (boost::shared_ptr<const Film>, std::vector<boost::shared_ptr<Content> >);
virtual std::string information () const;
virtual std::string identifier () const;
- VideoContent::Frame video_length () const {
+ ContentTime video_length () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_length;
}
- VideoContent::Frame video_length_after_3d_combine () const {
+ ContentTime video_length_after_3d_combine () const {
boost::mutex::scoped_lock lm (_mutex);
if (_video_frame_type == VIDEO_FRAME_TYPE_3D_ALTERNATE) {
- return _video_length / 2;
+ return ContentTime (_video_length.get() / 2);
}
return _video_length;
}
- libdcp::Size video_size () const {
+ dcp::Size video_size () const {
boost::mutex::scoped_lock lm (_mutex);
return _video_size;
}
return _colour_conversion;
}
- libdcp::Size video_size_after_3d_split () const;
- libdcp::Size video_size_after_crop () const;
+ dcp::Size video_size_after_3d_split () const;
+ dcp::Size video_size_after_crop () const;
- VideoContent::Frame time_to_content_video_frames (Time) const;
+ ContentTime dcp_time_to_content_time (DCPTime) const;
protected:
void take_from_video_examiner (boost::shared_ptr<VideoExaminer>);
- VideoContent::Frame _video_length;
+ ContentTime _video_length;
float _video_frame_rate;
private:
void setup_default_colour_conversion ();
- libdcp::Size _video_size;
+ dcp::Size _video_size;
VideoFrameType _video_frame_type;
Crop _crop;
VideoContentScale _scale;
#include "video_decoder.h"
#include "image.h"
+#include "content_video.h"
#include "i18n.h"
using std::cout;
+using std::list;
using boost::shared_ptr;
+using boost::optional;
-VideoDecoder::VideoDecoder (shared_ptr<const Film> f, shared_ptr<const VideoContent> c)
- : Decoder (f)
- , _video_content (c)
- , _video_position (0)
+VideoDecoder::VideoDecoder (shared_ptr<const VideoContent> c)
+ : _video_content (c)
{
}
+optional<ContentVideo>
+VideoDecoder::decoded_video (VideoFrame frame)
+{
+ for (list<ContentVideo>::const_iterator i = _decoded_video.begin(); i != _decoded_video.end(); ++i) {
+ if (i->frame == frame) {
+ return *i;
+ }
+ }
+
+ return optional<ContentVideo> ();
+}
+
+optional<ContentVideo>
+VideoDecoder::get_video (VideoFrame frame, bool accurate)
+{
+ if (_decoded_video.empty() || (frame < _decoded_video.front().frame || frame > (_decoded_video.back().frame + 1))) {
+ /* Either we have no decoded data, or what we do have is a long way from what we want: seek */
+ seek (ContentTime::from_frames (frame, _video_content->video_frame_rate()), accurate);
+ }
+
+ optional<ContentVideo> dec;
+
+ /* Now enough pass() calls will either:
+ * (a) give us what we want, or
+ * (b) hit the end of the decoder.
+ */
+ if (accurate) {
+ /* We are being accurate, so we want the right frame */
+ while (!decoded_video (frame) && !pass ()) {}
+ dec = decoded_video (frame);
+ } else {
+ /* Any frame will do: use the first one that comes out of pass() */
+ while (_decoded_video.empty() && !pass ()) {}
+ if (!_decoded_video.empty ()) {
+ dec = _decoded_video.front ();
+ }
+ }
+
+ /* Clean up decoded_video */
+ while (!_decoded_video.empty() && _decoded_video.front().frame < (frame - 1)) {
+ _decoded_video.pop_front ();
+ }
+
+ return dec;
+}
+
+
+/** Called by subclasses when they have a video frame ready */
void
-VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoContent::Frame frame)
+VideoDecoder::video (shared_ptr<const Image> image, VideoFrame frame)
{
+ /* Fill in gaps */
+ /* XXX: 3D */
+ while (!_decoded_video.empty () && (_decoded_video.back().frame + 1) < frame) {
+ _decoded_video.push_back (
+ ContentVideo (
+ _decoded_video.back().image,
+ _decoded_video.back().eyes,
+ _decoded_video.back().frame + 1
+ )
+ );
+ }
+
switch (_video_content->video_frame_type ()) {
case VIDEO_FRAME_TYPE_2D:
- Video (image, EYES_BOTH, same, frame);
+ _decoded_video.push_back (ContentVideo (image, EYES_BOTH, frame));
break;
case VIDEO_FRAME_TYPE_3D_ALTERNATE:
- Video (image, (frame % 2) ? EYES_RIGHT : EYES_LEFT, same, frame / 2);
+ _decoded_video.push_back (ContentVideo (image, (frame % 2) ? EYES_RIGHT : EYES_LEFT, frame));
break;
case VIDEO_FRAME_TYPE_3D_LEFT_RIGHT:
{
int const half = image->size().width / 2;
- Video (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, same, frame);
- Video (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, same, frame);
+ _decoded_video.push_back (ContentVideo (image->crop (Crop (0, half, 0, 0), true), EYES_LEFT, frame));
+ _decoded_video.push_back (ContentVideo (image->crop (Crop (half, 0, 0, 0), true), EYES_RIGHT, frame));
break;
}
case VIDEO_FRAME_TYPE_3D_TOP_BOTTOM:
{
int const half = image->size().height / 2;
- Video (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, same, frame);
- Video (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, same, frame);
+ _decoded_video.push_back (ContentVideo (image->crop (Crop (0, 0, 0, half), true), EYES_LEFT, frame));
+ _decoded_video.push_back (ContentVideo (image->crop (Crop (0, 0, half, 0), true), EYES_RIGHT, frame));
break;
}
- Video (image, EYES_LEFT, same, frame);
+ case VIDEO_FRAME_TYPE_3D_LEFT:
- Video (image, EYES_RIGHT, same, frame);
++ _decoded_video.push_back (ContentVideo (image, EYES_LEFT, frame));
+ break;
+ case VIDEO_FRAME_TYPE_3D_RIGHT:
++ _decoded_video.push_back (ContentVideo (image, EYES_RIGHT, frame));
+ break;
+ default:
+ assert (false);
}
-
- _video_position = frame + 1;
+}
+
+void
+VideoDecoder::seek (ContentTime, bool)
+{
+ _decoded_video.clear ();
}
#include <fstream>
#include <cerrno>
-#include <libdcp/mono_picture_asset.h>
-#include <libdcp/stereo_picture_asset.h>
-#include <libdcp/sound_asset.h>
-#include <libdcp/reel.h>
-#include <libdcp/dcp.h>
-#include <libdcp/cpl.h>
+#include <dcp/mono_picture_mxf.h>
+#include <dcp/stereo_picture_mxf.h>
+#include <dcp/sound_mxf.h>
+#include <dcp/sound_mxf_writer.h>
+#include <dcp/reel.h>
+#include <dcp/reel_mono_picture_asset.h>
+#include <dcp/reel_stereo_picture_asset.h>
+#include <dcp/reel_sound_asset.h>
+#include <dcp/dcp.h>
+#include <dcp/cpl.h>
#include "writer.h"
#include "compose.hpp"
#include "film.h"
#include "config.h"
#include "job.h"
#include "cross.h"
+#include "audio_buffers.h"
#include "i18n.h"
using std::stringstream;
using boost::shared_ptr;
using boost::weak_ptr;
+using boost::dynamic_pointer_cast;
int const Writer::_maximum_frames_in_memory = Config::instance()->num_local_encoding_threads() + 4;
, _last_written_eyes (EYES_RIGHT)
, _full_written (0)
, _fake_written (0)
- , _repeat_written (0)
, _pushed_to_disk (0)
{
/* Remove any old DCP */
*/
if (_film->three_d ()) {
- _picture_asset.reset (new libdcp::StereoPictureAsset (_film->internal_video_mxf_dir (), _film->internal_video_mxf_filename ()));
+ _picture_mxf.reset (new dcp::StereoPictureMXF (dcp::Fraction (_film->video_frame_rate (), 1)));
} else {
- _picture_asset.reset (new libdcp::MonoPictureAsset (_film->internal_video_mxf_dir (), _film->internal_video_mxf_filename ()));
+ _picture_mxf.reset (new dcp::MonoPictureMXF (dcp::Fraction (_film->video_frame_rate (), 1)));
}
- _picture_asset->set_edit_rate (_film->video_frame_rate ());
- _picture_asset->set_size (_film->frame_size ());
- _picture_asset->set_interop (_film->interop ());
+ _picture_mxf->set_size (_film->frame_size ());
if (_film->encrypted ()) {
- _picture_asset->set_key (_film->key ());
+ _picture_mxf->set_key (_film->key ());
}
- _picture_asset_writer = _picture_asset->start_write (_first_nonexistant_frame > 0);
+ _picture_mxf_writer = _picture_mxf->start_write (
+ _film->internal_video_mxf_dir() / _film->internal_video_mxf_filename(),
+ _film->interop() ? dcp::INTEROP : dcp::SMPTE,
+ _first_nonexistant_frame > 0
+ );
- _sound_mxf.reset (new dcp::SoundMXF (dcp::Fraction (_film->video_frame_rate(), 1), _film->audio_frame_rate (), _film->audio_channels ()));
+ if (_film->audio_channels ()) {
- _sound_asset.reset (new libdcp::SoundAsset (_film->directory (), _film->audio_mxf_filename ()));
- _sound_asset->set_edit_rate (_film->video_frame_rate ());
- _sound_asset->set_channels (_film->audio_channels ());
- _sound_asset->set_sampling_rate (_film->audio_frame_rate ());
- _sound_asset->set_interop (_film->interop ());
++ _sound_mxf.reset (new dcp::SoundMXF (dcp::Fraction (_film->video_frame_rate(), 1), _film->audio_frame_rate (), _film->audio_channels ()));
- if (_film->encrypted ()) {
- _sound_mxf->set_key (_film->key ());
- }
+ if (_film->encrypted ()) {
- _sound_asset->set_key (_film->key ());
++ _sound_mxf->set_key (_film->key ());
+ }
-
- /* Write the sound asset into the film directory so that we leave the creation
+
- /* Write the sound MXF into the film directory so that we leave the creation
- of the DCP directory until the last minute.
- */
- _sound_mxf_writer = _sound_mxf->start_write (_film->directory() / _film->audio_mxf_filename(), _film->interop() ? dcp::INTEROP : dcp::SMPTE);
++ /* Write the sound MXF into the film directory so that we leave the creation
+ of the DCP directory until the last minute.
+ */
- _sound_asset_writer = _sound_asset->start_write ();
++ _sound_mxf_writer = _sound_mxf->start_write (_film->directory() / _film->audio_mxf_filename(), _film->interop() ? dcp::INTEROP : dcp::SMPTE);
+ }
_thread = new boost::thread (boost::bind (&Writer::thread, this));
}
FILE* ifi = fopen_boost (_film->info_path (frame, eyes), "r");
- libdcp::FrameInfo info (ifi);
+ dcp::FrameInfo info (ifi);
fclose (ifi);
QueueItem qi;
void
Writer::write (shared_ptr<const AudioBuffers> audio)
{
- _sound_mxf_writer->write (audio->data(), audio->frames());
- if (_sound_asset) {
- _sound_asset_writer->write (audio->data(), audio->frames());
++ if (_sound_mxf_writer) {
++ _sound_mxf_writer->write (audio->data(), audio->frames());
+ }
}
/** This must be called from Writer::thread() with an appropriate lock held */
qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, qi.eyes, false)));
}
- libdcp::FrameInfo fin = _picture_asset_writer->write (qi.encoded->data(), qi.encoded->size());
+ dcp::FrameInfo fin = _picture_mxf_writer->write (qi.encoded->data(), qi.encoded->size());
qi.encoded->write_info (_film, qi.frame, qi.eyes, fin);
_last_written[qi.eyes] = qi.encoded;
++_full_written;
}
case QueueItem::FAKE:
_film->log()->log (String::compose (N_("Writer FAKE-writes %1 to MXF"), qi.frame));
- _picture_asset_writer->fake_write (qi.size);
+ _picture_mxf_writer->fake_write (qi.size);
_last_written[qi.eyes].reset ();
++_fake_written;
break;
- case QueueItem::REPEAT:
- {
- _film->log()->log (String::compose (N_("Writer REPEAT-writes %1 to MXF"), qi.frame));
- libdcp::FrameInfo fin = _picture_asset_writer->write (
- _last_written[qi.eyes]->data(),
- _last_written[qi.eyes]->size()
- );
-
- _last_written[qi.eyes]->write_info (_film, qi.frame, qi.eyes, fin);
- ++_repeat_written;
- break;
- }
}
lock.lock ();
_last_written_frame = qi.frame;
_last_written_eyes = qi.eyes;
- if (_film->length()) {
- shared_ptr<Job> job = _job.lock ();
- assert (job);
- int total = _film->time_to_video_frames (_film->length ());
- if (_film->three_d ()) {
- /* _full_written and so on are incremented for each eye, so we need to double the total
- frames to get the correct progress.
- */
- total *= 2;
- }
- job->set_progress (float (_full_written + _fake_written + _repeat_written) / total);
+ shared_ptr<Job> job = _job.lock ();
+ assert (job);
+ int64_t total = _film->length().frames (_film->video_frame_rate ());
+ if (_film->three_d ()) {
+ /* _full_written and so on are incremented for each eye, so we need to double the total
+ frames to get the correct progress.
+ */
+ total *= 2;
+ }
+ if (total) {
+ job->set_progress (float (_full_written + _fake_written) / total);
}
}
terminate_thread (true);
- _picture_asset_writer->finalize ();
- if (_sound_asset_writer) {
- _sound_asset_writer->finalize ();
+ _picture_mxf_writer->finalize ();
- _sound_mxf_writer->finalize ();
++ if (_sound_mxf_writer) {
++ _sound_mxf_writer->finalize ();
+ }
- int const frames = _last_written_frame + 1;
-
- _picture_asset->set_duration (frames);
-
/* Hard-link the video MXF into the DCP */
boost::filesystem::path video_from;
video_from /= _film->internal_video_mxf_dir();
_film->log()->log ("Hard-link failed; fell back to copying");
}
- /* And update the asset */
-
- _picture_asset->set_directory (_film->dir (_film->dcp_name ()));
- _picture_asset->set_file_name (_film->video_mxf_filename ());
-
/* Move the audio MXF into the DCP */
- boost::filesystem::path audio_to;
- audio_to /= _film->dir (_film->dcp_name ());
- audio_to /= _film->audio_mxf_filename ();
-
- boost::filesystem::rename (_film->file (_film->audio_mxf_filename ()), audio_to, ec);
- if (ec) {
- throw FileError (
- String::compose (_("could not move audio MXF into the DCP (%1)"), ec.value ()), _film->file (_film->audio_mxf_filename ())
- );
- if (_sound_asset) {
++ if (_sound_mxf) {
+ boost::filesystem::path audio_to;
+ audio_to /= _film->dir (_film->dcp_name ());
+ audio_to /= _film->audio_mxf_filename ();
+
+ boost::filesystem::rename (_film->file (_film->audio_mxf_filename ()), audio_to, ec);
+ if (ec) {
+ throw FileError (
+ String::compose (_("could not move audio MXF into the DCP (%1)"), ec.value ()), _film->file (_film->audio_mxf_filename ())
+ );
+ }
-
- _sound_asset->set_directory (_film->dir (_film->dcp_name ()));
- _sound_asset->set_duration (frames);
}
-
- libdcp::DCP dcp (_film->dir (_film->dcp_name()));
- shared_ptr<libdcp::CPL> cpl (
- new libdcp::CPL (
- _film->dir (_film->dcp_name()),
+ dcp::DCP dcp (_film->dir (_film->dcp_name()));
+
+ shared_ptr<dcp::CPL> cpl (
+ new dcp::CPL (
_film->dcp_name(),
- _film->dcp_content_type()->libdcp_kind (),
- frames,
- _film->video_frame_rate ()
+ _film->dcp_content_type()->libdcp_kind ()
)
);
- dcp.add_cpl (cpl);
+ dcp.add (cpl);
+
+ shared_ptr<dcp::Reel> reel (new dcp::Reel ());
- cpl->add_reel (shared_ptr<libdcp::Reel> (new libdcp::Reel (
- _picture_asset,
- _sound_asset,
- shared_ptr<libdcp::SubtitleAsset> ()
- )
- ));
+ shared_ptr<dcp::MonoPictureMXF> mono = dynamic_pointer_cast<dcp::MonoPictureMXF> (_picture_mxf);
+ if (mono) {
+ reel->add (shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelMonoPictureAsset (mono, 0)));
+ }
+
+ shared_ptr<dcp::StereoPictureMXF> stereo = dynamic_pointer_cast<dcp::StereoPictureMXF> (_picture_mxf);
+ if (stereo) {
+ reel->add (shared_ptr<dcp::ReelPictureAsset> (new dcp::ReelStereoPictureAsset (stereo, 0)));
+ }
+
+ reel->add (shared_ptr<dcp::ReelSoundAsset> (new dcp::ReelSoundAsset (_sound_mxf, 0)));
+
+ cpl->add (reel);
shared_ptr<Job> job = _job.lock ();
assert (job);
job->sub (_("Computing image digest"));
- _picture_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
+ _picture_mxf->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
- job->sub (_("Computing audio digest"));
- _sound_mxf->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
- if (_sound_asset) {
++ if (_sound_mxf) {
+ job->sub (_("Computing audio digest"));
- _sound_asset->compute_digest (boost::bind (&Job::set_progress, job.get(), _1, false));
++ _sound_mxf->hash (boost::bind (&Job::set_progress, job.get(), _1, false));
+ }
- libdcp::XMLMetadata meta = Config::instance()->dcp_metadata ();
+ dcp::XMLMetadata meta = Config::instance()->dcp_metadata ();
meta.set_issue_date_now ();
- dcp.write_xml (_film->interop (), meta, _film->is_signed() ? make_signer () : shared_ptr<const libdcp::Signer> ());
+ dcp.write_xml (_film->interop () ? dcp::INTEROP : dcp::SMPTE, meta, _film->is_signed() ? make_signer () : shared_ptr<const dcp::Signer> ());
_film->log()->log (
- String::compose (N_("Wrote %1 FULL, %2 FAKE, %3 REPEAT; %4 pushed to disk"), _full_written, _fake_written, _repeat_written, _pushed_to_disk)
+ String::compose (N_("Wrote %1 FULL, %2 FAKE, %3 pushed to disk"), _full_written, _fake_written, _pushed_to_disk)
);
}
-/** Tell the writer that frame `f' should be a repeat of the frame before it */
-void
-Writer::repeat (int f, Eyes e)
-{
- boost::mutex::scoped_lock lock (_mutex);
-
- while (_queued_full_in_memory > _maximum_frames_in_memory) {
- _full_condition.wait (lock);
- }
-
- QueueItem qi;
- qi.type = QueueItem::REPEAT;
- qi.frame = f;
- if (_film->three_d() && e == EYES_BOTH) {
- qi.eyes = EYES_LEFT;
- _queue.push_back (qi);
- qi.eyes = EYES_RIGHT;
- _queue.push_back (qi);
- } else {
- qi.eyes = e;
- _queue.push_back (qi);
- }
-
- _empty_condition.notify_all ();
-}
-
bool
Writer::check_existing_picture_mxf_frame (FILE* mxf, int f, Eyes eyes)
{
return false;
}
- libdcp::FrameInfo info (ifi);
+ dcp::FrameInfo info (ifi);
fclose (ifi);
if (info.size == 0) {
_film->log()->log (String::compose ("Existing frame %1 has no info file", f));
*/
+/** @file src/lib/writer.h
+ * @brief Writer class.
+ */
+
#include <list>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>
class AudioBuffers;
class Job;
-namespace libdcp {
- class MonoPictureAsset;
- class MonoPictureAssetWriter;
- class StereoPictureAsset;
- class StereoPictureAssetWriter;
- class PictureAsset;
- class PictureAssetWriter;
- class SoundAsset;
- class SoundAssetWriter;
+namespace dcp {
+ class MonoPictureMXF;
+ class MonoPictureMXFWriter;
+ class StereoPictureMXF;
+ class StereoPictureMXFWriter;
+ class PictureMXF;
+ class PictureMXFWriter;
+ class SoundMXF;
+ class SoundMXFWriter;
}
struct QueueItem
state but we use the data that is already on disk.
*/
FAKE,
-- /** this is a repeat of the last frame to be written */
-- REPEAT
} type;
/** encoded data for FULL */
bool operator< (QueueItem const & a, QueueItem const & b);
bool operator== (QueueItem const & a, QueueItem const & b);
+/** @class Writer
+ * @brief Class to manage writing JPEG2000 and audio data to MXFs on disk.
+ *
+ * This class creates sound and picture MXFs, then takes EncodedData
+ * or AudioBuffers objects (containing image or sound data respectively)
+ * and writes them to the MXFs.
+ *
+ * ::write() for EncodedData can be called out of order, and the Writer
+ * will sort it out. write() for AudioBuffers must be called in order.
+ */
+
class Writer : public ExceptionStore, public boost::noncopyable
{
public:
int _full_written;
/** number of FAKE written frames */
int _fake_written;
- /** number of REPEAT written frames */
- int _repeat_written;
/** number of frames pushed to disk and then recovered
due to the limit of frames to be held in memory.
*/
int _pushed_to_disk;
- boost::shared_ptr<libdcp::PictureAsset> _picture_asset;
- boost::shared_ptr<libdcp::PictureAssetWriter> _picture_asset_writer;
- boost::shared_ptr<libdcp::SoundAsset> _sound_asset;
- boost::shared_ptr<libdcp::SoundAssetWriter> _sound_asset_writer;
+ boost::shared_ptr<dcp::PictureMXF> _picture_mxf;
+ boost::shared_ptr<dcp::PictureMXFWriter> _picture_mxf_writer;
+ boost::shared_ptr<dcp::SoundMXF> _sound_mxf;
+ boost::shared_ptr<dcp::SoundMXFWriter> _sound_mxf_writer;
};
config.cc
content.cc
content_factory.cc
+ content_subtitle.cc
cross.cc
dci_metadata.cc
dcp_content_type.cc
+ dcp_video.cc
dcp_video_frame.cc
- decoder.cc
+ dcpomatic_time.cc
dolby_cp750.cc
encoder.cc
examine_content_job.cc
ffmpeg_examiner.cc
film.cc
filter.cc
+ frame_rate_change.cc
+ internet.cc
image.cc
image_content.cc
image_decoder.cc
player.cc
playlist.cc
ratio.cc
+ render_subtitles.cc
resampler.cc
scp_dcp_job.cc
scaler.cc
sndfile_content.cc
sndfile_decoder.cc
sound_processor.cc
+ subrip.cc
+ subrip_content.cc
+ subrip_decoder.cc
subtitle_content.cc
subtitle_decoder.cc
timer.cc
AVCODEC AVUTIL AVFORMAT AVFILTER SWSCALE SWRESAMPLE
BOOST_FILESYSTEM BOOST_THREAD BOOST_DATETIME BOOST_SIGNALS2
SNDFILE OPENJPEG POSTPROC TIFF MAGICK SSH DCP CXML GLIB LZMA XML++
- CURL ZIP QUICKMAIL
+ CURL ZIP QUICKMAIL PANGOMM CAIROMM
"""
if bld.env.TARGET_OSX:
#include <wx/preferences.h>
#include <wx/filepicker.h>
#include <wx/spinctrl.h>
-#include <libdcp/colour_matrix.h>
+#include <dcp/colour_matrix.h>
#include "lib/config.h"
#include "lib/ratio.h"
#include "lib/scaler.h"
_set_language = new wxCheckBox (panel, wxID_ANY, _("Set language"));
table->Add (_set_language, 1);
_language = new wxChoice (panel, wxID_ANY);
+ _language->Append (wxT ("Deutsch"));
_language->Append (wxT ("English"));
+ _language->Append (wxT ("Español"));
_language->Append (wxT ("Français"));
_language->Append (wxT ("Italiano"));
- _language->Append (wxT ("Español"));
+ _language->Append (wxT ("Nederlands"));
_language->Append (wxT ("Svenska"));
- _language->Append (wxT ("Deutsch"));
table->Add (_language);
wxStaticText* restart = add_label_to_sizer (table, panel, _("(restart DCP-o-matic to see language changes)"), false);
add_label_to_sizer (table, panel, _("Threads to use for encoding on this host"), true);
_num_local_encoding_threads = new wxSpinCtrl (panel);
table->Add (_num_local_encoding_threads, 1);
+
+ add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
+ _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
+ table->Add (_maximum_j2k_bandwidth, 1);
add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
_mail_server = new wxTextCtrl (panel, wxID_ANY);
_set_language->SetValue (config->language ());
if (config->language().get_value_or ("") == "fr") {
- _language->SetSelection (1);
+ _language->SetSelection (3);
} else if (config->language().get_value_or ("") == "it") {
- _language->SetSelection (2);
+ _language->SetSelection (4);
} else if (config->language().get_value_or ("") == "es") {
- _language->SetSelection (3);
+ _language->SetSelection (2);
} else if (config->language().get_value_or ("") == "sv") {
- _language->SetSelection (4);
+ _language->SetSelection (6);
} else if (config->language().get_value_or ("") == "de") {
+ _language->SetSelection (0);
+ } else if (config->language().get_value_or ("") == "nl") {
_language->SetSelection (5);
} else {
- _language->SetSelection (0);
+ _language->SetSelection (1);
}
setup_language_sensitivity ();
_num_local_encoding_threads->SetRange (1, 128);
_num_local_encoding_threads->SetValue (config->num_local_encoding_threads ());
_num_local_encoding_threads->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::num_local_encoding_threads_changed, this));
+
+ _maximum_j2k_bandwidth->SetRange (1, 500);
+ _maximum_j2k_bandwidth->SetValue (config->maximum_j2k_bandwidth() / 1000000);
+ _maximum_j2k_bandwidth->Bind (wxEVT_COMMAND_SPINCTRL_UPDATED, boost::bind (&GeneralPage::maximum_j2k_bandwidth_changed, this));
_mail_server->SetValue (std_to_wx (config->mail_server ()));
_mail_server->Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&GeneralPage::mail_server_changed, this));
{
switch (_language->GetSelection ()) {
case 0:
- Config::instance()->set_language ("en");
+ Config::instance()->set_language ("de");
break;
case 1:
- Config::instance()->set_language ("fr");
+ Config::instance()->set_language ("en");
break;
case 2:
- Config::instance()->set_language ("it");
+ Config::instance()->set_language ("es");
break;
case 3:
- Config::instance()->set_language ("es");
+ Config::instance()->set_language ("fr");
break;
case 4:
- Config::instance()->set_language ("sv");
+ Config::instance()->set_language ("it");
break;
case 5:
- Config::instance()->set_language ("de");
+ Config::instance()->set_language ("nl");
+ break;
+ case 6:
+ Config::instance()->set_language ("sv");
break;
}
}
{
Config::instance()->set_num_local_encoding_threads (_num_local_encoding_threads->GetValue ());
}
+
+ void maximum_j2k_bandwidth_changed ()
+ {
+ Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
+ }
wxCheckBox* _set_language;
wxChoice* _language;
wxSpinCtrl* _num_local_encoding_threads;
+ wxSpinCtrl* _maximum_j2k_bandwidth;
wxTextCtrl* _mail_server;
wxTextCtrl* _mail_user;
wxTextCtrl* _mail_password;
void issuer_changed ()
{
- libdcp::XMLMetadata m = Config::instance()->dcp_metadata ();
+ dcp::XMLMetadata m = Config::instance()->dcp_metadata ();
m.issuer = wx_to_std (_issuer->GetValue ());
Config::instance()->set_dcp_metadata (m);
}
void creator_changed ()
{
- libdcp::XMLMetadata m = Config::instance()->dcp_metadata ();
+ dcp::XMLMetadata m = Config::instance()->dcp_metadata ();
m.creator = wx_to_std (_creator->GetValue ());
Config::instance()->set_dcp_metadata (m);
}
wxSize ps = wxSize (480, -1);
int const border = 16;
#else
- wxSize ps = wxDefaultSize;
+ /* We seem to need to specify height here, otherwise the general panel
+ is too short (at least on Linux).
+ */
+ wxSize ps = wxSize (-1, 400);
int const border = 8;
#endif
/*
- Copyright (C) 2012-2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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
JobManager::instance()->ActiveJobsChanged.connect (
bind (&FilmEditor::active_jobs_changed, this, _1)
);
+
+ Config::instance()->Changed.connect (boost::bind (&FilmEditor::config_changed, this));
SetSizerAndFit (s);
}
}
_audio_channels->SetRange (0, MAX_AUDIO_CHANNELS);
- _j2k_bandwidth->SetRange (1, 250);
+ _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
_resolution->Append (_("2K"));
_resolution->Append (_("4K"));
_content->InsertColumn (0, wxT(""));
_content->SetColumnWidth (0, 512);
+ #ifdef DCPOMATIC_OSX
+ int const pad = 2;
+ #else
+ int const pad = 0;
+ #endif
+
wxBoxSizer* b = new wxBoxSizer (wxVERTICAL);
_content_add_file = new wxButton (_content_panel, wxID_ANY, _("Add file(s)..."));
- b->Add (_content_add_file, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ b->Add (_content_add_file, 1, wxEXPAND | wxALL, pad);
_content_add_folder = new wxButton (_content_panel, wxID_ANY, _("Add folder..."));
- b->Add (_content_add_folder, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ b->Add (_content_add_folder, 1, wxEXPAND | wxALL, pad);
_content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove"));
- b->Add (_content_remove, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ b->Add (_content_remove, 1, wxEXPAND | wxALL, pad);
_content_earlier = new wxButton (_content_panel, wxID_ANY, _("Up"));
- b->Add (_content_earlier, 1, wxEXPAND);
+ b->Add (_content_earlier, 1, wxEXPAND | wxALL, pad);
_content_later = new wxButton (_content_panel, wxID_ANY, _("Down"));
- b->Add (_content_later, 1, wxEXPAND);
+ b->Add (_content_later, 1, wxEXPAND | wxALL, pad);
_content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
- b->Add (_content_timeline, 1, wxEXPAND | wxLEFT | wxRIGHT);
+ b->Add (_content_timeline, 1, wxEXPAND | wxALL, pad);
s->Add (b, 0, wxALL, 4);
_video_panel->Enable (video_selection.size() > 0 && _generally_sensitive);
_audio_panel->Enable (audio_selection.size() > 0 && _generally_sensitive);
- _subtitle_panel->Enable (selection.size() == 1 && dynamic_pointer_cast<FFmpegContent> (selection.front()) && _generally_sensitive);
+ _subtitle_panel->Enable (selection.size() == 1 && dynamic_pointer_cast<SubtitleContent> (selection.front()) && _generally_sensitive);
_timing_panel->Enable (selection.size() == 1 && _generally_sensitive);
}
content_selection_changed ();
}
}
+
+ void
+ FilmEditor::config_changed ()
+ {
+ _j2k_bandwidth->SetRange (1, Config::instance()->maximum_j2k_bandwidth() / 1000000);
+ }
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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
using boost::lexical_cast;
PropertiesDialog::PropertiesDialog (wxWindow* parent, shared_ptr<Film> film)
- : wxDialog (parent, wxID_ANY, _("Film Properties"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE)
+ : TableDialog (parent, _("Film Properties"), 2, false)
, _film (film)
{
- _table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ add (_("Frames"), true);
+ _frames = add (new wxStaticText (this, wxID_ANY, wxT ("")));
- add_label_to_sizer (_table, this, _("Frames"), true);
- _frames = new wxStaticText (this, wxID_ANY, wxT (""));
- _table->Add (_frames, 1, wxALIGN_CENTER_VERTICAL);
+ add (_("Disk space required"), true);
+ _disk = add (new wxStaticText (this, wxID_ANY, wxT ("")));
- add_label_to_sizer (_table, this, _("Disk space required"), true);
- _disk = new wxStaticText (this, wxID_ANY, wxT (""));
- _table->Add (_disk, 1, wxALIGN_CENTER_VERTICAL);
-
- add_label_to_sizer (_table, this, _("Frames already encoded"), true);
- _encoded = new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this));
+ add (_("Frames already encoded"), true);
+ _encoded = add (new ThreadedStaticText (this, _("counting..."), boost::bind (&PropertiesDialog::frames_already_encoded, this)));
_encoded->Finished.connect (boost::bind (&PropertiesDialog::layout, this));
- _table->Add (_encoded, 1, wxALIGN_CENTER_VERTICAL);
-
-
- _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->time_to_video_frames (_film->length()))));
+ _frames->SetLabel (std_to_wx (lexical_cast<string> (_film->length().frames (_film->video_frame_rate ()))));
double const disk = double (_film->required_disk_space()) / 1073741824.0f;
stringstream s;
s << fixed << setprecision (1) << disk << wx_to_std (_("Gb"));
_disk->SetLabel (std_to_wx (s.str ()));
- wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
- overall_sizer->Add (_table, 0, wxALL, DCPOMATIC_DIALOG_BORDER);
-
- wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
- if (buttons) {
- overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
- }
-
- SetSizer (overall_sizer);
- overall_sizer->SetSizeHints (this);
- }
-
- void
- PropertiesDialog::layout ()
- {
- _table->Layout ();
- Fit ();
+ layout ();
}
string
} catch (boost::thread_interrupted &) {
return "";
}
-
- if (_film->length()) {
+
+ uint64_t const frames = _film->length().frames (_film->video_frame_rate ());
+ if (frames) {
/* XXX: encoded_frames() should check which frames have been encoded */
- u << " (" << (_film->encoded_frames() * 100 / _film->time_to_video_frames (_film->length())) << "%)";
+ u << " (" << (_film->encoded_frames() * 100 / frames) << "%)";
}
return u.str ();
}
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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 <wx/filepicker.h>
#include <wx/validate.h>
-#include <libdcp/exceptions.h>
+#include <dcp/exceptions.h>
#include "lib/compose.hpp"
+ #include "lib/util.h"
#include "screen_dialog.h"
#include "wx_util.h"
+ #include "doremi_certificate_dialog.h"
+ #include "dolby_certificate_dialog.h"
using std::string;
using std::cout;
using boost::shared_ptr;
-ScreenDialog::ScreenDialog (wxWindow* parent, string title, string name, shared_ptr<libdcp::Certificate> certificate)
+ScreenDialog::ScreenDialog (wxWindow* parent, string title, string name, shared_ptr<dcp::Certificate> certificate)
- : wxDialog (parent, wxID_ANY, std_to_wx (title))
+ : TableDialog (parent, std_to_wx (title), 2, true)
, _certificate (certificate)
{
- wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
- table->AddGrowableCol (1, 1);
+ add ("Name", true);
+ _name = add (new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (320, -1)));
- add_label_to_sizer (table, this, "Name", true);
- _name = new wxTextCtrl (this, wxID_ANY, std_to_wx (name), wxDefaultPosition, wxSize (320, -1));
- table->Add (_name, 1, wxEXPAND);
+ add ("Server manufacturer", true);
+ _manufacturer = add (new wxChoice (this, wxID_ANY));
- add_label_to_sizer (table, this, "Certificate", true);
- _certificate_load = new wxButton (this, wxID_ANY, wxT ("Load from file..."));
- table->Add (_certificate_load, 1, wxEXPAND);
+ add (_("Certificate"), true);
+ wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ _load_certificate = new wxButton (this, wxID_ANY, _("Load from file..."));
+ _download_certificate = new wxButton (this, wxID_ANY, _("Download"));
+ s->Add (_load_certificate, 1, wxEXPAND);
+ s->Add (_download_certificate, 1, wxEXPAND);
+ add (s);
- table->AddSpacer (0);
+ add_spacer ();
_certificate_text = new wxTextCtrl (this, wxID_ANY, wxT (""), wxDefaultPosition, wxSize (320, 256), wxTE_MULTILINE | wxTE_READONLY);
if (certificate) {
_certificate_text->SetValue (certificate->certificate ());
wxFont font = wxSystemSettings::GetFont (wxSYS_ANSI_FIXED_FONT);
font.SetPointSize (font.GetPointSize() / 2);
_certificate_text->SetFont (font);
- table->Add (_certificate_text, 1, wxEXPAND);
-
- wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
- overall_sizer->Add (table, 1, wxEXPAND | wxALL, 6);
-
- wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
- if (buttons) {
- overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
- }
+ add (_certificate_text);
- SetSizer (overall_sizer);
- overall_sizer->Layout ();
- overall_sizer->SetSizeHints (this);
+ _manufacturer->Append (_("Unknown"));
+ _manufacturer->Append (_("Doremi"));
+ _manufacturer->Append (_("Dolby"));
+ _manufacturer->Append (_("Other"));
+ _manufacturer->SetSelection (0);
- _certificate_load->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ScreenDialog::load_certificate, this));
+ _load_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ScreenDialog::select_certificate, this));
+ _download_certificate->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&ScreenDialog::download_certificate, this));
+ _manufacturer->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&ScreenDialog::setup_sensitivity, this));
setup_sensitivity ();
+ layout ();
}
string
return wx_to_std (_name->GetValue());
}
-shared_ptr<libdcp::Certificate>
+shared_ptr<dcp::Certificate>
ScreenDialog::certificate () const
{
return _certificate;
}
void
- ScreenDialog::load_certificate ()
+ ScreenDialog::load_certificate (boost::filesystem::path file)
{
- wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
+ try {
- _certificate.reset (new libdcp::Certificate (file));
++ _certificate.reset (new dcp::Certificate (file));
+ _certificate_text->SetValue (_certificate->certificate ());
- } catch (libdcp::MiscError& e) {
++ } catch (dcp::MiscError& e) {
+ error_dialog (this, String::compose ("Could not read certificate file (%1)", e.what()));
+ }
+ }
+ void
+ ScreenDialog::select_certificate ()
+ {
+ wxFileDialog* d = new wxFileDialog (this, _("Select Certificate File"));
if (d->ShowModal () == wxID_OK) {
- try {
- _certificate.reset (new dcp::Certificate (boost::filesystem::path (wx_to_std (d->GetPath ()))));
- _certificate_text->SetValue (_certificate->certificate ());
- } catch (dcp::MiscError& e) {
- error_dialog (this, String::compose ("Could not read certificate file (%1)", e.what()));
- }
+ load_certificate (boost::filesystem::path (wx_to_std (d->GetPath ())));
}
-
d->Destroy ();
setup_sensitivity ();
}
+ void
+ ScreenDialog::download_certificate ()
+ {
+ if (_manufacturer->GetStringSelection() == _("Doremi")) {
+ DownloadCertificateDialog* d = new DoremiCertificateDialog (this, boost::bind (&ScreenDialog::load_certificate, this, _1));
+ d->ShowModal ();
+ d->Destroy ();
+ } else if (_manufacturer->GetStringSelection() == _("Dolby")) {
+ DownloadCertificateDialog* d = new DolbyCertificateDialog (this, boost::bind (&ScreenDialog::load_certificate, this, _1));
+ d->ShowModal ();
+ d->Destroy ();
+ }
+
+ setup_sensitivity ();
+ }
+
void
ScreenDialog::setup_sensitivity ()
{
wxButton* ok = dynamic_cast<wxButton*> (FindWindowById (wxID_OK, this));
- ok->Enable (_certificate);
+ ok->Enable (_certificate.get ());
+
+ _download_certificate->Enable (
+ _manufacturer->GetStringSelection() == _("Doremi") ||
+ _manufacturer->GetStringSelection() == _("Dolby")
+ );
}
/*
- Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2012-2014 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 <wx/wx.h>
#include <boost/shared_ptr.hpp>
-#include <libdcp/certificates.h>
+#include <dcp/certificates.h>
+ #include "table_dialog.h"
- class ScreenDialog : public wxDialog
+ class Progress;
+
+ class ScreenDialog : public TableDialog
{
public:
- ScreenDialog (wxWindow *, std::string, std::string name = "", boost::shared_ptr<libdcp::Certificate> c = boost::shared_ptr<libdcp::Certificate> ());
+ ScreenDialog (wxWindow *, std::string, std::string name = "", boost::shared_ptr<dcp::Certificate> c = boost::shared_ptr<dcp::Certificate> ());
std::string name () const;
- boost::shared_ptr<libdcp::Certificate> certificate () const;
+ boost::shared_ptr<dcp::Certificate> certificate () const;
private:
- void load_certificate ();
+ void select_certificate ();
+ void load_certificate (boost::filesystem::path);
+ void download_certificate ();
void setup_sensitivity ();
wxTextCtrl* _name;
- wxButton* _certificate_load;
+ wxChoice* _manufacturer;
+ wxButton* _load_certificate;
+ wxButton* _download_certificate;
wxTextCtrl* _certificate_text;
- boost::shared_ptr<libdcp::Certificate> _certificate;
+ boost::shared_ptr<dcp::Certificate> _certificate;
};
}
void
-Timecode::set (Time t, int fps)
+Timecode::set (DCPTime t, int fps)
{
/* Do this calculation with frames so that we can round
to a frame boundary at the start rather than the end.
*/
- int64_t f = divide_with_round (t * fps, TIME_HZ);
+ int64_t f = rint (t.seconds() * fps);
int const h = f / (3600 * fps);
f -= h * 3600 * fps;
checked_set (_seconds, lexical_cast<string> (s));
checked_set (_frames, lexical_cast<string> (f));
- _fixed->SetLabel (wxString::Format ("%02d:%02d:%02d.%02ld", h, m, s, f));
+ _fixed->SetLabel (wxString::Format ("%02d:%02d:%02d.%02" wxLongLongFmtSpec "d", h, m, s, f));
}
-Time
+DCPTime
Timecode::get (int fps) const
{
- Time t = 0;
+ DCPTime t;
string const h = wx_to_std (_hours->GetValue ());
- t += lexical_cast<int> (h.empty() ? "0" : h) * 3600 * TIME_HZ;
+ t += DCPTime::from_seconds (lexical_cast<int> (h.empty() ? "0" : h) * 3600);
string const m = wx_to_std (_minutes->GetValue());
- t += lexical_cast<int> (m.empty() ? "0" : m) * 60 * TIME_HZ;
+ t += DCPTime::from_seconds (lexical_cast<int> (m.empty() ? "0" : m) * 60);
string const s = wx_to_std (_seconds->GetValue());
- t += lexical_cast<int> (s.empty() ? "0" : s) * TIME_HZ;
+ t += DCPTime::from_seconds (lexical_cast<int> (s.empty() ? "0" : s));
string const f = wx_to_std (_frames->GetValue());
- t += lexical_cast<int> (f.empty() ? "0" : f) * TIME_HZ / fps;
+ t += DCPTime::from_seconds (lexical_cast<double> (f.empty() ? "0" : f) / fps);
return t;
}
/*
- Copyright (C) 2013 Carl Hetherington <cth@carlh.net>
+ Copyright (C) 2013-2014 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
protected:
virtual void do_paint (wxGraphicsContext *) = 0;
- int time_x (Time t) const
+ int time_x (DCPTime t) const
{
- return _timeline.tracks_position().x + t * _timeline.pixels_per_time_unit();
+ return _timeline.tracks_position().x + t.seconds() * _timeline.pixels_per_second ();
}
Timeline& _timeline;
ContentView (Timeline& tl, shared_ptr<Content> c)
: View (tl)
, _content (c)
- , _track (0)
, _selected (false)
{
_content_connection = c->Changed.connect (bind (&ContentView::content_changed, this, _2, _3));
dcpomatic::Rect<int> bbox () const
{
+ assert (_track);
+
shared_ptr<const Film> film = _timeline.film ();
shared_ptr<const Content> content = _content.lock ();
if (!film || !content) {
return dcpomatic::Rect<int> (
time_x (content->position ()) - 8,
- y_pos (_track) - 8,
+ y_pos (_track.get()) - 8,
- content->length_after_trim () * _timeline.pixels_per_time_unit() + 16,
+ content->length_after_trim().seconds() * _timeline.pixels_per_second() + 16,
_timeline.track_height() + 16
);
}
_track = t;
}
- int track () const {
+ optional<int> track () const {
return _track;
}
void do_paint (wxGraphicsContext* gc)
{
+ assert (_track);
+
shared_ptr<const Film> film = _timeline.film ();
shared_ptr<const Content> cont = content ();
if (!film || !cont) {
return;
}
- Time const position = cont->position ();
- Time const len = cont->length_after_trim ();
+ DCPTime const position = cont->position ();
+ DCPTime const len = cont->length_after_trim ();
wxColour selected (colour().Red() / 2, colour().Green() / 2, colour().Blue() / 2);
}
wxGraphicsPath path = gc->CreatePath ();
- path.MoveToPoint (time_x (position), y_pos (_track) + 4);
- path.AddLineToPoint (time_x (position + len), y_pos (_track) + 4);
- path.AddLineToPoint (time_x (position + len), y_pos (_track + 1) - 4);
- path.AddLineToPoint (time_x (position), y_pos (_track + 1) - 4);
- path.AddLineToPoint (time_x (position), y_pos (_track) + 4);
+ path.MoveToPoint (time_x (position), y_pos (_track.get()) + 4);
+ path.AddLineToPoint (time_x (position + len), y_pos (_track.get()) + 4);
+ path.AddLineToPoint (time_x (position + len), y_pos (_track.get() + 1) - 4);
+ path.AddLineToPoint (time_x (position), y_pos (_track.get() + 1) - 4);
+ path.AddLineToPoint (time_x (position), y_pos (_track.get()) + 4);
gc->StrokePath (path);
gc->FillPath (path);
wxDouble name_leading;
gc->GetTextExtent (name, &name_width, &name_height, &name_descent, &name_leading);
- gc->Clip (wxRegion (time_x (position), y_pos (_track), len.seconds() * _timeline.pixels_per_second(), _timeline.track_height()));
- gc->DrawText (name, time_x (position) + 12, y_pos (_track + 1) - name_height - 4);
- gc->Clip (wxRegion (time_x (position), y_pos (_track.get()), len * _timeline.pixels_per_time_unit(), _timeline.track_height()));
++ gc->Clip (wxRegion (time_x (position), y_pos (_track.get()), len.seconds() * _timeline.pixels_per_second(), _timeline.track_height()));
+ gc->DrawText (name, time_x (position) + 12, y_pos (_track.get() + 1) - name_height - 4);
gc->ResetClip ();
}
}
if (!frequent) {
- _timeline.setup_pixels_per_time_unit ();
+ _timeline.setup_pixels_per_second ();
_timeline.Refresh ();
}
}
boost::weak_ptr<Content> _content;
- int _track;
+ optional<int> _track;
bool _selected;
boost::signals2::scoped_connection _content_connection;
}
};
+class SubtitleContentView : public ContentView
+{
+public:
+ SubtitleContentView (Timeline& tl, shared_ptr<Content> c)
+ : ContentView (tl, c)
+ {}
+
+private:
+ wxString type () const
+ {
+ return _("subtitles");
+ }
+
+ wxColour colour () const
+ {
+ return wxColour (163, 255, 154, 255);
+ }
+};
+
class TimeAxisView : public View
{
public:
{
gc->SetPen (*wxThePenList->FindOrCreatePen (wxColour (0, 0, 0), 1, wxPENSTYLE_SOLID));
- int mark_interval = rint (128 / (TIME_HZ * _timeline.pixels_per_time_unit ()));
+ double mark_interval = rint (128 / _timeline.pixels_per_second ());
if (mark_interval > 5) {
- mark_interval -= mark_interval % 5;
+ mark_interval -= int (rint (mark_interval)) % 5;
}
if (mark_interval > 10) {
- mark_interval -= mark_interval % 10;
+ mark_interval -= int (rint (mark_interval)) % 10;
}
if (mark_interval > 60) {
- mark_interval -= mark_interval % 60;
+ mark_interval -= int (rint (mark_interval)) % 60;
}
if (mark_interval > 3600) {
- mark_interval -= mark_interval % 3600;
+ mark_interval -= int (rint (mark_interval)) % 3600;
}
if (mark_interval < 1) {
path.AddLineToPoint (_timeline.width(), _y);
gc->StrokePath (path);
- Time t = 0;
- while ((t * _timeline.pixels_per_time_unit()) < _timeline.width()) {
+ /* Time in seconds */
+ DCPTime t;
+ while ((t.seconds() * _timeline.pixels_per_second()) < _timeline.width()) {
wxGraphicsPath path = gc->CreatePath ();
path.MoveToPoint (time_x (t), _y - 4);
path.AddLineToPoint (time_x (t), _y + 4);
gc->StrokePath (path);
- int tc = t / TIME_HZ;
+ double tc = t.seconds ();
int const h = tc / 3600;
tc -= h * 3600;
int const m = tc / 60;
wxDouble str_leading;
gc->GetTextExtent (str, &str_width, &str_height, &str_descent, &str_leading);
- int const tx = _timeline.x_offset() + t * _timeline.pixels_per_time_unit();
+ int const tx = _timeline.x_offset() + t.seconds() * _timeline.pixels_per_second();
if ((tx + str_width) < _timeline.width()) {
gc->DrawText (str, time_x (t), _y + 16);
}
- t += mark_interval * TIME_HZ;
+ t += DCPTime::from_seconds (mark_interval);
}
}
, _film (film)
, _time_axis_view (new TimeAxisView (*this, 32))
, _tracks (0)
- , _pixels_per_time_unit (0)
+ , _pixels_per_second (0)
, _left_down (false)
, _down_view_position (0)
, _first_move (false)
if (dynamic_pointer_cast<AudioContent> (*i)) {
_views.push_back (shared_ptr<View> (new AudioContentView (*this, *i)));
}
+ if (dynamic_pointer_cast<SubtitleContent> (*i)) {
+ _views.push_back (shared_ptr<View> (new SubtitleContentView (*this, *i)));
+ }
}
assign_tracks ();
- setup_pixels_per_time_unit ();
+ setup_pixels_per_second ();
Refresh ();
}
void
Timeline::assign_tracks ()
{
- list<shared_ptr<VideoContentView> > video;
- list<shared_ptr<AudioContentView> > audio;
- list<shared_ptr<SubtitleContentView> > subtitle;
-
for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
- shared_ptr<VideoContentView> v = dynamic_pointer_cast<VideoContentView> (*i);
- if (v) {
- video.push_back (v);
- }
-
- shared_ptr<AudioContentView> a = dynamic_pointer_cast<AudioContentView> (*i);
- if (a) {
- audio.push_back (a);
- }
-
- shared_ptr<SubtitleContentView> s = dynamic_pointer_cast<SubtitleContentView> (*i);
- if (s) {
- subtitle.push_back (s);
- }
- }
-
- _tracks = 0;
- if (!video.empty ()) {
- for (list<shared_ptr<VideoContentView> >::iterator i = video.begin(); i != video.end(); ++i) {
- (*i)->set_track (_tracks);
+ shared_ptr<ContentView> cv = dynamic_pointer_cast<ContentView> (*i);
+ if (!cv) {
+ continue;
}
- ++_tracks;
- }
--
- if (!subtitle.empty ()) {
- for (list<shared_ptr<SubtitleContentView> >::iterator i = subtitle.begin(); i != subtitle.end(); ++i) {
- (*i)->set_track (_tracks);
- }
- ++_tracks;
- }
+
- int const audio_start = _tracks;
+ shared_ptr<Content> content = cv->content();
- for (list<shared_ptr<AudioContentView> >::iterator i = audio.begin(); i != audio.end(); ++i) {
- shared_ptr<Content> acv_content = (*i)->content();
-
- int t = audio_start;
- while (1) {
- list<shared_ptr<AudioContentView> >::iterator j = audio.begin ();
- while (j != audio.end()) {
- if ((*j)->track() == t) {
+ int t = 0;
- while (1) {
++ while (true) {
+ ViewList::iterator j = _views.begin();
+ while (j != _views.end()) {
+ shared_ptr<ContentView> test = dynamic_pointer_cast<ContentView> (*j);
+ if (!test) {
+ ++j;
+ continue;
+ }
+
+ shared_ptr<Content> test_content = test->content();
+
+ if (test && test->track() && test->track().get() == t) {
bool const no_overlap =
- (acv_content->position() < (*j)->content()->position() && acv_content->end() < (*j)->content()->position()) ||
- (acv_content->position() > (*j)->content()->end() && acv_content->end() > (*j)->content()->end());
+ (content->position() < test_content->position() && content->end() < test_content->position()) ||
+ (content->position() > test_content->end() && content->end() > test_content->end());
if (!no_overlap) {
/* we have an overlap on track `t' */
++j;
}
- if (j == audio.end ()) {
+ if (j == _views.end ()) {
/* no overlap on `t' */
break;
}
}
- (*i)->set_track (t);
+ cv->set_track (t);
_tracks = max (_tracks, t + 1);
}
}
void
-Timeline::setup_pixels_per_time_unit ()
+Timeline::setup_pixels_per_second ()
{
shared_ptr<const Film> film = _film.lock ();
- if (!film || film->length() == 0) {
+ if (!film || film->length() == DCPTime ()) {
return;
}
- _pixels_per_time_unit = static_cast<double>(width() - x_offset() * 2) / film->length ();
+ _pixels_per_second = static_cast<double>(width() - x_offset() * 2) / film->length().seconds ();
}
shared_ptr<View>
return;
}
- Time new_position = _down_view_position + (p.x - _down_point.x) / _pixels_per_time_unit;
+ DCPTime new_position = _down_view_position + DCPTime::from_seconds ((p.x - _down_point.x) / _pixels_per_second);
if (_snap) {
bool first = true;
- Time nearest_distance = TIME_MAX;
- Time nearest_new_position = TIME_MAX;
+ DCPTime nearest_distance = DCPTime::max ();
+ DCPTime nearest_new_position = DCPTime::max ();
/* Find the nearest content edge; this is inefficient */
for (ViewList::iterator i = _views.begin(); i != _views.end(); ++i) {
{
/* Snap starts to ends */
- Time const d = abs (cv->content()->end() - new_position);
+ DCPTime const d = DCPTime (cv->content()->end() - new_position).abs ();
if (first || d < nearest_distance) {
nearest_distance = d;
nearest_new_position = cv->content()->end();
{
/* Snap ends to starts */
- Time const d = abs (cv->content()->position() - (new_position + _down_view->content()->length_after_trim()));
+ DCPTime const d = DCPTime (
+ cv->content()->position() - (new_position + _down_view->content()->length_after_trim())
+ ).abs ();
+
if (d < nearest_distance) {
nearest_distance = d;
nearest_new_position = cv->content()->position() - _down_view->content()->length_after_trim ();
if (!first) {
/* Snap if it's close; `close' means within a proportion of the time on the timeline */
- if (nearest_distance < (width() / pixels_per_time_unit()) / 32) {
+ if (nearest_distance < DCPTime::from_seconds ((width() / pixels_per_second()) / 32)) {
new_position = nearest_new_position;
}
}
}
- if (new_position < 0) {
- new_position = 0;
+ if (new_position < DCPTime ()) {
+ new_position = DCPTime ();
}
_down_view->content()->set_position (new_position);
void
Timeline::resized ()
{
- setup_pixels_per_time_unit ();
+ setup_pixels_per_second ();
}
void
#include "lib/content.h"
#include "lib/image_content.h"
+ #include "lib/sndfile_content.h"
#include "timing_panel.h"
#include "wx_util.h"
#include "timecode.h"
if (cl.size() == 1) {
content = cl.front ();
}
+
+ int const film_video_frame_rate = _editor->film()->video_frame_rate ();
if (property == ContentProperty::POSITION) {
if (content) {
- _position->set (content->position (), _editor->film()->video_frame_rate ());
+ _position->set (content->position (), film_video_frame_rate);
} else {
- _position->set (0, 24);
+ _position->set (DCPTime () , 24);
}
} else if (
property == ContentProperty::LENGTH ||
property == VideoContentProperty::VIDEO_FRAME_RATE ||
- property == VideoContentProperty::VIDEO_FRAME_TYPE ||
- property == SndfileContentProperty::VIDEO_FRAME_RATE
+ property == VideoContentProperty::VIDEO_FRAME_TYPE
) {
if (content) {
- _full_length->set (content->full_length (), _editor->film()->video_frame_rate ());
- _play_length->set (content->length_after_trim (), _editor->film()->video_frame_rate ());
+ _full_length->set (content->full_length (), film_video_frame_rate);
+ _play_length->set (content->length_after_trim (), film_video_frame_rate);
} else {
- _full_length->set (0, 24);
- _play_length->set (0, 24);
+ _full_length->set (DCPTime (), 24);
+ _play_length->set (DCPTime (), 24);
}
} else if (property == ContentProperty::TRIM_START) {
if (content) {
- _trim_start->set (content->trim_start (), _editor->film()->video_frame_rate ());
- _play_length->set (content->length_after_trim (), _editor->film()->video_frame_rate ());
+ _trim_start->set (content->trim_start (), film_video_frame_rate);
+ _play_length->set (content->length_after_trim (), film_video_frame_rate);
} else {
- _trim_start->set (0, 24);
- _play_length->set (0, 24);
+ _trim_start->set (DCPTime (), 24);
+ _play_length->set (DCPTime (), 24);
}
} else if (property == ContentProperty::TRIM_END) {
if (content) {
- _trim_end->set (content->trim_end (), _editor->film()->video_frame_rate ());
- _play_length->set (content->length_after_trim (), _editor->film()->video_frame_rate ());
+ _trim_end->set (content->trim_end (), film_video_frame_rate);
+ _play_length->set (content->length_after_trim (), film_video_frame_rate);
} else {
- _trim_end->set (0, 24);
- _play_length->set (0, 24);
+ _trim_end->set (DCPTime (), 24);
+ _play_length->set (DCPTime (), 24);
}
}
if (property == VideoContentProperty::VIDEO_FRAME_RATE) {
if (content) {
shared_ptr<VideoContent> vc = dynamic_pointer_cast<VideoContent> (content);
- shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (content);
if (vc) {
_video_frame_rate->SetValue (std_to_wx (lexical_cast<string> (vc->video_frame_rate ())));
- } else if (sc) {
- _video_frame_rate->SetValue (std_to_wx (lexical_cast<string> (sc->video_frame_rate ())));
} else {
_video_frame_rate->SetValue ("24");
}
}
shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (content);
+ shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (content);
_full_length->set_editable (ic && ic->still ());
_play_length->set_editable (!ic || !ic->still ());
- _video_frame_rate->Enable (ic && !ic->still ());
+ _video_frame_rate->Enable ((ic && !ic->still ()) || sc);
_set_video_frame_rate->Enable (false);
}
if (c.size() == 1) {
shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (c.front ());
if (ic && ic->still ()) {
- ic->set_video_length (rint (_full_length->get (_editor->film()->video_frame_rate()) * ic->video_frame_rate() / TIME_HZ));
+ /* XXX: No effective FRC here... is this right? */
+ ic->set_video_length (ContentTime (_full_length->get (_editor->film()->video_frame_rate()), FrameRateChange (1, 1)));
}
}
}
shared_ptr<ImageContent> ic = dynamic_pointer_cast<ImageContent> (c.front ());
if (ic) {
ic->set_video_frame_rate (lexical_cast<float> (wx_to_std (_video_frame_rate->GetValue ())));
- _set_video_frame_rate->Enable (false);
}
- shared_ptr<SndfileContent> sc = dynamic_pointer_cast<SndfileContent> (c.front ());
- if (sc) {
- sc->set_video_frame_rate (lexical_cast<float> (wx_to_std (_video_frame_rate->GetValue ())));
- }
+ _set_video_frame_rate->Enable (false);
}
}
_frame_type->wrapped()->Append (_("3D left/right"));
_frame_type->wrapped()->Append (_("3D top/bottom"));
_frame_type->wrapped()->Append (_("3D alternate"));
+ _frame_type->wrapped()->Append (_("3D left only"));
+ _frame_type->wrapped()->Append (_("3D right only"));
_filters_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&VideoPanel::edit_filters_clicked, this));
_colour_conversion_button->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&VideoPanel::edit_colour_conversion_clicked, this));
}
Crop const crop = vcs->crop ();
- if ((crop.left || crop.right || crop.top || crop.bottom) && vcs->video_size() != libdcp::Size (0, 0)) {
- libdcp::Size cropped = vcs->video_size_after_crop ();
+ if ((crop.left || crop.right || crop.top || crop.bottom) && vcs->video_size() != dcp::Size (0, 0)) {
+ dcp::Size cropped = vcs->video_size_after_crop ();
d << wxString::Format (
_("Cropped to %dx%d (%.2f:1)\n"),
cropped.width, cropped.height,
++lines;
}
- libdcp::Size const container_size = _editor->film()->frame_size ();
- libdcp::Size const scaled = vcs->scale().size (vcs, container_size, container_size);
+ dcp::Size const container_size = _editor->film()->frame_size ();
+ dcp::Size const scaled = vcs->scale().size (vcs, container_size, container_size);
if (scaled != vcs->video_size_after_crop ()) {
d << wxString::Format (
d << wxString::Format (_("Content frame rate %.4f\n"), vcs->video_frame_rate ());
++lines;
- FrameRateConversion frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
+ FrameRateChange frc (vcs->video_frame_rate(), _editor->film()->video_frame_rate ());
d << std_to_wx (frc.description) << "\n";
++lines;
content_menu.cc
dci_metadata_dialog.cc
dir_picker_ctrl.cc
+ dolby_certificate_dialog.cc
+ doremi_certificate_dialog.cc
+ download_certificate_dialog.cc
film_editor.cc
film_editor_panel.cc
film_viewer.cc
server_dialog.cc
servers_list_dialog.cc
subtitle_panel.cc
+ subtitle_view.cc
+ table_dialog.cc
timecode.cc
timeline.cc
timeline_dialog.cc
* @param initial Initial text for the wxStaticText while the computation is being run.
* @param fn Function which works out what the wxStaticText content should be and returns it.
*/
-ThreadedStaticText::ThreadedStaticText (wxWindow* parent, wxString initial, function<string ()> fn)
+ThreadedStaticText::ThreadedStaticText (wxWindow* parent, wxString initial, boost::function<string ()> fn)
: wxStaticText (parent, wxID_ANY, initial)
{
Bind (wxEVT_COMMAND_TEXT_UPDATED, boost::bind (&ThreadedStaticText::thread_finished, this, _1), _update_event_id);
/** Run our thread and post the result to the GUI thread via AddPendingEvent */
void
-ThreadedStaticText::run (function<string ()> fn)
+ThreadedStaticText::run (boost::function<string ()> fn)
try
{
wxCommandEvent ev (wxEVT_COMMAND_TEXT_UPDATED, _update_event_id);
{
return w->GetSelection ();
}
+
+ void
+ run_gui_loop ()
+ {
+ while (wxTheApp->Pending ()) {
+ wxTheApp->Dispatch ();
+ }
+ }
import sys
APPNAME = 'dcpomatic'
-VERSION = '1.66.9devel'
+VERSION = '2.0.0devel'
def options(opt):
opt.load('compiler_cxx')
conf.check_cfg(package='libopenjpeg', args='--cflags --libs', max_version='1.5.1', mandatory=True)
def static_dcp(conf, static_boost, static_xmlpp, static_xmlsec, static_ssh):
- conf.check_cfg(package='libdcp', atleast_version='0.92', args='--cflags', uselib_store='DCP', mandatory=True)
+ conf.check_cfg(package='libdcp-1.0', atleast_version='0.92', args='--cflags', uselib_store='DCP', mandatory=True)
conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
- conf.env.STLIB_DCP = ['dcp', 'asdcp-libdcp', 'kumu-libdcp']
+ conf.env.STLIB_DCP = ['dcp-1.0', 'asdcp-libdcp', 'kumu-libdcp']
conf.env.LIB_DCP = ['glibmm-2.4', 'ssl', 'crypto', 'bz2', 'xslt']
if static_boost:
conf.env.LIB_DCP.append('ssh')
def dynamic_dcp(conf):
- conf.check_cfg(package='libdcp', atleast_version='0.92', args='--cflags --libs', uselib_store='DCP', mandatory=True)
+ conf.check_cfg(package='libdcp-1.0', atleast_version='0.92', args='--cflags --libs', uselib_store='DCP', mandatory=True)
conf.env.DEFINES_DCP = [f.replace('\\', '') for f in conf.env.DEFINES_DCP]
def dynamic_ssh(conf):
conf.check_cfg(package='glib-2.0', args='--cflags --libs', uselib_store='GLIB', mandatory=True)
conf.check_cfg(package= '', path=conf.options.magickpp_config, args='--cppflags --cxxflags --libs', uselib_store='MAGICK', mandatory=True)
conf.check_cfg(package='libzip', args='--cflags --libs', uselib_store='ZIP', mandatory=True)
+ conf.check_cfg(package='pangomm-1.4', args='--cflags --libs', uselib_store='PANGOMM', mandatory=True)
+ conf.check_cfg(package='cairomm-1.0', args='--cflags --libs', uselib_store='CAIROMM', mandatory=True)
conf.check_cc(fragment="""
#include <glib.h>
def pot_merge(bld):
bld.recurse('src')
+
+ def tags(bld):
+ os.system('etags src/lib/*.cc src/lib/*.h src/wx/*.cc src/wx/*.h src/tools/*.cc src/tools/*.h')