diff options
| author | Carl Hetherington <cth@carlh.net> | 2013-07-11 14:29:55 +0100 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2013-07-11 14:29:55 +0100 |
| commit | b655bd3359e9a014da68cd9f61e2a5b1d233247d (patch) | |
| tree | 42ab16fdc9621fd0314a71f051804cdacd1cb823 | |
| parent | e64acff7da897cd03d88aa9e8e21682545a61cc2 (diff) | |
| parent | 2514ffcd1adcca4fc8e3db894afb5cdc6c857e94 (diff) | |
Merge branch '1.0' of /home/carl/git/dvdomatic into 1.0
39 files changed, 587 insertions, 674 deletions
@@ -6,9 +6,8 @@ def dependencies(target): if target.platform == 'windows': return () else: - # XXX: should be some versions in here return (('ffmpeg-cdist', 'e797834288eaf05a2f406524ae04aaa0f114cb08'), - ('libdcp', 'v0.54')) + ('libdcp', 'v0.59')) def build(target): cmd = './waf configure --prefix=%s' % target.work_dir_cscript() diff --git a/src/lib/combiner.cc b/src/lib/combiner.cc deleted file mode 100644 index f7d634832..000000000 --- a/src/lib/combiner.cc +++ /dev/null @@ -1,74 +0,0 @@ -/* - 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 "combiner.h" -#include "image.h" - -using boost::shared_ptr; - -Combiner::Combiner (shared_ptr<Log> log) - : TimedVideoProcessor (log) -{ - -} - -/** Process video for the left half of the frame. - * Subtitle parameter will be ignored. - * @param image Frame image. - */ -void -Combiner::process_video (shared_ptr<const Image> image, bool, shared_ptr<Subtitle>, double) -{ - _image.reset (new SimpleImage (image)); -} - -/** Process video for the right half of the frame. - * @param image Frame image. - * @param sub Subtitle (which will be put onto the whole frame) - */ -void -Combiner::process_video_b (shared_ptr<const Image> image, bool, shared_ptr<Subtitle> sub, double t) -{ - if (!_image) { - /* It's possible for filters in the A-side to mean that we get a B frame - before any A; just skip the B frame in that case. This at least prevents - a crash, but may not be right. - */ - return; - } - - /* Copy the right half of this image into our _image */ - /* XXX: this should probably be in the Image class */ - for (int i = 0; i < image->components(); ++i) { - int const line_size = image->line_size()[i]; - int const half_line_size = line_size / 2; - - uint8_t* p = _image->data()[i]; - uint8_t* q = image->data()[i]; - - for (int j = 0; j < image->lines (i); ++j) { - memcpy (p + half_line_size, q + half_line_size, half_line_size); - p += _image->stride()[i]; - q += image->stride()[i]; - } - } - - Video (_image, false, sub, t); - _image.reset (); -} diff --git a/src/lib/content.h b/src/lib/content.h index e33f517ab..5dcf27597 100644 --- a/src/lib/content.h +++ b/src/lib/content.h @@ -48,6 +48,7 @@ public: Content (boost::shared_ptr<const Film>, boost::filesystem::path); Content (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); Content (Content const &); + virtual ~Content () {} virtual void examine (boost::shared_ptr<Job>); virtual std::string summary () const = 0; diff --git a/src/lib/decoder.h b/src/lib/decoder.h index cfca6867f..dea4def3a 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -34,8 +34,6 @@ class Image; class Log; class DelayLine; -class TimedSubtitle; -class Subtitle; class FilterGraph; /** @class Decoder. diff --git a/src/lib/ffmpeg_content.cc b/src/lib/ffmpeg_content.cc index 1135cc9a3..35f9f71f2 100644 --- a/src/lib/ffmpeg_content.cc +++ b/src/lib/ffmpeg_content.cc @@ -47,6 +47,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, boost::filesystem::path : Content (f, p) , VideoContent (f, p) , AudioContent (f, p) + , SubtitleContent (f, p) { } @@ -55,6 +56,7 @@ FFmpegContent::FFmpegContent (shared_ptr<const Film> f, shared_ptr<const cxml::N : Content (f, node) , VideoContent (f, node) , AudioContent (f, node) + , SubtitleContent (f, node) { list<shared_ptr<cxml::Node> > c = node->node_children ("SubtitleStream"); for (list<shared_ptr<cxml::Node> >::const_iterator i = c.begin(); i != c.end(); ++i) { @@ -84,6 +86,7 @@ FFmpegContent::FFmpegContent (FFmpegContent const & o) : Content (o) , VideoContent (o) , AudioContent (o) + , SubtitleContent (o) , _subtitle_streams (o._subtitle_streams) , _subtitle_stream (o._subtitle_stream) , _audio_streams (o._audio_streams) @@ -99,6 +102,7 @@ FFmpegContent::as_xml (xmlpp::Node* node) const Content::as_xml (node); VideoContent::as_xml (node); AudioContent::as_xml (node); + SubtitleContent::as_xml (node); boost::mutex::scoped_lock lm (_mutex); diff --git a/src/lib/ffmpeg_content.h b/src/lib/ffmpeg_content.h index c5ccee77a..5b9f1f579 100644 --- a/src/lib/ffmpeg_content.h +++ b/src/lib/ffmpeg_content.h @@ -23,6 +23,7 @@ #include <boost/enable_shared_from_this.hpp> #include "video_content.h" #include "audio_content.h" +#include "subtitle_content.h" class Filter; @@ -79,7 +80,7 @@ public: static int const FILTERS; }; -class FFmpegContent : public VideoContent, public AudioContent +class FFmpegContent : public VideoContent, public AudioContent, public SubtitleContent { public: FFmpegContent (boost::shared_ptr<const Film>, boost::filesystem::path); diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index d8ee278b3..fddb70294 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -41,7 +41,6 @@ extern "C" { #include "log.h" #include "ffmpeg_decoder.h" #include "filter_graph.h" -#include "subtitle.h" #include "audio_buffers.h" #include "i18n.h" @@ -61,6 +60,7 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<const Film> f, shared_ptr<const FFmpegC : Decoder (f) , VideoDecoder (f) , AudioDecoder (f) + , SubtitleDecoder (f) , FFmpeg (c) , _subtitle_codec_context (0) , _subtitle_codec (0) @@ -142,27 +142,7 @@ FFmpegDecoder::pass () } else if (_ffmpeg_content->audio_stream() && _packet.stream_index == _ffmpeg_content->audio_stream()->id && _decode_audio) { decode_audio_packet (); } else if (_ffmpeg_content->subtitle_stream() && _packet.stream_index == _ffmpeg_content->subtitle_stream()->id) { -#if 0 - - int got_subtitle; - AVSubtitle sub; - if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) { - /* Sometimes we get an empty AVSubtitle, which is used by some codecs to - indicate that the previous subtitle should stop. - */ - if (sub.num_rects > 0) { - shared_ptr<TimedSubtitle> ts; - try { - subtitle (shared_ptr<TimedSubtitle> (new TimedSubtitle (sub))); - } catch (...) { - /* some problem with the subtitle; we probably didn't understand it */ - } - } else { - subtitle (shared_ptr<TimedSubtitle> ()); - } - avsubtitle_free (&sub); - } -#endif + decode_subtitle_packet (); } av_free_packet (&_packet); @@ -489,3 +469,73 @@ FFmpegDecoder::done () const return vd && ad; } +void +FFmpegDecoder::decode_subtitle_packet () +{ + int got_subtitle; + AVSubtitle sub; + if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) < 0 || !got_subtitle) { + return; + } + + /* Sometimes we get an empty AVSubtitle, which is used by some codecs to + indicate that the previous subtitle should stop. + */ + if (sub.num_rects <= 0) { + subtitle (shared_ptr<Image> (), dcpomatic::Rect<double> (), 0, 0); + return; + } else if (sub.num_rects > 1) { + throw DecodeError (_("multi-part subtitles not yet supported")); + } + + /* Subtitle PTS in seconds (within the source, not taking into account any of the + source that we may have chopped off for the DCP) + */ + double const packet_time = static_cast<double> (sub.pts) / AV_TIME_BASE; + + /* hence start time for this sub */ + Time const from = (packet_time + (double (sub.start_display_time) / 1e3)) * TIME_HZ; + Time const to = (packet_time + (double (sub.end_display_time) / 1e3)) * TIME_HZ; + + AVSubtitleRect const * rect = sub.rects[0]; + + if (rect->type != SUBTITLE_BITMAP) { + throw DecodeError (_("non-bitmap subtitles not yet supported")); + } + + shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGBA, libdcp::Size (rect->w, rect->h), true)); + + /* 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]; + + for (int y = 0; y < rect->h; ++y) { + uint8_t* sub_line_p = sub_p; + uint32_t* out_line_p = out_p; + for (int x = 0; x < rect->w; ++x) { + *out_line_p++ = palette[*sub_line_p++]; + } + sub_p += rect->pict.linesize[0]; + out_p += image->stride()[0] / sizeof (uint32_t); + } + + libdcp::Size const vs = _ffmpeg_content->video_size (); + + subtitle ( + image, + dcpomatic::Rect<double> ( + static_cast<double> (rect->x) / vs.width, + static_cast<double> (rect->y) / vs.height, + static_cast<double> (rect->w) / vs.width, + static_cast<double> (rect->h) / vs.height + ), + from, + to + ); + + + avsubtitle_free (&sub); +} diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index eebf75445..8819954db 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -35,6 +35,7 @@ extern "C" { #include "decoder.h" #include "video_decoder.h" #include "audio_decoder.h" +#include "subtitle_decoder.h" #include "ffmpeg.h" class Film; @@ -43,7 +44,7 @@ class ffmpeg_pts_offset_test; /** @class FFmpegDecoder * @brief A decoder using FFmpeg to decode content. */ -class FFmpegDecoder : public VideoDecoder, public AudioDecoder, public FFmpeg +class FFmpegDecoder : public VideoDecoder, public AudioDecoder, public SubtitleDecoder, public FFmpeg { public: FFmpegDecoder (boost::shared_ptr<const Film>, boost::shared_ptr<const FFmpegContent>, bool video, bool audio); @@ -69,6 +70,7 @@ private: bool decode_video_packet (); void decode_audio_packet (); + void decode_subtitle_packet (); void maybe_add_subtitle (); boost::shared_ptr<AudioBuffers> deinterleave_audio (uint8_t** data, int size); diff --git a/src/lib/film.cc b/src/lib/film.cc index 11fa87912..dad9d6808 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -94,8 +94,6 @@ Film::Film (string d) , _container (Config::instance()->default_container ()) , _scaler (Scaler::from_id ("bicubic")) , _with_subtitles (false) - , _subtitle_offset (0) - , _subtitle_scale (1) , _colour_lut (0) , _j2k_bandwidth (200000000) , _dci_metadata (Config::instance()->default_dci_metadata ()) @@ -142,8 +140,6 @@ Film::Film (Film const & o) , _container (o._container) , _scaler (o._scaler) , _with_subtitles (o._with_subtitles) - , _subtitle_offset (o._subtitle_offset) - , _subtitle_scale (o._subtitle_scale) , _colour_lut (o._colour_lut) , _j2k_bandwidth (o._j2k_bandwidth) , _dci_metadata (o._dci_metadata) @@ -348,8 +344,6 @@ Film::write_metadata () const root->add_child("Scaler")->add_child_text (_scaler->id ()); root->add_child("WithSubtitles")->add_child_text (_with_subtitles ? "1" : "0"); - root->add_child("SubtitleOffset")->add_child_text (lexical_cast<string> (_subtitle_offset)); - root->add_child("SubtitleScale")->add_child_text (lexical_cast<string> (_subtitle_scale)); root->add_child("ColourLUT")->add_child_text (lexical_cast<string> (_colour_lut)); root->add_child("J2KBandwidth")->add_child_text (lexical_cast<string> (_j2k_bandwidth)); _dci_metadata.as_xml (root->add_child ("DCIMetadata")); @@ -395,8 +389,6 @@ Film::read_metadata () _scaler = Scaler::from_id (f.string_child ("Scaler")); _with_subtitles = f.bool_child ("WithSubtitles"); - _subtitle_offset = f.number_child<float> ("SubtitleOffset"); - _subtitle_scale = f.number_child<float> ("SubtitleScale"); _colour_lut = f.number_child<int> ("ColourLUT"); _j2k_bandwidth = f.number_child<int> ("J2KBandwidth"); _dci_metadata = DCIMetadata (f.node_child ("DCIMetadata")); @@ -594,26 +586,6 @@ Film::set_with_subtitles (bool w) } void -Film::set_subtitle_offset (int o) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _subtitle_offset = o; - } - signal_changed (SUBTITLE_OFFSET); -} - -void -Film::set_subtitle_scale (float s) -{ - { - boost::mutex::scoped_lock lm (_state_mutex); - _subtitle_scale = s; - } - signal_changed (SUBTITLE_SCALE); -} - -void Film::set_colour_lut (int i) { { diff --git a/src/lib/film.h b/src/lib/film.h index 5bb9acf29..08fdc587b 100644 --- a/src/lib/film.h +++ b/src/lib/film.h @@ -135,8 +135,6 @@ public: CONTAINER, SCALER, WITH_SUBTITLES, - SUBTITLE_OFFSET, - SUBTITLE_SCALE, COLOUR_LUT, J2K_BANDWIDTH, DCI_METADATA, @@ -181,16 +179,6 @@ public: return _with_subtitles; } - int subtitle_offset () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _subtitle_offset; - } - - float subtitle_scale () const { - boost::mutex::scoped_lock lm (_state_mutex); - return _subtitle_scale; - } - int colour_lut () const { boost::mutex::scoped_lock lm (_state_mutex); return _colour_lut; @@ -229,8 +217,6 @@ public: void set_container (Ratio const *); void set_scaler (Scaler const *); void set_with_subtitles (bool); - void set_subtitle_offset (int); - void set_subtitle_scale (float); void set_colour_lut (int); void set_j2k_bandwidth (int); void set_dci_metadata (DCIMetadata); @@ -278,12 +264,6 @@ private: Scaler const * _scaler; /** True if subtitles should be shown for this film */ bool _with_subtitles; - /** y offset for placing subtitles, in source pixels; +ve is further down - the frame, -ve is further up. - */ - int _subtitle_offset; - /** scale factor to apply to subtitles */ - float _subtitle_scale; /** index of colour LUT to use when converting RGB to XYZ. * 0: sRGB * 1: Rec 709 diff --git a/src/lib/image.cc b/src/lib/image.cc index ac30f4ff0..c11bcbb8d 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -336,7 +336,7 @@ Image::make_black () } void -Image::alpha_blend (shared_ptr<const Image> other, Position position) +Image::alpha_blend (shared_ptr<const Image> other, Position<int> position) { /* Only implemented for RGBA onto RGB24 so far */ assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA); @@ -372,7 +372,7 @@ Image::alpha_blend (shared_ptr<const Image> other, Position position) } void -Image::copy (shared_ptr<const Image> other, Position position) +Image::copy (shared_ptr<const Image> other, Position<int> position) { /* Only implemented for RGB24 onto RGB24 so far */ assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGB24); diff --git a/src/lib/image.h b/src/lib/image.h index 5407ce66e..d40ba77b4 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -32,6 +32,7 @@ extern "C" { #include <libavfilter/avfilter.h> } #include "util.h" +#include "position.h" class Scaler; class SimpleImage; @@ -75,8 +76,8 @@ public: boost::shared_ptr<Image> scale_and_convert_to_rgb (libdcp::Size, Scaler const *, bool) const; boost::shared_ptr<Image> scale (libdcp::Size, Scaler const *, bool aligned) const; boost::shared_ptr<Image> post_process (std::string, bool aligned) const; - void alpha_blend (boost::shared_ptr<const Image> image, Position pos); - void copy (boost::shared_ptr<const Image> image, Position pos); + void alpha_blend (boost::shared_ptr<const Image> image, Position<int> pos); + void copy (boost::shared_ptr<const Image> image, Position<int> pos); boost::shared_ptr<Image> crop (Crop c, bool aligned) const; void make_black (); diff --git a/src/lib/player.cc b/src/lib/player.cc index 6b7dc2722..58ba57bdc 100644 --- a/src/lib/player.cc +++ b/src/lib/player.cc @@ -26,11 +26,13 @@ #include "imagemagick_content.h" #include "sndfile_decoder.h" #include "sndfile_content.h" +#include "subtitle_content.h" #include "playlist.h" #include "job.h" #include "image.h" #include "ratio.h" #include "resampler.h" +#include "scaler.h" using std::list; using std::cout; @@ -43,7 +45,7 @@ using boost::shared_ptr; using boost::weak_ptr; using boost::dynamic_pointer_cast; -#define DEBUG_PLAYER 1 +//#define DEBUG_PLAYER 1 class Piece { @@ -215,48 +217,32 @@ Player::process_video (weak_ptr<Piece> weak_piece, shared_ptr<const Image> image return; } - image = image->crop (content->crop(), true); + shared_ptr<Image> work_image = image->crop (content->crop(), true); libdcp::Size const image_size = content->ratio()->size (_video_container_size); - image = image->scale_and_convert_to_rgb (image_size, _film->scaler(), true); + work_image = work_image->scale_and_convert_to_rgb (image_size, _film->scaler(), true); -#if 0 - if (film->with_subtitles ()) { - shared_ptr<Subtitle> sub; - if (_timed_subtitle && _timed_subtitle->displayed_at (t)) { - sub = _timed_subtitle->subtitle (); - } - - if (sub) { - dcpomatic::Rect const tx = subtitle_transformed_area ( - float (image_size.width) / content->video_size().width, - float (image_size.height) / content->video_size().height, - sub->area(), film->subtitle_offset(), film->subtitle_scale() - ); - - shared_ptr<Image> im = sub->image()->scale (tx.size(), film->scaler(), true); - image->alpha_blend (im, tx.position()); - } + Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->dcp_video_frame_rate()); + + if (_film->with_subtitles () && _out_subtitle.image && time >= _out_subtitle.from && time <= _out_subtitle.to) { + work_image->alpha_blend (_out_subtitle.image, _out_subtitle.position); } -#endif if (image_size != _video_container_size) { assert (image_size.width <= _video_container_size.width); assert (image_size.height <= _video_container_size.height); shared_ptr<Image> im (new SimpleImage (PIX_FMT_RGB24, _video_container_size, true)); im->make_black (); - im->copy (image, Position ((_video_container_size.width - image_size.width) / 2, (_video_container_size.height - image_size.height) / 2)); - image = im; + im->copy (work_image, Position<int> ((_video_container_size.width - image_size.width) / 2, (_video_container_size.height - image_size.height) / 2)); + work_image = im; } - Time time = content->start() + (frame * frc.factor() * TIME_HZ / _film->dcp_video_frame_rate()); - - Video (image, same, time); + Video (work_image, same, time); time += TIME_HZ / _film->dcp_video_frame_rate(); if (frc.repeat) { - Video (image, true, time); + Video (work_image, true, time); time += TIME_HZ / _film->dcp_video_frame_rate(); } @@ -390,6 +376,7 @@ Player::setup_pieces () fd->Video.connect (bind (&Player::process_video, this, piece, _1, _2, _3)); fd->Audio.connect (bind (&Player::process_audio, this, piece, _1, _2)); + fd->Subtitle.connect (bind (&Player::process_subtitle, this, piece, _1, _2, _3, _4)); piece->decoder = fd; } @@ -448,6 +435,10 @@ Player::content_changed (weak_ptr<Content> w, int p) _have_valid_pieces = false; Changed (); + + } else if (p == SubtitleContentProperty::SUBTITLE_OFFSET || p == SubtitleContentProperty::SUBTITLE_SCALE) { + update_subtitle (); + Changed (); } } @@ -505,12 +496,59 @@ Player::film_changed (Film::Property p) last time we were run. */ - if ( - p == Film::SCALER || p == Film::WITH_SUBTITLES || - p == Film::SUBTITLE_SCALE || p == Film::SUBTITLE_OFFSET || - p == Film::CONTAINER - ) { - + if (p == Film::SCALER || p == Film::WITH_SUBTITLES || p == Film::CONTAINER) { Changed (); } } + +void +Player::process_subtitle (weak_ptr<Piece> weak_piece, shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to) +{ + _in_subtitle.piece = weak_piece; + _in_subtitle.image = image; + _in_subtitle.rect = rect; + _in_subtitle.from = from; + _in_subtitle.to = to; + + update_subtitle (); +} + +void +Player::update_subtitle () +{ + shared_ptr<Piece> piece = _in_subtitle.piece.lock (); + if (!piece) { + return; + } + + shared_ptr<SubtitleContent> sc = dynamic_pointer_cast<SubtitleContent> (piece->content); + assert (sc); + + dcpomatic::Rect<double> in_rect = _in_subtitle.rect; + libdcp::Size scaled_size; + + in_rect.y += sc->subtitle_offset (); + + /* We will scale the subtitle up to fit _video_container_size, and also by the additional subtitle_scale */ + scaled_size.width = in_rect.width * _video_container_size.width * sc->subtitle_scale (); + scaled_size.height = in_rect.height * _video_container_size.height * sc->subtitle_scale (); + + /* Then we need a corrective translation, consisting of two parts: + * + * 1. that which is the result of the scaling of the subtitle by _video_container_size; this will be + * rect.x * _video_container_size.width and rect.y * _video_container_size.height. + * + * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be + * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and + * (height_before_subtitle_scale * (1 - subtitle_scale) / 2). + * + * Combining these two translations gives these expressions. + */ + + _out_subtitle.position.x = rint (_video_container_size.width * (in_rect.x + (in_rect.width * (1 - sc->subtitle_scale ()) / 2))); + _out_subtitle.position.y = rint (_video_container_size.height * (in_rect.y + (in_rect.height * (1 - sc->subtitle_scale ()) / 2))); + + _out_subtitle.image = _in_subtitle.image->scale (libdcp::Size (scaled_size.width, scaled_size.height), Scaler::from_id ("bicubic"), true); + _out_subtitle.from = _in_subtitle.from + piece->content->start (); + _out_subtitle.to = _in_subtitle.to + piece->content->start (); +} diff --git a/src/lib/player.h b/src/lib/player.h index 15fa4dbd6..5a4ee97be 100644 --- a/src/lib/player.h +++ b/src/lib/player.h @@ -27,6 +27,7 @@ #include "audio_buffers.h" #include "content.h" #include "film.h" +#include "rect.h" class Job; class Film; @@ -77,6 +78,7 @@ private: void process_video (boost::weak_ptr<Piece>, boost::shared_ptr<const Image>, bool, VideoContent::Frame); void process_audio (boost::weak_ptr<Piece>, boost::shared_ptr<const AudioBuffers>, AudioContent::Frame); + void process_subtitle (boost::weak_ptr<Piece>, boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time); void setup_pieces (); void playlist_changed (); void content_changed (boost::weak_ptr<Content>, int); @@ -86,6 +88,7 @@ private: void emit_silence (OutputAudioFrame); boost::shared_ptr<Resampler> resampler (boost::shared_ptr<AudioContent>); void film_changed (Film::Property); + void update_subtitle (); boost::shared_ptr<const Film> _film; boost::shared_ptr<const Playlist> _playlist; @@ -107,6 +110,21 @@ private: libdcp::Size _video_container_size; boost::shared_ptr<Image> _black_frame; std::map<boost::shared_ptr<AudioContent>, boost::shared_ptr<Resampler> > _resamplers; + + struct { + boost::weak_ptr<Piece> piece; + boost::shared_ptr<Image> image; + dcpomatic::Rect<double> rect; + Time from; + Time to; + } _in_subtitle; + + struct { + boost::shared_ptr<Image> image; + Position<int> position; + Time from; + Time to; + } _out_subtitle; }; #endif diff --git a/src/lib/position.h b/src/lib/position.h new file mode 100644 index 000000000..f904fe661 --- /dev/null +++ b/src/lib/position.h @@ -0,0 +1,46 @@ +/* + Copyright (C) 2013 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. + +*/ + +#ifndef DVDOMATIC_POSITION_H +#define DVDOMATIC_POSITION_H + +/** @struct Position + * @brief A position. + */ +template <class T> +class Position +{ +public: + Position () + : x (0) + , y (0) + {} + + Position (T x_, T y_) + : x (x_) + , y (y_) + {} + + /** x coordinate */ + T x; + /** y coordinate */ + T y; +}; + +#endif diff --git a/src/lib/rect.h b/src/lib/rect.h new file mode 100644 index 000000000..df1869841 --- /dev/null +++ b/src/lib/rect.h @@ -0,0 +1,79 @@ +/* + Copyright (C) 2013 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. + +*/ + +#ifndef DVDOMATIC_RECT_H +#define DVDOMATIC_RECT_H + +#include "position.h" + +/* Put this inside a namespace as Apple put a Rect in the global namespace */ + +namespace dcpomatic +{ + +/** @struct Rect + * @brief A rectangle. + */ +template <class T> +class Rect +{ +public: + + Rect () + : x (0) + , y (0) + , width (0) + , height (0) + {} + + Rect (T x_, T y_, T w_, T h_) + : x (x_) + , y (y_) + , width (w_) + , height (h_) + {} + + T x; + T y; + T width; + T height; + + Position<T> position () const { + return Position<T> (x, y); + } + + Rect<T> intersection (Rect<T> const & other) const { + T const tx = max (x, other.x); + T const ty = max (y, other.y); + + return Rect ( + tx, ty, + min (x + width, other.x + other.width) - tx, + min (y + height, other.y + other.height) - ty + ); + } + + bool contains (Position<T> p) const { + return (p.x >= x && p.x <= (x + width) && p.y >= y && p.y <= (y + height)); + } +}; + +} + +#endif diff --git a/src/lib/server.cc b/src/lib/server.cc index 5ca04c692..40d1c4c0c 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -36,7 +36,6 @@ #include "image.h" #include "dcp_video_frame.h" #include "config.h" -#include "subtitle.h" #include "i18n.h" diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc deleted file mode 100644 index 7013f1d7d..000000000 --- a/src/lib/subtitle.cc +++ /dev/null @@ -1,149 +0,0 @@ -/* - 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. - -*/ - -/** @file src/subtitle.cc - * @brief Representations of subtitles. - */ - -#include "subtitle.h" -#include "image.h" -#include "exceptions.h" - -#include "i18n.h" - -using boost::shared_ptr; -using libdcp::Size; - -/** Construct a TimedSubtitle. This is a subtitle image, position, - * and a range of time over which it should be shown. - * @param sub AVSubtitle to read. - */ -TimedSubtitle::TimedSubtitle (AVSubtitle const & sub) -{ - assert (sub.num_rects > 0); - - /* Subtitle PTS in seconds (within the source, not taking into account any of the - source that we may have chopped off for the DCP) - */ - double const packet_time = static_cast<double> (sub.pts) / AV_TIME_BASE; - - /* hence start time for this sub */ - _from = (packet_time + (double (sub.start_display_time) / 1e3)) * TIME_HZ; - _to = (packet_time + (double (sub.end_display_time) / 1e3)) * TIME_HZ; - - if (sub.num_rects > 1) { - throw DecodeError (_("multi-part subtitles not yet supported")); - } - - AVSubtitleRect const * rect = sub.rects[0]; - - if (rect->type != SUBTITLE_BITMAP) { - throw DecodeError (_("non-bitmap subtitles not yet supported")); - } - - shared_ptr<Image> image (new SimpleImage (PIX_FMT_RGBA, libdcp::Size (rect->w, rect->h), true)); - - /* 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]; - - for (int y = 0; y < rect->h; ++y) { - uint8_t* sub_line_p = sub_p; - uint32_t* out_line_p = out_p; - for (int x = 0; x < rect->w; ++x) { - *out_line_p++ = palette[*sub_line_p++]; - } - sub_p += rect->pict.linesize[0]; - out_p += image->stride()[0] / sizeof (uint32_t); - } - - _subtitle.reset (new Subtitle (Position (rect->x, rect->y), image)); -} - -/** @param t Time from the start of the source */ -bool -TimedSubtitle::displayed_at (Time t) const -{ - return t >= _from && t <= _to; -} - -/** Construct a subtitle, which is an image and a position. - * @param p Position within the (uncropped) source frame. - * @param i Image of the subtitle (should be RGBA). - */ -Subtitle::Subtitle (Position p, shared_ptr<Image> i) - : _position (p) - , _image (i) -{ - -} - -/** Given the area of a subtitle, work out the area it should - * take up when its video frame is scaled up, and it is optionally - * itself scaled and offset. - * @param target_x_scale the x scaling of the video frame that the subtitle is in. - * @param target_y_scale the y scaling of the video frame that the subtitle is in. - * @param sub_area The area of the subtitle within the original source. - * @param subtitle_offset y offset to apply to the subtitle position (+ve is down) - * in the coordinate space of the source. - * @param subtitle_scale scaling factor to apply to the subtitle image. - */ -dcpomatic::Rect -subtitle_transformed_area ( - float target_x_scale, float target_y_scale, - dcpomatic::Rect sub_area, int subtitle_offset, float subtitle_scale - ) -{ - dcpomatic::Rect tx; - - sub_area.y += subtitle_offset; - - /* We will scale the subtitle by the same amount as the video frame, and also by the additional - 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: - * - * 1. that which is the result of the scaling of the subtitle by target_x_scale and target_y_scale; this will be - * sub_area.x * target_x_scale and sub_area.y * target_y_scale. - * - * 2. that to shift the origin of the scale by subtitle_scale to the centre of the subtitle; this will be - * (width_before_subtitle_scale * (1 - subtitle_scale) / 2) and - * (height_before_subtitle_scale * (1 - subtitle_scale) / 2). - * - * Combining these two translations gives these expressions. - */ - - tx.x = rint (target_x_scale * (sub_area.x + (sub_area.width * (1 - subtitle_scale) / 2))); - tx.y = rint (target_y_scale * (sub_area.y + (sub_area.height * (1 - subtitle_scale) / 2))); - - return tx; -} - -/** @return area that this subtitle takes up, in the original uncropped source's coordinate space */ -dcpomatic::Rect -Subtitle::area () const -{ - return dcpomatic::Rect (_position.x, _position.y, _image->size().width, _image->size().height); -} diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h deleted file mode 100644 index 1020397cc..000000000 --- a/src/lib/subtitle.h +++ /dev/null @@ -1,81 +0,0 @@ -/* - 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. - -*/ - -/** @file src/subtitle.h - * @brief Representations of subtitles. - */ - -#include <list> -#include <boost/shared_ptr.hpp> -#include "types.h" - -struct AVSubtitle; -class Image; - -/** A subtitle, consisting of an image and a position */ -class Subtitle -{ -public: - Subtitle (Position p, boost::shared_ptr<Image> i); - - void set_position (Position p) { - _position = p; - } - - Position position () const { - return _position; - } - - boost::shared_ptr<Image> image () const { - return _image; - } - - dcpomatic::Rect area () const; - -private: - Position _position; - boost::shared_ptr<Image> _image; -}; - -dcpomatic::Rect -subtitle_transformed_area ( - float target_x_scale, float target_y_scale, - dcpomatic::Rect sub_area, int subtitle_offset, float subtitle_scale - ); - -/** A Subtitle class with details of the time over which it should be shown */ -class TimedSubtitle -{ -public: - TimedSubtitle (AVSubtitle const &); - - bool displayed_at (Time) const; - - boost::shared_ptr<Subtitle> subtitle () const { - return _subtitle; - } - -private: - /** the subtitle */ - boost::shared_ptr<Subtitle> _subtitle; - /** display from time from the start of the content */ - Time _from; - /** display to time from the start of the content */ - Time _to; -}; diff --git a/src/lib/subtitle_content.cc b/src/lib/subtitle_content.cc new file mode 100644 index 000000000..9fefbbfcd --- /dev/null +++ b/src/lib/subtitle_content.cc @@ -0,0 +1,72 @@ +/* + Copyright (C) 2013 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 <libcxml/cxml.h> +#include "subtitle_content.h" + +using std::string; +using boost::shared_ptr; +using boost::lexical_cast; + +int const SubtitleContentProperty::SUBTITLE_OFFSET = 500; +int const SubtitleContentProperty::SUBTITLE_SCALE = 501; + +SubtitleContent::SubtitleContent (shared_ptr<const Film> f, boost::filesystem::path p) + : Content (f, p) + , _subtitle_offset (0) + , _subtitle_scale (1) +{ + +} + +SubtitleContent::SubtitleContent (shared_ptr<const Film> f, shared_ptr<const cxml::Node> node) + : Content (f, node) + , _subtitle_offset (0) + , _subtitle_scale (1) +{ + _subtitle_offset = node->number_child<float> ("SubtitleOffset"); + _subtitle_scale = node->number_child<float> ("SubtitleScale"); +} + +void +SubtitleContent::as_xml (xmlpp::Node* root) const +{ + root->add_child("SubtitleOffset")->add_child_text (lexical_cast<string> (_subtitle_offset)); + root->add_child("SubtitleScale")->add_child_text (lexical_cast<string> (_subtitle_scale)); +} + +void +SubtitleContent::set_subtitle_offset (double o) +{ + { + boost::mutex::scoped_lock lm (_mutex); + _subtitle_offset = o; + } + signal_changed (SubtitleContentProperty::SUBTITLE_OFFSET); +} + +void +SubtitleContent::set_subtitle_scale (double s) +{ + { + boost::mutex::scoped_lock lm (_mutex); + _subtitle_scale = s; + } + signal_changed (SubtitleContentProperty::SUBTITLE_SCALE); +} diff --git a/src/lib/subtitle_content.h b/src/lib/subtitle_content.h new file mode 100644 index 000000000..1092b7b1c --- /dev/null +++ b/src/lib/subtitle_content.h @@ -0,0 +1,62 @@ +/* + Copyright (C) 2013 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. + +*/ + +#ifndef DCPOMATIC_SUBTITLE_CONTENT_H +#define DCPOMATIC_SUBTITLE_CONTENT_H + +#include "content.h" + +class SubtitleContentProperty +{ +public: + static int const SUBTITLE_OFFSET; + static int const SUBTITLE_SCALE; +}; + +class SubtitleContent : public virtual Content +{ +public: + SubtitleContent (boost::shared_ptr<const Film>, boost::filesystem::path); + SubtitleContent (boost::shared_ptr<const Film>, boost::shared_ptr<const cxml::Node>); + + void as_xml (xmlpp::Node *) const; + + void set_subtitle_offset (double); + void set_subtitle_scale (double); + + double subtitle_offset () const { + boost::mutex::scoped_lock lm (_mutex); + return _subtitle_offset; + } + + double subtitle_scale () const { + boost::mutex::scoped_lock lm (_mutex); + return _subtitle_scale; + } + +private: + /** y offset for placing subtitles, as a proportion of the container height; + +ve is further down the frame, -ve is further up. + */ + double _subtitle_offset; + /** scale factor to apply to subtitles */ + double _subtitle_scale; +}; + +#endif diff --git a/src/lib/subtitle_decoder.cc b/src/lib/subtitle_decoder.cc new file mode 100644 index 000000000..c06f3d718 --- /dev/null +++ b/src/lib/subtitle_decoder.cc @@ -0,0 +1,39 @@ +/* + Copyright (C) 2013 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 <boost/shared_ptr.hpp> +#include "subtitle_decoder.h" + +using boost::shared_ptr; + +SubtitleDecoder::SubtitleDecoder (shared_ptr<const Film> f) + : Decoder (f) +{ + +} + + +/** Called by subclasses when a subtitle is ready. + * Image may be 0 to say that there is no current subtitle. + */ +void +SubtitleDecoder::subtitle (shared_ptr<Image> image, dcpomatic::Rect<double> rect, Time from, Time to) +{ + Subtitle (image, rect, from, to); +} diff --git a/src/lib/subtitle_decoder.h b/src/lib/subtitle_decoder.h new file mode 100644 index 000000000..628f4d60d --- /dev/null +++ b/src/lib/subtitle_decoder.h @@ -0,0 +1,37 @@ +/* + Copyright (C) 2013 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 <boost/signals2.hpp> +#include "decoder.h" +#include "rect.h" +#include "types.h" + +class Film; +class TimedSubtitle; + +class SubtitleDecoder : public virtual Decoder +{ +public: + SubtitleDecoder (boost::shared_ptr<const Film>); + + boost::signals2::signal<void (boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time)> Subtitle; + +protected: + void subtitle (boost::shared_ptr<Image>, dcpomatic::Rect<double>, Time, Time); +}; diff --git a/src/lib/types.cc b/src/lib/types.cc index 78cb4cd64..035c8363d 100644 --- a/src/lib/types.cc +++ b/src/lib/types.cc @@ -32,25 +32,3 @@ bool operator!= (Crop const & a, Crop const & b) return !(a == b); } - -/** @param other A Rect. - * @return The intersection of this with `other'. - */ -dcpomatic::Rect -dcpomatic::Rect::intersection (Rect const & other) const -{ - int const tx = max (x, other.x); - int const ty = max (y, other.y); - - return Rect ( - tx, ty, - min (x + width, other.x + other.width) - tx, - min (y + height, other.y + other.height) - ty - ); -} - -bool -dcpomatic::Rect::contains (Position p) const -{ - return (p.x >= x && p.x <= (x + width) && p.y >= y && p.y <= (y + height)); -} diff --git a/src/lib/types.h b/src/lib/types.h index 33f8239d8..67384103d 100644 --- a/src/lib/types.h +++ b/src/lib/types.h @@ -53,66 +53,4 @@ struct Crop extern bool operator== (Crop const & a, Crop const & b); extern bool operator!= (Crop const & a, Crop const & b); -/** @struct Position - * @brief A position. - */ -struct Position -{ - Position () - : x (0) - , y (0) - {} - - Position (int x_, int y_) - : x (x_) - , y (y_) - {} - - /** x coordinate */ - int x; - /** y coordinate */ - int y; -}; - -namespace dcpomatic { - -/** @struct Rect - * @brief A rectangle. - */ -struct Rect -{ - Rect () - : x (0) - , y (0) - , width (0) - , height (0) - {} - - Rect (int x_, int y_, int w_, int h_) - : x (x_) - , y (y_) - , width (w_) - , height (h_) - {} - - int x; - int y; - int width; - int height; - - Position position () const { - return Position (x, y); - } - - libdcp::Size size () const { - return libdcp::Size (width, height); - } - - Rect intersection (Rect const & other) const; - - bool contains (Position) const; -}; - -} - #endif diff --git a/src/lib/util.cc b/src/lib/util.cc index 2e4abe64d..53c457898 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -91,8 +91,8 @@ using boost::lexical_cast; using boost::optional; using libdcp::Size; -boost::thread::id ui_thread; -boost::filesystem::path backtrace_file; +static boost::thread::id ui_thread; +static boost::filesystem::path backtrace_file; /** Convert some number of seconds to a string representation * in hours, minutes and seconds. diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index f61e63d4d..457cfe47b 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -18,10 +18,7 @@ */ #include "video_decoder.h" -#include "subtitle.h" -#include "film.h" #include "image.h" -#include "ratio.h" #include "i18n.h" @@ -42,21 +39,3 @@ VideoDecoder::video (shared_ptr<const Image> image, bool same, VideoContent::Fra _video_position = frame + 1; } -#if 0 - -/** Called by subclasses when a subtitle is ready. - * s may be 0 to say that there is no current subtitle. - * @param s New current subtitle, or 0. - */ -void -VideoDecoder::subtitle (shared_ptr<TimedSubtitle> s) -{ - _timed_subtitle = s; - - if (_timed_subtitle) { - Position const p = _timed_subtitle->subtitle()->position (); - _timed_subtitle->subtitle()->set_position (Position (p.x - _video_content->crop().left, p.y - _video_content->crop().top)); - } -} -#endif - diff --git a/src/lib/wscript b/src/lib/wscript index 7660afb45..5c381b69c 100644 --- a/src/lib/wscript +++ b/src/lib/wscript @@ -44,7 +44,8 @@ sources = """ sndfile_content.cc sndfile_decoder.cc sound_processor.cc - subtitle.cc + subtitle_content.cc + subtitle_decoder.cc timer.cc transcode_job.cc transcoder.cc diff --git a/src/tools/dcpomatic.cc b/src/tools/dcpomatic.cc index ac39d4fed..e58b2c182 100644 --- a/src/tools/dcpomatic.cc +++ b/src/tools/dcpomatic.cc @@ -164,7 +164,13 @@ setup_menu (wxMenuBar* m) #ifndef __WXOSX__ file->AppendSeparator (); #endif + +#ifdef __WXOSX__ add_item (file, _("&Exit"), wxID_EXIT, ALWAYS); +#else + add_item (file, _("&Quit"), wxID_EXIT, ALWAYS); +#endif + #ifdef __WXOSX__ add_item (file, _("&Preferences..."), wxID_PREFERENCES, ALWAYS); @@ -286,7 +292,7 @@ private: if (r == wxID_OK) { - if (boost::filesystem::exists (d->get_path()) && !boost::filesystem::is_empty(d->get_path())) { + if (boost::filesystem::is_directory (d->get_path()) && !boost::filesystem::is_empty(d->get_path())) { if (!confirm_dialog ( this, std_to_wx ( @@ -297,6 +303,12 @@ private: )) { return; } + } else if (boost::filesystem::is_regular_file (d->get_path())) { + error_dialog ( + this, + String::compose (wx_to_std (_("%1 already exists as a file, so you cannot use it for a new film.")), d->get_path().c_str()) + ); + return; } maybe_save_then_delete_film (); diff --git a/src/wx/about_dialog.cc b/src/wx/about_dialog.cc index ca19326a1..4e25f163f 100644 --- a/src/wx/about_dialog.cc +++ b/src/wx/about_dialog.cc @@ -32,15 +32,18 @@ AboutDialog::AboutDialog (wxWindow* parent) wxBoxSizer* sizer = new wxBoxSizer (wxVERTICAL); wxFont title_font (*wxNORMAL_FONT); - title_font.SetPointSize (title_font.GetPointSize() + 4); + title_font.SetPointSize (title_font.GetPointSize() + 12); title_font.SetWeight (wxFONTWEIGHT_BOLD); + wxFont subtitle_font (*wxNORMAL_FONT); + subtitle_font.SetPointSize (subtitle_font.GetPointSize() + 2); + wxFont version_font (*wxNORMAL_FONT); version_font.SetWeight (wxFONTWEIGHT_BOLD); wxStaticText* t = new wxStaticText (this, wxID_ANY, _("DCP-o-matic")); t->SetFont (title_font); - sizer->Add (t, wxSizerFlags().Centre().Border()); + sizer->Add (t, wxSizerFlags().Centre().Border(wxALL, 16)); wxString s; if (strcmp (dcpomatic_git_commit, "release") == 0) { @@ -49,7 +52,7 @@ AboutDialog::AboutDialog (wxWindow* parent) t = new wxStaticText (this, wxID_ANY, std_to_wx (String::compose ("Version %1 git %2", dcpomatic_version, dcpomatic_git_commit))); } t->SetFont (version_font); - sizer->Add (t, wxSizerFlags().Centre().Border()); + sizer->Add (t, wxSizerFlags().Centre().Border(wxALL, 2)); sizer->AddSpacer (12); t = new wxStaticText ( @@ -57,8 +60,9 @@ AboutDialog::AboutDialog (wxWindow* parent) _("Free, open-source DCP generation from almost anything."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER ); + t->SetFont (subtitle_font); - sizer->Add (t, wxSizerFlags().Centre().Border()); + sizer->Add (t, wxSizerFlags().Centre().Border(wxALL, 8)); wxHyperlinkCtrl* h = new wxHyperlinkCtrl ( this, wxID_ANY, @@ -66,7 +70,7 @@ AboutDialog::AboutDialog (wxWindow* parent) wxT ("http://dcpomatic.com") ); - sizer->Add (h, wxSizerFlags().Centre().Border()); + sizer->Add (h, wxSizerFlags().Centre().Border(wxALL, 8)); t = new wxStaticText ( this, wxID_ANY, @@ -74,7 +78,7 @@ AboutDialog::AboutDialog (wxWindow* parent) wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER ); - sizer->Add (t, wxSizerFlags().Centre().Border()); + sizer->Add (t, wxSizerFlags().Centre().Border(wxLEFT | wxRIGHT, 16)); _notebook = new wxNotebook (this, wxID_ANY); @@ -115,7 +119,7 @@ AboutDialog::AboutDialog (wxWindow* parent) supported_by.Add (wxT ("Kieran Carroll")); add_section (_("Supported by"), supported_by); - sizer->Add (_notebook, wxSizerFlags().Centre().Border().Expand()); + sizer->Add (_notebook, wxSizerFlags().Centre().Border(wxALL, 16).Expand()); SetSizerAndFit (sizer); } diff --git a/src/wx/film_editor.cc b/src/wx/film_editor.cc index 315951168..92b4d0691 100644 --- a/src/wx/film_editor.cc +++ b/src/wx/film_editor.cc @@ -426,7 +426,7 @@ FilmEditor::make_subtitle_panel () wxBoxSizer* s = new wxBoxSizer (wxHORIZONTAL); _subtitle_offset = new wxSpinCtrl (_subtitle_panel); s->Add (_subtitle_offset); - add_label_to_sizer (s, _subtitle_panel, _("pixels"), false); + add_label_to_sizer (s, _subtitle_panel, _("%"), false); grid->Add (s); } @@ -443,7 +443,7 @@ FilmEditor::make_subtitle_panel () _subtitle_stream = new wxChoice (_subtitle_panel, wxID_ANY); grid->Add (_subtitle_stream, 1, wxEXPAND); - _subtitle_offset->SetRange (-1024, 1024); + _subtitle_offset->SetRange (-100, 100); _subtitle_scale->SetRange (1, 1000); } @@ -527,21 +527,23 @@ FilmEditor::name_changed (wxCommandEvent &) void FilmEditor::subtitle_offset_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr<SubtitleContent> c = selected_subtitle_content (); + if (!c) { return; } - _film->set_subtitle_offset (_subtitle_offset->GetValue ()); + c->set_subtitle_offset (_subtitle_offset->GetValue() / 100.0); } void FilmEditor::subtitle_scale_changed (wxCommandEvent &) { - if (!_film) { + shared_ptr<SubtitleContent> c = selected_subtitle_content (); + if (!c) { return; } - _film->set_subtitle_scale (_subtitle_scale->GetValue() / 100.0); + c->set_subtitle_scale (_subtitle_scale->GetValue() / 100.0); } void @@ -626,12 +628,6 @@ FilmEditor::film_changed (Film::Property p) setup_subtitle_control_sensitivity (); setup_dcp_name (); break; - case Film::SUBTITLE_OFFSET: - checked_set (_subtitle_offset, _film->subtitle_offset ()); - break; - case Film::SUBTITLE_SCALE: - checked_set (_subtitle_scale, _film->subtitle_scale() * 100); - break; case Film::COLOUR_LUT: checked_set (_colour_lut, _film->colour_lut ()); break; @@ -679,10 +675,12 @@ FilmEditor::film_content_changed (weak_ptr<Content> weak_content, int property) shared_ptr<Content> content = weak_content.lock (); shared_ptr<VideoContent> video_content; shared_ptr<AudioContent> audio_content; + shared_ptr<SubtitleContent> subtitle_content; shared_ptr<FFmpegContent> ffmpeg_content; if (content) { video_content = dynamic_pointer_cast<VideoContent> (content); audio_content = dynamic_pointer_cast<AudioContent> (content); + subtitle_content = dynamic_pointer_cast<SubtitleContent> (content); ffmpeg_content = dynamic_pointer_cast<FFmpegContent> (content); } @@ -776,6 +774,10 @@ FilmEditor::film_content_changed (weak_ptr<Content> weak_content, int property) } _dcp_sizer->Layout (); } + } else if (property == SubtitleContentProperty::SUBTITLE_OFFSET) { + checked_set (_subtitle_offset, subtitle_content ? (subtitle_content->subtitle_offset() * 100) : 0); + } else if (property == SubtitleContentProperty::SUBTITLE_SCALE) { + checked_set (_subtitle_scale, subtitle_content ? (subtitle_content->subtitle_scale() * 100) : 100); } } @@ -861,8 +863,6 @@ FilmEditor::set_film (shared_ptr<Film> f) film_changed (Film::CONTAINER); film_changed (Film::SCALER); film_changed (Film::WITH_SUBTITLES); - film_changed (Film::SUBTITLE_OFFSET); - film_changed (Film::SUBTITLE_SCALE); film_changed (Film::COLOUR_LUT); film_changed (Film::J2K_BANDWIDTH); film_changed (Film::DCI_METADATA); @@ -892,7 +892,6 @@ FilmEditor::set_things_sensitive (bool s) _filters_button->Enable (s); _scaler->Enable (s); _dcp_content_type->Enable (s); - _best_dcp_frame_rate->Enable (s); _dcp_frame_rate->Enable (s); _colour_lut->Enable (s); _j2k_bandwidth->Enable (s); @@ -907,6 +906,7 @@ FilmEditor::set_things_sensitive (bool s) setup_subtitle_control_sensitivity (); setup_show_audio_sensitivity (); setup_content_sensitivity (); + _best_dcp_frame_rate->Enable (s && _film && _film->best_dcp_video_frame_rate () != _film->dcp_video_frame_rate ()); } /** Called when the `Edit filters' button has been clicked */ @@ -1214,6 +1214,9 @@ FilmEditor::content_selection_changed (wxListEvent &) film_content_changed (s, FFmpegContentProperty::AUDIO_STREAM); film_content_changed (s, FFmpegContentProperty::AUDIO_STREAMS); film_content_changed (s, FFmpegContentProperty::SUBTITLE_STREAM); + film_content_changed (s, FFmpegContentProperty::SUBTITLE_STREAMS); + film_content_changed (s, SubtitleContentProperty::SUBTITLE_OFFSET); + film_content_changed (s, SubtitleContentProperty::SUBTITLE_SCALE); } void @@ -1270,6 +1273,17 @@ FilmEditor::selected_audio_content () return dynamic_pointer_cast<AudioContent> (c); } +shared_ptr<SubtitleContent> +FilmEditor::selected_subtitle_content () +{ + shared_ptr<Content> c = selected_content (); + if (!c) { + return shared_ptr<SubtitleContent> (); + } + + return dynamic_pointer_cast<SubtitleContent> (c); +} + void FilmEditor::setup_scaling_description () { diff --git a/src/wx/film_editor.h b/src/wx/film_editor.h index 4b096a2e1..fdc6e077d 100644 --- a/src/wx/film_editor.h +++ b/src/wx/film_editor.h @@ -118,6 +118,7 @@ private: boost::shared_ptr<Content> selected_content (); boost::shared_ptr<VideoContent> selected_video_content (); boost::shared_ptr<AudioContent> selected_audio_content (); + boost::shared_ptr<SubtitleContent> selected_subtitle_content (); wxNotebook* _main_notebook; wxNotebook* _content_notebook; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 185c3c53f..d7ec26f7d 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -132,6 +132,7 @@ FilmViewer::set_film (shared_ptr<Film> f) _player->Changed.connect (boost::bind (&FilmViewer::player_changed, this)); calculate_sizes (); + fetch_current_frame_again (); } void @@ -301,7 +302,7 @@ FilmViewer::fetch_next_frame () try { _got_frame = false; - while (!_got_frame && !_player->pass ()); + while (!_got_frame && !_player->pass ()) {} } catch (DecodeError& e) { _play_button->SetValue (false); check_play_state (); diff --git a/src/wx/timeline.cc b/src/wx/timeline.cc index bd2d314a4..f9223f19d 100644 --- a/src/wx/timeline.cc +++ b/src/wx/timeline.cc @@ -55,7 +55,7 @@ public: _timeline.force_redraw (bbox ()); } - virtual dcpomatic::Rect bbox () const = 0; + virtual dcpomatic::Rect<int> bbox () const = 0; protected: virtual void do_paint (wxGraphicsContext *) = 0; @@ -68,7 +68,7 @@ protected: Timeline& _timeline; private: - dcpomatic::Rect _last_paint_bbox; + dcpomatic::Rect<int> _last_paint_bbox; }; class ContentView : public View @@ -83,15 +83,15 @@ public: _content_connection = c->Changed.connect (bind (&ContentView::content_changed, this, _2)); } - dcpomatic::Rect bbox () const + dcpomatic::Rect<int> bbox () const { shared_ptr<const Film> film = _timeline.film (); shared_ptr<const Content> content = _content.lock (); if (!film || !content) { - return dcpomatic::Rect (); + return dcpomatic::Rect<int> (); } - return dcpomatic::Rect ( + return dcpomatic::Rect<int> ( time_x (content->start ()) - 8, y_pos (_track) - 8, content->length () * _timeline.pixels_per_time_unit() + 16, @@ -243,9 +243,9 @@ public: , _y (y) {} - dcpomatic::Rect bbox () const + dcpomatic::Rect<int> bbox () const { - return dcpomatic::Rect (0, _y - 4, _timeline.width(), 24); + return dcpomatic::Rect<int> (0, _y - 4, _timeline.width(), 24); } void set_y (int y) @@ -476,7 +476,7 @@ void Timeline::left_down (wxMouseEvent& ev) { list<shared_ptr<View> >::iterator i = _views.begin(); - Position const p (ev.GetX(), ev.GetY()); + Position<int> const p (ev.GetX(), ev.GetY()); while (i != _views.end() && !(*i)->bbox().contains (p)) { ++i; } @@ -545,7 +545,7 @@ Timeline::mouse_moved (wxMouseEvent& ev) } void -Timeline::force_redraw (dcpomatic::Rect const & r) +Timeline::force_redraw (dcpomatic::Rect<int> const & r) { RefreshRect (wxRect (r.x, r.y, r.width, r.height), false); } diff --git a/src/wx/timeline.h b/src/wx/timeline.h index 5c25a6426..3e984bfe1 100644 --- a/src/wx/timeline.h +++ b/src/wx/timeline.h @@ -22,6 +22,7 @@ #include <boost/signals2.hpp> #include <wx/wx.h> #include "util.h" +#include "rect.h" class Film; class View; @@ -36,7 +37,7 @@ public: boost::shared_ptr<const Film> film () const; - void force_redraw (dcpomatic::Rect const &); + void force_redraw (dcpomatic::Rect<int> const &); int x_offset () const { return 8; @@ -54,8 +55,8 @@ public: return _pixels_per_time_unit; } - Position tracks_position () const { - return Position (8, 8); + Position<int> tracks_position () const { + return Position<int> (8, 8); } int tracks () const; diff --git a/test/client_server_test.cc b/test/client_server_test.cc index 51b52331a..232190286 100644 --- a/test/client_server_test.cc +++ b/test/client_server_test.cc @@ -56,7 +56,7 @@ BOOST_AUTO_TEST_CASE (client_server_test) p += sub_image->stride()[0]; } - shared_ptr<Subtitle> subtitle (new Subtitle (Position (50, 60), sub_image)); +// shared_ptr<Subtitle> subtitle (new Subtitle (Position<int> (50, 60), sub_image)); shared_ptr<FileLog> log (new FileLog ("build/test/client_server_test.log")); diff --git a/test/test.cc b/test/test.cc index d6c7842d7..0a682383a 100644 --- a/test/test.cc +++ b/test/test.cc @@ -36,7 +36,6 @@ #include "server.h" #include "cross.h" #include "job.h" -#include "subtitle.h" #include "scaler.h" #include "ffmpeg_decoder.h" #include "sndfile_decoder.h" diff --git a/test/trimmer_test.cc b/test/trimmer_test.cc deleted file mode 100644 index ad2f2f6f5..000000000 --- a/test/trimmer_test.cc +++ /dev/null @@ -1,110 +0,0 @@ -/* - 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. - -*/ - -using boost::shared_ptr; - -shared_ptr<const Image> trimmer_test_last_video; -int trimmer_test_video_frames = 0; -shared_ptr<const AudioBuffers> trimmer_test_last_audio; - -void -trimmer_test_video_helper (shared_ptr<const Image> image, bool, shared_ptr<Subtitle>) -{ - trimmer_test_last_video = image; - ++trimmer_test_video_frames; -} - -void -trimmer_test_audio_helper (shared_ptr<const AudioBuffers> audio) -{ - trimmer_test_last_audio = audio; -} - -BOOST_AUTO_TEST_CASE (trimmer_passthrough_test) -{ - Trimmer trimmer (shared_ptr<Log> (), 0, 0, 200, 48000, 25, 25); - trimmer.Video.connect (bind (&trimmer_test_video_helper, _1, _2, _3)); - trimmer.Audio.connect (bind (&trimmer_test_audio_helper, _1)); - - shared_ptr<SimpleImage> video (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (1998, 1080), true)); - shared_ptr<AudioBuffers> audio (new AudioBuffers (6, 42 * 1920)); - - trimmer.process_video (video, false, shared_ptr<Subtitle> ()); - trimmer.process_audio (audio); - - BOOST_CHECK_EQUAL (video.get(), trimmer_test_last_video.get()); - BOOST_CHECK_EQUAL (audio.get(), trimmer_test_last_audio.get()); - BOOST_CHECK_EQUAL (audio->frames(), trimmer_test_last_audio->frames()); -} - - -/** Test the audio handling of the Trimmer */ -BOOST_AUTO_TEST_CASE (trimmer_audio_test) -{ - Trimmer trimmer (shared_ptr<Log> (), 25, 75, 200, 48000, 25, 25); - - trimmer.Audio.connect (bind (&trimmer_test_audio_helper, _1)); - - /* 21 video frames-worth of audio frames; should be completely stripped */ - trimmer_test_last_audio.reset (); - shared_ptr<AudioBuffers> audio (new AudioBuffers (6, 21 * 1920)); - trimmer.process_audio (audio); - BOOST_CHECK (trimmer_test_last_audio == 0); - - /* 42 more video frames-worth, 4 should be stripped from the start */ - audio.reset (new AudioBuffers (6, 42 * 1920)); - trimmer.process_audio (audio); - BOOST_CHECK (trimmer_test_last_audio); - BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 38 * 1920); - - /* 42 more video frames-worth, should be kept as-is */ - trimmer_test_last_audio.reset (); - audio.reset (new AudioBuffers (6, 42 * 1920)); - trimmer.process_audio (audio); - BOOST_CHECK (trimmer_test_last_audio); - BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 42 * 1920); - - /* 25 more video frames-worth, 5 should be trimmed from the end */ - trimmer_test_last_audio.reset (); - audio.reset (new AudioBuffers (6, 25 * 1920)); - trimmer.process_audio (audio); - BOOST_CHECK (trimmer_test_last_audio); - BOOST_CHECK_EQUAL (trimmer_test_last_audio->frames(), 20 * 1920); - - /* Now some more; all should be trimmed */ - trimmer_test_last_audio.reset (); - audio.reset (new AudioBuffers (6, 100 * 1920)); - trimmer.process_audio (audio); - BOOST_CHECK (trimmer_test_last_audio == 0); -} - -BOOST_AUTO_TEST_CASE (trim_end_test) -{ - Trimmer trimmer (shared_ptr<Log> (), 0, 75, 200, 48000, 25, 25); - - shared_ptr<SimpleImage> image (new SimpleImage (PIX_FMT_RGB24, libdcp::Size (256, 256), true)); - - trimmer.Video.connect (bind (&trimmer_test_video_helper, _1, _2, _3)); - trimmer_test_video_frames = 0; - for (int i = 0; i < 200; ++i) { - trimmer.process_video (image, false, shared_ptr<Subtitle> ()); - } - - BOOST_CHECK_EQUAL (trimmer_test_video_frames, 125); -} @@ -60,6 +60,7 @@ def configure(conf): if conf.env.TARGET_OSX: conf.env.append_value('CXXFLAGS', '-DDCPOMATIC_OSX') + conf.env.append_value('LINKFLAGS', '-headerpad_max_install_names') if conf.options.enable_debug: conf.env.append_value('CXXFLAGS', ['-g', '-DDCPOMATIC_DEBUG']) |
