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. --- test/test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'test') diff --git a/test/test.cc b/test/test.cc index c801d538e..b14935da3 100644 --- a/test/test.cc +++ b/test/test.cc @@ -270,7 +270,6 @@ do_remote_encode (shared_ptr frame, ServerDescription* descriptio BOOST_AUTO_TEST_CASE (client_server_test) { shared_ptr image (new SimpleImage (PIX_FMT_RGB24, Size (1998, 1080))); - image->set_line_size (0, 1998 * 3); uint8_t* p = image->data()[0]; @@ -287,6 +286,7 @@ BOOST_AUTO_TEST_CASE (client_server_test) shared_ptr frame ( new DCPVideoFrame ( image, + shared_ptr (), Size (1998, 1080), 0, Scaler::from_id ("bicubic"), -- cgit v1.2.3 From 01f91d3d743b1d9c5306e8817cfd926eddc61736 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 00:24:26 +0100 Subject: Fix up tests; possibly working subtitle transforms. --- src/lib/subtitle.cc | 36 +++++++++++++++++++++++++++ src/lib/subtitle.h | 15 ++++++++++++ src/wx/film_viewer.cc | 67 +++++++++++++++++++++------------------------------ test/metadata.ref | 4 +++ test/test.cc | 20 +++------------ 5 files changed, 86 insertions(+), 56 deletions(-) (limited to 'test') diff --git a/src/lib/subtitle.cc b/src/lib/subtitle.cc index 18dded02c..3559197a0 100644 --- a/src/lib/subtitle.cc +++ b/src/lib/subtitle.cc @@ -20,6 +20,7 @@ #include "subtitle.h" #include "image.h" #include "exceptions.h" +#include "film_state.h" using namespace std; using namespace boost; @@ -68,3 +69,38 @@ SubtitleImage::SubtitleImage (AVSubtitleRect const * rect) sub_p += rect->pict.linesize[0]; } } + +SubtitleTransform +subtitle_transform ( + int target_base_width, int target_base_height, + float target_x_scale, float target_y_scale, + Position sub_pos, int sub_width, int sub_height, + shared_ptr fs + ) +{ + SubtitleTransform tx; + + Rectangle sub_area (sub_pos.x, sub_pos.y + fs->subtitle_offset, sub_width, sub_height); + + Rectangle cropped_target_area ( + fs->crop.left, + fs->crop.top, + target_base_width - (fs->crop.left + fs->crop.right), + target_base_height - (fs->crop.top + fs->crop.bottom) + ); + + Rectangle cropped_sub_area = sub_area.intersection (cropped_target_area); + + tx.crop.x = cropped_sub_area.x - sub_area.x; + tx.crop.y = cropped_sub_area.y - sub_area.y; + tx.crop.w = cropped_sub_area.w; + tx.crop.h = cropped_sub_area.h; + + tx.transformed.w = cropped_sub_area.w * target_x_scale * fs->subtitle_scale; + tx.transformed.h = cropped_sub_area.h * target_y_scale * fs->subtitle_scale; + + tx.transformed.x = target_x_scale * ((sub_area.x - fs->crop.left) + (cropped_sub_area.w * (1 - fs->subtitle_scale) / 2)); + tx.transformed.y = target_y_scale * ((sub_area.y - fs->crop.top) + (cropped_sub_area.h * (1 - fs->subtitle_scale) / 2)); + + return tx; +} diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h index 18d2590eb..fcb6bc70c 100644 --- a/src/lib/subtitle.h +++ b/src/lib/subtitle.h @@ -24,6 +24,7 @@ struct AVSubtitle; class SubtitleImage; class SimpleImage; +class FilmState; class Subtitle { @@ -44,6 +45,20 @@ private: std::list > _images; }; +struct SubtitleTransform +{ +public: + Rectangle crop; + Rectangle transformed; +}; + +extern SubtitleTransform subtitle_transform ( + int target_base_width, int target_base_height, + float target_x_scale, float target_y_scale, + Position sub_pos, int sub_width, int sub_height, + boost::shared_ptr fs + ); + class SubtitleImage { public: diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index fb7317dc6..725ba57de 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -30,6 +30,7 @@ #include "lib/job_manager.h" #include "lib/film_state.h" #include "lib/options.h" +#include "lib/subtitle.h" #include "film_viewer.h" #include "wx_util.h" @@ -81,7 +82,7 @@ public: if (_film->with_subtitles ()) { for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - dc.DrawBitmap (*i->bitmap, i->cropped_position.x, i->cropped_position.y, true); + dc.DrawBitmap (*i->bitmap, i->transformed_position.x, i->transformed_position.y, true); } } } @@ -146,7 +147,7 @@ private: GetSize (&vw, &vh); /* Cropped rectangle */ - Rectangle cropped ( + Rectangle cropped_area ( _film->crop().left, _film->crop().top, _image->GetWidth() - (_film->crop().left + _film->crop().right), @@ -156,56 +157,44 @@ private: /* Target ratio */ float const target = _film->format() ? _film->format()->ratio_as_float (_film) : 1.78; - _cropped_image = _image->GetSubImage (wxRect (cropped.x, cropped.y, cropped.w, cropped.h)); + _transformed_image = _image->GetSubImage (wxRect (cropped_area.x, cropped_area.y, cropped_area.w, cropped_area.h)); float x_scale = 1; float y_scale = 1; if ((float (vw) / vh) > target) { /* view is longer (horizontally) than the ratio; fit height */ - _cropped_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH); - x_scale = vh * target / cropped.w; - y_scale = float (vh) / cropped.h; + _transformed_image.Rescale (vh * target, vh, wxIMAGE_QUALITY_HIGH); + x_scale = vh * target / cropped_area.w; + y_scale = float (vh) / cropped_area.h; } else { /* view is shorter (horizontally) than the ratio; fit width */ - _cropped_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH); - x_scale = float (vw) / cropped.w; - y_scale = (vw / target) / cropped.h; + _transformed_image.Rescale (vw, vw / target, wxIMAGE_QUALITY_HIGH); + x_scale = float (vw) / cropped_area.w; + y_scale = (vw / target) / cropped_area.h; } - _bitmap.reset (new wxBitmap (_cropped_image)); + _bitmap.reset (new wxBitmap (_transformed_image)); for (list::iterator i = _subtitles.begin(); i != _subtitles.end(); ++i) { - /* Area of the subtitle graphic within the (uncropped) picture frame */ - Rectangle sub_rect (i->position.x, i->position.y + _film->subtitle_offset(), i->image.GetWidth(), i->image.GetHeight()); - /* Hence the subtitle graphic after it has been cropped */ - Rectangle cropped_sub_rect = sub_rect.intersection (cropped); - - /* Get the cropped version of the subtitle image */ - i->cropped_image = i->image.GetSubImage ( - wxRect ( - cropped_sub_rect.x - sub_rect.x, - cropped_sub_rect.y - sub_rect.y, - cropped_sub_rect.w, - cropped_sub_rect.h - ) + SubtitleTransform tx = subtitle_transform ( + _image->GetWidth(), _image->GetHeight(), + x_scale, y_scale, + i->base_position, i->base_image.GetWidth(), i->base_image.GetHeight(), + _film->state_copy() ); - i->cropped_image.Rescale (cropped_sub_rect.w * x_scale, cropped_sub_rect.h * y_scale, wxIMAGE_QUALITY_HIGH); - - i->cropped_position = Position ( - cropped_sub_rect.x * x_scale, - (cropped_sub_rect.y - _film->crop().top) * y_scale - ); - - i->bitmap.reset (new wxBitmap (i->cropped_image)); + i->transformed_image = i->base_image.GetSubImage (wxRect (tx.crop.x, tx.crop.y, tx.crop.w, tx.crop.h)); + i->transformed_image.Rescale (tx.transformed.w, tx.transformed.h, wxIMAGE_QUALITY_HIGH); + i->transformed_position = Position (tx.transformed.x, tx.transformed.y); + i->bitmap.reset (new wxBitmap (i->transformed_image)); } } Film* _film; shared_ptr _image; - wxImage _cropped_image; + wxImage _transformed_image; /** currently-displayed thumbnail index */ int _index; shared_ptr _bitmap; @@ -215,14 +204,14 @@ private: struct SubtitleView { SubtitleView (Position p, wxString const & i) - : position (p) - , image (i) + : base_position (p) + , base_image (i) {} - - Position position; - wxImage image; - Position cropped_position; - wxImage cropped_image; + + Position base_position; + Position transformed_position; + wxImage base_image; + wxImage transformed_image; shared_ptr bitmap; }; diff --git a/test/metadata.ref b/test/metadata.ref index 817fecffd..c24b77de8 100644 --- a/test/metadata.ref +++ b/test/metadata.ref @@ -16,6 +16,9 @@ dcp_ab 1 audio_gain 0 audio_delay 0 still_duration 10 +with_subtitles 0 +subtitle_offset 0 +subtitle_scale 1 width 0 height 0 length 0 @@ -23,3 +26,4 @@ audio_channels 0 audio_sample_rate 0 audio_sample_format Unknown content_digest +has_subtitles 0 diff --git a/test/test.cc b/test/test.cc index b14935da3..5a7625dd7 100644 --- a/test/test.cc +++ b/test/test.cc @@ -36,6 +36,7 @@ #include "server.h" #include "cross.h" #include "job.h" +#include "subtitle.h" #define BOOST_TEST_DYN_LINK #define BOOST_TEST_MODULE dvdomatic_test #include @@ -248,7 +249,7 @@ BOOST_AUTO_TEST_CASE (paths_test) FilmState s; s.directory = "build/test/a/b/c/d/e"; s.thumbs.push_back (42); - BOOST_CHECK_EQUAL (s.thumb_file (0), "build/test/a/b/c/d/e/thumbs/00000042.tiff"); + BOOST_CHECK_EQUAL (s.thumb_file (0), "build/test/a/b/c/d/e/thumbs/00000042.png"); s.content = "/foo/bar/baz"; BOOST_CHECK_EQUAL (s.content_path(), "/foo/bar/baz"); @@ -438,24 +439,9 @@ BOOST_AUTO_TEST_CASE (job_manager_test) dvdomatic_sleep (2); BOOST_CHECK_EQUAL (a->finished_ok(), true); - /* Two jobs, no dependency */ - a.reset (new TestJob (s, o, &log, shared_ptr ())); - shared_ptr b (new TestJob (s, o, &log, shared_ptr ())); - - JobManager::instance()->add (a); - JobManager::instance()->add (b); - dvdomatic_sleep (2); - BOOST_CHECK_EQUAL (a->running (), true); - BOOST_CHECK_EQUAL (b->running (), true); - a->set_finished_ok (); - b->set_finished_ok (); - dvdomatic_sleep (2); - BOOST_CHECK_EQUAL (a->finished_ok (), true); - BOOST_CHECK_EQUAL (b->finished_ok (), true); - /* Two jobs, dependency */ a.reset (new TestJob (s, o, &log, shared_ptr ())); - b.reset (new TestJob (s, o, &log, a)); + shared_ptr b (new TestJob (s, o, &log, a)); JobManager::instance()->add (a); JobManager::instance()->add (b); -- cgit v1.2.3 From 27fac0b4c6d42cb3b47bc1240d50ce11923fb66a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sun, 14 Oct 2012 13:22:47 +0100 Subject: Simplify use of Image hierarchy a bit. --- src/lib/image.cc | 12 ++++++------ src/lib/image.h | 4 ++-- src/lib/server.cc | 2 +- src/lib/subtitle.h | 6 +++--- test/test.cc | 2 +- 5 files changed, 13 insertions(+), 13 deletions(-) (limited to 'test') diff --git a/src/lib/image.cc b/src/lib/image.cc index 98c5228a9..602b20842 100644 --- a/src/lib/image.cc +++ b/src/lib/image.cc @@ -88,7 +88,7 @@ Image::scale (Size out_size, Scaler const * scaler) const { assert (scaler); - shared_ptr scaled (new SimpleImage (pixel_format(), out_size)); + shared_ptr scaled (new SimpleImage (pixel_format(), out_size)); struct SwsContext* scale_context = sws_getContext ( size().width, size().height, pixel_format(), @@ -112,7 +112,7 @@ Image::scale (Size out_size, Scaler const * scaler) const * @param out_size Output image size in pixels. * @param scaler Scaler to use. */ -shared_ptr +shared_ptr Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scaler) const { assert (scaler); @@ -120,7 +120,7 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal Size content_size = out_size; content_size.width -= (padding * 2); - shared_ptr rgb (new RGBFrameImage (content_size)); + shared_ptr rgb (new SimpleImage (PIX_FMT_RGB24, content_size)); struct SwsContext* scale_context = sws_getContext ( size().width, size().height, pixel_format(), @@ -141,7 +141,7 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal scheme of things. */ if (padding > 0) { - shared_ptr padded_rgb (new RGBFrameImage (out_size)); + shared_ptr padded_rgb (new SimpleImage (PIX_FMT_RGB24, out_size)); padded_rgb->make_black (); /* XXX: we are cheating a bit here; we know the frame is RGB so we can @@ -167,10 +167,10 @@ Image::scale_and_convert_to_rgb (Size out_size, int padding, Scaler const * scal * @param pp Flags for the required set of post processes. * @return Post-processed image. */ -shared_ptr +shared_ptr Image::post_process (string pp) const { - shared_ptr out (new SimpleImage (PIX_FMT_YUV420P, size ())); + shared_ptr out (new SimpleImage (PIX_FMT_YUV420P, size ())); pp_mode* mode = pp_get_mode_by_name_and_quality (pp.c_str (), PP_QUALITY_MAX); pp_context* context = pp_get_context (size().width, size().height, PP_FORMAT_420 | PP_CPU_CAPS_MMX2); diff --git a/src/lib/image.h b/src/lib/image.h index e06a82b7f..970750719 100644 --- a/src/lib/image.h +++ b/src/lib/image.h @@ -65,9 +65,9 @@ public: int components () const; int lines (int) const; - boost::shared_ptr scale_and_convert_to_rgb (Size, int, Scaler const *) const; + 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; + boost::shared_ptr post_process (std::string) const; void make_black (); diff --git a/src/lib/server.cc b/src/lib/server.cc index 76989d078..2fda5952f 100644 --- a/src/lib/server.cc +++ b/src/lib/server.cc @@ -112,7 +112,7 @@ Server::process (shared_ptr socket) post_process = ""; } - shared_ptr image (new SimpleImage (pixel_format, in_size)); + shared_ptr image (new SimpleImage (pixel_format, in_size)); for (int i = 0; i < image->components(); ++i) { socket->read_definite_and_consume (image->data()[i], image->line_size()[i] * image->lines(i), 30); diff --git a/src/lib/subtitle.h b/src/lib/subtitle.h index 0b82320a1..d9717564e 100644 --- a/src/lib/subtitle.h +++ b/src/lib/subtitle.h @@ -23,7 +23,7 @@ struct AVSubtitle; class SubtitleImage; -class SimpleImage; +class Image; class FilmState; class Subtitle @@ -64,11 +64,11 @@ public: return _position; } - boost::shared_ptr image () const { + boost::shared_ptr image () const { return _image; } private: Position _position; - boost::shared_ptr _image; + boost::shared_ptr _image; }; diff --git a/test/test.cc b/test/test.cc index 5a7625dd7..789724b53 100644 --- a/test/test.cc +++ b/test/test.cc @@ -270,7 +270,7 @@ do_remote_encode (shared_ptr frame, ServerDescription* descriptio BOOST_AUTO_TEST_CASE (client_server_test) { - shared_ptr image (new SimpleImage (PIX_FMT_RGB24, Size (1998, 1080))); + shared_ptr image (new SimpleImage (PIX_FMT_RGB24, Size (1998, 1080))); uint8_t* p = image->data()[0]; -- 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 'test') 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