Subs successfully exported with thumbs.
authorCarl Hetherington <cth@carlh.net>
Wed, 10 Oct 2012 14:47:06 +0000 (15:47 +0100)
committerCarl Hetherington <cth@carlh.net>
Wed, 10 Oct 2012 14:47:06 +0000 (15:47 +0100)
25 files changed:
src/lib/ab_transcoder.cc
src/lib/ab_transcoder.h
src/lib/dcp_video_frame.cc
src/lib/dcp_video_frame.h
src/lib/decoder.cc
src/lib/decoder.h
src/lib/encoder.h
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/image.cc
src/lib/image.h
src/lib/imagemagick_decoder.cc
src/lib/imagemagick_decoder.h
src/lib/j2k_still_encoder.cc
src/lib/j2k_still_encoder.h
src/lib/j2k_wav_encoder.cc
src/lib/j2k_wav_encoder.h
src/lib/job_manager.cc
src/lib/options.h
src/lib/server.cc
src/lib/tiff_decoder.cc
src/lib/tiff_encoder.cc
src/lib/tiff_encoder.h
src/lib/wscript
test/test.cc

index 1c20ae4776fe23d0bf0c9f6d1f57cab5b3402246..a32d82c54bef9625c20c6383c35ec021d2cf9615 100644 (file)
@@ -70,7 +70,7 @@ ABTranscoder::~ABTranscoder ()
 }
 
 void
-ABTranscoder::process_video (shared_ptr<Image> yuv, int frame, int index)
+ABTranscoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Subtitle> sub, int index)
 {
        if (index == 0) {
                /* Keep this image around until we get the other half */
@@ -92,7 +92,7 @@ ABTranscoder::process_video (shared_ptr<Image> yuv, int frame, int index)
                }
                        
                /* And pass it to the encoder */
-               _encoder->process_video (_image, frame);
+               _encoder->process_video (_image, frame, sub);
                _image.reset ();
        }
        
index 0310bb9233803fe618d81b084e4c14ad0a1613e3..491205ef715cbc934c96cd9e3403a68de7253856 100644 (file)
@@ -32,6 +32,7 @@ class FilmState;
 class Options;
 class Image;
 class Log;
+class Subtitle;
 
 /** @class ABTranscoder
  *  @brief A transcoder which uses one FilmState for the left half of the screen, and a different one
@@ -54,7 +55,7 @@ public:
        void go ();
 
 private:
-       void process_video (boost::shared_ptr<Image>, int, int);
+       void process_video (boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle>, int);
        
        boost::shared_ptr<const FilmState> _fs_a;
        boost::shared_ptr<const FilmState> _fs_b;
index 90826a99f5a1ea04c5633c43205c89766cc0e4c6..13d3efcbf2c4c93ff0e5e32068f163ef2aaaf690 100644 (file)
@@ -72,8 +72,9 @@ using namespace boost;
  *  @param l Log to write to.
  */
 DCPVideoFrame::DCPVideoFrame (
-       shared_ptr<Image> yuv, 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, Scaler const * s, int f, float fps, string pp, int clut, int bw, Log* l)
        : _input (yuv)
+       , _subtitle (sub)
        , _out_size (out)
        , _padding (p)
        , _scaler (s)
@@ -296,10 +297,6 @@ DCPVideoFrame::encode_remotely (ServerDescription const * serv)
          << Config::instance()->colour_lut_index () << " "
          << Config::instance()->j2k_bandwidth () << " ";
 
