Various hacks to subtitles etc.
authorCarl Hetherington <cth@carlh.net>
Sun, 16 Dec 2012 16:26:37 +0000 (16:26 +0000)
committerCarl Hetherington <cth@carlh.net>
Sun, 16 Dec 2012 16:26:37 +0000 (16:26 +0000)
src/lib/ffmpeg_decoder.cc
src/lib/image.cc
src/lib/image.h
src/lib/video_decoder.cc
src/wx/film_viewer.cc
src/wx/film_viewer.h

index 136843190d9cbd1cb46290eb8e5dcd7c209ee671..52848d3fcf8392ac35a062c8dea3cc5c45dbc19b 100644 (file)
@@ -79,6 +79,10 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr<Film> f, shared_ptr<const DecodeOptions
        setup_subtitle ();
 
        _film_connection = f->Changed.connect (bind (&FFmpegDecoder::film_changed, this, _1));
+
+       if (!o->video_sync) {
+               _first_video = 0;
+       }
 }
 
 FFmpegDecoder::~FFmpegDecoder ()
@@ -254,7 +258,7 @@ FFmpegDecoder::pass ()
        avcodec_get_frame_defaults (_frame);
 
        shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
-       
+
        if (_packet.stream_index == _video_stream) {
 
                int frame_finished;
@@ -516,6 +520,7 @@ FFmpegDecoder::set_subtitle_stream (shared_ptr<SubtitleStream> s)
 {
        VideoDecoder::set_subtitle_stream (s);
        setup_subtitle ();
+       OutputChanged ();
 }
 
 void
@@ -551,9 +556,14 @@ FFmpegDecoder::filter_and_emit_video (AVFrame* frame)
 bool
 FFmpegDecoder::seek (SourceFrame f)
 {
-       int64_t const t = static_cast<int64_t>(f) / (av_q2d (_format_context->streams[_video_stream]->time_base) * frames_per_second());
-       int const r = av_seek_frame (_format_context, _video_stream, t, 0);
+       int64_t const vt = static_cast<int64_t>(f) / (av_q2d (_format_context->streams[_video_stream]->time_base) * frames_per_second());
+       int const r = av_seek_frame (_format_context, _video_stream, vt, 0);
+       
        avcodec_flush_buffers (_video_codec_context);
+       if (_subtitle_codec_context) {
+               avcodec_flush_buffers (_subtitle_codec_context);
+       }
+       
        return r < 0;
 }
 
index 748e9ae4ba9f10ce819e18eda2fd55f9bd419590..2e4c18323d22a77430c8a8645783caef286c95c2 100644 (file)
@@ -228,7 +228,7 @@ Image::make_black ()
 }
 
 void
-Image::alpha_blend (shared_ptr<Image> other, Position position)
+Image::alpha_blend (shared_ptr<const Image> other, Position position)
 {
        /* Only implemented for RGBA onto RGB24 so far */
        assert (_pixel_format == PIX_FMT_RGB24 && other->pixel_format() == PIX_FMT_RGBA);
@@ -376,7 +376,7 @@ AlignedImage::AlignedImage (AVPixelFormat f, Size s)
 
 }
 
-AlignedImage::AlignedImage (shared_ptr<Image> im)
+AlignedImage::AlignedImage (shared_ptr<const Image> im)
        : SimpleImage (im->pixel_format(), im->size(), boost::bind (stride_round_up, _1, _2, 32))
 {
        assert (components() == im->components());
@@ -402,7 +402,7 @@ CompactImage::CompactImage (AVPixelFormat f, Size s)
 
 }
 
-CompactImage::CompactImage (shared_ptr<Image> im)
+CompactImage::CompactImage (shared_ptr<const Image> im)
        : SimpleImage (im->pixel_format(), im->size(), boost::bind (stride_round_up, _1, _2, 1))
 {
        assert (components() == im->components());
@@ -459,3 +459,30 @@ FilterBufferImage::size () const
        return Size (_buffer->video->w, _buffer->video->h);
 }
 
