summaryrefslogtreecommitdiff
path: root/src/lib
diff options
context:
space:
mode:
authorCarl Hetherington <cth@carlh.net>2012-10-14 16:50:08 +0100
committerCarl Hetherington <cth@carlh.net>2012-10-14 16:50:08 +0100
commita066feba1b455a72fe10b7baa79f17f69cd24ba9 (patch)
treeba2ee6308f200c42870d42b7c716fddf941f8c31 /src/lib
parent27fac0b4c6d42cb3b47bc1240d50ce11923fb66a (diff)
Various fixes to subtitling.
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/dcp_video_frame.cc30
-rw-r--r--src/lib/dcp_video_frame.h4
-rw-r--r--src/lib/decoder.cc24
-rw-r--r--src/lib/decoder.h5
-rw-r--r--src/lib/ffmpeg_decoder.cc18
-rw-r--r--src/lib/ffmpeg_decoder.h1
-rw-r--r--src/lib/film.cc1
-rw-r--r--src/lib/image.cc42
-rw-r--r--src/lib/image.h1
-rw-r--r--src/lib/imagemagick_decoder.cc2
-rw-r--r--src/lib/j2k_still_encoder.cc2
-rw-r--r--src/lib/j2k_wav_encoder.cc3
-rw-r--r--src/lib/options.h2
-rw-r--r--src/lib/server.cc7
-rw-r--r--src/lib/subtitle.cc25
-rw-r--r--src/lib/subtitle.h5
-rw-r--r--src/lib/tiff_decoder.cc2
17 files changed, 126 insertions, 48 deletions
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<Image> yuv, shared_ptr<Subtitle> sub, Size out, int p, Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l)
+ shared_ptr<Image> yuv, shared_ptr<Subtitle> 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<EncodedData>
DCPVideoFrame::encode_locally ()
{
- shared_ptr<Image> 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<Image> prepared = _input->scale_and_convert_to_rgb (_out_size, _padding, _scaler);
+
+ if (_subtitle) {
+ list<shared_ptr<SubtitleImage> > subs = _subtitle->images ();
+ for (list<shared_ptr<SubtitleImage> >::iterator i = subs.begin(); i != subs.end(); ++i) {
+ Rectangle tx = transformed_subtitle_area (
+ float (_out_size.width) / _input->size().width,
+ float (_out_size.height) / _input->size().height,
+ (*i)->area(), _subtitle_offset, _subtitle_scale
+ );
+
+ shared_ptr<Image> im = (*i)->image()->scale (Size (tx.w, tx.h), _scaler);
+ prepared->alpha_blend (im, Position (tx.x, tx.y));
+ }
+ }
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<Image>, boost::shared_ptr<Subtitle>, Size, int, Scaler const *, int, float, std::string, int, int, Log *);
+ DCPVideoFrame (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>, Size, int, int, float, Scaler const *, int, float, std::string, int, int, Log *);
virtual ~DCPVideoFrame ();
boost::shared_ptr<EncodedData> encode_locally ();
@@ -124,6 +124,8 @@ private:
boost::shared_ptr<Subtitle> _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<Subtitle> sub)
+Decoder::process_video (AVFrame* frame)
{
if (_minimal) {
++_video_frame;
@@ -304,12 +304,9 @@ Decoder::process_video (AVFrame* frame, shared_ptr<Subtitle> sub)
image->make_black ();
}
- if (sub && _opt->apply_crop) {
- list<shared_ptr<SubtitleImage> > im = sub->images ();
- for (list<shared_ptr<SubtitleImage> >::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<Subtitle> 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<Subtitle> s)
+{
+ _subtitle = s;
+
+ if (_opt->apply_crop) {
+ list<shared_ptr<SubtitleImage> > im = _subtitle->images ();
+ for (list<shared_ptr<SubtitleImage> >::iterator i = im.begin(); i != im.end(); ++i) {
+ Position const p = (*i)->position ();
+ (*i)->set_position (Position (p.x - _fs->crop.left, p.y - _fs->crop.top));
+ }
+ }
+}
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<Subtitle>);
+ void process_video (AVFrame *);
void process_audio (uint8_t *, int);
+ void process_subtitle (boost::shared_ptr<Subtitle>);
/** our FilmState */
boost::shared_ptr<const FilmState> _fs;
@@ -138,6 +139,8 @@ private:
(at the DCP sample rate).
*/
int64_t _audio_frames_processed;
+
+ boost::shared_ptr<Subtitle> _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<Subtitle> 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<Subtitle> 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<Subtitle> (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> _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<Job> 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<Image> 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<Image> scale_and_convert_to_rgb (Size, int, Scaler const *) const;
boost::shared_ptr<Image> scale (Size, Scaler const *) const;
boost::shared_ptr<Image> post_process (std::string) const;
+ void alpha_blend (boost::shared_ptr<Image> 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<Subtitle> ());
+ 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<Image> yuv, int frame, shared_ptr<Sub
{
pair<string, string> 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<Image> yuv, int frame, shared_ptr<Subti
TIMING ("adding to queue of %1", _queue.size ());
_queue.push_back (boost::shared_ptr<DCPVideoFrame> (
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> 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> 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> socket)
/* XXX: subtitle */
DCPVideoFrame dcp_video_frame (
- image, shared_ptr<Subtitle> (), out_size, padding, scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log
+ image, shared_ptr<Subtitle> (), out_size, padding, subtitle_offset, subtitle_scale,
+ scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log
);
shared_ptr<EncodedData> encoded = dcp_video_frame.encode_locally ();
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<FilmState> 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<FilmState> 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> _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<Subtitle> ());
+ process_video (image.frame ());
++_iter;
return false;