summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2012-10-16 03:29:55 +0100
committerCarl Hetherington <cth@carlh.net>2012-10-16 03:29:55 +0100
commit068bb5b95652c022419c64f3d29cc42c4610ae93 (patch)
tree373b5b9bd91a351ae9727e6d4f388eb7157fda7f /src
parent13511ed2fcc23f4d5f9c507c775c3c5cfd82d155 (diff)
parent27e1f73c75e6c0f75d4863cfe2c8e50f811913d9 (diff)
Merge branch 'master' of /home/carl/git/dvdomatic
Diffstat (limited to 'src')
-rw-r--r--src/lib/dcp_video_frame.cc79
-rw-r--r--src/lib/decoder.cc15
-rw-r--r--src/lib/decoder.h5
-rw-r--r--src/lib/ffmpeg_decoder.cc2
-rw-r--r--src/lib/ffmpeg_decoder.h1
-rw-r--r--src/lib/film.cc123
-rw-r--r--src/lib/film.h51
-rw-r--r--src/lib/film_state.cc60
-rw-r--r--src/lib/film_state.h7
-rw-r--r--src/lib/image.cc24
-rw-r--r--src/lib/image.h3
-rw-r--r--src/lib/imagemagick_encoder.cc39
-rw-r--r--src/lib/make_dcp_job.cc10
-rw-r--r--src/lib/scp_dcp_job.cc6
-rw-r--r--src/lib/server.cc76
-rw-r--r--src/lib/subtitle.cc52
-rw-r--r--src/lib/subtitle.h56
-rw-r--r--src/lib/util.cc87
-rw-r--r--src/lib/util.h27
-rw-r--r--src/wx/dci_name_dialog.cc129
-rw-r--r--src/wx/dci_name_dialog.h48
-rw-r--r--src/wx/film_editor.cc54
-rw-r--r--src/wx/film_editor.h3
-rw-r--r--src/wx/film_viewer.cc53
-rw-r--r--src/wx/wscript17
25 files changed, 750 insertions, 277 deletions
diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc
index 04735269c..bf29e6819 100644
--- a/src/lib/dcp_video_frame.cc
+++ b/src/lib/dcp_video_frame.cc
@@ -161,17 +161,14 @@ DCPVideoFrame::encode_locally ()
shared_ptr<Image> prepared = _input->scale_and_convert_to_rgb (_out_size, _padding, _scaler);
if (_subtitle) {
- list<shared_ptr<SubtitleImage> > subs = _subtitle->images ();
- for (list<shared_ptr<SubtitleImage> >::iterator i = subs.begin(); i != subs.end(); ++i) {
- Rectangle tx = transformed_subtitle_area (
- float (_out_size.width) / _input->size().width,
- float (_out_size.height) / _input->size().height,
- (*i)->area(), _subtitle_offset, _subtitle_scale
- );
-
- shared_ptr<Image> im = (*i)->image()->scale (Size (tx.w, tx.h), _scaler);
- prepared->alpha_blend (im, Position (tx.x, tx.y));
- }
+ Rectangle tx = subtitle_transformed_area (
+ float (_out_size.width) / _input->size().width,
+ float (_out_size.height) / _input->size().height,
+ _subtitle->area(), _subtitle_offset, _subtitle_scale
+ );
+
+ shared_ptr<Image> im = _subtitle->image()->scale (tx.size(), _scaler);
+ prepared->alpha_blend (im, tx.position());
}
create_openjpeg_container ();
@@ -310,38 +307,52 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv)
asio::ip::tcp::resolver::query query (serv->host_name(), boost::lexical_cast<string> (Config::instance()->server_port ()));
asio::ip::tcp::resolver::iterator endpoint_iterator = resolver.resolve (query);
- Socket socket;
+ shared_ptr<Socket> socket (new Socket);
- socket.connect (*endpoint_iterator, 30);
+ socket->connect (*endpoint_iterator, 30);
stringstream s;
- s << "encode "
- << _input->size().width << " " << _input->size().height << " "
- << _input->pixel_format() << " "
- << _out_size.width << " " << _out_size.height << " "
- << _padding << " "
- << _subtitle_offset << " "
- << _subtitle_scale << " "
- << _scaler->id () << " "
- << _frame << " "
- << _frames_per_second << " "
- << (_post_process.empty() ? "none" : _post_process) << " "
- << Config::instance()->colour_lut_index () << " "
- << Config::instance()->j2k_bandwidth () << " ";
-
- socket.write ((uint8_t *) s.str().c_str(), s.str().length() + 1, 30);
-
- for (int i = 0; i < _input->components(); ++i) {
- socket.write (_input->data()[i], _input->stride()[i] * _input->lines(i), 30);
+ s << "encode please\n"
+ << "input_width " << _input->size().width << "\n"
+ << "input_height " << _input->size().height << "\n"
+ << "input_pixel_format " << _input->pixel_format() << "\n"
+ << "output_width " << _out_size.width << "\n"
+ << "output_height " << _out_size.height << "\n"
+ << "padding " << _padding << "\n"
+ << "subtitle_offset " << _subtitle_offset << "\n"
+ << "subtitle_scale " << _subtitle_scale << "\n"
+ << "scaler " << _scaler->id () << "\n"
+ << "frame " << _frame << "\n"
+ << "frames_per_second " << _frames_per_second << "\n";
+
+ if (!_post_process.empty()) {
+ s << "post_process " << _post_process << "\n";
+ }
+
+ s << "colour_lut " << Config::instance()->colour_lut_index () << "\n"
+ << "j2k_bandwidth " << Config::instance()->j2k_bandwidth () << "\n";
+
+ if (_subtitle) {
+ s << "subtitle_x " << _subtitle->position().x << "\n"
+ << "subtitle_y " << _subtitle->position().y << "\n"
+ << "subtitle_width " << _subtitle->image()->size().width << "\n"
+ << "subtitle_height " << _subtitle->image()->size().height << "\n";
+ }
+
+ socket->write ((uint8_t *) s.str().c_str(), s.str().length() + 1, 30);
+
+ _input->write_to_socket (socket);
+ if (_subtitle) {
+ _subtitle->image()->write_to_socket (socket);
}
char buffer[32];
- socket.read_indefinite ((uint8_t *) buffer, sizeof (buffer), 30);
- socket.consume (strlen (buffer) + 1);
+ socket->read_indefinite ((uint8_t *) buffer, sizeof (buffer), 30);
+ socket->consume (strlen (buffer) + 1);
shared_ptr<EncodedData> e (new RemotelyEncodedData (atoi (buffer)));
/* now read the rest */
- socket.read_definite_and_consume (e->data(), e->size(), 30);
+ socket->read_definite_and_consume (e->data(), e->size(), 30);
_log->log (String::compose ("Finished remotely-encoded frame %1", _frame));
diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc
index 1f771da2d..ef2ef6fb9 100644
--- a/src/lib/decoder.cc
+++ b/src/lib/decoder.cc
@@ -305,8 +305,8 @@ Decoder::process_video (AVFrame* frame)
}
shared_ptr<Subtitle> sub;
- if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) {
- sub = _subtitle;
+ if (_timed_subtitle && _timed_subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) {
+ sub = _timed_subtitle->subtitle ();
}
TIMING ("Decoder emits %1", _video_frame);
@@ -412,15 +412,12 @@ Decoder::setup_video_filters ()
}
void
-Decoder::process_subtitle (shared_ptr<Subtitle> s)
+Decoder::process_subtitle (shared_ptr<TimedSubtitle> s)
{
- _subtitle = s;
+ _timed_subtitle = s;
if (_opt->apply_crop) {
- list<shared_ptr<SubtitleImage> > im = _subtitle->images ();
- for (list<shared_ptr<SubtitleImage> >::iterator i = im.begin(); i != im.end(); ++i) {
- Position const p = (*i)->position ();
- (*i)->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top));
- }
+ Position const p = _timed_subtitle->subtitle()->position ();
+ _timed_subtitle->subtitle()->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top));
}
}
diff --git a/src/lib/decoder.h b/src/lib/decoder.h
index 805955b9d..8a04ec9f2 100644
--- a/src/lib/decoder.h
+++ b/src/lib/decoder.h
@@ -37,6 +37,7 @@ class Options;
class Image;
class Log;
class DelayLine;
+class TimedSubtitle;
class Subtitle;
/** @class Decoder.
@@ -103,7 +104,7 @@ protected:
void process_video (AVFrame *);
void process_audio (uint8_t *, int);
- void process_subtitle (boost::shared_ptr<Subtitle>);
+ void process_subtitle (boost::shared_ptr<TimedSubtitle>);
/** our FilmState */
boost::shared_ptr<const FilmState> _fs;
@@ -140,7 +141,7 @@ private:
*/
int64_t _audio_frames_processed;
- boost::shared_ptr<Subtitle> _subtitle;
+ boost::shared_ptr<TimedSubtitle> _timed_subtitle;
};
#endif
diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc
index e01405191..e85439f6e 100644
--- a/src/lib/ffmpeg_decoder.cc
+++ b/src/lib/ffmpeg_decoder.cc
@@ -248,7 +248,7 @@ FFmpegDecoder::do_pass ()
int got_subtitle;
AVSubtitle sub;
if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) {
- process_subtitle (shared_ptr<Subtitle> (new Subtitle (sub)));
+ process_subtitle (shared_ptr<TimedSubtitle> (new TimedSubtitle (sub)));
avsubtitle_free (&sub);
}
}
diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h
index d34c22785..ec822bf79 100644
--- a/src/lib/ffmpeg_decoder.h
+++ b/src/lib/ffmpeg_decoder.h
@@ -44,7 +44,6 @@ class FilmState;
class Options;
class Image;
class Log;
-class Subtitle;
/** @class FFmpegDecoder
* @brief A decoder using FFmpeg to decode content.
diff --git a/src/lib/film.cc b/src/lib/film.cc
index 31af2f1c2..ce83b1324 100644
--- a/src/lib/film.cc
+++ b/src/lib/film.cc
@@ -120,28 +120,10 @@ void
Film::read_metadata ()
{
ifstream f (metadata_file().c_str ());
- string line;
- while (getline (f, line)) {
- if (line.empty ()) {
- continue;
- }
-
- if (line[0] == '#') {
- continue;
- }
-
- if (line[line.size() - 1] == '\r') {
- line = line.substr (0, line.size() - 1);
- }
-
- size_t const s = line.find (' ');
- if (s == string::npos) {
- continue;
- }
-
- _state.read_metadata (line.substr (0, s), line.substr (s + 1));
+ multimap<string, string> kv = read_key_value (f);
+ for (multimap<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
+ _state.read_metadata (i->first, i->second);
}
-
_dirty = false;
}
@@ -485,8 +467,7 @@ Film::signal_changed (Property p)
void
Film::make_dcp (bool transcode, int freq)
{
- string const t = name ();
- if (t.find ("/") != string::npos) {
+ if (dcp_name().find ("/") != string::npos) {
throw BadSettingError ("name", "cannot contain slashes");
}
@@ -685,46 +666,82 @@ Film::set_subtitle_scale (float s)
signal_changed (SUBTITLE_SCALE);
}
-list<pair<Position, string> >
-Film::thumb_subtitles (int n) const
+pair<Position, string>
+Film::thumb_subtitle (int n) const
{
string sub_file = _state.thumb_base(n) + ".sub";
if (!filesystem::exists (sub_file)) {
- return list<pair<Position, string> > ();
+ return pair<Position, string> ();
}
- ifstream f (sub_file.c_str ());
- string line;
-
- int sub_number;
- int sub_x;
- list<pair<Position, string> > subs;
+ pair<Position, string> sub;
- while (getline (f, line)) {
- if (line.empty ()) {
- continue;
+ ifstream f (sub_file.c_str ());
+ multimap<string, string> kv = read_key_value (f);
+ for (map<string, string>::const_iterator i = kv.begin(); i != kv.end(); ++i) {
+ if (i->first == "x") {
+ sub.first.x = lexical_cast<int> (i->second);
+ } else if (i->first == "y") {
+ sub.first.y = lexical_cast<int> (i->second);
+ sub.second = String::compose ("%1.sub.png", _state.thumb_base(n));
}
+ }
+
+ return sub;
+}
- if (line[line.size() - 1] == '\r') {
- line = line.substr (0, line.size() - 1);
- }
+void
+Film::set_audio_language (string v)
+{
+ _state.audio_language = v;
+ signal_changed (DCI_METADATA);
+}
- size_t const s = line.find (' ');
- if (s == string::npos) {
- continue;
- }
+void
+Film::set_subtitle_language (string v)
+{
+ _state.subtitle_language = v;
+ signal_changed (DCI_METADATA);
+}
- string const k = line.substr (0, s);
- int const v = lexical_cast<int> (line.substr(s + 1));
+void
+Film::set_territory (string v)
+{
+ _state.territory = v;
+ signal_changed (DCI_METADATA);
+}
- if (k == "image") {
- sub_number = v;
- } else if (k == "x") {
- sub_x = v;
- } else if (k == "y") {
- subs.push_back (make_pair (Position (sub_x, v), String::compose ("%1.sub.%2.png", _state.thumb_base(n), sub_number)));
- }
- }
+void
+Film::set_rating (string v)
+{
+ _state.rating = v;
+ signal_changed (DCI_METADATA);
+}
+
+void
+Film::set_studio (string v)
+{
+ _state.studio = v;
+ signal_changed (DCI_METADATA);
+}
+
+void
+Film::set_facility (string v)
+{
+ _state.facility = v;
+ signal_changed (DCI_METADATA);
+}
- return subs;
+void
+Film::set_package_type (string v)
+{
+ _state.package_type = v;
+ signal_changed (DCI_METADATA);
+}
+
+void
+Film::set_use_dci_name (bool v)
+{
+ _state.use_dci_name = v;
+ signal_changed (USE_DCI_NAME);
}
diff --git a/src/lib/film.h b/src/lib/film.h
index c006eae36..a1cf26bc6 100644
--- a/src/lib/film.h
+++ b/src/lib/film.h
@@ -71,6 +71,15 @@ public:
return _state.name;
}
+ /** @return name to give the DCP */
+ std::string dcp_name () const {
+ return _state.dcp_name ();
+ }
+
+ bool use_dci_name () const {
+ return _state.use_dci_name;
+ }
+
/** @return number of pixels to crop from the sides of the original picture */
Crop crop () const {
return _state.crop;
@@ -146,6 +155,7 @@ public:
void set_dcp_ab (bool);
void set_name (std::string);
+ void set_use_dci_name (bool);
void set_content (std::string);
void set_top_crop (int);
void set_bottom_crop (int);
@@ -159,6 +169,13 @@ public:
void set_with_subtitles (bool);
void set_subtitle_offset (int);
void set_subtitle_scale (float);
+ void set_audio_language (std::string);
+ void set_subtitle_language (std::string);
+ void set_territory (std::string);
+ void set_rating (std::string);
+ void set_studio (std::string);
+ void set_facility (std::string);
+ void set_package_type (std::string);
/** @return size, in pixels, of the source (ignoring cropping) */
Size size () const {
@@ -193,7 +210,35 @@ public:
bool has_subtitles () const {
return _state.has_subtitles;
}
+
+ std::string audio_language () const {
+ return _state.audio_language;
+ }
+
+ std::string subtitle_language () const {
+ return _state.subtitle_language;
+ }
+ std::string territory () const {
+ return _state.territory;
+ }
+
+ std::string rating () const {
+ return _state.rating;
+ }
+
+ std::string studio () const {
+ return _state.studio;
+ }
+
+ std::string facility () const {
+ return _state.facility;
+ }
+
+ std::string package_type () const {
+ return _state.package_type;
+ }
+
std::string j2k_dir () const;
std::vector<std::string> audio_files () const;
@@ -203,7 +248,7 @@ public:
int num_thumbs () const;
int thumb_frame (int) const;
std::string thumb_file (int) const;
- std::list<std::pair<Position, std::string> > thumb_subtitles (int) const;
+ std::pair<Position, std::string> thumb_subtitle (int) const;
void copy_from_dvd_post_gui ();
void examine_content ();
@@ -241,7 +286,9 @@ public:
STILL_DURATION,
WITH_SUBTITLES,
SUBTITLE_OFFSET,
- SUBTITLE_SCALE
+ SUBTITLE_SCALE,
+ USE_DCI_NAME,
+ DCI_METADATA
};
boost::shared_ptr<FilmState> state_copy () const;
diff --git a/src/lib/film_state.cc b/src/lib/film_state.cc
index fed506863..094342d7b 100644
--- a/src/lib/film_state.cc
+++ b/src/lib/film_state.cc
@@ -49,6 +49,7 @@ FilmState::write_metadata (ofstream& f) const
{
/* User stuff */
f << "name " << name << "\n";
+ f << "use_dci_name " << use_dci_name << "\n";
f << "content " << content << "\n";
if (dcp_content_type) {
f << "dcp_content_type " << dcp_content_type->pretty_name () << "\n";
@@ -99,6 +100,13 @@ FilmState::write_metadata (ofstream& f) const
f << "audio_sample_format " << audio_sample_format_to_string (audio_sample_format) << "\n";
f << "content_digest " << content_digest << "\n";
f << "has_subtitles " << has_subtitles << "\n";
+ f << "audio_language " << audio_language << "\n";
+ f << "subtitle_language " << subtitle_language << "\n";
+ f << "territory " << territory << "\n";
+ f << "rating " << rating << "\n";
+ f << "studio " << studio << "\n";
+ f << "facility " << facility << "\n";
+ f << "package_type " << package_type << "\n";
}
/** Read state from a key / value pair.
@@ -111,6 +119,8 @@ FilmState::read_metadata (string k, string v)
/* User-specified stuff */
if (k == "name") {
name = v;
+ } else if (k == "use_dci_name") {
+ use_dci_name = (v == "1");
} else if (k == "content") {
content = v;
} else if (k == "dcp_content_type") {
@@ -178,6 +188,20 @@ FilmState::read_metadata (string k, string v)
content_digest = v;
} else if (k == "has_subtitles") {
has_subtitles = (v == "1");
+ } else if (k == "audio_language") {
+ audio_language = v;
+ } else if (k == "subtitle_language") {
+ subtitle_language = v;
+ } else if (k == "territory") {
+ territory = v;
+ } else if (k == "rating") {
+ rating = v;
+ } else if (k == "studio") {
+ studio = v;
+ } else if (k == "facility") {
+ facility = v;
+ } else if (k == "package_type") {
+ package_type = v;
}
}
@@ -335,7 +359,15 @@ string
FilmState::dci_name () const
{
stringstream d;
- d << dci_name_prefix << "_";
+
+ string fixed_name = to_upper_copy (name);
+ for (size_t i = 0; i < fixed_name.length(); ++i) {
+ if (fixed_name[i] == ' ') {
+ fixed_name[i] = '-';
+ }
+ }
+
+ d << fixed_name << "_";
if (dcp_content_type) {
d << dcp_content_type->dci_name() << "_";
@@ -347,9 +379,14 @@ FilmState::dci_name () const
if (!audio_language.empty ()) {
d << audio_language;
- if (!subtitle_language.empty ()) {
- d << "-" << subtitle_language;
+ if (with_subtitles) {
+ if (!subtitle_language.empty ()) {
+ d << "-" << subtitle_language;
+ } else {
+ d << "-XX";
+ }
}
+
d << "_";
}
@@ -363,10 +400,10 @@ FilmState::dci_name () const
switch (audio_channels) {
case 1:
- d << "1_";
+ d << "10_";
break;
case 2:
- d << "2_";
+ d << "20_";
break;
case 6:
d << "51_";
@@ -392,3 +429,16 @@ FilmState::dci_name () const
return d.str ();
}
+
+/** @return name to give the DCP */
+string
+FilmState::dcp_name () const
+{
+ if (use_dci_name) {
+ return dci_name ();
+ }
+
+ return name;
+}
+
+
diff --git a/src/lib/film_state.h b/src/lib/film_state.h
index e58d46b0f..01bcdfd5c 100644
--- a/src/lib/film_state.h
+++ b/src/lib/film_state.h
@@ -52,7 +52,8 @@ class FilmState
{
public:
FilmState ()
- : dcp_content_type (0)
+ : use_dci_name (false)
+ , dcp_content_type (0)
, frames_per_second (0)
, format (0)
, scaler (Scaler::from_id ("bicubic"))
@@ -94,12 +95,15 @@ public:
int dcp_length () const;
std::string dci_name () const;
+ std::string dcp_name () const;
+
/** Complete path to directory containing the film metadata;
must not be relative.
*/
std::string directory;
/** Name for DVD-o-matic */
std::string name;
+ bool use_dci_name;
/** File or directory containing content; may be relative to our directory
* or an absolute path.
*/
@@ -139,7 +143,6 @@ public:
float subtitle_scale;
/* DCI naming stuff */
- std::string dci_name_prefix;
std::string audio_language;
std::string subtitle_language;
std::string territory;
diff --git a/src/lib/image.cc b/src/lib/image.cc
index f5aef8444..30dc4292f 100644
--- a/src/lib/image.cc
+++ b/src/lib/image.cc
@@ -244,6 +244,30 @@ Image::alpha_blend (shared_ptr<Image> other, Position position)
}
}
+void
+Image::read_from_socket (shared_ptr<Socket> socket)
+{
+ for (int i = 0; i < components(); ++i) {
+ uint8_t* p = data()[i];
+ for (int y = 0; y < lines(i); ++y) {
+ socket->read_definite_and_consume (p, line_size()[i], 30);
+ p += stride()[i];
+ }
+ }
+}
+
+void
+Image::write_to_socket (shared_ptr<Socket> socket) const
+{
+ for (int i = 0; i < components(); ++i) {
+ uint8_t* p = data()[i];
+ for (int y = 0; y < lines(i); ++y) {
+ socket->write (p, line_size()[i], 30);
+ p += stride()[i];
+ }
+ }
+}
+
/** Construct a SimpleImage of a given size and format, allocating memory
* as required.
*
diff --git a/src/lib/image.h b/src/lib/image.h
index 30c8519e7..6c3c9109e 100644
--- a/src/lib/image.h
+++ b/src/lib/image.h
@@ -75,6 +75,9 @@ public:
void alpha_blend (boost::shared_ptr<Image> image, Position pos);
void make_black ();
+
+ void read_from_socket (boost::shared_ptr<Socket>);
+ void write_to_socket (boost::shared_ptr<Socket>) const;
PixelFormat pixel_format () const {
return _pixel_format;
diff --git a/src/lib/imagemagick_encoder.cc b/src/lib/imagemagick_encoder.cc
index 9bd8162f8..2518f8dc5 100644
--- a/src/lib/imagemagick_encoder.cc
+++ b/src/lib/imagemagick_encoder.cc
@@ -69,32 +69,23 @@ ImageMagickEncoder::process_video (shared_ptr<Image> image, int frame, shared_pt
string tmp_metadata_file = _opt->frame_out_path (frame, false, ".sub");
ofstream metadata (tmp_metadata_file.c_str ());
- list<shared_ptr<SubtitleImage> > images = sub->images ();
- int n = 0;
- for (list<shared_ptr<SubtitleImage> >::iterator i = images.begin(); i != images.end(); ++i) {
- stringstream ext;
- ext << ".sub." << n << ".png";
-
- Size new_size = (*i)->image()->size ();
- new_size.width *= x_scale;
- new_size.height *= y_scale;
- shared_ptr<Image> scaled = (*i)->image()->scale (new_size, _fs->scaler);
- shared_ptr<Image> compact (new CompactImage (scaled));
-
- string tmp_sub_file = _opt->frame_out_path (frame, true, ext.str ());
- Magick::Image sub_thumb (compact->size().width, compact->size().height, "RGBA", MagickCore::CharPixel, compact->data()[0]);
- sub_thumb.magick ("PNG");
- sub_thumb.write (tmp_sub_file);
- filesystem::rename (tmp_sub_file, _opt->frame_out_path (frame, false, ext.str ()));
-
- metadata << "image " << n << "\n"
- << "x " << (*i)->position().x << "\n"
- << "y " << (*i)->position().y << "\n";
+ Size new_size = sub->image()->size ();
+ new_size.width *= x_scale;
+ new_size.height *= y_scale;
+ shared_ptr<Image> scaled = sub->image()->scale (new_size, _fs->scaler);
+ shared_ptr<Image> compact (new CompactImage (scaled));
+
+ string tmp_sub_file = _opt->frame_out_path (frame, true, ".sub.png");
+ Magick::Image sub_thumb (compact->size().width, compact->size().height, "RGBA", MagickCore::CharPixel, compact->data()[0]);
+ sub_thumb.magick ("PNG");
+ sub_thumb.write (tmp_sub_file);
+ filesystem::rename (tmp_sub_file, _opt->frame_out_path (frame, false, ".sub.png"));
- metadata.close ();
- filesystem::rename (tmp_metadata_file, _opt->frame_out_path (frame, false, ".sub"));
- }
+ metadata << "x " << sub->position().x << "\n"
+ << "y " << sub->position().y << "\n";
+ metadata.close ();
+ filesystem::rename (tmp_metadata_file, _opt->frame_out_path (frame, false, ".sub"));
}
frame_done (frame);
diff --git a/src/lib/make_dcp_job.cc b/src/lib/make_dcp_job.cc
index b42a38429..efd35ba44 100644
--- a/src/lib/make_dcp_job.cc
+++ b/src/lib/make_dcp_job.cc
@@ -70,7 +70,7 @@ MakeDCPJob::wav_path (libdcp::Channel c) const
void
MakeDCPJob::run ()
{
- string const dcp_path = _fs->dir (_fs->name);
+ string const dcp_path = _fs->dir (_fs->dcp_name());
/* Remove any old DCP */
filesystem::remove_all (dcp_path);
@@ -85,17 +85,17 @@ MakeDCPJob::run ()
break;
}
- libdcp::DCP dcp (_fs->dir (_fs->name));
+ libdcp::DCP dcp (_fs->dir (_fs->dcp_name()));
dcp.Progress.connect (sigc::mem_fun (*this, &MakeDCPJob::dcp_progress));
- shared_ptr<libdcp::CPL> cpl (new libdcp::CPL (_fs->dir (_fs->name), _fs->name, _fs->dcp_content_type->libdcp_kind (), frames, rint (_fs->frames_per_second)));
+ shared_ptr<libdcp::CPL> cpl (new libdcp::CPL (_fs->dir (_fs->dcp_name()), _fs->dcp_name(), _fs->dcp_content_type->libdcp_kind (), frames, rint (_fs->frames_per_second)));
dcp.add_cpl (cpl);
descend (0.9);
shared_ptr<libdcp::MonoPictureAsset> pa (
new libdcp::MonoPictureAsset (
sigc::mem_fun (*this, &MakeDCPJob::j2c_path),
- _fs->dir (_fs->name),
+ _fs->dir (_fs->dcp_name()),
"video.mxf",
&dcp.Progress,
rint (_fs->frames_per_second),
@@ -114,7 +114,7 @@ MakeDCPJob::run ()
sa.reset (
new libdcp::SoundAsset (
sigc::mem_fun (*this, &MakeDCPJob::wav_path),
- _fs->dir (_fs->name),
+ _fs->dir (_fs->dcp_name()),
"audio.mxf",
&dcp.Progress,
rint (_fs->frames_per_second),
diff --git a/src/lib/scp_dcp_job.cc b/src/lib/scp_dcp_job.cc
index 90122cea7..300a8fe33 100644
--- a/src/lib/scp_dcp_job.cc
+++ b/src/lib/scp_dcp_job.cc
@@ -140,12 +140,12 @@ SCPDCPJob::run ()
throw NetworkError (String::compose ("Could not start SCP session (%1)", ssh_get_error (ss.session)));
}
- r = ssh_scp_push_directory (sc.scp, _fs->name.c_str(), S_IRWXU);
+ r = ssh_scp_push_directory (sc.scp, _fs->dcp_name().c_str(), S_IRWXU);
if (r != SSH_OK) {
- throw NetworkError (String::compose ("Could not create remote directory %1 (%2)", _fs->name, ssh_get_error (ss.session)));
+ throw NetworkError (String::compose ("Could not create remote directory %1 (%2)", _fs->dcp_name(), ssh_get_error (ss.session)));
}
- string const dcp_dir = _fs->dir (_fs->name);
+ string const dcp_dir = _fs->dir (_fs->dcp_name());
boost::uintmax_t bytes_to_transfer = 0;
for (filesystem::directory_iterator i = filesystem::directory_iterator (dcp_dir); i != filesystem::directory_iterator(); ++i) {
diff --git a/src/lib/server.cc b/src/lib/server.cc
index b5eda2eb8..e8a9804ed 100644
--- a/src/lib/server.cc
+++ b/src/lib/server.cc
@@ -27,12 +27,14 @@
#include <sstream>
#include <iostream>
#include <boost/algorithm/string.hpp>
+#include <boost/lexical_cast.hpp>
#include "server.h"
#include "util.h"
#include "scaler.h"
#include "image.h"
#include "dcp_video_frame.h"
#include "config.h"
+#include "subtitle.h"
using namespace std;
using namespace boost;
@@ -72,65 +74,57 @@ Server::Server (Log* log)
int
Server::process (shared_ptr<Socket> socket)
{
- char buffer[128];
+ char buffer[512];
socket->read_indefinite ((uint8_t *) buffer, sizeof (buffer), 30);
socket->consume (strlen (buffer) + 1);
stringstream s (buffer);
-
- string command;
- s >> command;
- if (command != "encode") {
+ multimap<string, string> kv = read_key_value (s);
+
+ if (get_required_string (kv, "encode") != "please") {
return -1;
}
-
- Size in_size;
- int pixel_format_int;
- Size out_size;
- int padding;
- int subtitle_offset;
- int subtitle_scale;
- string scaler_id;
- int frame;
- float frames_per_second;
- string post_process;
- int colour_lut_index;
- int j2k_bandwidth;
-
- s >> in_size.width >> in_size.height
- >> pixel_format_int
- >> out_size.width >> out_size.height
- >> padding
- >> subtitle_offset
- >> subtitle_scale
- >> scaler_id
- >> frame
- >> frames_per_second
- >> post_process
- >> colour_lut_index
- >> j2k_bandwidth;
-
+
+ Size in_size (get_required_int (kv, "input_width"), get_required_int (kv, "input_height"));
+ int pixel_format_int = get_required_int (kv, "input_pixel_format");
+ Size out_size (get_required_int (kv, "output_width"), get_required_int (kv, "output_height"));
+ int padding = get_required_int (kv, "padding");
+ int subtitle_offset = get_required_int (kv, "subtitle_offset");
+ int subtitle_scale = get_required_int (kv, "subtitle_scale");
+ string scaler_id = get_required_string (kv, "scaler");
+ int frame = get_required_int (kv, "frame");
+ int frames_per_second = get_required_int (kv, "frames_per_second");
+ string post_process = get_optional_string (kv, "post_process");
+ int colour_lut_index = get_required_int (kv, "colour_lut");
+ int j2k_bandwidth = get_required_int (kv, "j2k_bandwidth");
+ Position subtitle_position (get_optional_int (kv, "subtitle_x"), get_optional_int (kv, "subtitle_y"));
+ Size subtitle_size (get_optional_int (kv, "subtitle_width"), get_optional_int (kv, "subtitle_height"));
+
+ /* This checks that colour_lut_index is within range */
+ colour_lut_index_to_name (colour_lut_index);
+
PixelFormat pixel_format = (PixelFormat) pixel_format_int;
Scaler const * scaler = Scaler::from_id (scaler_id);
- if (post_process == "none") {
- post_process = "";
- }
shared_ptr<Image> image (new AlignedImage (pixel_format, in_size));
-
- for (int i = 0; i < image->components(); ++i) {
- socket->read_definite_and_consume (image->data()[i], image->stride()[i] * image->lines(i), 30);
+
+ image->read_from_socket (socket);
+
+ shared_ptr<Subtitle> sub;
+ if (subtitle_size.width && subtitle_size.height) {
+ shared_ptr<Image> subtitle_image (new AlignedImage (PIX_FMT_RGBA, subtitle_size));
+ subtitle_image->read_from_socket (socket);
+ sub.reset (new Subtitle (subtitle_position, subtitle_image));
}
- /* XXX: subtitle */
DCPVideoFrame dcp_video_frame (
- image, shared_ptr<Subtitle> (), out_size, padding, subtitle_offset, subtitle_scale,
+ image, sub, out_size, padding, subtitle_offset, subtitle_scale,
scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log
);
shared_ptr<EncodedData> encoded = dcp_video_frame.encode_locally ();
encoded->send (socket);
-
+
return frame;
}
diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc
index 0eb40b14e..01e8cac13 100644
--- a/src/lib/subtitle.cc
+++ b/src/lib/subtitle.cc
@@ -25,7 +25,7 @@
using namespace std;
using namespace boost;
-Subtitle::Subtitle (AVSubtitle const & sub)
+TimedSubtitle::TimedSubtitle (AVSubtitle const & sub)
{
/* subtitle PTS in seconds */
float const packet_time = (sub.pts / AV_TIME_BASE) + float (sub.pts % AV_TIME_BASE) / 1e6;
@@ -34,32 +34,24 @@ Subtitle::Subtitle (AVSubtitle const & sub)
_from = packet_time + (double (sub.start_display_time) / 1e3);
_to = packet_time + (double (sub.end_display_time) / 1e3);
- for (unsigned int i = 0; i < sub.num_rects; ++i) {
- _images.push_back (shared_ptr<SubtitleImage> (new SubtitleImage (sub.rects[i])));
+ if (sub.num_rects > 1) {
+ throw DecodeError ("multi-part subtitles not yet supported");
}
-}
-/** @param t Time in seconds from the start of the film */
-bool
-Subtitle::displayed_at (double t)
-{
- return t >= _from && t <= _to;
-}
+ AVSubtitleRect const * rect = sub.rects[0];
-SubtitleImage::SubtitleImage (AVSubtitleRect const * rect)
- : _position (rect->x, rect->y)
- , _image (new AlignedImage (PIX_FMT_RGBA, Size (rect->w, rect->h)))
-{
if (rect->type != SUBTITLE_BITMAP) {
throw DecodeError ("non-bitmap subtitles not yet supported");
}
+
+ shared_ptr<Image> image (new AlignedImage (PIX_FMT_RGBA, Size (rect->w, rect->h)));
/* Start of the first line in the subtitle */
uint8_t* sub_p = rect->pict.data[0];
/* sub_p looks up into a RGB palette which is here */
uint32_t const * palette = (uint32_t *) rect->pict.data[1];
/* Start of the output data */
- uint32_t* out_p = (uint32_t *) _image->data()[0];
+ uint32_t* out_p = (uint32_t *) image->data()[0];
for (int y = 0; y < rect->h; ++y) {
uint8_t* sub_line_p = sub_p;
@@ -68,12 +60,28 @@ SubtitleImage::SubtitleImage (AVSubtitleRect const * rect)
*out_line_p++ = palette[*sub_line_p++];
}
sub_p += rect->pict.linesize[0];
- out_p += _image->stride()[0] / sizeof (uint32_t);
+ out_p += image->stride()[0] / sizeof (uint32_t);
}
+
+ _subtitle.reset (new Subtitle (Position (rect->x, rect->y), image));
+}
+
+/** @param t Time in seconds from the start of the film */
+bool
+TimedSubtitle::displayed_at (double t) const
+{
+ return t >= _from && t <= _to;
}
+Subtitle::Subtitle (Position p, shared_ptr<Image> i)
+ : _position (p)
+ , _image (i)
+{
+
+}
+
Rectangle
-transformed_subtitle_area (
+subtitle_transformed_area (
float target_x_scale, float target_y_scale,
Rectangle sub_area, int subtitle_offset, float subtitle_scale
)
@@ -85,8 +93,8 @@ transformed_subtitle_area (
/* We will scale the subtitle by the same amount as the video frame, and also by the additional
subtitle_scale
*/
- tx.w = sub_area.w * target_x_scale * subtitle_scale;
- tx.h = sub_area.h * target_y_scale * subtitle_scale;
+ tx.width = sub_area.width * target_x_scale * subtitle_scale;
+ tx.height = sub_area.height * target_y_scale * subtitle_scale;
/* Then we need a corrective translation, consisting of two parts:
*
@@ -100,14 +108,14 @@ transformed_subtitle_area (
* Combining these two translations gives these expressions.
*/
- tx.x = target_x_scale * (sub_area.x + (sub_area.w * (1 - subtitle_scale) / 2));
- tx.y = target_y_scale * (sub_area.y + (sub_area.h * (1 - subtitle_scale) / 2));
+ tx.x = target_x_scale * (sub_area.x + (sub_area.width * (1 - subtitle_scale) / 2));
+ tx.y = target_y_scale * (sub_area.y + (sub_area.height * (1 - subtitle_scale) / 2));
return tx;
}
Rectangle
-SubtitleImage::area () const
+Subtitle::area () const
{
return Rectangle (_position.x, _position.y, _image->size().width, _image->size().height);
}
diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h
index 6fd0d8772..de8f02596 100644
--- a/src/lib/subtitle.h
+++ b/src/lib/subtitle.h
@@ -22,38 +22,12 @@
#include "util.h"
struct AVSubtitle;
-class SubtitleImage;
class Image;
-class FilmState;
class Subtitle
{
public:
- Subtitle (AVSubtitle const &);
-
- bool displayed_at (double t);
-
- std::list<boost::shared_ptr<SubtitleImage> > images () const {
- return _images;
- }
-
-private:
- /** display from time in seconds from the start of the film */
- double _from;
- /** display to time in seconds from the start of the film */
- double _to;
- std::list<boost::shared_ptr<SubtitleImage> > _images;
-};
-
-extern Rectangle transformed_subtitle_area (
- float target_x_scale, float target_y_scale,
- Rectangle sub_area, int subtitle_offset, float subtitle_scale
- );
-
-class SubtitleImage
-{
-public:
- SubtitleImage (AVSubtitleRect const *);
+ Subtitle (Position p, boost::shared_ptr<Image> i);
void set_position (Position p) {
_position = p;
@@ -68,8 +42,34 @@ public:
}
Rectangle area () const;
-
+
private:
Position _position;
boost::shared_ptr<Image> _image;
};
+
+Rectangle
+subtitle_transformed_area (
+ float target_x_scale, float target_y_scale,
+ Rectangle sub_area, int subtitle_offset, float subtitle_scale
+ );
+
+class TimedSubtitle
+{
+public:
+ TimedSubtitle (AVSubtitle const &);
+
+ bool displayed_at (double t) const;
+
+ boost::shared_ptr<Subtitle> subtitle () const {
+ return _subtitle;
+ }
+
+private:
+ boost::shared_ptr<Subtitle> _subtitle;
+
+ /** display from time in seconds from the start of the film */
+ double _from;
+ /** display to time in seconds from the start of the film */
+ double _to;
+};
diff --git a/src/lib/util.cc b/src/lib/util.cc
index fbe77461e..c2b24944d 100644
--- a/src/lib/util.cc
+++ b/src/lib/util.cc
@@ -35,6 +35,7 @@
#include <boost/algorithm/string.hpp>
#include <boost/bind.hpp>
#include <boost/lambda/lambda.hpp>
+#include <boost/lexical_cast.hpp>
#include <openjpeg.h>
#include <openssl/md5.h>
#include <magick/MagickCore.h>
@@ -609,8 +610,8 @@ Rectangle::intersection (Rectangle const & other) const
return Rectangle (
tx, ty,
- min (x + w, other.x + other.w) - tx,
- min (y + h, other.y + other.h) - ty
+ min (x + width, other.x + other.width) - tx,
+ min (y + height, other.y + other.height) - ty
);
}
@@ -621,3 +622,85 @@ round_up (int a, int t)
return a - (a % t);
}
+multimap<string, string>
+read_key_value (istream &s)
+{
+ multimap<string, string> kv;
+
+ string line;
+ while (getline (s, line)) {
+ if (line.empty ()) {
+ continue;
+ }
+
+ if (line[0] == '#') {
+ continue;
+ }
+
+ if (line[line.size() - 1] == '\r') {
+ line = line.substr (0, line.size() - 1);
+ }
+
+ size_t const s = line.find (' ');
+ if (s == string::npos) {
+ continue;
+ }
+
+ kv.insert (make_pair (line.substr (0, s), line.substr (s + 1)));
+ }
+
+ return kv;
+}
+
+string
+get_required_string (multimap<string, string> const & kv, string k)
+{
+ if (kv.count (k) > 1) {
+ throw StringError ("unexpected multiple keys in key-value set");
+ }
+
+ multimap<string, string>::const_iterator i = kv.find (k);
+
+ if (i == kv.end ()) {
+ throw StringError (String::compose ("missing key %1 in key-value set", k));
+ }
+
+ return i->second;
+}
+
+int
+get_required_int (multimap<string, string> const & kv, string k)
+{
+ string const v = get_required_string (kv, k);
+ return lexical_cast<int> (v);
+}
+
+string
+get_optional_string (multimap<string, string> const & kv, string k)
+{
+ if (kv.count (k) > 1) {
+ throw StringError ("unexpected multiple keys in key-value set");
+ }
+
+ multimap<string, string>::const_iterator i = kv.find (k);
+ if (i == kv.end ()) {
+ return "";
+ }
+
+ return i->second;
+}
+
+int
+get_optional_int (multimap<string, string> const & kv, string k)
+{
+ if (kv.count (k) > 1) {
+ throw StringError ("unexpected multiple keys in key-value set");
+ }
+
+ multimap<string, string>::const_iterator i = kv.find (k);
+ if (i == kv.end ()) {
+ return 0;
+ }
+
+ return lexical_cast<int> (i->second);
+}
diff --git a/src/lib/util.h b/src/lib/util.h
index bd7675a8a..5f2e9a7fa 100644
--- a/src/lib/util.h
+++ b/src/lib/util.h
@@ -127,21 +127,29 @@ struct Rectangle
Rectangle ()
: x (0)
, y (0)
- , w (0)
- , h (0)
+ , width (0)
+ , height (0)
{}
Rectangle (int x_, int y_, int w_, int h_)
: x (x_)
, y (y_)
- , w (w_)
- , h (h_)
+ , width (w_)
+ , height (h_)
{}
int x;
int y;
- int w;
- int h;
+ int width;
+ int height;
+
+ Position position () const {
+ return Position (x, y);
+ }
+
+ Size size () const {
+ return Size (width, height);
+ }
Rectangle intersection (Rectangle const & other) const;
};
@@ -150,6 +158,11 @@ extern std::string crop_string (Position, Size);
extern int dcp_audio_sample_rate (int);
extern std::string colour_lut_index_to_name (int index);
extern int round_up (int, int);
+extern std::multimap<std::string, std::string> read_key_value (std::istream& s);
+extern int get_required_int (std::multimap<std::string, std::string> const & kv, std::string k);
+extern std::string get_required_string (std::multimap<std::string, std::string> const & kv, std::string k);
+extern int get_optional_int (std::multimap<std::string, std::string> const & kv, std::string k);
+extern std::string get_optional_string (std::multimap<std::string, std::string> const & kv, std::string k);
/** @class Socket
* @brief A class to wrap a boost::asio::ip::tcp::socket with some things
@@ -186,7 +199,7 @@ private:
boost::asio::deadline_timer _deadline;
boost::asio::ip::tcp::socket _socket;
/** a buffer for small reads */
- uint8_t _buffer[256];
+ uint8_t _buffer[1024];
/** amount of valid data in the buffer */
int _buffer_data;
};
diff --git a/src/wx/dci_name_dialog.cc b/src/wx/dci_name_dialog.cc
new file mode 100644
index 000000000..88cc7efa4
--- /dev/null
+++ b/src/wx/dci_name_dialog.cc
@@ -0,0 +1,129 @@
+/*
+ Copyright (C) 2012 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/sizer.h>
+#include "dci_name_dialog.h"
+#include "wx_util.h"
+#include "film.h"
+
+DCINameDialog::DCINameDialog (wxWindow* parent, Film* film)
+ : wxDialog (parent, wxID_ANY, _("DCI name"), wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
+ , _film (film)
+{
+ wxFlexGridSizer* table = new wxFlexGridSizer (2, 6, 6);
+ table->AddGrowableCol (1, 1);
+
+ add_label_to_sizer (table, this, "Audio Language (e.g. EN)");
+ _audio_language = new wxTextCtrl (this, wxID_ANY);
+ table->Add (_audio_language, 1, wxEXPAND);
+
+ add_label_to_sizer (table, this, "Subtitle Language (e.g. FR)");
+ _subtitle_language = new wxTextCtrl (this, wxID_ANY);
+ table->Add (_subtitle_language, 1, wxEXPAND);
+
+ add_label_to_sizer (table, this, "Territory (e.g. UK)");
+ _territory = new wxTextCtrl (this, wxID_ANY);
+ table->Add (_territory, 1, wxEXPAND);
+
+ add_label_to_sizer (table, this, "Rating (e.g. 15");
+ _rating = new wxTextCtrl (this, wxID_ANY);
+ table->Add (_rating, 1, wxEXPAND);
+
+ add_label_to_sizer (table, this, "Studio (e.g. TCF)");
+ _studio = new wxTextCtrl (this, wxID_ANY);
+ table->Add (_studio, 1, wxEXPAND);
+
+ add_label_to_sizer (table, this, "Facility (e.g. DLA)");
+ _facility = new wxTextCtrl (this, wxID_ANY);
+ table->Add (_facility, 1, wxEXPAND);
+
+ add_label_to_sizer (table, this, "Package Type (e.g. OV");
+ _package_type = new wxTextCtrl (this, wxID_ANY);
+ table->Add (_package_type, 1, wxEXPAND);
+
+ _audio_language->SetValue (std_to_wx (_film->audio_language ()));
+ _subtitle_language->SetValue (std_to_wx (_film->subtitle_language ()));
+ _territory->SetValue (std_to_wx (_film->territory ()));
+ _rating->SetValue (std_to_wx (_film->rating ()));
+ _studio->SetValue (std_to_wx (_film->studio ()));
+ _facility->SetValue (std_to_wx (_film->facility ()));
+ _package_type->SetValue (std_to_wx (_film->package_type ()));
+
+ _audio_language->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (DCINameDialog::audio_language_changed), 0, this);
+ _subtitle_language->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (DCINameDialog::subtitle_language_changed), 0, this);
+ _territory->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (DCINameDialog::territory_changed), 0, this);
+ _rating->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (DCINameDialog::rating_changed), 0, this);
+ _studio->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (DCINameDialog::studio_changed), 0, this);
+ _facility->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (DCINameDialog::facility_changed), 0, this);
+ _package_type->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (DCINameDialog::package_type_changed), 0, this);
+
+ wxBoxSizer* overall_sizer = new wxBoxSizer (wxVERTICAL);
+ overall_sizer->Add (table, 1, wxEXPAND | wxALL, 6);
+
+ wxSizer* buttons = CreateSeparatedButtonSizer (wxOK);
+ if (buttons) {
+ overall_sizer->Add (buttons, wxSizerFlags().Expand().DoubleBorder());
+ }
+
+ SetSizer (overall_sizer);
+ overall_sizer->Layout ();
+ overall_sizer->SetSizeHints (this);
+}
+
+void
+DCINameDialog::audio_language_changed (wxCommandEvent &)
+{
+ _film->set_audio_language (wx_to_std (_audio_language->GetValue ()));
+}
+
+void
+DCINameDialog::subtitle_language_changed (wxCommandEvent &)
+{
+ _film->set_subtitle_language (wx_to_std (_subtitle_language->GetValue ()));
+}
+
+void
+DCINameDialog::territory_changed (wxCommandEvent &)
+{
+ _film->set_territory (wx_to_std (_territory->GetValue ()));
+}
+
+void
+DCINameDialog::rating_changed (wxCommandEvent &)
+{
+ _film->set_rating (wx_to_std (_rating->GetValue ()));
+}
+
+void
+DCINameDialog::studio_changed (wxCommandEvent &)
+{
+ _film->set_studio (wx_to_std (_studio->GetValue ()));
+}
+
+void
+DCINameDialog::facility_changed (wxCommandEvent &)
+{
+ _film->set_facility (wx_to_std (_facility->GetValue ()));
+}
+
+void
+DCINameDialog::package_type_changed (wxCommandEvent &)
+{
+ _film->set_package_type (wx_to_std (_package_type->GetValue ()));
+}
diff --git a/src/wx/dci_name_dialog.h b/src/wx/dci_name_dialog.h
new file mode 100644
index 000000000..d06420a6e
--- /dev/null
+++ b/src/wx/dci_name_dialog.h
@@ -0,0 +1,48 @@
+/*
+ Copyright (C) 2012 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
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+*/
+
+#include <wx/dialog.h>
+#include <wx/textctrl.h>
+
+class Film;
+
+class DCINameDialog : public wxDialog
+{
+public:
+ DCINameDialog (wxWindow *, Film *);
+
+private:
+ void audio_language_changed (wxCommandEvent &);
+ void subtitle_language_changed (wxCommandEvent &);
+ void territory_changed (wxCommandEvent &);
+ void rating_changed (wxCommandEvent &);
+ void studio_changed (wxCommandEvent &);
+ void facility_changed (wxCommandEvent &);
+ void package_type_changed (wxCommandEvent &);
+
+ wxTextCtrl* _audio_language;
+ wxTextCtrl* _subtitle_language;
+ wxTextCtrl* _territory;
+ wxTextCtrl* _rating;
+ wxTextCtrl* _studio;
+ wxTextCtrl* _facility;
+ wxTextCtrl* _package_type;
+
+ Film* _film;
+};
diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc
index 7fd2eb9fc..22063256f 100644
--- a/src/wx/film_editor.cc
+++ b/src/wx/film_editor.cc
@@ -43,6 +43,7 @@
#include "dcp_range_dialog.h"
#include "gain_calculator_dialog.h"
#include "sound_processor.h"
+#include "dci_name_dialog.h"
using namespace std;
using namespace boost;
@@ -60,6 +61,10 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent)
_name = new wxTextCtrl (this, wxID_ANY);
_sizer->Add (_name, 1, wxEXPAND);
+ add_label_to_sizer (_sizer, this, "DCP Name");
+ _dcp_name = new wxStaticText (this, wxID_ANY, wxT (""));
+ _sizer->Add (_dcp_name, 0, wxALIGN_CENTER_VERTICAL | wxSHRINK);
+
_use_dci_name = new wxCheckBox (this, wxID_ANY, wxT ("Use DCI name"));
_sizer->Add (_use_dci_name, 1, wxEXPAND);
_edit_dci_button = new wxButton (this, wxID_ANY, wxT ("Edit..."));
@@ -222,6 +227,8 @@ FilmEditor::FilmEditor (Film* f, wxWindow* parent)
/* Now connect to them, since initial values are safely set */
_name->Connect (wxID_ANY, wxEVT_COMMAND_TEXT_UPDATED, wxCommandEventHandler (FilmEditor::name_changed), 0, this);
+ _use_dci_name->Connect (wxID_ANY, wxEVT_COMMAND_CHECKBOX_CLICKED, wxCommandEventHandler (FilmEditor::use_dci_name_toggled), 0, this);
+ _edit_dci_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmEditor::edit_dci_button_clicked), 0, this);
_format->Connect (wxID_ANY, wxEVT_COMMAND_COMBOBOX_SELECTED, wxCommandEventHandler (FilmEditor::format_changed), 0, this);
_content->Connect (wxID_ANY, wxEVT_COMMAND_FILEPICKER_CHANGED, wxCommandEventHandler (FilmEditor::content_changed), 0, this);
_left_crop->Connect (wxID_ANY, wxEVT_COMMAND_SPINCTRL_UPDATED, wxCommandEventHandler (FilmEditor::left_crop_changed), 0, this);
@@ -347,6 +354,8 @@ FilmEditor::name_changed (wxCommandEvent &)
_ignore_changes = Film::NAME;
_film->set_name (string (_name->GetValue().mb_str()));
_ignore_changes = Film::NONE;
+
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
}
void
@@ -405,6 +414,7 @@ FilmEditor::film_changed (Film::Property p)
++n;
}
_format->SetSelection (n);
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
break;
}
case Film::CROP:
@@ -427,6 +437,7 @@ FilmEditor::film_changed (Film::Property p)
}
case Film::NAME:
_name->ChangeValue (std_to_wx (_film->name ()));
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
break;
case Film::FRAMES_PER_SECOND:
{
@@ -436,6 +447,7 @@ FilmEditor::film_changed (Film::Property p)
break;
}
case Film::AUDIO_CHANNELS:
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
case Film::AUDIO_SAMPLE_RATE:
if (_film->audio_channels() == 0 && _film->audio_sample_rate() == 0) {
_audio->SetLabel (wxT (""));
@@ -462,6 +474,7 @@ FilmEditor::film_changed (Film::Property p)
break;
case Film::DCP_CONTENT_TYPE:
_dcp_content_type->SetSelection (DCPContentType::as_index (_film->dcp_content_type ()));
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
break;
case Film::THUMBS:
break;
@@ -496,6 +509,7 @@ FilmEditor::film_changed (Film::Property p)
_with_subtitles->SetValue (_film->with_subtitles ());
_subtitle_scale->Enable (_film->with_subtitles ());
_subtitle_offset->Enable (_film->with_subtitles ());
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
break;
case Film::SUBTITLE_OFFSET:
_subtitle_offset->SetValue (_film->subtitle_offset ());
@@ -503,6 +517,13 @@ FilmEditor::film_changed (Film::Property p)
case Film::SUBTITLE_SCALE:
_subtitle_scale->SetValue (_film->subtitle_scale() * 100);
break;
+ case Film::USE_DCI_NAME:
+ _use_dci_name->SetValue (_film->use_dci_name ());
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+ break;
+ case Film::DCI_METADATA:
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+ break;
}
}
@@ -521,6 +542,8 @@ FilmEditor::format_changed (wxCommandEvent &)
_film->set_format (_formats[n]);
}
_ignore_changes = Film::NONE;
+
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
}
/** Called when the DCP content type widget has been changed */
@@ -537,6 +560,8 @@ FilmEditor::dcp_content_type_changed (wxCommandEvent &)
_film->set_dcp_content_type (DCPContentType::from_index (n));
}
_ignore_changes = Film::NONE;
+
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
}
/** Sets the Film that we are editing */
@@ -578,6 +603,8 @@ FilmEditor::set_film (Film* f)
film_changed (Film::WITH_SUBTITLES);
film_changed (Film::SUBTITLE_OFFSET);
film_changed (Film::SUBTITLE_SCALE);
+ film_changed (Film::USE_DCI_NAME);
+ film_changed (Film::DCI_METADATA);
}
/** Updates the sensitivity of lots of widgets to a given value.
@@ -587,6 +614,8 @@ void
FilmEditor::set_things_sensitive (bool s)
{
_name->Enable (s);
+ _use_dci_name->Enable (s);
+ _edit_dci_button->Enable (s);
_frames_per_second->Enable (s);
_format->Enable (s);
_content->Enable (s);
@@ -799,3 +828,28 @@ FilmEditor::setup_subtitle_button ()
}
}
+void
+FilmEditor::use_dci_name_toggled (wxCommandEvent &)
+{
+ if (!_film) {
+ return;
+ }
+
+ _ignore_changes = Film::USE_DCI_NAME;
+ _film->set_use_dci_name (_use_dci_name->GetValue ());
+ _ignore_changes = Film::NONE;
+
+ _dcp_name->SetLabel (std_to_wx (_film->dcp_name ()));
+}
+
+void
+FilmEditor::edit_dci_button_clicked (wxCommandEvent &)
+{
+ if (!_film) {
+ return;
+ }
+
+ DCINameDialog* d = new DCINameDialog (this, _film);
+ d->ShowModal ();
+ d->Destroy ();
+}
diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h
index 2a3be6d0c..6fb8973bf 100644
--- a/src/wx/film_editor.h
+++ b/src/wx/film_editor.h
@@ -46,6 +46,8 @@ public:
private:
/* Handle changes to the view */
void name_changed (wxCommandEvent &);
+ void use_dci_name_toggled (wxCommandEvent &);
+ void edit_dci_button_clicked (wxCommandEvent &);
void left_crop_changed (wxCommandEvent &);
void right_crop_changed (wxCommandEvent &);
void top_crop_changed (wxCommandEvent &);
@@ -84,6 +86,7 @@ private:
Film* _film;
/** The Film's name */
wxTextCtrl* _name;
+ wxStaticText* _dcp_name;
wxCheckBox* _use_dci_name;
wxButton* _edit_dci_button;
/** The Film's format */
diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc
index bf082adc2..c42ffaeeb 100644
--- a/src/wx/film_viewer.cc
+++ b/src/wx/film_viewer.cc
@@ -58,21 +58,18 @@ public:
if (_frame_rebuild_needed) {
_image.reset (new wxImage (std_to_wx (_film->thumb_file (_index))));
- _subtitles.clear ();
- list<pair<Position, string> > s = _film->thumb_subtitles (_index);
- for (list<pair<Position, string> >::iterator i = s.begin(); i != s.end(); ++i) {
- _subtitles.push_back (SubtitleView (i->first, std_to_wx (i->second)));
+ _subtitle.reset ();
+ pair<Position, string> s = _film->thumb_subtitle (_index);
+ if (!s.second.empty ()) {
+ _subtitle.reset (new SubtitleView (s.first, std_to_wx (s.second)));
}
_frame_rebuild_needed = false;
-
compose ();
- _composition_needed = false;
}
if (_composition_needed) {
compose ();
- _composition_needed = false;
}
wxPaintDC dc (this);
@@ -80,10 +77,8 @@ public:
dc.DrawBitmap (*_bitmap, 0, 0, false);
}
- if (_film->with_subtitles ()) {
- for (list<SubtitleView>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
- dc.DrawBitmap (*i->bitmap, i->transformed_area.x, i->transformed_area.y, true);
- }
+ if (_film->with_subtitles() && _subtitle) {
+ dc.DrawBitmap (*_subtitle->bitmap, _subtitle->transformed_area.x, _subtitle->transformed_area.y, true);
}
}
@@ -123,7 +118,7 @@ public:
{
_bitmap.reset ();
_image.reset ();
- _subtitles.clear ();
+ _subtitle.reset ();
}
void recompose ()
@@ -138,6 +133,8 @@ private:
void compose ()
{
+ _composition_needed = false;
+
if (!_film || !_image) {
return;
}
@@ -157,7 +154,7 @@ private:
/* Target ratio */
float const target = _film->format() ? _film->format()->ratio_as_float (_film) : 1.78;
- _transformed_image = _image->GetSubImage (wxRect (cropped_area.x, cropped_area.y, cropped_area.w, cropped_area.h));
+ _transformed_image = _image->GetSubImage (wxRect (cropped_area.x, cropped_area.y, cropped_area.width, cropped_area.height));
float x_scale = 1;
float y_scale = 1;
@@ -165,28 +162,28 @@ private:
if ((float (vw) / vh) > target) {
/* view is longer (horizontally) than the ratio; fit height */
_transformed_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH);
- x_scale = vh * target / cropped_area.w;
- y_scale = float (vh) / cropped_area.h;
+ x_scale = vh * target / cropped_area.width;
+ y_scale = float (vh) / cropped_area.height;
} else {
/* view is shorter (horizontally) than the ratio; fit width */
_transformed_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH);
- x_scale = float (vw) / cropped_area.w;
- y_scale = (vw / target) / cropped_area.h;
+ x_scale = float (vw) / cropped_area.width;
+ y_scale = (vw / target) / cropped_area.height;
}
_bitmap.reset (new wxBitmap (_transformed_image));
- for (list<SubtitleView>::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) {
+ if (_subtitle) {
- i->transformed_area = transformed_subtitle_area (
- x_scale, y_scale, i->base_area, _film->subtitle_offset(), _film->subtitle_scale()
+ _subtitle->transformed_area = subtitle_transformed_area (
+ x_scale, y_scale, _subtitle->base_area, _film->subtitle_offset(), _film->subtitle_scale()
);
- i->transformed_image = i->base_image;
- i->transformed_image.Rescale (i->transformed_area.w, i->transformed_area.h, wxIMAGE_QUALITY_HIGH);
- i->transformed_area.x -= _film->crop().left;
- i->transformed_area.y -= _film->crop().top;
- i->bitmap.reset (new wxBitmap (i->transformed_image));
+ _subtitle->transformed_image = _subtitle->base_image;
+ _subtitle->transformed_image.Rescale (_subtitle->transformed_area.width, _subtitle->transformed_area.height, wxIMAGE_QUALITY_HIGH);
+ _subtitle->transformed_area.x -= _film->crop().left;
+ _subtitle->transformed_area.y -= _film->crop().top;
+ _subtitle->bitmap.reset (new wxBitmap (_subtitle->transformed_image));
}
}
@@ -206,8 +203,8 @@ private:
{
base_area.x = p.x;
base_area.y = p.y;
- base_area.w = base_image.GetWidth ();
- base_area.h = base_image.GetHeight ();
+ base_area.width = base_image.GetWidth ();
+ base_area.height = base_image.GetHeight ();
}
Rectangle base_area;
@@ -217,7 +214,7 @@ private:
shared_ptr<wxBitmap> bitmap;
};
- list<SubtitleView> _subtitles;
+ shared_ptr<SubtitleView> _subtitle;
};
BEGIN_EVENT_TABLE (ThumbPanel, wxPanel)
diff --git a/src/wx/wscript b/src/wx/wscript
index 38107bb54..df2edbde1 100644
--- a/src/wx/wscript
+++ b/src/wx/wscript
@@ -9,20 +9,21 @@ def build(bld):
obj.uselib = 'WXWIDGETS'
obj.use = 'libdvdomatic'
obj.source = """
+ config_dialog.cc
+ dci_name_dialog.cc
+ dcp_range_dialog.cc
+ dir_picker_ctrl.cc
film_editor.cc
- wx_util.cc
film_viewer.cc
- job_manager_view.cc
- job_wrapper.cc
- gain_calculator_dialog.cc
- config_dialog.cc
filter_dialog.cc
filter_view.cc
- dcp_range_dialog.cc
- server_dialog.cc
+ gain_calculator_dialog.cc
+ job_manager_view.cc
+ job_wrapper.cc
new_film_dialog.cc
- dir_picker_ctrl.cc
properties_dialog.cc
+ server_dialog.cc
+ wx_util.cc
"""
# alignment.cc