From cc4a67b7eb8ecaed076e261960848f70e3e741af Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 10 Oct 2012 15:47:06 +0100 Subject: Subs successfully exported with thumbs. --- src/lib/imagemagick_decoder.cc | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'src/lib/imagemagick_decoder.cc') diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index 7cee01ec5..df20479c9 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -1,3 +1,22 @@ +/* + Copyright (C) 2012 Carl Hetherington + + 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 #include #include "imagemagick_decoder.h" @@ -5,6 +24,7 @@ #include "image.h" using namespace std; +using namespace boost; ImageMagickDecoder::ImageMagickDecoder ( boost::shared_ptr s, boost::shared_ptr o, Job* j, Log* l, bool minimal, bool ignore_length) @@ -41,7 +61,7 @@ ImageMagickDecoder::do_pass () } - process_video (image.frame ()); + process_video (image.frame (), shared_ptr ()); _done = true; return false; -- cgit v1.2.3 From a066feba1b455a72fe10b7baa79f17f69cd24ba9 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 16:50:08 +0100 Subject: Various fixes to subtitling. --- run/dvdomatic | 6 ++++-- src/lib/dcp_video_frame.cc | 30 +++++++++++++++++++++++++----- src/lib/dcp_video_frame.h | 4 +++- src/lib/decoder.cc | 24 +++++++++++++++++------- src/lib/decoder.h | 5 ++++- src/lib/ffmpeg_decoder.cc | 18 ++++-------------- src/lib/ffmpeg_decoder.h | 1 - src/lib/film.cc | 1 + src/lib/image.cc | 42 ++++++++++++++++++++++++++++++++++++++++-- src/lib/image.h | 1 + src/lib/imagemagick_decoder.cc | 2 +- src/lib/j2k_still_encoder.cc | 2 +- src/lib/j2k_wav_encoder.cc | 3 ++- src/lib/options.h | 2 ++ src/lib/server.cc | 7 ++++++- src/lib/subtitle.cc | 25 +++++++++++++++---------- src/lib/subtitle.h | 5 +++-- src/lib/tiff_decoder.cc | 2 +- src/wx/film_viewer.cc | 3 ++- test/test.cc | 2 ++ 20 files changed, 134 insertions(+), 51 deletions(-) (limited to 'src/lib/imagemagick_decoder.cc') diff --git a/run/dvdomatic b/run/dvdomatic index eeb2c7b44..125fd9d27 100755 --- a/run/dvdomatic +++ b/run/dvdomatic @@ -2,9 +2,11 @@ export LD_LIBRARY_PATH=build/src/lib:build/src/wx:build/src/asdcplib/src:$LD_LIBRARY_PATH if [ "$1" == "--debug" ]; then - gdb --args build/src/tools/dvdomatic $2 + shift + gdb --args build/src/tools/dvdomatic "$1" elif [ "$1" == "--valgrind" ]; then - valgrind --tool="memcheck" build/src/tools/dvdomatic $2 + shift + valgrind --tool="memcheck" build/src/tools/dvdomatic "$1" else build/src/tools/dvdomatic "$1" fi diff --git a/src/lib/dcp_video_frame.cc b/src/lib/dcp_video_frame.cc index 13d3efcbf..ce660add5 100644 --- a/src/lib/dcp_video_frame.cc +++ b/src/lib/dcp_video_frame.cc @@ -55,6 +55,7 @@ #include "scaler.h" #include "image.h" #include "log.h" +#include "subtitle.h" using namespace std; using namespace boost; @@ -72,11 +73,16 @@ using namespace boost; * @param l Log to write to. */ DCPVideoFrame::DCPVideoFrame ( - shared_ptr yuv, shared_ptr sub, Size out, int p, Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l) + shared_ptr yuv, shared_ptr sub, + Size out, int p, int subtitle_offset, float subtitle_scale, + Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l + ) : _input (yuv) , _subtitle (sub) , _out_size (out) , _padding (p) + , _subtitle_offset (subtitle_offset) + , _subtitle_scale (subtitle_scale) , _scaler (s) , _frame (f) /* we round here; not sure if this is right */ @@ -148,13 +154,25 @@ DCPVideoFrame::~DCPVideoFrame () shared_ptr DCPVideoFrame::encode_locally () { - shared_ptr prepared = _input; - if (!_post_process.empty ()) { - prepared = prepared->post_process (_post_process); + _input = _input->post_process (_post_process); } - prepared = prepared->scale_and_convert_to_rgb (_out_size, _padding, _scaler); + shared_ptr prepared = _input->scale_and_convert_to_rgb (_out_size, _padding, _scaler); + + if (_subtitle) { + list > subs = _subtitle->images (); + for (list >::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 im = (*i)->image()->scale (Size (tx.w, tx.h), _scaler); + prepared->alpha_blend (im, Position (tx.x, tx.y)); + } + } create_openjpeg_container (); @@ -290,6 +308,8 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv) << _input->pixel_format() << " " << _out_size.width << " " << _out_size.height << " " << _padding << " " + << _subtitle_offset << " " + << _subtitle_scale << " " << _scaler->id () << " " << _frame << " " << _frames_per_second << " " diff --git a/src/lib/dcp_video_frame.h b/src/lib/dcp_video_frame.h index fe2e27966..4e9a777bd 100644 --- a/src/lib/dcp_video_frame.h +++ b/src/lib/dcp_video_frame.h @@ -106,7 +106,7 @@ public: class DCPVideoFrame { public: - DCPVideoFrame (boost::shared_ptr, boost::shared_ptr, Size, int, Scaler const *, int, float, std::string, int, int, Log *); + DCPVideoFrame (boost::shared_ptr, boost::shared_ptr, Size, int, int, float, Scaler const *, int, float, std::string, int, int, Log *); virtual ~DCPVideoFrame (); boost::shared_ptr encode_locally (); @@ -124,6 +124,8 @@ private: boost::shared_ptr _subtitle; ///< any subtitle that should be on the image Size _out_size; ///< the required size of the output, in pixels int _padding; + int _subtitle_offset; + float _subtitle_scale; Scaler const * _scaler; ///< scaler to use int _frame; ///< frame index within the Film int _frames_per_second; ///< Frames per second that we will use for the DCP (rounded) diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index c4759a872..1f771da2d 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -229,7 +229,7 @@ Decoder::process_audio (uint8_t* data, int size) * @param frame to decode; caller manages memory. */ void -Decoder::process_video (AVFrame* frame, shared_ptr sub) +Decoder::process_video (AVFrame* frame) { if (_minimal) { ++_video_frame; @@ -304,12 +304,9 @@ Decoder::process_video (AVFrame* frame, shared_ptr sub) image->make_black (); } - if (sub && _opt->apply_crop) { - list > im = sub->images (); - for (list >::iterator i = im.begin(); i != im.end(); ++i) { - Position p = (*i)->position (); - (*i)->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top)); - } + shared_ptr sub; + if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) { + sub = _subtitle; } TIMING ("Decoder emits %1", _video_frame); @@ -414,3 +411,16 @@ Decoder::setup_video_filters () /* XXX: leaking `inputs' / `outputs' ? */ } +void +Decoder::process_subtitle (shared_ptr s) +{ + _subtitle = s; + + if (_opt->apply_crop) { + list > im = _subtitle->images (); + for (list >::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)); + } + } +} diff --git a/src/lib/decoder.h b/src/lib/decoder.h index 5cb44b8d9..805955b9d 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -101,8 +101,9 @@ protected: virtual int sample_aspect_ratio_numerator () const = 0; virtual int sample_aspect_ratio_denominator () const = 0; - void process_video (AVFrame *, boost::shared_ptr); + void process_video (AVFrame *); void process_audio (uint8_t *, int); + void process_subtitle (boost::shared_ptr); /** our FilmState */ boost::shared_ptr _fs; @@ -138,6 +139,8 @@ private: (at the DCP sample rate). */ int64_t _audio_frames_processed; + + boost::shared_ptr _subtitle; }; #endif diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index c2ee9297b..e01405191 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -205,12 +205,7 @@ FFmpegDecoder::do_pass () int frame_finished; while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - shared_ptr s; - if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) { - s = _subtitle; - } - - process_video (_frame, s); + process_video (_frame); } if (_audio_stream >= 0 && _opt->decode_audio) { @@ -231,12 +226,7 @@ FFmpegDecoder::do_pass () int frame_finished; if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - shared_ptr s; - if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) { - s = _subtitle; - } - - process_video (_frame, s); + process_video (_frame); } } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio) { @@ -253,12 +243,12 @@ FFmpegDecoder::do_pass () process_audio (_frame->data[0], data_size); } - } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream) { + } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _opt->decode_subtitles) { int got_subtitle; AVSubtitle sub; if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) { - _subtitle.reset (new Subtitle (sub)); + process_subtitle (shared_ptr (new Subtitle (sub))); avsubtitle_free (&sub); } } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index e92b326b8..d34c22785 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -96,5 +96,4 @@ private: AVCodec* _subtitle_codec; ///< may be 0 if there is no subtitle AVPacket _packet; - boost::shared_ptr _subtitle; }; diff --git a/src/lib/film.cc b/src/lib/film.cc index e4155c9f6..31af2f1c2 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -535,6 +535,7 @@ Film::make_dcp (bool transcode, int freq) o->decode_video_frequency = freq; o->padding = format()->dcp_padding (this); o->ratio = format()->ratio_as_float (this); + o->decode_subtitles = with_subtitles (); shared_ptr r; diff --git a/src/lib/image.cc b/src/lib/image.cc index 602b20842..ce44ec95b 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -207,6 +207,42 @@ Image::make_black () } } +void +Image::alpha_blend (shared_ptr other, Position position) +{ + /* Only implemented for RGBA onto RGB24 so far */ + assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA); + + int start_tx = position.x; + int start_ox = 0; + + if (start_tx < 0) { + start_ox = -start_tx; + start_tx = 0; + } + + int start_ty = position.y; + int start_oy = 0; + + if (start_ty < 0) { + start_oy = -start_ty; + start_ty = 0; + } + + for (int ty = start_ty, oy = start_oy; ty < size().height && oy < other->size().height; ++ty, ++oy) { + uint8_t* tp = data()[0] + ty * line_size()[0] + position.x * 3; + uint8_t* op = other->data()[0] + oy * other->line_size()[0]; + for (int tx = start_tx, ox = start_ox; tx < size().width && ox < other->size().width; ++tx, ++ox) { + float const alpha = float (op[3]) / 255; + tp[0] = (tp[0] * (1 - alpha)) + op[0] * alpha; + tp[1] = (tp[1] * (1 - alpha)) + op[1] * alpha; + tp[2] = (tp[2] * (1 - alpha)) + op[2] * alpha; + tp += 3; + op += 4; + } + } +} + /** Construct a SimpleImage of a given size and format, allocating memory * as required. * @@ -217,8 +253,10 @@ SimpleImage::SimpleImage (PixelFormat p, Size s) : Image (p) , _size (s) { - _data = (uint8_t **) av_malloc (components() * sizeof (uint8_t *)); - _line_size = (int *) av_malloc (components() * sizeof (int)); + _data = (uint8_t **) av_malloc (4 * sizeof (uint8_t *)); + _data[0] = _data[1] = _data[2] = _data[3] = 0; + _line_size = (int *) av_malloc (4); + _line_size[0] = _line_size[1] = _line_size[2] = _line_size[3] = 0; switch (p) { case PIX_FMT_RGB24: diff --git a/src/lib/image.h b/src/lib/image.h index 970750719..ea35fa0b9 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -68,6 +68,7 @@ public: boost::shared_ptr scale_and_convert_to_rgb (Size, int, Scaler const *) const; boost::shared_ptr scale (Size, Scaler const *) const; boost::shared_ptr post_process (std::string) const; + void alpha_blend (boost::shared_ptr image, Position pos); void make_black (); diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index df20479c9..32c433d09 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -61,7 +61,7 @@ ImageMagickDecoder::do_pass () } - process_video (image.frame (), shared_ptr ()); + process_video (image.frame ()); _done = true; return false; diff --git a/src/lib/j2k_still_encoder.cc b/src/lib/j2k_still_encoder.cc index a241dd6e3..2b8aca649 100644 --- a/src/lib/j2k_still_encoder.cc +++ b/src/lib/j2k_still_encoder.cc @@ -52,7 +52,7 @@ J2KStillEncoder::process_video (shared_ptr yuv, int frame, shared_ptr const s = Filter::ffmpeg_strings (_fs->filters); DCPVideoFrame* f = new DCPVideoFrame ( - yuv, sub, _opt->out_size, _opt->padding, _fs->scaler, 0, _fs->frames_per_second, s.second, + yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset, _fs->subtitle_scale, _fs->scaler, 0, _fs->frames_per_second, s.second, Config::instance()->colour_lut_index(), Config::instance()->j2k_bandwidth(), _log ); diff --git a/src/lib/j2k_wav_encoder.cc b/src/lib/j2k_wav_encoder.cc index 7bcac483b..e2a3a5ed7 100644 --- a/src/lib/j2k_wav_encoder.cc +++ b/src/lib/j2k_wav_encoder.cc @@ -126,7 +126,8 @@ J2KWAVEncoder::process_video (shared_ptr yuv, int frame, shared_ptr ( new DCPVideoFrame ( - yuv, sub, _opt->out_size, _opt->padding, _fs->scaler, frame, _fs->frames_per_second, s.second, + yuv, sub, _opt->out_size, _opt->padding, _fs->subtitle_offset, _fs->subtitle_scale, + _fs->scaler, frame, _fs->frames_per_second, s.second, Config::instance()->colour_lut_index (), Config::instance()->j2k_bandwidth (), _log ) diff --git a/src/lib/options.h b/src/lib/options.h index 2fc5f77ff..86db35210 100644 --- a/src/lib/options.h +++ b/src/lib/options.h @@ -42,6 +42,7 @@ public: , black_after (0) , decode_video_frequency (0) , decode_audio (true) + , decode_subtitles (false) , _frame_out_path (f) , _frame_out_extension (e) , _multichannel_audio_out_path (m) @@ -99,6 +100,7 @@ public: int black_after; ///< first frame for which to output a black frame, rather than the actual video content, or 0 for none int decode_video_frequency; ///< skip frames so that this many are decoded in all (or 0) (for generating thumbnails) bool decode_audio; ///< true to decode audio, otherwise false + bool decode_subtitles; private: /** Path of the directory to write video frames to */ diff --git a/src/lib/server.cc b/src/lib/server.cc index 2fda5952f..26b2be7c7 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -88,6 +88,8 @@ Server::process (shared_ptr socket) int pixel_format_int; Size out_size; int padding; + int subtitle_offset; + int subtitle_scale; string scaler_id; int frame; float frames_per_second; @@ -99,6 +101,8 @@ Server::process (shared_ptr socket) >> pixel_format_int >> out_size.width >> out_size.height >> padding + >> subtitle_offset + >> subtitle_scale >> scaler_id >> frame >> frames_per_second @@ -120,7 +124,8 @@ Server::process (shared_ptr socket) /* XXX: subtitle */ DCPVideoFrame dcp_video_frame ( - image, shared_ptr (), out_size, padding, scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log + image, shared_ptr (), out_size, padding, subtitle_offset, subtitle_scale, + scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log ); shared_ptr encoded = dcp_video_frame.encode_locally (); diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc index 0ea5a72d5..f0d77c511 100644 --- a/src/lib/subtitle.cc +++ b/src/lib/subtitle.cc @@ -73,34 +73,39 @@ SubtitleImage::SubtitleImage (AVSubtitleRect const * rect) Rectangle transformed_subtitle_area ( float target_x_scale, float target_y_scale, - Rectangle sub_area, - shared_ptr fs + Rectangle sub_area, int subtitle_offset, float subtitle_scale ) { Rectangle tx; - sub_area.y += fs->subtitle_offset; + 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.w = sub_area.w * target_x_scale * fs->subtitle_scale; - tx.h = sub_area.h * target_y_scale * fs->subtitle_scale; + tx.w = sub_area.w * target_x_scale * subtitle_scale; + tx.h = sub_area.h * 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 fs->subtitle_scale to the centre of the subtitle; this will be - * (width_before_subtitle_scale * (1 - fs->subtitle_scale) / 2) and - * (height_before_subtitle_scale * (1 - fs->subtitle_scale) / 2). + * 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 = target_x_scale * (sub_area.x + (sub_area.w * (1 - fs->subtitle_scale) / 2)); - tx.y = target_y_scale * (sub_area.y + (sub_area.h * (1 - fs->subtitle_scale) / 2)); + 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)); return tx; } + +Rectangle +SubtitleImage::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 d9717564e..6fd0d8772 100644 --- a/src/lib/subtitle.h +++ b/src/lib/subtitle.h @@ -47,8 +47,7 @@ private: extern Rectangle transformed_subtitle_area ( float target_x_scale, float target_y_scale, - Rectangle sub_area, - boost::shared_ptr fs + Rectangle sub_area, int subtitle_offset, float subtitle_scale ); class SubtitleImage @@ -68,6 +67,8 @@ public: return _image; } + Rectangle area () const; + private: Position _position; boost::shared_ptr _image; diff --git a/src/lib/tiff_decoder.cc b/src/lib/tiff_decoder.cc index 116c65f7b..101e0c047 100644 --- a/src/lib/tiff_decoder.cc +++ b/src/lib/tiff_decoder.cc @@ -179,7 +179,7 @@ TIFFDecoder::do_pass () _TIFFfree (raster); TIFFClose (t); - process_video (image.frame (), shared_ptr ()); + process_video (image.frame ()); ++_iter; return false; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index bfeb44cb9..bf082adc2 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -179,7 +179,7 @@ private: for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { i->transformed_area = transformed_subtitle_area ( - x_scale, y_scale, i->base_area, _film->state_copy() + x_scale, y_scale, i->base_area, _film->subtitle_offset(), _film->subtitle_scale() ); i->transformed_image = i->base_image; @@ -328,6 +328,7 @@ FilmViewer::update_thumbs () o->apply_crop = false; o->decode_audio = false; o->decode_video_frequency = 128; + o->decode_subtitles = true; shared_ptr j (new ThumbsJob (s, o, _film->log(), shared_ptr ())); j->Finished.connect (sigc::mem_fun (_film, &Film::update_thumbs_post_gui)); diff --git a/test/test.cc b/test/test.cc index 789724b53..aaa911b32 100644 --- a/test/test.cc +++ b/test/test.cc @@ -290,6 +290,8 @@ BOOST_AUTO_TEST_CASE (client_server_test) shared_ptr (), Size (1998, 1080), 0, + 0, + 0, Scaler::from_id ("bicubic"), 0, 24, -- cgit v1.2.3