diff options
| author | Carl Hetherington <cth@carlh.net> | 2012-10-16 03:29:55 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2012-10-16 03:29:55 +0100 |
| commit | 068bb5b95652c022419c64f3d29cc42c4610ae93 (patch) | |
| tree | 373b5b9bd91a351ae9727e6d4f388eb7157fda7f /src | |
| parent | 13511ed2fcc23f4d5f9c507c775c3c5cfd82d155 (diff) | |
| parent | 27e1f73c75e6c0f75d4863cfe2c8e50f811913d9 (diff) | |
Merge branch 'master' of /home/carl/git/dvdomatic
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/dcp_video_frame.cc | 79 | ||||
| -rw-r--r-- | src/lib/decoder.cc | 15 | ||||
| -rw-r--r-- | src/lib/decoder.h | 5 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 2 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.h | 1 | ||||
| -rw-r--r-- | src/lib/film.cc | 123 | ||||
| -rw-r--r-- | src/lib/film.h | 51 | ||||
| -rw-r--r-- | src/lib/film_state.cc | 60 | ||||
| -rw-r--r-- | src/lib/film_state.h | 7 | ||||
| -rw-r--r-- | src/lib/image.cc | 24 | ||||
| -rw-r--r-- | src/lib/image.h | 3 | ||||
| -rw-r--r-- | src/lib/imagemagick_encoder.cc | 39 | ||||
| -rw-r--r-- | src/lib/make_dcp_job.cc | 10 | ||||
| -rw-r--r-- | src/lib/scp_dcp_job.cc | 6 | ||||
| -rw-r--r-- | src/lib/server.cc | 76 | ||||
| -rw-r--r-- | src/lib/subtitle.cc | 52 | ||||
| -rw-r--r-- | src/lib/subtitle.h | 56 | ||||
| -rw-r--r-- | src/lib/util.cc | 87 | ||||
| -rw-r--r-- | src/lib/util.h | 27 | ||||
| -rw-r--r-- | src/wx/dci_name_dialog.cc | 129 | ||||
| -rw-r--r-- | src/wx/dci_name_dialog.h | 48 | ||||
| -rw-r--r-- | src/wx/film_editor.cc | 54 | ||||
| -rw-r--r-- | src/wx/film_editor.h | 3 | ||||
| -rw-r--r-- | src/wx/film_viewer.cc | 53 | ||||
| -rw-r--r-- | src/wx/wscript | 17 |
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 |
