* Add subtitle view.
+2014-05-29 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.18 released.
+
+2014-05-28 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.17 released.
+
+2014-05-28 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.16 released.
+
+2014-05-28 Carl Hetherington <cth@carlh.net>
+
+ * Rework KDM generation to be about CPLs rather than DCPs,
+ and allow specification of any CPL to generate KDMs for.
+
+ Requested-by: Richard Turner
+
+2014-05-27 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.15 released.
+
+2014-05-26 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.14 released.
+
+2014-05-26 Carl Hetherington <cth@carlh.net>
+
+ * Fix problems with non-zero FFmpeg content start times.
+
+2014-05-24 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.13 released.
+
+2014-05-24 Carl Hetherington <cth@carlh.net>
+
+ * Fix problems with log setup from config.
+
+2014-05-23 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.12 released.
+
+2014-05-22 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.11 released.
+
+2014-05-21 Carl Hetherington <cth@carlh.net>
+
+ * Version 1.69.10 released.
+
+2014-05-21 Carl Hetherington <cth@carlh.net>
+
+ * Tidy up logging a bit and make it configurable from the GUI
+ (moving a few things into an Advanced preferences tab at
+ the same time).
+
2014-05-19 Carl Hetherington <cth@carlh.net>
* Version 1.69.9 released.
-dcpomatic (1.69.9-1) UNRELEASED; urgency=low
+dcpomatic (1.69.18-1) UNRELEASED; urgency=low
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
* New upstream release.
+ * New upstream release.
+ * New upstream release.
+ * New upstream release.
+ * New upstream release.
+ * New upstream release.
+ * New upstream release.
+ * New upstream release.
+ * New upstream release.
+ * New upstream release.
- -- Carl Hetherington <carl@d1stkfactory> Mon, 19 May 2014 11:16:15 +0100
+ -- Carl Hetherington <carl@d1stkfactory> Thu, 29 May 2014 16:38:35 +0100
dcpomatic (0.87-1) UNRELEASED; urgency=low
universal_copy $ENV lib/libffi*.dylib $WORK/$libs
universal_copy $ENV lib/libiconv*.dylib $WORK/$libs
-for obj in $WORK/$macos/dcpomatic $WORK/$macos/ffprobe $WORK/$libs/*.dylib; do
+for obj in $WORK/$macos/dcpomatic $WORK/$macos/dcpomatic_batch $WORK/$macos/dcpomatic_cli $WORK/$macos/dcpomatic_server_cli $WORK/$macos/ffprobe $WORK/$libs/*.dylib; do
deps=`otool -L $obj | awk '{print $1}' | egrep "(/Users/carl|libboost|libssh|libltdl|libxmlsec)"`
changes=""
for dep in $deps; do
, _check_for_updates (false)
, _check_for_test_updates (false)
, _maximum_j2k_bandwidth (250000000)
+ , _log_types (Log::TYPE_GENERAL | Log::TYPE_WARNING | Log::TYPE_ERROR)
{
_allowed_dcp_frame_rates.push_back (24);
_allowed_dcp_frame_rates.push_back (25);
_maximum_j2k_bandwidth = f.optional_number_child<int> ("MaximumJ2KBandwidth").get_value_or (250000000);
_allow_any_dcp_frame_rate = f.optional_bool_child ("AllowAnyDCPFrameRate");
+
+ _log_types = f.optional_number_child<int> ("LogTypes").get_value_or (Log::TYPE_GENERAL | Log::TYPE_WARNING | Log::TYPE_ERROR);
}
void
root->add_child("MaximumJ2KBandwidth")->add_child_text (raw_convert<string> (_maximum_j2k_bandwidth));
root->add_child("AllowAnyDCPFrameRate")->add_child_text (_allow_any_dcp_frame_rate ? "1" : "0");
+ root->add_child("LogTypes")->add_child_text (raw_convert<string> (_log_types));
doc.write_to_file_formatted (file(false).string ());
}
int maximum_j2k_bandwidth () const {
return _maximum_j2k_bandwidth;
}
+
+ int log_types () const {
+ return _log_types;
+ }
/** @param n New number of local encoding threads */
void set_num_local_encoding_threads (int n) {
_maximum_j2k_bandwidth = b;
changed ();
}
+
+ void set_log_types (int t) {
+ _log_types = t;
+ changed ();
+ }
boost::filesystem::path signer_chain_directory () const;
bool _check_for_test_updates;
/** maximum allowed J2K bandwidth in bits per second */
int _maximum_j2k_bandwidth;
+ int _log_types;
/** Singleton instance, or 0 */
static Config* _instance;
_change_signals_frequent = f;
}
+ boost::shared_ptr<const Film> film () const {
+ return _film.lock ();
+ }
+
boost::signals2::signal<void (boost::weak_ptr<Content>, int, bool)> Changed;
protected:
#endif
#include "exceptions.h"
+#include "i18n.h"
+
+#define LOG_GENERAL(...) log->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_ERROR(...) log->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_ERROR_NC(...) log->log (__VA_ARGS__, Log::TYPE_ERROR);
+
using std::pair;
using std::list;
using std::ifstream;
HANDLE child_stderr_read;
HANDLE child_stderr_write;
if (!CreatePipe (&child_stderr_read, &child_stderr_write, &security, 0)) {
- log->log ("ffprobe call failed (could not CreatePipe)");
+ LOG_ERROR_NC ("ffprobe call failed (could not CreatePipe)");
return;
}
PROCESS_INFORMATION process_info;
ZeroMemory (&process_info, sizeof (process_info));
if (!CreateProcess (0, command, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &startup_info, &process_info)) {
- log->log ("ffprobe call failed (could not CreateProcess)");
+ LOG_ERROR_NC (N_("ffprobe call failed (could not CreateProcess)"));
return;
}
FILE* o = fopen_boost (out, "w");
if (!o) {
- log->log ("ffprobe call failed (could not create output file)");
+ LOG_ERROR_NC (N_("ffprobe call failed (could not create output file)"));
return;
}
#ifdef DCPOMATIC_LINUX
string ffprobe = "ffprobe \"" + content.string() + "\" 2> \"" + out.string() + "\"";
- log->log (String::compose ("Probing with %1", ffprobe));
+ LOG_GENERAL (N_("Probing with %1"), ffprobe);
system (ffprobe.c_str ());
#endif
path /= "ffprobe";
string ffprobe = path.string() + " \"" + content.string() + "\" 2> \"" + out.string() + "\"";
- log->log (String::compose ("Probing with %1", ffprobe));
+ LOG_GENERAL (N_("Probing with %1"), ffprobe);
system (ffprobe.c_str ());
#endif
}
#include "cross.h"
#include "player_video_frame.h"
+#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+
#include "i18n.h"
using std::string;
switch (_frame->eyes()) {
case EYES_BOTH:
- _log->log (String::compose (N_("Finished locally-encoded frame %1 for mono"), _index));
+ LOG_GENERAL (N_("Finished locally-encoded frame %1 for mono"), _index);
break;
case EYES_LEFT:
- _log->log (String::compose (N_("Finished locally-encoded frame %1 for L"), _index));
+ LOG_GENERAL (N_("Finished locally-encoded frame %1 for L"), _index);
break;
case EYES_RIGHT:
- _log->log (String::compose (N_("Finished locally-encoded frame %1 for R"), _index));
+ LOG_GENERAL (N_("Finished locally-encoded frame %1 for R"), _index);
break;
default:
break;
root->add_child("Version")->add_child_text (raw_convert<string> (SERVER_LINK_VERSION));
add_metadata (root);
- _log->log (String::compose (N_("Sending frame %1 to remote"), _index));
+ LOG_GENERAL (N_("Sending frame %1 to remote"), _index);
/* Send XML metadata */
stringstream xml;
shared_ptr<EncodedData> e (new RemotelyEncodedData (socket->read_uint32 ()));
socket->read (e->data(), e->size());
- _log->log (String::compose (N_("Finished remotely-encoded frame %1"), _index));
+ LOG_GENERAL (N_("Finished remotely-encoded frame %1"), _index);
return e;
}
#include "dcpomatic_time.h"
class Decoded;
-class Film;
/** @class Decoder.
* @brief Parent class for decoders of content.
#include "i18n.h"
+#define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_ERROR(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_TIMING(...) _film->log()->microsecond_log (String::compose (__VA_ARGS__), Log::TYPE_TIMING);
+
using std::pair;
using std::string;
using std::stringstream;
void
Encoder::add_worker_threads (ServerDescription d)
{
- _film->log()->log (String::compose (N_("Adding %1 worker threads for remote %2"), d.host_name ()));
+ LOG_GENERAL (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)));
}
{
boost::mutex::scoped_lock lock (_mutex);
- _film->log()->log (String::compose (N_("Clearing queue of %1"), _queue.size ()));
+ LOG_GENERAL (N_("Clearing queue of %1"), _queue.size ());
/* Keep waking workers until the queue is empty */
while (!_queue.empty ()) {
- _film->log()->log (String::compose (N_("Waking with %1"), _queue.size ()), Log::VERBOSE);
_condition.notify_all ();
_condition.wait (lock);
}
terminate_threads ();
- _film->log()->log (String::compose (N_("Mopping up %1"), _queue.size()));
+ LOG_GENERAL (N_("Mopping up %1"), _queue.size());
/* The following sequence of events can occur in the above code:
1. a remote worker takes the last image off the queue
*/
for (list<shared_ptr<DCPVideoFrame> >::iterator i = _queue.begin(); i != _queue.end(); ++i) {
- _film->log()->log (String::compose (N_("Encode left-over frame %1"), (*i)->index ()));
+ LOG_GENERAL (N_("Encode left-over frame %1"), (*i)->index ());
try {
_writer->write ((*i)->encode_locally(), (*i)->index (), (*i)->eyes ());
frame_done ();
} catch (std::exception& e) {
- _film->log()->log (String::compose (N_("Local encode failed (%1)"), e.what ()));
+ LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
}
}
/* Wait until the queue has gone down a bit */
while (_queue.size() >= _threads.size() * 2 && !_terminate) {
- TIMING ("decoder sleeps with queue of %1", _queue.size());
+ LOG_TIMING ("decoder sleeps with queue of %1", _queue.size());
_condition.wait (lock);
- TIMING ("decoder wakes with queue of %1", _queue.size());
+ LOG_TIMING ("decoder wakes with queue of %1", _queue.size());
}
if (_terminate) {
frame_done ();
} else {
/* Queue this new frame for encoding */
- TIMING ("adding to queue of %1", _queue.size ());
+ LOG_TIMING ("adding to queue of %1", _queue.size ());
_queue.push_back (shared_ptr<DCPVideoFrame> (
new DCPVideoFrame (
pvf,
while (1) {
- TIMING ("encoder thread %1 sleeps", boost::this_thread::get_id());
+ LOG_TIMING ("[%1] encoder thread sleeps", boost::this_thread::get_id());
boost::mutex::scoped_lock lock (_mutex);
while (_queue.empty () && !_terminate) {
_condition.wait (lock);
return;
}
- TIMING ("encoder thread %1 wakes with queue of %2", boost::this_thread::get_id(), _queue.size());
+ LOG_TIMING ("[%1] encoder thread wakes with queue of %2", boost::this_thread::get_id(), _queue.size());
shared_ptr<DCPVideoFrame> vf = _queue.front ();
- TIMING ("encoder thread %1 pops frame %2 (%3) from queue", boost::this_thread::get_id(), vf->index(), vf->eyes ());
+ LOG_TIMING ("[%1] encoder thread pops frame %2 (%3) from queue", boost::this_thread::get_id(), vf->index(), vf->eyes ());
_queue.pop_front ();
lock.unlock ();
encoded = vf->encode_remotely (server.get ());
if (remote_backoff > 0) {
- _film->log()->log (String::compose (N_("%1 was lost, but now she is found; removing backoff"), server->host_name ()));
+ LOG_GENERAL ("%1 was lost, but now she is found; removing backoff", server->host_name ());
}
/* This job succeeded, so remove any backoff */
/* back off more */
remote_backoff += 10;
}
- _film->log()->log (
- String::compose (
- N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
- vf->index(), server->host_name(), e.what(), remote_backoff)
+ LOG_ERROR (
+ N_("Remote encode of %1 on %2 failed (%3); thread sleeping for %4s"),
+ vf->index(), server->host_name(), e.what(), remote_backoff
);
}
} else {
try {
- TIMING ("encoder thread %1 begins local encode of %2", boost::this_thread::get_id(), vf->index());
+ LOG_TIMING ("[%1] encoder thread begins local encode of %2", boost::this_thread::get_id(), vf->index());
encoded = vf->encode_locally ();
- TIMING ("encoder thread %1 finishes local encode of %2", boost::this_thread::get_id(), vf->index());
+ LOG_TIMING ("[%1] encoder thread finishes local encode of %2", boost::this_thread::get_id(), vf->index());
} catch (std::exception& e) {
- _film->log()->log (String::compose (N_("Local encode failed (%1)"), e.what ()));
+ LOG_ERROR (N_("Local encode failed (%1)"), e.what ());
}
}
frame_done ();
} else {
lock.lock ();
- _film->log()->log (
- String::compose (N_("Encoder thread %1 pushes frame %2 back onto queue after failure"), boost::this_thread::get_id(), vf->index())
- );
+ LOG_GENERAL (N_("[%1] Encoder thread pushes frame %2 back onto queue after failure"), boost::this_thread::get_id(), vf->index());
_queue.push_front (vf);
lock.unlock ();
}
#include "i18n.h"
+#define LOG_GENERAL(...) film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+
using std::string;
using std::stringstream;
using std::vector;
shared_ptr<const Film> film = _film.lock ();
assert (film);
- film->log()->log (String::compose ("Video length obtained from header as %1 frames", video_length.frames (video_frame_rate ())));
+ LOG_GENERAL ("Video length obtained from header as %1 frames", video_length.frames (video_frame_rate ()));
{
boost::mutex::scoped_lock lm (_mutex);
#include "audio_buffers.h"
#include "ffmpeg_content.h"
#include "image_proxy.h"
+#include "film.h"
#include "i18n.h"
+#define LOG_GENERAL(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_ERROR(...) _video_content->film()->log()->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_WARNING(...) _video_content->film()->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
+
using std::cout;
using std::string;
using std::vector;
/* Maybe we should fail here, but for now we'll just finish off instead */
char buf[256];
av_strerror (r, buf, sizeof(buf));
- _log->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r));
+ LOG_ERROR (N_("error on av_read_frame (%1) (%2)"), buf, r);
}
flush ();
int const decode_result = avcodec_decode_audio4 (audio_codec_context(), _frame, &frame_finished, ©_packet);
if (decode_result < 0) {
- _log->log (String::compose ("avcodec_decode_audio4 failed (%1)", decode_result));
+ LOG_ERROR ("avcodec_decode_audio4 failed (%1)", decode_result);
return;
}
if (i == _filter_graphs.end ()) {
graph.reset (new FilterGraph (_ffmpeg_content, dcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format));
_filter_graphs.push_back (graph);
- _log->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format));
+ LOG_GENERAL (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format);
} else {
graph = *i;
}
if (i->second != AV_NOPTS_VALUE) {
double const pts = i->second * av_q2d (_format_context->streams[_video_stream]->time_base) + _pts_offset.seconds ();
- video (shared_ptr<ImageProxy> (new RawImageProxy (image)), rint (pts * _ffmpeg_content->video_frame_rate ()));
+ video (
+ shared_ptr<ImageProxy> (new RawImageProxy (image, _video_content->film()->log())),
+ rint (pts * _ffmpeg_content->video_frame_rate ())
+ );
} else {
- _log->log ("Dropping frame without PTS");
+ LOG_WARNING ("Dropping frame without PTS");
}
}
ContentTime
FFmpegExaminer::video_length () const
{
- ContentTime const length = ContentTime::from_seconds (double (_format_context->duration) / AV_TIME_BASE);
+ ContentTime const length = ContentTime::from_seconds (double (_format_context->duration - _format_context->start_time) / AV_TIME_BASE);
return ContentTime (max (int64_t (1), length.get ()));
}
using dcp::Signer;
using dcp::raw_convert;
+#define LOG_GENERAL(...) log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_GENERAL_NC(...) log()->log (__VA_ARGS__, Log::TYPE_GENERAL);
+
/* 5 -> 6
* AudioMapping XML changed.
* 6 -> 7
*/
write_metadata ();
- log()->log (String::compose ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary()));
+ LOG_GENERAL ("DCP-o-matic %1 git %2 using %3", dcpomatic_version, dcpomatic_git_commit, dependency_version_summary());
{
char buffer[128];
gethostname (buffer, sizeof (buffer));
- log()->log (String::compose ("Starting to make DCP on %1", buffer));
+ LOG_GENERAL ("Starting to make DCP on %1", buffer);
}
ContentList cl = content ();
for (ContentList::const_iterator i = cl.begin(); i != cl.end(); ++i) {
- log()->log (String::compose ("Content: %1", (*i)->technical_summary()));
+ LOG_GENERAL ("Content: %1", (*i)->technical_summary());
}
- log()->log (String::compose ("DCP video rate %1 fps", video_frame_rate()));
- log()->log (String::compose ("%1 threads", Config::instance()->num_local_encoding_threads()));
- log()->log (String::compose ("J2K bandwidth %1", j2k_bandwidth()));
+ LOG_GENERAL ("DCP video rate %1 fps", video_frame_rate());
+ LOG_GENERAL ("%1 threads", Config::instance()->num_local_encoding_threads());
+ LOG_GENERAL ("J2K bandwidth %1", j2k_bandwidth());
#ifdef DCPOMATIC_DEBUG
- log()->log ("DCP-o-matic built in debug mode.");
+ LOG_GENERAL_NC ("DCP-o-matic built in debug mode.");
#else
- log()->log ("DCP-o-matic built in optimised mode.");
+ LOG_GENERAL_NC ("DCP-o-matic built in optimised mode.");
#endif
#ifdef LIBDCP_DEBUG
- log()->log ("libdcp built in debug mode.");
+ LOG_GENERAL_NC ("libdcp built in debug mode.");
#else
- log()->log ("libdcp built in optimised mode.");
+ LOG_GENERAL_NC ("libdcp built in optimised mode.");
#endif
#ifdef DCPOMATIC_WINDOWS
OSVERSIONINFO info;
info.dwOSVersionInfoSize = sizeof (info);
GetVersionEx (&info);
- log()->log (String::compose ("Windows version %1.%2.%3 SP %4", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber, info.szCSDVersion));
+ LOG_GENERAL ("Windows version %1.%2.%3 SP %4", info.dwMajorVersion, info.dwMinorVersion, info.dwBuildNumber, info.szCSDVersion);
#endif
- log()->log (String::compose ("CPU: %1, %2 processors", cpu_info(), boost::thread::hardware_concurrency ()));
+ LOG_GENERAL ("CPU: %1, %2 processors", cpu_info(), boost::thread::hardware_concurrency ());
list<pair<string, string> > const m = mount_info ();
for (list<pair<string, string> >::const_iterator i = m.begin(); i != m.end(); ++i) {
- log()->log (String::compose ("Mount: %1 %2", i->first, i->second));
+ LOG_GENERAL ("Mount: %1 %2", i->first, i->second);
}
if (container() == 0) {
return file (p);
}
-/** @return List of subdirectories (not full paths) containing DCPs that can be successfully dcp::DCP::read() */
-list<boost::filesystem::path>
-Film::dcps () const
+/** Find all the DCPs in our directory that can be libdcp::DCP::read() and return details of their CPLs */
+vector<CPLSummary>
+Film::cpls () const
{
- list<boost::filesystem::path> out;
+ vector<CPLSummary> out;
boost::filesystem::path const dir = directory ();
for (boost::filesystem::directory_iterator i = boost::filesystem::directory_iterator(dir); i != boost::filesystem::directory_iterator(); ++i) {
try {
dcp::DCP dcp (*i);
dcp.read ();
- out.push_back (i->path().leaf ());
+ out.push_back (
+ CPLSummary (
+ i->path().leaf().string(),
+ dcp.cpls().front()->id(),
+ dcp.cpls().front()->annotation_text(),
+ dcp.cpls().front()->file()
+ )
+ );
} catch (...) {
}
dcp::EncryptedKDM
Film::make_kdm (
shared_ptr<dcp::Certificate> target,
- boost::filesystem::path dcp_dir,
+ boost::filesystem::path cpl_file,
dcp::LocalTime from,
dcp::LocalTime until
) const
{
- shared_ptr<const Signer> signer = make_signer ();
-
- dcp::DCP dcp (dir (dcp_dir.string ()));
-
- try {
- dcp.read ();
- } catch (...) {
- throw KDMError (_("Could not read DCP to make KDM for"));
- }
-
- dcp.cpls().front()->set_mxf_keys (key ());
-
+ shared_ptr<const dcp::CPL> cpl (new dcp::CPL (cpl_file));
return dcp::DecryptedKDM (
- dcp.cpls().front(), from, until, "DCP-o-matic", dcp.cpls().front()->content_title_text(), dcp::LocalTime().as_string()
- ).encrypt (signer, target);
+ cpl, from, until, "DCP-o-matic", cpl->content_title_text(), dcp::LocalTime().as_string()
+ ).encrypt (make_signer(), target);
}
list<dcp::EncryptedKDM>
dcp::Size full_frame () const;
dcp::Size frame_size () const;
- std::list<boost::filesystem::path> dcps () const;
+ std::vector<CPLSummary> cpls () const;
boost::shared_ptr<Player> make_player () const;
boost::shared_ptr<Playlist> playlist () const;
dcp::EncryptedKDM
make_kdm (
boost::shared_ptr<dcp::Certificate> target,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl_file,
dcp::LocalTime from,
dcp::LocalTime until
) const;
std::list<dcp::EncryptedKDM> make_kdms (
std::list<boost::shared_ptr<Screen> >,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl_file,
dcp::LocalTime from,
dcp::LocalTime until
) const;
if (!_image_content->still() || !_image) {
/* Either we need an image or we are using moving images, so load one */
- _image.reset (new MagickImageProxy (_image_content->path (_image_content->still() ? 0 : _video_position)));
+ _image.reset (new MagickImageProxy (_image_content->path (_image_content->still() ? 0 : _video_position), _image_content->film()->log ()));
}
video (_image, _video_position);
#include "image.h"
#include "exceptions.h"
#include "cross.h"
+#include "log.h"
#include "i18n.h"
+#define LOG_TIMING(...) _log->microsecond_log (String::compose (__VA_ARGS__), Log::TYPE_TIMING);
+
using std::cout;
using std::string;
using std::stringstream;
using boost::shared_ptr;
-RawImageProxy::RawImageProxy (shared_ptr<Image> image)
- : _image (image)
+ImageProxy::ImageProxy (shared_ptr<Log> log)
+ : _log (log)
{
}
-RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
+RawImageProxy::RawImageProxy (shared_ptr<Image> image, shared_ptr<Log> log)
+ : ImageProxy (log)
+ , _image (image)
+{
+
+}
+
+RawImageProxy::RawImageProxy (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shared_ptr<Log> log)
+ : ImageProxy (log)
{
dcp::Size size (
xml->number_child<int> ("Width"), xml->number_child<int> ("Height")
_image->write_to_socket (socket);
}
-MagickImageProxy::MagickImageProxy (boost::filesystem::path path)
+MagickImageProxy::MagickImageProxy (boost::filesystem::path path, shared_ptr<Log> log)
+ : ImageProxy (log)
{
/* Read the file into a Blob */
delete[] data;
}
-MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> socket)
+MagickImageProxy::MagickImageProxy (shared_ptr<cxml::Node>, shared_ptr<Socket> socket, shared_ptr<Log> log)
+ : ImageProxy (log)
{
uint32_t const size = socket->read_uint32 ();
uint8_t* data = new uint8_t[size];
return _image;
}
+ LOG_TIMING ("[%1] MagickImageProxy begins decode and convert of %2 bytes", boost::this_thread::get_id(), _blob.length());
+
Magick::Image* magick_image = 0;
try {
magick_image = new Magick::Image (_blob);
}
dcp::Size size (magick_image->columns(), magick_image->rows());
+ LOG_TIMING ("[%1] MagickImageProxy decode finished", boost::this_thread::get_id ());
_image.reset (new Image (PIX_FMT_RGB24, size, true));
delete magick_image;
+ LOG_TIMING ("[%1] MagickImageProxy completes decode and convert of %2 bytes", boost::this_thread::get_id(), _blob.length());
+
return _image;
}
}
shared_ptr<ImageProxy>
-image_proxy_factory (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket)
+image_proxy_factory (shared_ptr<cxml::Node> xml, shared_ptr<Socket> socket, shared_ptr<Log> log)
{
if (xml->string_child("Type") == N_("Raw")) {
- return shared_ptr<ImageProxy> (new RawImageProxy (xml, socket));
+ return shared_ptr<ImageProxy> (new RawImageProxy (xml, socket, log));
} else if (xml->string_child("Type") == N_("Magick")) {
- return shared_ptr<MagickImageProxy> (new MagickImageProxy (xml, socket));
+ return shared_ptr<MagickImageProxy> (new MagickImageProxy (xml, socket, log));
}
throw NetworkError (_("Unexpected image type received by server"));
class Image;
class Socket;
+class Log;
namespace cxml {
class Node;
* the TIFF data TIFF until such a time that the actual image is needed.
* At this point, the class decodes the TIFF to an Image.
*/
-class ImageProxy
+class ImageProxy : public boost::noncopyable
{
public:
+ ImageProxy (boost::shared_ptr<Log> log);
+
virtual boost::shared_ptr<Image> image () const = 0;
virtual void add_metadata (xmlpp::Node *) const = 0;
virtual void send_binary (boost::shared_ptr<Socket>) const = 0;
+
+protected:
+ boost::shared_ptr<Log> _log;
};
class RawImageProxy : public ImageProxy
{
public:
- RawImageProxy (boost::shared_ptr<Image>);
- RawImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
+ RawImageProxy (boost::shared_ptr<Image>, boost::shared_ptr<Log> log);
+ RawImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
boost::shared_ptr<Image> image () const;
void add_metadata (xmlpp::Node *) const;
class MagickImageProxy : public ImageProxy
{
public:
- MagickImageProxy (boost::filesystem::path);
- MagickImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
+ MagickImageProxy (boost::filesystem::path, boost::shared_ptr<Log> log);
+ MagickImageProxy (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
boost::shared_ptr<Image> image () const;
void add_metadata (xmlpp::Node *) const;
mutable boost::shared_ptr<Image> _image;
};
-boost::shared_ptr<ImageProxy> image_proxy_factory (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket);
+boost::shared_ptr<ImageProxy> image_proxy_factory (boost::shared_ptr<cxml::Node> xml, boost::shared_ptr<Socket> socket, boost::shared_ptr<Log> log);
void
Job::set_error (string s, string d)
{
- _film->log()->log (String::compose ("Error in job: %1 (%2)", s, d));
+ _film->log()->log (String::compose ("Error in job: %1 (%2)", s, d), Log::TYPE_ERROR);
boost::mutex::scoped_lock lm (_state_mutex);
_error_summary = s;
_error_details = d;
make_screen_kdms (
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl,
dcp::LocalTime from,
dcp::LocalTime to
)
{
- list<dcp::EncryptedKDM> kdms = film->make_kdms (screens, dcp, from, to);
+ list<dcp::EncryptedKDM> kdms = film->make_kdms (screens, cpl, from, to);
list<ScreenKDM> screen_kdms;
make_cinema_kdms (
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl,
dcp::LocalTime from,
dcp::LocalTime to
)
{
- list<ScreenKDM> screen_kdms = make_screen_kdms (film, screens, dcp, from, to);
+ list<ScreenKDM> screen_kdms = make_screen_kdms (film, screens, cpl, from, to);
list<CinemaKDMs> cinema_kdms;
while (!screen_kdms.empty ()) {
write_kdm_files (
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl,
dcp::LocalTime from,
dcp::LocalTime to,
boost::filesystem::path directory
)
{
- list<ScreenKDM> screen_kdms = make_screen_kdms (film, screens, dcp, from, to);
+ list<ScreenKDM> screen_kdms = make_screen_kdms (film, screens, cpl, from, to);
/* Write KDMs to the specified directory */
for (list<ScreenKDM>::iterator i = screen_kdms.begin(); i != screen_kdms.end(); ++i) {
write_kdm_zip_files (
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl,
dcp::LocalTime from,
dcp::LocalTime to,
boost::filesystem::path directory
)
{
- list<CinemaKDMs> cinema_kdms = make_cinema_kdms (film, screens, dcp, from, to);
+ list<CinemaKDMs> cinema_kdms = make_cinema_kdms (film, screens, cpl, from, to);
for (list<CinemaKDMs>::const_iterator i = cinema_kdms.begin(); i != cinema_kdms.end(); ++i) {
boost::filesystem::path path = directory;
email_kdms (
shared_ptr<const Film> film,
list<shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl,
dcp::LocalTime from,
dcp::LocalTime to
)
{
- list<CinemaKDMs> cinema_kdms = make_cinema_kdms (film, screens, dcp, from, to);
+ list<CinemaKDMs> cinema_kdms = make_cinema_kdms (film, screens, cpl, from, to);
for (list<CinemaKDMs>::const_iterator i = cinema_kdms.begin(); i != cinema_kdms.end(); ++i) {
extern void write_kdm_files (
boost::shared_ptr<const Film> film,
std::list<boost::shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl,
dcp::LocalTime from,
dcp::LocalTime to,
boost::filesystem::path directory
extern void write_kdm_zip_files (
boost::shared_ptr<const Film> film,
std::list<boost::shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl,
dcp::LocalTime from,
dcp::LocalTime to,
boost::filesystem::path directory
extern void email_kdms (
boost::shared_ptr<const Film> film,
std::list<boost::shared_ptr<Screen> > screens,
- boost::filesystem::path dcp,
+ boost::filesystem::path cpl,
dcp::LocalTime from,
dcp::LocalTime to
);
#include <cstdio>
#include "log.h"
#include "cross.h"
+#include "config.h"
#include "i18n.h"
using namespace std;
+int const Log::TYPE_GENERAL = 0x1;
+int const Log::TYPE_WARNING = 0x2;
+int const Log::TYPE_ERROR = 0x4;
+int const Log::TYPE_TIMING = 0x8;
+
Log::Log ()
- : _level (STANDARD)
+ : _types (0)
{
+ Config::instance()->Changed.connect (boost::bind (&Log::config_changed, this));
+ config_changed ();
+}
+void
+Log::config_changed ()
+{
+ set_types (Config::instance()->log_types ());
}
/** @param n String to log */
void
-Log::log (string m, Level l)
+Log::log (string message, int type)
{
boost::mutex::scoped_lock lm (_mutex);
- if (l > _level) {
+ if ((_types & type) == 0) {
return;
}
string a = ctime (&t);
stringstream s;
- s << a.substr (0, a.length() - 1) << N_(": ") << m;
+ s << a.substr (0, a.length() - 1) << N_(": ");
+
+ if (type & TYPE_ERROR) {
+ s << "ERROR: ";
+ }
+
+ if (type & TYPE_WARNING) {
+ s << "WARNING: ";
+ }
+
+ s << message;
do_log (s.str ());
}
void
-Log::microsecond_log (string m, Level l)
+Log::microsecond_log (string m, int t)
{
boost::mutex::scoped_lock lm (_mutex);
- if (l > _level) {
+ if ((_types & t) == 0) {
return;
}
}
void
-Log::set_level (Level l)
+Log::set_types (int t)
{
boost::mutex::scoped_lock lm (_mutex);
- _level = l;
-}
-
-void
-Log::set_level (string l)
-{
- if (l == N_("verbose")) {
- set_level (VERBOSE);
- return;
- } else if (l == N_("timing")) {
- set_level (TIMING);
- return;
- }
-
- set_level (STANDARD);
+ _types = t;
}
/** @param file Filename to write log to */
Log ();
virtual ~Log () {}
- enum Level {
- STANDARD = 0,
- VERBOSE = 1,
- TIMING = 2
- };
+ static const int TYPE_GENERAL;
+ static const int TYPE_WARNING;
+ static const int TYPE_ERROR;
+ static const int TYPE_TIMING;
- void log (std::string m, Level l = STANDARD);
- void microsecond_log (std::string m, Level l = STANDARD);
+ void log (std::string message, int type);
+ void microsecond_log (std::string message, int type);
- void set_level (Level l);
- void set_level (std::string l);
+ void set_types (int types);
protected:
/** mutex to protect the log */
private:
virtual void do_log (std::string m) = 0;
+ void config_changed ();
- /** level above which to ignore log messages */
- Level _level;
+ /** bit-field of log types which should be put into the log (others are ignored) */
+ int _types;
};
class FileLog : public Log
#include "content_video.h"
#include "player_video_frame.h"
+#define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+
using std::list;
using std::cout;
using std::min;
{
return shared_ptr<PlayerVideoFrame> (
new PlayerVideoFrame (
- shared_ptr<const ImageProxy> (new RawImageProxy (_black_image)),
+ shared_ptr<const ImageProxy> (new RawImageProxy (_black_image, _film->log ())),
Crop (),
_video_container_size,
_video_container_size,
min (AudioFrame (all->audio->frames()), length_frames) - offset.frames (_film->audio_frame_rate ())
);
}
-
- return audio;
}
VideoFrame
void
PlayerStatistics::dump (shared_ptr<Log> log) const
{
- log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat));
- log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()));
+ log->log (String::compose ("Video: %1 good %2 skipped %3 black %4 repeat", video.good, video.skip, video.black, video.repeat), Log::TYPE_GENERAL);
+ log->log (String::compose ("Audio: %1 good %2 skipped %3 silence", audio.good, audio.skip, audio.silence.seconds()), Log::TYPE_GENERAL);
}
PlayerStatistics const &
}
-PlayerVideoFrame::PlayerVideoFrame (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket)
+PlayerVideoFrame::PlayerVideoFrame (shared_ptr<cxml::Node> node, shared_ptr<Socket> socket, shared_ptr<Log> log)
{
_crop = Crop (node);
_part = (Part) node->number_child<int> ("Part");
_colour_conversion = ColourConversion (node);
- _in = image_proxy_factory (node->node_child ("In"), socket);
+ _in = image_proxy_factory (node->node_child ("In"), socket, log);
if (node->optional_number_child<int> ("SubtitleX")) {
class ImageProxy;
class Scaler;
class Socket;
+class Log;
/** Everything needed to describe a video frame coming out of the player, but with the
* bits still their raw form. We may want to combine the bits on a remote machine,
{
public:
PlayerVideoFrame (boost::shared_ptr<const ImageProxy>, Crop, dcp::Size, dcp::Size, Scaler const *, Eyes, Part, ColourConversion);
- PlayerVideoFrame (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>);
+ PlayerVideoFrame (boost::shared_ptr<cxml::Node>, boost::shared_ptr<Socket>, boost::shared_ptr<Log>);
void set_subtitle (PositionImage);
#include "i18n.h"
+#define LOG_GENERAL_NC(...) _film->log()->log (__VA_ARGS__, Log::TYPE_GENERAL);
+
using std::string;
using std::stringstream;
using std::min;
void
SCPDCPJob::run ()
{
- _film->log()->log (N_("SCP DCP job starting"));
+ LOG_GENERAL_NC (N_("SCP DCP job starting"));
SSHSession ss;
#include "i18n.h"
+#define LOG_GENERAL(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_GENERAL_NC(...) _log->log (__VA_ARGS__, Log::TYPE_GENERAL);
+#define LOG_ERROR(...) _log->log (String::compose (__VA_ARGS__), Log::TYPE_ERROR);
+#define LOG_ERROR_NC(...) _log->log (__VA_ARGS__, Log::TYPE_ERROR);
+
using std::string;
using std::stringstream;
using std::multimap;
xml->read_stream (s);
if (xml->number_child<int> ("Version") != SERVER_LINK_VERSION) {
cerr << "Mismatched server/client versions\n";
- _log->log ("Mismatched server/client versions");
+ LOG_ERROR_NC ("Mismatched server/client versions");
return -1;
}
- shared_ptr<PlayerVideoFrame> pvf (new PlayerVideoFrame (xml, socket));
+ shared_ptr<PlayerVideoFrame> pvf (new PlayerVideoFrame (xml, socket, _log));
DCPVideoFrame dcp_video_frame (pvf, xml, _log);
try {
encoded->send (socket);
} catch (std::exception& e) {
- _log->log (String::compose ("Send failed; frame %1", dcp_video_frame.index()));
+ LOG_ERROR ("Send failed; frame %1", dcp_video_frame.index());
throw;
}
frame = process (socket, after_read, after_encode);
ip = socket->socket().remote_endpoint().address().to_string();
} catch (std::exception& e) {
- _log->log (String::compose ("Error: %1", e.what()));
+ LOG_ERROR ("Error: %1", e.what());
}
gettimeofday (&end, 0);
cout << message.str() << "\n";
}
- _log->log (message.str ());
+ LOG_GENERAL_NC (message.str ());
}
_worker_condition.notify_all ();
void
Server::run (int num_threads)
{
- _log->log (String::compose ("Server starting with %1 threads", num_threads));
+ LOG_GENERAL ("Server starting with %1 threads", num_threads);
if (_verbose) {
cout << "DCP-o-matic server starting with " << num_threads << " threads.\n";
}
#include "i18n.h"
+#define LOG_GENERAL_NC(...) _film->log()->log (__VA_ARGS__, Log::TYPE_GENERAL);
+#define LOG_ERROR_NC(...) _film->log()->log (__VA_ARGS__, Log::TYPE_ERROR);
+
using std::string;
using std::stringstream;
using std::fixed;
{
try {
- _film->log()->log (N_("Transcode job starting"));
+ LOG_GENERAL_NC (N_("Transcode job starting"));
_transcoder.reset (new Transcoder (_film, shared_from_this ()));
_transcoder->go ();
set_progress (1);
set_state (FINISHED_OK);
- _film->log()->log (N_("Transcode job completed successfully"));
+ LOG_GENERAL_NC (N_("Transcode job completed successfully"));
_transcoder.reset ();
} catch (...) {
+ set_progress (1);
+ set_state (FINISHED_ERROR);
+ LOG_ERROR_NC (N_("Transcode job failed or cancelled"));
_transcoder.reset ();
throw;
}
void as_xml (xmlpp::Node *) const;
};
+struct CPLSummary
+{
+ CPLSummary (std::string d, std::string i, std::string a, boost::filesystem::path f)
+ : dcp_directory (d)
+ , cpl_id (i)
+ , cpl_annotation_text (a)
+ , cpl_file (f)
+ {}
+
+ std::string dcp_directory;
+ std::string cpl_id;
+ std::string cpl_annotation_text;
+ boost::filesystem::path cpl_file;
+};
+
extern bool operator== (Crop const & a, Crop const & b);
extern bool operator!= (Crop const & a, Crop const & b);
#include "types.h"
#include "video_content.h"
-#ifdef DCPOMATIC_DEBUG
-#define TIMING(...) _film->log()->microsecond_log (String::compose (__VA_ARGS__), Log::TIMING);
-#else
-#define TIMING(...)
-#endif
-
#undef check
/** The maximum number of audio channels that we can have in a DCP */
#include "i18n.h"
+#define LOG_GENERAL(...) _film->log()->log (String::compose (__VA_ARGS__), Log::TYPE_GENERAL);
+#define LOG_TIMING(...) _film->log()->microsecond_log (String::compose (__VA_ARGS__), Log::TYPE_TIMING);
+#define LOG_WARNING_NC(...) _film->log()->log (__VA_ARGS__, Log::TYPE_WARNING);
+
/* OS X strikes again */
#undef set_key
break;
}
- TIMING (N_("writer sleeps with a queue of %1"), _queue.size());
+ LOG_TIMING (N_("writer sleeps with a queue of %1"), _queue.size());
_empty_condition.wait (lock);
- TIMING (N_("writer wakes with a queue of %1"), _queue.size());
+ LOG_TIMING (N_("writer wakes with a queue of %1"), _queue.size());
}
if (_finish && _queue.empty()) {
switch (qi.type) {
case QueueItem::FULL:
{
- _film->log()->log (String::compose (N_("Writer FULL-writes %1 to MXF"), qi.frame));
+ LOG_GENERAL (N_("Writer FULL-writes %1 to MXF"), qi.frame);
if (!qi.encoded) {
qi.encoded.reset (new EncodedData (_film->j2c_path (qi.frame, qi.eyes, false)));
}
break;
}
case QueueItem::FAKE:
- _film->log()->log (String::compose (N_("Writer FAKE-writes %1 to MXF"), qi.frame));
+ LOG_GENERAL (N_("Writer FAKE-writes %1 to MXF"), qi.frame);
_picture_mxf_writer->fake_write (qi.size);
_last_written[qi.eyes].reset ();
++_fake_written;
lock.unlock ();
- _film->log()->log (
- String::compose (
- "Writer full (awaiting %1 [last eye was %2]); pushes %3 to disk",
- _last_written_frame + 1,
- _last_written_eyes, qi.frame)
+ LOG_GENERAL (
+ "Writer full (awaiting %1 [last eye was %2]); pushes %3 to disk",
+ _last_written_frame + 1,
+ _last_written_eyes, qi.frame
);
qi.encoded->write (_film, qi.frame, qi.eyes);
if (ec) {
/* hard link failed; copy instead */
boost::filesystem::copy_file (video_from, video_to);
- _film->log()->log ("Hard-link failed; fell back to copying");
+ LOG_WARNING_NC ("Hard-link failed; fell back to copying");
}
_picture_mxf->set_file (video_to);
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 pushed to disk"), _full_written, _fake_written, _pushed_to_disk)
+ LOG_GENERAL (
+ N_("Wrote %1 FULL, %2 FAKE, %3 pushed to disk"), _full_written, _fake_written, _pushed_to_disk
);
}
/* Read the frame info as written */
FILE* ifi = fopen_boost (_film->info_path (f, eyes), "r");
if (!ifi) {
- _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
+ LOG_GENERAL ("Existing frame %1 has no info file", f);
return false;
}
dcp::FrameInfo info (ifi);
fclose (ifi);
if (info.size == 0) {
- _film->log()->log (String::compose ("Existing frame %1 has no info file", f));
+ LOG_GENERAL ("Existing frame %1 has no info file", f);
return false;
}
EncodedData data (info.size);
size_t const read = fread (data.data(), 1, data.size(), mxf);
if (read != static_cast<size_t> (data.size ())) {
- _film->log()->log (String::compose ("Existing frame %1 is incomplete", f));
+ LOG_GENERAL ("Existing frame %1 is incomplete", f);
return false;
}
string const existing_hash = md5_digest (data.data(), data.size());
if (existing_hash != info.hash) {
- _film->log()->log (String::compose ("Existing frame %1 failed hash check", f));
+ LOG_GENERAL ("Existing frame %1 failed hash check", f);
return false;
}
p /= _film->internal_video_mxf_filename ();
FILE* mxf = fopen_boost (p, "rb");
if (!mxf) {
- _film->log()->log (String::compose ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno));
+ LOG_GENERAL ("Could not open existing MXF at %1 (errno=%2)", p.string(), errno);
return;
}
}
}
- _film->log()->log (String::compose ("Have existing frame %1", _first_nonexistant_frame));
+ LOG_GENERAL ("Have existing frame %1", _first_nonexistant_frame);
++_first_nonexistant_frame;
}
#include <wx/stdpaths.h>
#include <wx/cmdline.h>
#include <wx/preferences.h>
+#include <libdcp/exceptions.h>
#include "wx/film_viewer.h"
#include "wx/film_editor.h"
#include "wx/job_manager_view.h"
static FilmEditor* film_editor = 0;
static FilmViewer* film_viewer = 0;
static shared_ptr<Film> film;
-static std::string log_level;
static std::string film_to_load;
static std::string film_to_create;
static std::string content_to_add;
for (list<string>::const_iterator i = notes.begin(); i != notes.end(); ++i) {
error_dialog (0, std_to_wx (*i));
}
- film->log()->set_level (log_level);
}
#define ALWAYS 0x0
#define NEEDS_FILM 0x1
#define NOT_DURING_DCP_CREATION 0x2
-#define NEEDS_DCP 0x4
+#define NEEDS_CPL 0x4
map<wxMenuItem*, int> menu_items;
++i;
}
bool const dcp_creation = (i != jobs.end ()) && !(*i)->finished ();
- bool const have_dcp = film && !film->dcps().empty ();
+ bool const have_cpl = film && !film->cpls().empty ();
for (map<wxMenuItem*, int>::iterator j = menu_items.begin(); j != menu_items.end(); ++j) {
enabled = false;
}
- if ((j->second & NEEDS_DCP) && !have_dcp) {
+ if ((j->second & NEEDS_CPL) && !have_cpl) {
enabled = false;
}
jobs_menu = new wxMenu;
add_item (jobs_menu, _("&Make DCP"), ID_jobs_make_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION);
- add_item (jobs_menu, _("Make &KDMs..."), ID_jobs_make_kdms, NEEDS_FILM | NEEDS_DCP);
- add_item (jobs_menu, _("&Send DCP to TMS"), ID_jobs_send_dcp_to_tms, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_DCP);
- add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_DCP);
+ add_item (jobs_menu, _("Make &KDMs..."), ID_jobs_make_kdms, NEEDS_FILM);
+ add_item (jobs_menu, _("&Send DCP to TMS"), ID_jobs_send_dcp_to_tms, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL);
+ add_item (jobs_menu, _("S&how DCP"), ID_jobs_show_dcp, NEEDS_FILM | NOT_DURING_DCP_CREATION | NEEDS_CPL);
wxMenu* tools = new wxMenu;
add_item (tools, _("Hints..."), ID_tools_hints, 0);
maybe_save_then_delete_film ();
film.reset (new Film (d->get_path ()));
film->write_metadata ();
- film->log()->set_level (log_level);
film->set_name (boost::filesystem::path (d->get_path()).filename().generic_string());
set_film ();
}
try {
if (d->write_to ()) {
- write_kdm_files (film, d->screens (), d->dcp (), d->from (), d->until (), d->directory ());
+ write_kdm_files (film, d->screens (), d->cpl (), d->from (), d->until (), d->directory ());
} else {
JobManager::instance()->add (
- shared_ptr<Job> (new SendKDMEmailJob (film, d->screens (), d->dcp (), d->from (), d->until ()))
+ shared_ptr<Job> (new SendKDMEmailJob (film, d->screens (), d->cpl (), d->from (), d->until ()))
);
}
+ } catch (libdcp::NotEncryptedError& e) {
+ error_dialog (this, _("CPL's content is not encrypted."));
} catch (exception& e) {
error_dialog (this, e.what ());
} catch (...) {
};
static const wxCmdLineEntryDesc command_line_description[] = {
- { wxCMD_LINE_OPTION, "l", "log", "set log level (silent, verbose or timing)", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_SWITCH, "n", "new", "create new film", wxCMD_LINE_VAL_NONE, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_OPTION, "c", "content", "add content file", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
{ wxCMD_LINE_PARAM, 0, 0, "film to load or create", wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL },
if (!film_to_create.empty ()) {
film.reset (new Film (film_to_create));
film->write_metadata ();
- film->log()->set_level (log_level);
film->set_name (boost::filesystem::path (film_to_create).filename().generic_string ());
}
content_to_add = wx_to_std (content);
}
- wxString log;
- if (parser.Found (wxT ("log"), &log)) {
- log_level = wx_to_std (log);
- }
-
return true;
}
string film_dir;
bool progress = true;
bool no_remote = false;
- int log_level = 0;
int json_port = 0;
bool keep_going = false;
{ "flags", no_argument, 0, 'f'},
{ "no-progress", no_argument, 0, 'n'},
{ "no-remote", no_argument, 0, 'r'},
- { "log-level", required_argument, 0, 'l' },
{ "json", required_argument, 0, 'j' },
{ "keep-going", no_argument, 0, 'k' },
{ 0, 0, 0, 0 }
};
- int c = getopt_long (argc, argv, "vhdfnrl:j:k", long_options, &option_index);
+ int c = getopt_long (argc, argv, "vhdfnrj:k", long_options, &option_index);
if (c == -1) {
break;
case 'r':
no_remote = true;
break;
- case 'l':
- log_level = atoi (optarg);
- break;
case 'j':
json_port = atoi (optarg);
break;
exit (EXIT_FAILURE);
}
- film->log()->set_level ((Log::Level) log_level);
-
cout << "\nMaking DCP for " << film->name() << "\n";
// cout << "Content: " << film->content() << "\n";
// pair<string, string> const f = Filter::ffmpeg_strings (film->filters ());
using std::cout;
using std::cerr;
using std::list;
+using std::vector;
using boost::shared_ptr;
static string program_name;
}
/* XXX: allow specification of this */
- list<boost::filesystem::path> dcps = film->dcps ();
- if (dcps.empty ()) {
- error ("no DCPs found in film");
- } else if (dcps.size() > 1) {
- error ("more than one DCP found in film");
+ vector<CPLSummary> cpls = film->cpls ();
+ if (cpls.empty ()) {
+ error ("no CPLs found in film");
+ } else if (cpls.size() > 1) {
+ error ("more than one CPL found in film");
}
- boost::filesystem::path dcp = dcps.front ();
+ boost::filesystem::path cpl = cpls.front().cpl_file;
if (cinema_name.empty ()) {
}
shared_ptr<dcp::Certificate> certificate (new dcp::Certificate (boost::filesystem::path (certificate_file)));
- dcp::EncryptedKDM kdm = film->make_kdm (certificate, dcp, valid_from.get(), valid_to.get());
+ dcp::EncryptedKDM kdm = film->make_kdm (certificate, cpl, valid_from.get(), valid_to.get());
kdm.as_xml (output);
if (verbose) {
cout << "Generated KDM " << output << " for certificate.\n";
try {
if (zip) {
- write_kdm_zip_files (film, (*i)->screens(), dcp, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), output);
+ write_kdm_zip_files (film, (*i)->screens(), cpl, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), output);
+
if (verbose) {
cout << "Wrote ZIP files to " << output << "\n";
}
} else {
- write_kdm_files (film, (*i)->screens(), dcp, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), output);
+ write_kdm_files (film, (*i)->screens(), cpl, dcp::LocalTime (valid_from.get()), dcp::LocalTime (valid_to.get()), output);
if (verbose) {
cout << "Wrote KDM files to " << output << "\n";
}
tested_by.Add (wxT ("Andreas Eli"));
tested_by.Add (wxT ("Maurizio Giampà "));
tested_by.Add (wxT ("Luke Granger-Brown"));
+ tested_by.Add (wxT ("Sumit Guha"));
tested_by.Add (wxT ("Steve Guttag"));
tested_by.Add (wxT ("Patrick Haderer"));
+ tested_by.Add (wxT ("Bill Hamell"));
tested_by.Add (wxT ("Jonathan Jensen"));
tested_by.Add (wxT ("Thierry Journet"));
tested_by.Add (wxT ("Ada de Kamper"));
_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);
-
- _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
- table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
- table->AddSpacer (0);
-
add_label_to_sizer (table, panel, _("Outgoing mail server"), true);
_mail_server = new wxTextCtrl (panel, wxID_ANY);
table->Add (_mail_server, 1, wxEXPAND | wxALL);
_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));
_mail_user->SetValue (std_to_wx (config->mail_user ()));
_check_for_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_updates_changed, this));
_check_for_test_updates->SetValue (config->check_for_test_updates ());
_check_for_test_updates->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::check_for_test_updates_changed, this));
- _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
- _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&GeneralPage::allow_any_dcp_frame_rate_changed, this));
return panel;
}
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);
- }
-
- void allow_any_dcp_frame_rate_changed ()
- {
- Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
- }
-
wxCheckBox* _set_language;
wxChoice* _language;
wxSpinCtrl* _num_local_encoding_threads;
- wxSpinCtrl* _maximum_j2k_bandwidth;
- wxCheckBox* _allow_any_dcp_frame_rate;
wxTextCtrl* _mail_server;
wxTextCtrl* _mail_user;
wxTextCtrl* _mail_password;
wxTextCtrl* _kdm_email;
};
+class AdvancedPage : public wxStockPreferencesPage, public Page
+{
+public:
+
+ AdvancedPage (wxSize panel_size, int border)
+ : wxStockPreferencesPage (Kind_Advanced)
+ , Page (panel_size, border)
+ {}
+
+ wxWindow* CreateWindow (wxWindow* parent)
+ {
+ wxPanel* panel = new wxPanel (parent);
+
+ wxBoxSizer* s = new wxBoxSizer (wxVERTICAL);
+ panel->SetSizer (s);
+
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ table->AddGrowableCol (1, 1);
+ s->Add (table, 1, wxALL | wxEXPAND, _border);
+
+ add_label_to_sizer (table, panel, _("Maximum JPEG2000 bandwidth"), true);
+ _maximum_j2k_bandwidth = new wxSpinCtrl (panel);
+ table->Add (_maximum_j2k_bandwidth, 1);
+
+ _allow_any_dcp_frame_rate = new wxCheckBox (panel, wxID_ANY, _("Allow any DCP frame rate"));
+ table->Add (_allow_any_dcp_frame_rate, 1, wxEXPAND | wxALL);
+ table->AddSpacer (0);
+
+ add_label_to_sizer (table, panel, _("Log"), true);
+ _log_general = new wxCheckBox (panel, wxID_ANY, _("General"));
+ table->Add (_log_general, 1, wxEXPAND | wxALL);
+ _log_warning = new wxCheckBox (panel, wxID_ANY, _("Warnings"));
+ table->AddSpacer (0);
+ table->Add (_log_warning, 1, wxEXPAND | wxALL);
+ _log_error = new wxCheckBox (panel, wxID_ANY, _("Errors"));
+ table->AddSpacer (0);
+ table->Add (_log_error, 1, wxEXPAND | wxALL);
+ _log_timing = new wxCheckBox (panel, wxID_ANY, _("Timing"));
+ table->AddSpacer (0);
+ table->Add (_log_timing, 1, wxEXPAND | wxALL);
+
+ Config* config = Config::instance ();
+
+ _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 (&AdvancedPage::maximum_j2k_bandwidth_changed, this));
+ _allow_any_dcp_frame_rate->SetValue (config->allow_any_dcp_frame_rate ());
+ _allow_any_dcp_frame_rate->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::allow_any_dcp_frame_rate_changed, this));
+ _log_general->SetValue (config->log_types() & Log::TYPE_GENERAL);
+ _log_general->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
+ _log_warning->SetValue (config->log_types() & Log::TYPE_WARNING);
+ _log_warning->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
+ _log_error->SetValue (config->log_types() & Log::TYPE_ERROR);
+ _log_error->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
+ _log_timing->SetValue (config->log_types() & Log::TYPE_TIMING);
+ _log_timing->Bind (wxEVT_COMMAND_CHECKBOX_CLICKED, boost::bind (&AdvancedPage::log_changed, this));
+
+ return panel;
+ }
+
+private:
+
+ void maximum_j2k_bandwidth_changed ()
+ {
+ Config::instance()->set_maximum_j2k_bandwidth (_maximum_j2k_bandwidth->GetValue() * 1000000);
+ }
+
+ void allow_any_dcp_frame_rate_changed ()
+ {
+ Config::instance()->set_allow_any_dcp_frame_rate (_allow_any_dcp_frame_rate->GetValue ());
+ }
+
+ void log_changed ()
+ {
+ int types = 0;
+ if (_log_general->GetValue ()) {
+ types |= Log::TYPE_GENERAL;
+ }
+ if (_log_warning->GetValue ()) {
+ types |= Log::TYPE_WARNING;
+ }
+ if (_log_error->GetValue ()) {
+ types |= Log::TYPE_ERROR;
+ }
+ if (_log_timing->GetValue ()) {
+ types |= Log::TYPE_TIMING;
+ }
+ Config::instance()->set_log_types (types);
+ }
+
+ wxSpinCtrl* _maximum_j2k_bandwidth;
+ wxCheckBox* _allow_any_dcp_frame_rate;
+ wxCheckBox* _log_general;
+ wxCheckBox* _log_warning;
+ wxCheckBox* _log_error;
+ wxCheckBox* _log_timing;
+};
+
wxPreferencesEditor*
create_config_dialog ()
{
e->AddPage (new ColourConversionsPage (ps, border));
e->AddPage (new TMSPage (ps, border));
e->AddPage (new KDMEmailPage (ps, border));
+ e->AddPage (new AdvancedPage (ps, border));
return e;
}
_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 | wxALL, pad);
+ b->Add (_content_add_file, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_content_add_folder = new wxButton (_content_panel, wxID_ANY, _("Add folder..."));
- b->Add (_content_add_folder, 1, wxEXPAND | wxALL, pad);
+ b->Add (_content_add_folder, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_content_remove = new wxButton (_content_panel, wxID_ANY, _("Remove"));
- b->Add (_content_remove, 1, wxEXPAND | wxALL, pad);
+ b->Add (_content_remove, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_content_earlier = new wxButton (_content_panel, wxID_ANY, _("Up"));
- b->Add (_content_earlier, 1, wxEXPAND | wxALL, pad);
+ b->Add (_content_earlier, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_content_later = new wxButton (_content_panel, wxID_ANY, _("Down"));
- b->Add (_content_later, 1, wxEXPAND | wxALL, pad);
+ b->Add (_content_later, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_content_timeline = new wxButton (_content_panel, wxID_ANY, _("Timeline..."));
- b->Add (_content_timeline, 1, wxEXPAND | wxALL, pad);
+ b->Add (_content_timeline, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
s->Add (b, 0, wxALL, 4);
#include <wx/timectrl.h>
#include <wx/stdpaths.h>
#include <wx/listctrl.h>
+#include <libcxml/cxml.h>
#include "lib/cinema.h"
#include "lib/config.h"
#include "lib/film.h"
using std::list;
using std::pair;
using std::cout;
+using std::vector;
using std::make_pair;
using boost::shared_ptr;
KDMDialog::KDMDialog (wxWindow* parent, boost::shared_ptr<const Film> film)
: wxDialog (parent, wxID_ANY, _("Make KDMs"))
{
+ /* Main sizer */
wxBoxSizer* vertical = new wxBoxSizer (wxVERTICAL);
- wxBoxSizer* targets = new wxBoxSizer (wxHORIZONTAL);
+
+ /* Font for sub-headings */
+ wxFont subheading_font (*wxNORMAL_FONT);
+ subheading_font.SetWeight (wxFONTWEIGHT_BOLD);
+
+
+ /* Sub-heading: Screens */
+ wxStaticText* h = new wxStaticText (this, wxID_ANY, _("Screens"));
+ h->SetFont (subheading_font);
+ vertical->Add (h, 0, wxALIGN_CENTER_VERTICAL);
+ wxBoxSizer* targets = new wxBoxSizer (wxHORIZONTAL);
_targets = new wxTreeCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTR_HIDE_ROOT | wxTR_MULTIPLE | wxTR_HAS_BUTTONS);
- targets->Add (_targets, 1, wxEXPAND | wxALL, 6);
+ targets->Add (_targets, 1, wxEXPAND | wxTOP | wxRIGHT, DCPOMATIC_SIZER_GAP);
_root = _targets->AddRoot ("Foo");
wxBoxSizer* target_buttons = new wxBoxSizer (wxVERTICAL);
_add_cinema = new wxButton (this, wxID_ANY, _("Add Cinema..."));
- target_buttons->Add (_add_cinema, 1, wxEXPAND, 6);
+ target_buttons->Add (_add_cinema, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_edit_cinema = new wxButton (this, wxID_ANY, _("Edit Cinema..."));
- target_buttons->Add (_edit_cinema, 1, wxEXPAND, 6);
+ target_buttons->Add (_edit_cinema, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_remove_cinema = new wxButton (this, wxID_ANY, _("Remove Cinema"));
- target_buttons->Add (_remove_cinema, 1, wxEXPAND, 6);
+ target_buttons->Add (_remove_cinema, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_add_screen = new wxButton (this, wxID_ANY, _("Add Screen..."));
- target_buttons->Add (_add_screen, 1, wxEXPAND, 6);
+ target_buttons->Add (_add_screen, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_edit_screen = new wxButton (this, wxID_ANY, _("Edit Screen..."));
- target_buttons->Add (_edit_screen, 1, wxEXPAND, 6);
+ target_buttons->Add (_edit_screen, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
_remove_screen = new wxButton (this, wxID_ANY, _("Remove Screen"));
- target_buttons->Add (_remove_screen, 1, wxEXPAND, 6);
+ target_buttons->Add (_remove_screen, 1, wxEXPAND | wxALL, DCPOMATIC_BUTTON_STACK_GAP);
+
+ targets->Add (target_buttons, 0, 0);
- targets->Add (target_buttons, 0, 0, 6);
+ vertical->Add (targets, 1, wxEXPAND);
- vertical->Add (targets, 1, wxEXPAND | wxALL, 6);
- wxFlexGridSizer* table = new wxFlexGridSizer (3, 2, 6);
+ /* Sub-heading: Timing */
+ h = new wxStaticText (this, wxID_ANY, _("Timing"));
+ h->SetFont (subheading_font);
+ vertical->Add (h, 0, wxALIGN_CENTER_VERTICAL | wxTOP, DCPOMATIC_SIZER_Y_GAP * 2);
+
+ wxFlexGridSizer* table = new wxFlexGridSizer (3, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
add_label_to_sizer (table, this, _("From"), true);
wxDateTime from;
from.SetToCurrent ();
_until_time = new wxTimePickerCtrl (this, wxID_ANY, to);
table->Add (_until_time, 1, wxEXPAND);
- vertical->Add (table, 0, wxEXPAND | wxALL, 6);
+ vertical->Add (table, 0, wxEXPAND | wxTOP, DCPOMATIC_SIZER_GAP);
+
- _dcps = new wxListCtrl (this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxLC_SINGLE_SEL);
- wxListItem ip;
- ip.SetId (0);
- ip.SetText (_("DCP"));
- ip.SetWidth (400);
- _dcps->InsertColumn (0, ip);
- vertical->Add (_dcps, 0, wxEXPAND | wxALL, 6);
+ /* Sub-heading: CPL */
+ h = new wxStaticText (this, wxID_ANY, _("CPL"));
+ h->SetFont (subheading_font);
+ vertical->Add (h, 0, wxALIGN_CENTER_VERTICAL | wxTOP, DCPOMATIC_SIZER_Y_GAP * 2);
- list<boost::filesystem::path> dcps = film->dcps ();
- for (list<boost::filesystem::path>::const_iterator i = dcps.begin(); i != dcps.end(); ++i) {
- wxListItem item;
- int const n = _dcps->GetItemCount ();
- item.SetId (n);
- _dcps->InsertItem (item);
- _dcps->SetItem (n, 0, std_to_wx (i->string ()));
-
- if (dcps.size() == 1 || i->string() == film->dcp_name ()) {
- _dcps->SetItemState (n, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED);
- }
- }
+ /* CPL choice */
+ wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL);
+ add_label_to_sizer (s, this, _("CPL"), true);
+ _cpl = new wxChoice (this, wxID_ANY);
+ s->Add (_cpl, 1, wxEXPAND);
+ _cpl_browse = new wxButton (this, wxID_ANY, _("Browse..."));
+ s->Add (_cpl_browse, 0);
+ vertical->Add (s, 0, wxEXPAND | wxTOP, DCPOMATIC_SIZER_GAP + 2);
+
+ /* CPL details */
+ table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, DCPOMATIC_SIZER_Y_GAP);
+ add_label_to_sizer (table, this, _("DCP directory"), true);
+ _dcp_directory = new wxStaticText (this, wxID_ANY, "");
+ table->Add (_dcp_directory);
+ add_label_to_sizer (table, this, _("CPL ID"), true);
+ _cpl_id = new wxStaticText (this, wxID_ANY, "");
+ table->Add (_cpl_id);
+ add_label_to_sizer (table, this, _("CPL annotation text"), true);
+ _cpl_annotation_text = new wxStaticText (this, wxID_ANY, "");
+ table->Add (_cpl_annotation_text);
+ vertical->Add (table, 0, wxEXPAND | wxTOP, DCPOMATIC_SIZER_GAP + 2);
+
+ _cpls = film->cpls ();
+ update_cpl_choice ();
- table = new wxFlexGridSizer (2, 2, 6);
+
+ /* Sub-heading: Output */
+ h = new wxStaticText (this, wxID_ANY, _("Output"));
+ h->SetFont (subheading_font);
+ vertical->Add (h, 0, wxALIGN_CENTER_VERTICAL | wxTOP, DCPOMATIC_SIZER_Y_GAP * 2);
+
+ table = new wxFlexGridSizer (2, DCPOMATIC_SIZER_X_GAP, 0);
_write_to = new wxRadioButton (this, wxID_ANY, _("Write to"));
table->Add (_write_to, 1, wxEXPAND);
table->Add (_email, 1, wxEXPAND);
table->AddSpacer (0);
- vertical->Add (table, 0, wxEXPAND | wxALL, 6);
+ vertical->Add (table, 0, wxEXPAND | wxTOP, DCPOMATIC_SIZER_GAP);
+
+ /* Make an overall sizer to get a nice border, and put some buttons in */
+
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ overall_sizer->Add (vertical, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, DCPOMATIC_DIALOG_BORDER);
wxSizer* buttons = CreateSeparatedButtonSizer (wxOK | wxCANCEL);
if (buttons) {
- vertical->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ overall_sizer->Add (buttons, 0, wxEXPAND | wxTOP, DCPOMATIC_SIZER_Y_GAP);
}
_write_to->SetValue (true);
+ /* Bind */
+
_targets->Bind (wxEVT_COMMAND_TREE_SEL_CHANGED, boost::bind (&KDMDialog::setup_sensitivity, this));
_add_cinema->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMDialog::add_cinema_clicked, this));
_edit_screen->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMDialog::edit_screen_clicked, this));
_remove_screen->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMDialog::remove_screen_clicked, this));
- _dcps->Bind (wxEVT_COMMAND_LIST_ITEM_SELECTED, boost::bind (&KDMDialog::setup_sensitivity, this));
- _dcps->Bind (wxEVT_COMMAND_LIST_ITEM_DESELECTED, boost::bind (&KDMDialog::setup_sensitivity, this));
+ _cpl->Bind (wxEVT_COMMAND_CHOICE_SELECTED, boost::bind (&KDMDialog::update_cpl_summary, this));
+ _cpl_browse->Bind (wxEVT_COMMAND_BUTTON_CLICKED, boost::bind (&KDMDialog::cpl_browse_clicked, this));
_write_to->Bind (wxEVT_COMMAND_RADIOBUTTON_SELECTED, boost::bind (&KDMDialog::setup_sensitivity, this));
_email->Bind (wxEVT_COMMAND_RADIOBUTTON_SELECTED, boost::bind (&KDMDialog::setup_sensitivity, this));
setup_sensitivity ();
-
- SetSizer (vertical);
- vertical->Layout ();
- vertical->SetSizeHints (this);
+
+ SetSizer (overall_sizer);
+ overall_sizer->Layout ();
+ overall_sizer->SetSizeHints (this);
}
list<pair<wxTreeItemId, shared_ptr<Cinema> > >
{
bool const sc = selected_cinemas().size() == 1;
bool const ss = selected_screens().size() == 1;
- bool const sd = _dcps->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED) != -1;
+ bool const sd = _cpl->GetSelection() != -1;
_edit_cinema->Enable (sc);
_remove_cinema->Enable (sc);
}
boost::filesystem::path
-KDMDialog::dcp () const
+KDMDialog::cpl () const
{
- int const item = _dcps->GetNextItem (-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED);
+ int const item = _cpl->GetSelection ();
assert (item >= 0);
- return wx_to_std (_dcps->GetItemText (item));
+ return _cpls[item].cpl_file;
}
boost::filesystem::path
{
return _write_to->GetValue ();
}
+
+void
+KDMDialog::update_cpl_choice ()
+{
+ _cpl->Clear ();
+
+ for (vector<CPLSummary>::const_iterator i = _cpls.begin(); i != _cpls.end(); ++i) {
+ _cpl->Append (std_to_wx (i->cpl_id));
+
+ if (_cpls.size() > 0) {
+ _cpl->SetSelection (0);
+ }
+ }
+
+ update_cpl_summary ();
+}
+
+void
+KDMDialog::update_cpl_summary ()
+{
+ int const n = _cpl->GetSelection();
+ if (n == wxNOT_FOUND) {
+ return;
+ }
+
+ _dcp_directory->SetLabel (std_to_wx (_cpls[n].dcp_directory));
+ _cpl_id->SetLabel (std_to_wx (_cpls[n].cpl_id));
+ _cpl_annotation_text->SetLabel (std_to_wx (_cpls[n].cpl_annotation_text));
+}
+
+void
+KDMDialog::cpl_browse_clicked ()
+{
+ wxFileDialog d (this, _("Select CPL XML file"), wxEmptyString, wxEmptyString, "*.xml");
+ if (d.ShowModal() == wxID_CANCEL) {
+ return;
+ }
+
+ boost::filesystem::path cpl_file (wx_to_std (d.GetPath ()));
+ boost::filesystem::path dcp_dir = cpl_file.parent_path ();
+
+ /* XXX: hack alert */
+ cxml::Document cpl_document ("CompositionPlaylist");
+ cpl_document.read_file (cpl_file);
+
+ try {
+ _cpls.push_back (
+ CPLSummary (
+ dcp_dir.filename().string(),
+ cpl_document.string_child("Id").substr (9),
+ cpl_document.string_child ("ContentTitleText"),
+ cpl_file
+ )
+ );
+ } catch (cxml::Error) {
+ error_dialog (this, _("This is not a valid CPL file"));
+ return;
+ }
+
+ update_cpl_choice ();
+ _cpl->SetSelection (_cpls.size() - 1);
+ update_cpl_summary ();
+}
/** @return KDM until time in local time */
boost::posix_time::ptime until () const;
- boost::filesystem::path dcp () const;
+ boost::filesystem::path cpl () const;
boost::filesystem::path directory () const;
bool write_to () const;
std::list<std::pair<wxTreeItemId, boost::shared_ptr<Cinema> > > selected_cinemas () const;
std::list<std::pair<wxTreeItemId, boost::shared_ptr<Screen> > > selected_screens () const;
void setup_sensitivity ();
+ void update_cpl_choice ();
+ void update_cpl_summary ();
+ void cpl_browse_clicked ();
static boost::posix_time::ptime posix_time (wxDatePickerCtrl *, wxTimePickerCtrl *);
wxDatePickerCtrl* _until_date;
wxTimePickerCtrl* _from_time;
wxTimePickerCtrl* _until_time;
- wxListCtrl* _dcps;
+ wxChoice* _cpl;
+ wxButton* _cpl_browse;
+ wxStaticText* _dcp_directory;
+ wxStaticText* _cpl_id;
+ wxStaticText* _cpl_annotation_text;
wxRadioButton* _write_to;
#ifdef DCPOMATIC_USE_OWN_DIR_PICKER
DirPickerCtrl* _folder;
wxTreeItemId _root;
std::map<wxTreeItemId, boost::shared_ptr<Cinema> > _cinemas;
std::map<wxTreeItemId, boost::shared_ptr<Screen> > _screens;
+ std::vector<CPLSummary> _cpls;
};
#define DCPOMATIC_SIZER_X_GAP 8
#define DCPOMATIC_SIZER_Y_GAP 8
+#define DCPOMATIC_SIZER_GAP 8
#define DCPOMATIC_DIALOG_BORDER 12
+/** Spacing to use between buttons in a vertical line */
+#ifdef DCPOMATIC_OSX
+#define DCPOMATIC_BUTTON_STACK_GAP 2
+#else
+#define DCPOMATIC_BUTTON_STACK_GAP 0
+#endif
+
/** @file src/wx/wx_util.h
* @brief Some utility functions and classes.
*/
p += sub_image->stride()[0];
}
+ shared_ptr<FileLog> log (new FileLog ("build/test/client_server_test.log"));
+
shared_ptr<PlayerVideoFrame> pvf (
new PlayerVideoFrame (
- shared_ptr<ImageProxy> (new RawImageProxy (image)),
+ shared_ptr<ImageProxy> (new RawImageProxy (image, log)),
Crop (),
dcp::Size (1998, 1080),
dcp::Size (1998, 1080),
pvf->set_subtitle (PositionImage (sub_image, Position<int> (50, 60)));
- shared_ptr<FileLog> log (new FileLog ("build/test/client_server_test.log"));
-
shared_ptr<DCPVideoFrame> frame (
new DCPVideoFrame (
pvf,
wait_for_jobs ();
}
-/** Test Film::have_dcp(). Requires the output from ffmpeg_dcp_test above */
+/** Briefly test Film::cpls(). Requires the output from ffmpeg_dcp_test above */
BOOST_AUTO_TEST_CASE (ffmpeg_have_dcp_test)
{
boost::filesystem::path p = test_film_dir ("ffmpeg_dcp_test");
shared_ptr<Film> f (new Film (p.string ()));
f->read_metadata ();
- BOOST_CHECK (!f->dcps().empty());
+ BOOST_CHECK (!f->cpls().empty());
p /= f->dcp_name();
p /= f->video_mxf_filename();
boost::filesystem::remove (p);
- BOOST_CHECK (f->dcps().empty());
+ BOOST_CHECK (f->cpls().empty());
}