-       for (int i = 0; i < _input->components(); ++i) {
-               s << _input->line_size()[i] << " ";
-       }
-
        socket.write ((uint8_t *) s.str().c_str(), s.str().length() + 1, 30);
 
        for (int i = 0; i < _input->components(); ++i) {
index 72f885e45e6e0804f675d0f61abf62814314b3bd..fe2e27966d2cc5f536f8904b45753b6123ce140f 100644 (file)
@@ -31,6 +31,7 @@ class ServerDescription;
 class Scaler;
 class Image;
 class Log;
+class Subtitle;
 
 /** @class EncodedData
  *  @brief Container for J2K-encoded data.
@@ -105,7 +106,7 @@ public:
 class DCPVideoFrame
 {
 public:
-       DCPVideoFrame (boost::shared_ptr<Image>, Size, int, Scaler const *, int, float, std::string, int, int, Log *);
+       DCPVideoFrame (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>, Size, int, Scaler const *, int, float, std::string, int, int, Log *);
        virtual ~DCPVideoFrame ();
 
        boost::shared_ptr<EncodedData> encode_locally ();
@@ -120,6 +121,7 @@ private:
        void write_encoded (boost::shared_ptr<const Options>, uint8_t *, int);
 
        boost::shared_ptr<Image> _input; ///< the input image
+       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;
        Scaler const * _scaler;          ///< scaler to use
index 15d74022cf95cb0797c383bf94aac84de8f32a22..e4b892ea4667bf418bac0b8236e3167caf9fad42 100644 (file)
@@ -228,7 +228,7 @@ Decoder::process_audio (uint8_t* data, int size)
  *  @param frame to decode; caller manages memory.
  */
 void
-Decoder::process_video (AVFrame* frame)
+Decoder::process_video (AVFrame* frame, shared_ptr<Subtitle> sub)
 {
        if (_minimal) {
                ++_video_frame;
@@ -303,10 +303,8 @@ Decoder::process_video (AVFrame* frame)
                                image->make_black ();
                        }
 
-                       overlay (image);
-
                        TIMING ("Decoder emits %1", _video_frame);
-                       Video (image, _video_frame);
+                       Video (image, _video_frame, sub);
                        ++_video_frame;
                }
        }
index 9a4c7695eaba4e4c7ec718b62f31ffc8691f0bd4..5cb44b8d92ad8e0f0d0c3d490c1892d0bc33698c 100644 (file)
@@ -37,6 +37,7 @@ class Options;
 class Image;
 class Log;
 class DelayLine;
+class Subtitle;
 
 /** @class Decoder.
  *  @brief Parent class for decoders of content.
@@ -81,8 +82,9 @@ public:
        /** Emitted when a video frame is ready.
         *  First parameter is the frame.
         *  Second parameter is its index within the content.
+        *  Third parameter is either 0 or a subtitle that should be on this frame.
         */
-       sigc::signal<void, boost::shared_ptr<Image>, int> Video;
+       sigc::signal<void, boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle> > Video;
 
        /** Emitted when some audio data is ready.
         *  First parameter is the interleaved sample data, format is given in the FilmState.
@@ -98,9 +100,8 @@ protected:
        virtual int time_base_denominator () const = 0;
        virtual int sample_aspect_ratio_numerator () const = 0;
        virtual int sample_aspect_ratio_denominator () const = 0;
-       virtual void overlay (boost::shared_ptr<Image> image) const {}
        
-       void process_video (AVFrame *);
+       void process_video (AVFrame *, boost::shared_ptr<Subtitle>);
        void process_audio (uint8_t *, int);
 
        /** our FilmState */
index ea356cec4ba05cead7b17e63f727644d77eb3f73..02a2d772325bdbd602291f2b2b5c1f26aa631f4f 100644 (file)
@@ -36,6 +36,7 @@ class FilmState;
 class Options;
 class Image;
 class Log;
+class Subtitle;
 
 /** @class Encoder
  *  @brief Parent class for classes which can encode video and audio frames.
@@ -58,8 +59,9 @@ public:
        /** Called with a frame of video.
         *  @param i Video frame image.
         *  @param f Frame number within the film.
+        *  @param s A subtitle that should be on this frame, or 0.
         */