+RGBPlusAlphaImage::RGBPlusAlphaImage (shared_ptr<const Image> im)
+       : SimpleImage (im->pixel_format(), im->size(), boost::bind (stride_round_up, _1, _2, 1))
+{
+       assert (im->pixel_format() == PIX_FMT_RGBA);
+
+       _alpha = (uint8_t *) av_malloc (im->size().width * im->size().height);
+
+       uint8_t* in = im->data()[0];
+       uint8_t* out = data()[0];
+       uint8_t* out_alpha = _alpha;
+       for (int y = 0; y < im->size().height; ++y) {
+               uint8_t* in_r = in;
+               for (int x = 0; x < im->size().width; ++x) {
+                       *out++ = *in_r++;
+                       *out++ = *in_r++;
+                       *out++ = *in_r++;
+                       *out_alpha++ = *in_r++;
+               }
+
+               in += im->stride()[0];
+       }
+}
+
+RGBPlusAlphaImage::~RGBPlusAlphaImage ()
+{
+       av_free (_alpha);
+}
index 7c118f338c84f0c52998dda9d814871354c90677..0cd38da11a9b97b0871488801541fc98d332ccdf 100644 (file)
@@ -72,7 +72,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 alpha_blend (boost::shared_ptr<const Image> image, Position pos);
        
        void make_black ();
 