-       virtual void process_video (boost::shared_ptr<Image> i, int f) = 0;
+       virtual void process_video (boost::shared_ptr<Image> i, int f, boost::shared_ptr<Subtitle> s) = 0;
 
        /** Called with some audio data.
         *  @param d Data.
index ca35c6e81279b7c90f6e029a5c0ae03e9746f1f7..c2ee9297b09772c0d6253871e85e244fc0231569 100644 (file)
@@ -48,6 +48,7 @@ extern "C" {
 #include "util.h"
 #include "log.h"
 #include "ffmpeg_decoder.h"
+#include "subtitle.h"
 
 using namespace std;
 using namespace boost;
@@ -65,7 +66,6 @@ FFmpegDecoder::FFmpegDecoder (boost::shared_ptr<const FilmState> s, boost::share
        , _audio_codec (0)
        , _subtitle_codec_context (0)
        , _subtitle_codec (0)
-       , _have_subtitle (false)
 {
        setup_general ();
        setup_video ();
@@ -83,10 +83,6 @@ FFmpegDecoder::~FFmpegDecoder ()
                avcodec_close (_video_codec_context);
        }
 
-       if (_have_subtitle) {
-               avsubtitle_free (&_subtitle);
-       }
-
        if (_subtitle_codec_context) {
                avcodec_close (_subtitle_codec_context);
        }
@@ -209,7 +205,12 @@ FFmpegDecoder::do_pass ()
                int frame_finished;
 
                while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
-                       process_video (_frame);
+                       shared_ptr<Subtitle> s;
+                       if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) {
+                               s = _subtitle;
+                       }
+                       
+                       process_video (_frame, s);
                }
 
                if (_audio_stream >= 0 && _opt->decode_audio) {
@@ -230,7 +231,12 @@ FFmpegDecoder::do_pass ()
 
                int frame_finished;
                if (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) {
-                       process_video (_frame);
+                       shared_ptr<Subtitle> s;
+                       if (_subtitle && _subtitle->displayed_at (double (last_video_frame()) / rint (_fs->frames_per_second))) {
+                               s = _subtitle;
+                       }
+                       
+                       process_video (_frame, s);
                }
 
        } else if (_audio_stream >= 0 && _packet.stream_index == _audio_stream && _opt->decode_audio) {
@@ -247,16 +253,13 @@ FFmpegDecoder::do_pass ()
                        process_audio (_frame->data[0], data_size);
                }
 
-       } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream && _fs->with_subtitles) {
-
-               if (_have_subtitle) {
-                       avsubtitle_free (&_subtitle);
-                       _have_subtitle = false;
-               }
+       } else if (_subtitle_stream >= 0 && _packet.stream_index == _subtitle_stream) {
 
                int got_subtitle;
-               if (avcodec_decode_subtitle2 (_subtitle_codec_context, &_subtitle, &got_subtitle, &_packet) && got_subtitle) {
-                       _have_subtitle = true;
+               AVSubtitle sub;
+               if (avcodec_decode_subtitle2 (_subtitle_codec_context, &sub, &got_subtitle, &_packet) && got_subtitle) {
+                       _subtitle.reset (new Subtitle (sub));
+                       avsubtitle_free (&sub);
                }
        }
        
@@ -358,105 +361,6 @@ FFmpegDecoder::sample_aspect_ratio_denominator () const
        return _video_codec_context->sample_aspect_ratio.den;
 }
 
-void
-FFmpegDecoder::overlay (shared_ptr<Image> image) const
-{
-       if (!_have_subtitle) {
-               return;
-       }
-       
-       /* subtitle PTS in seconds */
-       float const packet_time = (_subtitle.pts / AV_TIME_BASE) + float (_subtitle.pts % AV_TIME_BASE) / 1e6;
-       /* hence start time for this sub */
-       float const from = packet_time + (float (_subtitle.start_display_time) / 1e3);
-       float const to = packet_time + (float (_subtitle.end_display_time) / 1e3);
-       
-       float const video_frame_time = float (last_video_frame ()) / rint (_fs->frames_per_second);
-
-       if (from > video_frame_time || video_frame_time < to) {
-               return;
-       }
-
-       for (unsigned int i = 0; i < _subtitle.num_rects; ++i) {
-               AVSubtitleRect* rect = _subtitle.rects[i];
-               if (rect->type != SUBTITLE_BITMAP) {
-                       throw DecodeError ("non-bitmap subtitles not yet supported");
-               }
-
-               /* XXX: all this assumes YUV420 in image */
-               
-               assert (rect->pict.data[0]);
-
-               /* Start of the first line in the target image */
-               uint8_t* frame_y_p = image->data()[0] + rect->y * image->line_size()[0];
-               uint8_t* frame_u_p = image->data()[1] + (rect->y / 2) * image->line_size()[1];
-               uint8_t* frame_v_p = image->data()[2] + (rect->y / 2) * image->line_size()[2];
-
-               int const hlim = min (rect->y + rect->h, image->size().height) - rect->y;
-               
-               /* 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];
-               
-               for (int sub_y = 0; sub_y < hlim; ++sub_y) {
-                       /* Pointers to the start of this line */
-                       uint8_t* sub_line_p = sub_p;
-                       uint8_t* frame_line_y_p = frame_y_p + rect->x;
-                       uint8_t* frame_line_u_p = frame_u_p + (rect->x / 2);
-                       uint8_t* frame_line_v_p = frame_v_p + (rect->x / 2);
-                       
-                       /* U and V are subsampled */
-                       uint8_t next_u = 0;
-                       uint8_t next_v = 0;
-                       int subsample_step = 0;
-                       
-                       for (int sub_x = 0; sub_x < rect->w; ++sub_x) {
-                               
-                               /* RGB value for this subtitle pixel */
-                               uint32_t const val = palette[*sub_line_p++];
-                               
-                               int const     red =  (val &       0xff);
-                               int const   green =  (val &     0xff00) >> 8;
-                               int const    blue =  (val &   0xff0000) >> 16;
-                               float const alpha = ((val & 0xff000000) >> 24) / 255.0;
-                               
-                               /* Alpha-blend Y */
-                               int const cy = *frame_line_y_p;
-                               *frame_line_y_p++ = int (cy * (1 - alpha)) + int (RGB_TO_Y_CCIR (red, green, blue) * alpha);
-                               
-                               /* Store up U and V */
-                               next_u |= ((RGB_TO_U_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step);
-                               next_v |= ((RGB_TO_V_CCIR (red, green, blue, 0) & 0xf0) >> 4) << (4 * subsample_step);
-                               
-                               if (subsample_step == 1 && (sub_y % 2) == 0) {
-                                       int const cu = *frame_line_u_p;
-                                       int const cv = *frame_line_v_p;
-
-                                       *frame_line_u_p++ =
-                                               int (((cu & 0x0f) * (1 - alpha) + (next_u & 0x0f) * alpha)) |
-                                               int (((cu & 0xf0) * (1 - alpha) + (next_u & 0xf0) * alpha));
-
-                                       *frame_line_v_p++ = 
-                                               int (((cv & 0x0f) * (1 - alpha) + (next_v & 0x0f) * alpha)) |
-                                               int (((cv & 0xf0) * (1 - alpha) + (next_v & 0xf0) * alpha));
-                                       
-                                       next_u = next_v = 0;
-                               }
-                               
-                               subsample_step = (subsample_step + 1) % 2;
-                       }
-                       
-                       sub_p += rect->pict.linesize[0];
-                       frame_y_p += image->line_size()[0];
-                       if ((sub_y % 2) == 0) {
-                               frame_u_p += image->line_size()[1];
-                               frame_v_p += image->line_size()[2];
-                       }
-               }
-       }
-}
-    
 bool
 FFmpegDecoder::has_subtitles () const
 {
index 59ec7573d1691b2d524f872f76e99ff557d8a6f1..e92b326b8ac588e8a73e8898a0a01bfd61cf779f 100644 (file)
@@ -44,6 +44,7 @@ class FilmState;
 class Options;
 class Image;
 class Log;
+class Subtitle;
 
 /** @class FFmpegDecoder
  *  @brief A decoder using FFmpeg to decode content.
@@ -73,7 +74,6 @@ private:
        int time_base_denominator () const;
        int sample_aspect_ratio_numerator () const;
        int sample_aspect_ratio_denominator () const;
-       void overlay (boost::shared_ptr<Image> image) const;
 
        void setup_general ();
        void setup_video ();
@@ -96,6 +96,5 @@ private:
        AVCodec* _subtitle_codec;                ///< may be 0 if there is no subtitle
 
        AVPacket _packet;
-       AVSubtitle _subtitle;
-       bool _have_subtitle;
+       boost::shared_ptr<Subtitle> _subtitle;
 };
index 2df7636af689a14e6a7e175ca677cd2bf8791972..8fd43093d36a9b707a76eb97898a0e35ef9a1d07 100644 (file)
@@ -57,6 +57,7 @@ Image::lines (int n) const
                }
                break;
        case PIX_FMT_RGB24:
+       case PIX_FMT_RGBA:
                return size().height;
        default:
                assert (false);
@@ -73,6 +74,7 @@ Image::components () const
        case PIX_FMT_YUV420P:
                return 3;
        case PIX_FMT_RGB24:
+       case PIX_FMT_RGBA:
                return 1;
        default:
                assert (false);
@@ -81,6 +83,31 @@ Image::components () const
        return 0;
 }
 
+shared_ptr<Image>
+Image::scale (Size out_size, Scaler const * scaler) const
+{
+       assert (scaler);
+
+       shared_ptr<SimpleImage> scaled (new SimpleImage (pixel_format(), out_size));
+
+       struct SwsContext* scale_context = sws_getContext (
+               size().width, size().height, pixel_format(),
+               out_size.width, out_size.height, pixel_format(),
+               scaler->ffmpeg_id (), 0, 0, 0
+               );
+
+       sws_scale (
+               scale_context,
+               data(), line_size(),
+               0, size().height,
+               scaled->data (), scaled->line_size ()
+               );
+
+       sws_freeContext (scale_context);
+
+       return scaled;
+}
+
 /** Scale this image to a given size and convert it to RGB.
  *  @param out_size Output image size in pixels.
  *  @param scaler Scaler to use.
@@ -192,10 +219,25 @@ SimpleImage::SimpleImage (PixelFormat p, Size s)
 {
        _data = (uint8_t **) av_malloc (components() * sizeof (uint8_t *));
        _line_size = (int *) av_malloc (components() * sizeof (int));
+
+       switch (p) {
+       case PIX_FMT_RGB24:
+               _line_size[0] = s.width * 3;
+               break;
+       case PIX_FMT_RGBA:
+               _line_size[0] = s.width * 4;
+               break;
+       case PIX_FMT_YUV420P:
+               _line_size[0] = s.width;
+               _line_size[1] = s.width / 2;
+               _line_size[2] = s.width / 2;
+               break;
+       default:
+               assert (false);
+       }
        
        for (int i = 0; i < components(); ++i) {
-               _data[i] = 0;
-               _line_size[i] = 0;
+               _data[i] = (uint8_t *) av_malloc (_line_size[i] * lines (i));
        }
 }
 
@@ -210,17 +252,6 @@ SimpleImage::~SimpleImage ()
        av_free (_line_size);
 }
 
-/** Set the size in bytes of each horizontal line of a given component.
- *  @param i Component index.
- *  @param s Size of line in bytes.
- */
-void
-SimpleImage::set_line_size (int i, int s)
-{
-       _line_size[i] = s;
-       _data[i] = (uint8_t *) av_malloc (s * lines (i));
-}
-
 uint8_t **
 SimpleImage::data () const
 {
index 0161d2b01f9850b28f4deb47fc9a184e21f6b10f..d10bcae9efcaf83412ec6795c1d0c90706f50013 100644 (file)
@@ -66,6 +66,7 @@ public:
        int components () const;
        int lines (int) const;
        boost::shared_ptr<RGBFrameImage> scale_and_convert_to_rgb (Size, int, Scaler const *) const;
+       boost::shared_ptr<Image> scale (Size, Scaler const *) const;
        boost::shared_ptr<PostProcessImage> post_process (std::string) const;
        
        void make_black ();
@@ -108,8 +109,6 @@ public:
        int * line_size () const;
        Size size () const;
        
-       void set_line_size (int, int);
-
 private:
        Size _size; ///< size in pixels
        uint8_t** _data; ///< array of pointers to components
index 7cee01ec56c52ef1eb0dd637e5e71a5bee5a570f..df20479c9e0134bc73409c26416d2147e0477013 100644 (file)
@@ -1,3 +1,22 @@
+/*
+    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 <iostream>
 #include <Magick++/Image.h>
 #include "imagemagick_decoder.h"
@@ -5,6 +24,7 @@
 #include "image.h"
 
 using namespace std;
+using namespace boost;
 
 ImageMagickDecoder::ImageMagickDecoder (
        boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> 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<Subtitle> ());
 
        _done = true;
        return false;
index 05dc7f113386caef7384493e0ff9bdf8e9114fe8..809f3aecdb61ce6d6cb24fd7f933ebbf4abbd7a2 100644 (file)
@@ -1,3 +1,22 @@
+/*
+    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 "decoder.h"
 
 namespace Magick {
index d218d08fee0ec62ec4a2e54001a7f9448283b1dc..a241dd6e35165468029afe99e00896b770cf254c 100644 (file)
@@ -48,11 +48,11 @@ J2KStillEncoder::J2KStillEncoder (shared_ptr<const FilmState> s, shared_ptr<cons
 }
 
 void
-J2KStillEncoder::process_video (shared_ptr<Image> yuv, int frame)
+J2KStillEncoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Subtitle> sub)
 {
        pair<string, string> const s = Filter::ffmpeg_strings (_fs->filters);
        DCPVideoFrame* f = new DCPVideoFrame (
-               yuv, _opt->out_size, _opt->padding, _fs->scaler, 0, _fs->frames_per_second, s.second,
+               yuv, sub, _opt->out_size, _opt->padding, _fs->scaler, 0, _fs->frames_per_second, s.second,
                Config::instance()->colour_lut_index(), Config::instance()->j2k_bandwidth(),
                _log
                );
index 7a03e1195800a1a946e11586f16d47ced3471b51..c48b9e69c9e1cbcdbad9f000be595b1cf5c449ac 100644 (file)
@@ -37,7 +37,7 @@ public:
        J2KStillEncoder (boost::shared_ptr<const FilmState>, boost::shared_ptr<const Options>, Log *);
 
        void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format) {}
-       void process_video (boost::shared_ptr<Image>, int);
+       void process_video (boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle>);
        void process_audio (uint8_t *, int) {}
        void process_end () {}
 };
index a1f70a08a338f23d480f637ac5c806fc8aa8f196..7bcac483b7778aaa66ffb85bc9a0cde2432b6656 100644 (file)
@@ -105,7 +105,7 @@ J2KWAVEncoder::close_sound_files ()
 }      
 
 void
-J2KWAVEncoder::process_video (shared_ptr<Image> yuv, int frame)
+J2KWAVEncoder::process_video (shared_ptr<Image> yuv, int frame, shared_ptr<Subtitle> sub)
 {
        boost::mutex::scoped_lock lock (_worker_mutex);
 
@@ -126,7 +126,7 @@ J2KWAVEncoder::process_video (shared_ptr<Image> yuv, int frame)
                TIMING ("adding to queue of %1", _queue.size ());
                _queue.push_back (boost::shared_ptr<DCPVideoFrame> (
                                          new DCPVideoFrame (
-                                                 yuv, _opt->out_size, _opt->padding, _fs->scaler, frame, _fs->frames_per_second, s.second,
+                                                 yuv, sub, _opt->out_size, _opt->padding, _fs->scaler, frame, _fs->frames_per_second, s.second,
                                                  Config::instance()->colour_lut_index (), Config::instance()->j2k_bandwidth (),
                                                  _log
                                                  )
index e11358c2c6328dd3f1ea584f2654d7268a1f9118..87068ad3df05c4e1592f2e352f0ee4b11b05a1d4 100644 (file)
@@ -38,6 +38,7 @@ class ServerDescription;
 class DCPVideoFrame;
 class Image;
 class Log;
+class Subtitle;
 
 /** @class J2KWAVEncoder
  *  @brief An encoder which writes JPEG2000 and WAV files.
@@ -49,7 +50,7 @@ public:
        ~J2KWAVEncoder ();
 
        void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format);
-       void process_video (boost::shared_ptr<Image>, int);
+       void process_video (boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle>);
        void process_audio (uint8_t *, int);
        void process_end ();
 
index 562c887de9b57479c75f702928ac2ae6aec5d313..2db91a177cc08c194644e7b35efc7095e577cddd 100644 (file)
@@ -95,6 +95,11 @@ JobManager::scheduler ()
                {
                        boost::mutex::scoped_lock lm (_mutex);
                        for (list<shared_ptr<Job> >::iterator i = _jobs.begin(); i != _jobs.end(); ++i) {
+                               if ((*i)->running ()) {
+                                       /* Something is already happening */
+                                       break;
+                               }
+                               
                                if ((*i)->is_new()) {
                                        shared_ptr<Job> r = (*i)->required ();
                                        if (!r || r->finished_ok ()) {
index 39068c24f5210c382109494a9bd54281b2290484..2fc5f77ff7b17bbfec492a11ac82b280ba811472 100644 (file)
@@ -56,11 +56,15 @@ public:
         *  @param t true to return a temporary file path, otherwise a permanent one.
         *  @return The path to write this video frame to.
         */
-       std::string frame_out_path (int f, bool t) const {
+       std::string frame_out_path (int f, bool t, std::string e = "") const {
+               if (e.empty ()) {
+                       e = _frame_out_extension;
+               }
+               
                std::stringstream s;
                s << _frame_out_path << "/";
                s.width (8);
-               s << std::setfill('0') << f << _frame_out_extension;
+               s << std::setfill('0') << f << e;
 
                if (t) {
                        s << ".tmp";
index 28236e3e04804e39ccab9b5167e7d2de7101f193..76989d078d3c07eb53030d41f32f06001b1702e6 100644 (file)
@@ -114,17 +114,15 @@ Server::process (shared_ptr<Socket> socket)
        
        shared_ptr<SimpleImage> image (new SimpleImage (pixel_format, in_size));
        
-       for (int i = 0; i < image->components(); ++i) {
-               int line_size;
-               s >> line_size;
-               image->set_line_size (i, line_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);
        }
+
+       /* 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
+               );
        
-       DCPVideoFrame dcp_video_frame (image, out_size, padding, scaler, frame, frames_per_second, post_process, colour_lut_index, j2k_bandwidth, _log);
        shared_ptr<EncodedData> encoded = dcp_video_frame.encode_locally ();
        encoded->send (socket);
        
index 101e0c047ee69cd575827fa8e856e71d33a86086..116c65f7ba073c555445fe23672489ccc35b9953 100644 (file)
@@ -179,7 +179,7 @@ TIFFDecoder::do_pass ()
        _TIFFfree (raster);
        TIFFClose (t);
 
-       process_video (image.frame ());
+       process_video (image.frame (), shared_ptr<Subtitle> ());
 
        ++_iter;
        return false;
index 19e34741d9326f3d72e38506d97dd2b723c7f183..9aa7b68c3c86f4f89b13ce1fefe4708d6200d6c0 100644 (file)
@@ -26,6 +26,7 @@
 #include <sstream>
 #include <iomanip>
 #include <iostream>
+#include <fstream>
 #include <boost/filesystem.hpp>
 #include <tiffio.h>
 #include "tiff_encoder.h"
@@ -34,6 +35,7 @@
 #include "options.h"
 #include "exceptions.h"
 #include "image.h"
+#include "subtitle.h"
 
 using namespace std;
 using namespace boost;
@@ -49,7 +51,7 @@ TIFFEncoder::TIFFEncoder (shared_ptr<const FilmState> s, shared_ptr<const Option
 }
 
 void
-TIFFEncoder::process_video (shared_ptr<Image> image, int frame)
+TIFFEncoder::process_video (shared_ptr<Image> image, int frame, shared_ptr<Subtitle> sub)
 {
        shared_ptr<Image> scaled = image->scale_and_convert_to_rgb (_opt->out_size, _opt->padding, _fs->scaler);
        string tmp_file = _opt->frame_out_path (frame, true);
@@ -72,6 +74,56 @@ TIFFEncoder::process_video (shared_ptr<Image> image, int frame)
 
        TIFFClose (output);
 
-       boost::filesystem::rename (tmp_file, _opt->frame_out_path (frame, false));
+       filesystem::rename (tmp_file, _opt->frame_out_path (frame, false));
+
+       if (sub) {
+               float const x_scale = float (_opt->out_size.width) / _fs->size.width;
+               float const y_scale = float (_opt->out_size.height) / _fs->size.height;
+
+               string tmp_metadata_file = _opt->frame_out_path (frame, false, ".sub");
+               ofstream metadata (tmp_metadata_file.c_str ());
+               
+               list<shared_ptr<SubtitleImage> > images = sub->images ();
+               int n = 0;
+               for (list<shared_ptr<SubtitleImage> >::iterator i = images.begin(); i != images.end(); ++i) {
+                       stringstream ext;
+                       ext << ".sub." << n << ".tiff";
+                       
+                       string tmp_sub_file = _opt->frame_out_path (frame, true, ext.str ());
+                       output = TIFFOpen (tmp_sub_file.c_str(), "w");
+                       if (output == 0) {
+                               throw CreateFileError (tmp_file);
+                       }
+
+                       Size new_size = (*i)->image()->size ();
+                       new_size.width *= x_scale;
+                       new_size.height *= y_scale;
+                       shared_ptr<Image> scaled = (*i)->image()->scale (new_size, _fs->scaler);
+                       
+                       TIFFSetField (output, TIFFTAG_IMAGEWIDTH, scaled->size().width);
+                       TIFFSetField (output, TIFFTAG_IMAGELENGTH, scaled->size().height);
+                       TIFFSetField (output, TIFFTAG_COMPRESSION, COMPRESSION_NONE);
+                       TIFFSetField (output, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
+                       TIFFSetField (output, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB);
+                       TIFFSetField (output, TIFFTAG_BITSPERSAMPLE, 8);
+                       TIFFSetField (output, TIFFTAG_SAMPLESPERPIXEL, 4);
+               
+                       if (TIFFWriteEncodedStrip (output, 0, scaled->data()[0], scaled->size().width * scaled->size().height * 4) == 0) {
+                               throw WriteFileError (tmp_file, 0);
+                       }
+               
+                       TIFFClose (output);
+                       filesystem::rename (tmp_sub_file, _opt->frame_out_path (frame, false, ext.str ()));
+
+                       metadata << "image " << n << "\n"
+                                << "x " << (*i)->position().x << "\n"
+                                << "y " << (*i)->position().y << "\n";
+
+                       metadata.close ();
+                       filesystem::rename (tmp_metadata_file, _opt->frame_out_path (frame, false, ".sub"));
+               }
+
+       }
+       
        frame_done (frame);
 }
index ef1ce25d267c26c3242dab3b37cd5cb72b49eaa1..1c9f33f4ac37bfc0dbc4482119a50e23698263e5 100644 (file)
@@ -37,7 +37,7 @@ public:
        TIFFEncoder (boost::shared_ptr<const FilmState> s, boost::shared_ptr<const Options> o, Log* l);
 
        void process_begin (int64_t audio_channel_layout, AVSampleFormat audio_sample_format) {}
-       void process_video (boost::shared_ptr<Image>, int);
+       void process_video (boost::shared_ptr<Image>, int, boost::shared_ptr<Subtitle>);
        void process_audio (uint8_t *, int) {}
        void process_end () {}
 };
index c809226ce59adbb8a5377d8bcd8b500b205953b9..67292047c31fb10e936521d487f4ab52fd1ed167 100644 (file)
@@ -42,6 +42,7 @@ def build(bld):
                  screen.cc
                 server.cc
                  sound_processor.cc
+                 subtitle.cc
                 thumbs_job.cc
                  tiff_decoder.cc
                 tiff_encoder.cc
index c801d538e9ab52a631eafca271c49df8fe2fdfe0..b14935da3816709d669e4ec482d6100aef64d7d9 100644 (file)
@@ -270,7 +270,6 @@ do_remote_encode (shared_ptr<DCPVideoFrame> frame, ServerDescription* descriptio
 BOOST_AUTO_TEST_CASE (client_server_test)
 {
        shared_ptr<SimpleImage> 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<DCPVideoFrame> frame (
                new DCPVideoFrame (
                        image,
+                       shared_ptr<Subtitle> (),
                        Size (1998, 1080),
                        0,
                        Scaler::from_id ("bicubic"),