@@ -134,7 +134,7 @@ class AlignedImage : public SimpleImage
 {
 public:
        AlignedImage (AVPixelFormat, Size);
-       AlignedImage (boost::shared_ptr<Image>);
+       AlignedImage (boost::shared_ptr<const Image>);
 };
 
 /** @class CompactImage
@@ -144,7 +144,21 @@ class CompactImage : public SimpleImage
 {
 public:
        CompactImage (AVPixelFormat, Size);
-       CompactImage (boost::shared_ptr<Image>);
+       CompactImage (boost::shared_ptr<const Image>);
+};
+
+class RGBPlusAlphaImage : public SimpleImage
+{
+public:
+       RGBPlusAlphaImage (boost::shared_ptr<const Image>);
+       ~RGBPlusAlphaImage ();
+
+       uint8_t* alpha () const {
+               return _alpha;
+       }
+       
+private:
+       uint8_t* _alpha;
 };
 
 #endif
index d3b441fbfc1d859ef6300ac97455414ddef6d3a1..cb55b4d188a0af99baea677721b417269e628186 100644 (file)
@@ -44,7 +44,7 @@ void
 VideoDecoder::emit_video (shared_ptr<Image> image, SourceFrame f)
 {
        shared_ptr<Subtitle> sub;
-       if (_timed_subtitle && _timed_subtitle->displayed_at (double (video_frame()) / _film->frames_per_second())) {
+       if (_timed_subtitle && _timed_subtitle->displayed_at (f / _film->frames_per_second())) {
                sub = _timed_subtitle->subtitle ();
        }
 
index 8312fa5e5527bcded5dea1373fb245b47e41ffb0..dd0159ff49e63da299e485702112f9634273368a 100644 (file)
@@ -86,12 +86,22 @@ FilmViewer::film_changed (Film::Property p)
        {
                shared_ptr<DecodeOptions> o (new DecodeOptions);
                o->decode_audio = false;
+               o->decode_subtitles = true;
                o->video_sync = false;
                _decoders = decoder_factory (_film, o, 0);
                _decoders.video->Video.connect (bind (&FilmViewer::process_video, this, _1, _2));
                _decoders.video->OutputChanged.connect (boost::bind (&FilmViewer::decoder_changed, this));
+               _decoders.video->set_subtitle_stream (_film->subtitle_stream());
                break;
        }
+       case Film::WITH_SUBTITLES:
+       case Film::SUBTITLE_OFFSET:
+       case Film::SUBTITLE_SCALE:
+               update_from_raw ();
+               break;
+       case Film::SUBTITLE_STREAM:
+               _decoders.video->set_subtitle_stream (_film->subtitle_stream ());
+               break;
        default:
                break;
        }
@@ -115,6 +125,10 @@ FilmViewer::set_film (shared_ptr<Film> f)
        film_changed (Film::CONTENT);
        film_changed (Film::CROP);
        film_changed (Film::FORMAT);
+       film_changed (Film::WITH_SUBTITLES);
+       film_changed (Film::SUBTITLE_OFFSET);
+       film_changed (Film::SUBTITLE_SCALE);
+       film_changed (Film::SUBTITLE_STREAM);
 }
 
 void
@@ -129,10 +143,7 @@ FilmViewer::timer (wxTimerEvent& ev)
        _panel->Refresh ();
        _panel->Update ();
 
-       shared_ptr<Image> last = _display;
-       while (last == _display) {
-               _decoders.video->pass ();
-       }
+       get_frame ();
 
        if (_film->length()) {
                int const new_slider_position = 4096 * _decoders.video->last_source_frame() / _film->length().get();
@@ -147,13 +158,19 @@ void
 FilmViewer::paint_panel (wxPaintEvent& ev)
 {
        wxPaintDC dc (_panel);
-       if (!_display) {
+       if (!_display_frame) {
                return;
        }
 
-       wxImage i (_out_width, _out_height, _display->data()[0], true);
-       wxBitmap b (i);
-       dc.DrawBitmap (b, 0, 0);
+       wxImage frame (_out_width, _out_height, _display_frame->data()[0], true);
+       wxBitmap frame_bitmap (frame);
+       dc.DrawBitmap (frame_bitmap, 0, 0);
+
+       if (_film->with_subtitles() && _display_sub) {
+               wxImage sub (_display_sub->size().width, _display_sub->size().height, _display_sub->data()[0], _display_sub->alpha(), true);
+               wxBitmap sub_bitmap (sub);
+               dc.DrawBitmap (sub_bitmap, _display_sub_position.x, _display_sub_position.y);
+       }
 }
 
 
@@ -171,11 +188,8 @@ FilmViewer::seek_and_update (SourceFrame f)
        if (_decoders.video->seek (f)) {
                return;
        }
-       
-       shared_ptr<Image> last = _display;
-       while (last == _display) {
-               _decoders.video->pass ();
-       }
+
+       get_frame ();
        _panel->Refresh ();
        _panel->Update ();
 }
@@ -192,18 +206,39 @@ FilmViewer::panel_sized (wxSizeEvent& ev)
 void
 FilmViewer::update_from_raw ()
 {
-       if (!_raw) {
+       if (!_raw_frame) {
                return;
        }
 
-       if (_out_width && _out_height) {
-               _display = _raw->scale_and_convert_to_rgb (Size (_out_width, _out_height), 0, Scaler::from_id ("bicubic"));
-       }
+       raw_to_display ();
        
        _panel->Refresh ();
        _panel->Update ();
 }
 
+void
+FilmViewer::raw_to_display ()
+{
+       if (!_out_width || !_out_height) {
+               return;
+       }
+       
+       _display_frame = _raw_frame->scale_and_convert_to_rgb (Size (_out_width, _out_height), 0, _film->scaler());
+
+       if (_raw_sub) {
+               Rect tx = subtitle_transformed_area (
+                       float (_out_width) / _film->size().width,
+                       float (_out_height) / _film->size().height,
+                       _raw_sub->area(), _film->subtitle_offset(), _film->subtitle_scale()
+                       );
+               
+               _display_sub.reset (new RGBPlusAlphaImage (_raw_sub->image()->scale (tx.size(), _film->scaler ())));
+               _display_sub_position = tx.position();
+       } else {
+               _display_sub.reset ();
+       }
+}      
+
 void
 FilmViewer::calculate_sizes ()
 {
@@ -239,8 +274,21 @@ FilmViewer::check_play_state ()
 void
 FilmViewer::process_video (shared_ptr<Image> image, shared_ptr<Subtitle> sub)
 {
-       _raw = image;
-       if (_out_width && _out_height) {
-               _display = _raw->scale_and_convert_to_rgb (Size (_out_width, _out_height), 0, Scaler::from_id ("bicubic"));
+       _raw_frame = image;
+       _raw_sub = sub;
+
+       raw_to_display ();
+}
+
+void
+FilmViewer::get_frame ()
+{
+       if (!_out_width || !_out_height) {
+               return;
+       }
+       
+       shared_ptr<Image> last = _display_frame;
+       while (last == _display_frame) {
+               _decoders.video->pass ();
        }
 }
index 8194d02062932ab930146699991e5e653bc59ce9..2d0e17fa2cf704c84dcf8adaa6317c6ec98a6f38 100644 (file)
@@ -28,6 +28,7 @@
 class wxToggleButton;
 class FFmpegPlayer;
 class Image;
+class RGBPlusAlphaImage;
 class Subtitle;
 
 /** @class FilmViewer
@@ -53,6 +54,8 @@ private:
        void update_from_raw ();
        void decoder_changed ();
        void seek_and_update (SourceFrame);
+       void raw_to_display ();
+       void get_frame ();
 
        boost::shared_ptr<Film> _film;
        
@@ -62,8 +65,11 @@ private:
        wxTimer _timer;
 
        Decoders _decoders;
-       boost::shared_ptr<Image> _raw;
-       boost::shared_ptr<Image> _display;
+       boost::shared_ptr<Image> _raw_frame;
+       boost::shared_ptr<Subtitle> _raw_sub;
+       boost::shared_ptr<Image> _display_frame;
+       boost::shared_ptr<RGBPlusAlphaImage> _display_sub;
+       Position _display_sub_position;
 
        int _out_width;
        int _out_height;