Integrated FFmpeg player (slow).
authorCarl Hetherington <cth@carlh.net>
Sun, 16 Dec 2012 12:18:08 +0000 (12:18 +0000)
committerCarl Hetherington <cth@carlh.net>
Sun, 16 Dec 2012 12:18:08 +0000 (12:18 +0000)
17 files changed:
src/lib/ab_transcoder.cc
src/lib/ab_transcoder.h
src/lib/decoder.cc
src/lib/decoder.h
src/lib/decoder_factory.cc
src/lib/decoder_factory.h
src/lib/examine_content_job.cc
src/lib/ffmpeg_decoder.cc
src/lib/ffmpeg_decoder.h
src/lib/film.cc
src/lib/transcoder.cc
src/lib/transcoder.h
src/wx/ffmpeg_player.cc [deleted file]
src/wx/ffmpeg_player.h [deleted file]
src/wx/film_viewer.cc
src/wx/film_viewer.h
src/wx/wscript

index 537cb4dd7b1ed9e15180c0a8b70ac1e02821c5ab..d65fcbd4e0990f1e210622ddd1dbcef62ac69eef 100644 (file)
@@ -67,12 +67,12 @@ ABTranscoder::ABTranscoder (
        }
 
        /* Set up the decoder to use the film's set streams */
-       _da.first->set_subtitle_stream (_film_a->subtitle_stream ());
-       _db.first->set_subtitle_stream (_film_a->subtitle_stream ());
-       _da.second->set_audio_stream (_film_a->audio_stream ());
+       _da.video->set_subtitle_stream (_film_a->subtitle_stream ());
+       _db.video->set_subtitle_stream (_film_a->subtitle_stream ());
+       _da.audio->set_audio_stream (_film_a->audio_stream ());
 
-       _da.first->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2));
-       _db.first->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2));
+       _da.video->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2));
+       _db.video->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2));
 
        if (_matcher) {
                _combiner->connect_video (_matcher);
@@ -82,7 +82,7 @@ ABTranscoder::ABTranscoder (
        }
        
        if (_matcher && _delay_line) {
-               _da.second->connect_audio (_delay_line);
+               _da.audio->connect_audio (_delay_line);
                _delay_line->connect_audio (_matcher);
                _matcher->connect_audio (_gain);
                _gain->connect_audio (_encoder);
@@ -95,11 +95,11 @@ ABTranscoder::go ()
        _encoder->process_begin ();
        
        while (1) {
-               bool const va = _da.first->pass ();
-               bool const vb = _db.first->pass ();
-               bool const a = _da.first->pass ();
+               bool const va = _da.video->pass ();
+               bool const vb = _db.video->pass ();
+               bool const a = _da.audio->pass ();
 
-               _da.first->set_progress ();
+               _da.video->set_progress ();
 
                if (va && vb && a) {
                        break;
index 9b57e4f73497c2accbde989f135fb148a9522b3b..86094301b97902f63d6d4ac1558c575632c121bc 100644 (file)
@@ -25,6 +25,7 @@
 #include <boost/shared_ptr.hpp>
 #include <stdint.h>
 #include "util.h"
+#include "decoder_factory.h"
 
 class Job;
 class Encoder;
@@ -63,8 +64,8 @@ private:
        boost::shared_ptr<const Options> _opt;
        Job* _job;
        boost::shared_ptr<Encoder> _encoder;
-       std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _da;
-       std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _db;
+       Decoders _da;
+       Decoders _db;
        boost::shared_ptr<Combiner> _combiner;
        boost::shared_ptr<Matcher> _matcher;
        boost::shared_ptr<DelayLine> _delay_line;
index 2bacf58e73b929a57948a6097465fd90d4cd15e8..5778949963ccb63cff592626b96094da2e4a6cc5 100644 (file)
@@ -56,3 +56,9 @@ Decoder::Decoder (boost::shared_ptr<Film> f, boost::shared_ptr<const Options> o,
 {
        
 }
+
+void
+Decoder::seek (SourceFrame f)
+{
+       throw DecodeError ("decoder does not support seek");
+}
index e757e5401aaef804d7407c92e4536a3de3213cb6..96d3a201460df5b2b9e9e0be77f44708c430b9d4 100644 (file)
@@ -58,6 +58,7 @@ public:
        virtual ~Decoder () {}
 
        virtual bool pass () = 0;
+       virtual void seek (SourceFrame);
 
 protected:
        /** our Film */
index b2118ef744832649f6a176223153dbc4326b83b3..8674c6262e03aab9e940f3808d03940634d75e8d 100644 (file)
@@ -26,6 +26,7 @@
 #include "imagemagick_decoder.h"
 #include "film.h"
 #include "external_audio_decoder.h"
+#include "decoder_factory.h"
 
 using std::string;
 using std::pair;
@@ -33,14 +34,14 @@ using std::make_pair;
 using boost::shared_ptr;
 using boost::dynamic_pointer_cast;
 
-pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> >
+Decoders
 decoder_factory (
        shared_ptr<Film> f, shared_ptr<const Options> o, Job* j
        )
 {
        if (boost::filesystem::is_directory (f->content_path()) || f->content_type() == STILL) {
                /* A single image file, or a directory of them */
-               return make_pair (
+               return Decoders (
                        shared_ptr<VideoDecoder> (new ImageMagickDecoder (f, o, j)),
                        shared_ptr<AudioDecoder> ()
                        );
@@ -48,8 +49,8 @@ decoder_factory (
 
        shared_ptr<FFmpegDecoder> fd (new FFmpegDecoder (f, o, j));
        if (f->use_content_audio()) {
-               return make_pair (fd, fd);
+               return Decoders (fd, fd);
        }
 
-       return make_pair (fd, shared_ptr<AudioDecoder> (new ExternalAudioDecoder (f, o, j)));
+       return Decoders (fd, shared_ptr<AudioDecoder> (new ExternalAudioDecoder (f, o, j)));
 }
index 1f369061192e3e7c0050b5ef8f5f0c9d9a778ac0..88f719ba230307d3932c7136e132fad2dc06f8df 100644 (file)
@@ -17,6 +17,9 @@
 
 */
 
+#ifndef DVDOMATIC_DECODER_FACTORY_H
+#define DVDOMATIC_DECODER_FACTORY_H
+
 /** @file  src/decoder_factory.h
  *  @brief A method to create appropriate decoders for some content.
  */
@@ -27,6 +30,20 @@ class Job;
 class VideoDecoder;
 class AudioDecoder;
 
-extern std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > decoder_factory (
+struct Decoders {
+       Decoders () {}
+       
+       Decoders (boost::shared_ptr<VideoDecoder> v, boost::shared_ptr<AudioDecoder> a)
+               : video (v)
+               , audio (a)
+       {}
+
+       boost::shared_ptr<VideoDecoder> video;
+       boost::shared_ptr<AudioDecoder> audio;
+};
+
+extern Decoders decoder_factory (
        boost::shared_ptr<Film>, boost::shared_ptr<const Options>, Job *
        );
+
+#endif
index 7e0f7692bea02279e8d9a6fecdf6210a60dc718d..5c565cd88a333a5e00e1c11aa0171a27cb8f28a5 100644 (file)
@@ -73,14 +73,14 @@ ExamineContentJob::run ()
 
        descend (1);
 
-       pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > decoders = decoder_factory (_film, o, this);
+       Decoders decoders = decoder_factory (_film, o, this);
 
        set_progress_unknown ();
-       while (!decoders.first->pass()) {
+       while (!decoders.video->pass()) {
                /* keep going */
        }
 
-       _film->set_length (decoders.first->video_frame());
+       _film->set_length (decoders.video->video_frame());
 
        _film->log()->log (String::compose ("Video length is %1 frames", _film->length()));
 
index 74eb8934ca5e87ffbde809bf3e9fb70b2360014d..f4c7d3d857f5e6cf04a92e2b23ce0ed3171aca28 100644 (file)
@@ -584,6 +584,14 @@ FFmpegDecoder::filter_and_emit_video (AVFrame* frame)
        }
 }
 
+void
+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());
+       av_seek_frame (_format_context, _video_stream, t, 0);
+       avcodec_flush_buffers (_video_codec_context);
+}
+
 shared_ptr<FFmpegAudioStream>
 FFmpegAudioStream::create (string t, optional<int> v)
 {
@@ -636,3 +644,4 @@ FFmpegAudioStream::to_string () const
        return String::compose ("ffmpeg %1 %2 %3 %4", _id, _sample_rate, _channel_layout, _name);
 }
 
+
index 87eebe1ecc4ba7b83f92fb23f096435caf5bdd2b..de0a6a67c377bc7859f1c164ca9c5dab30230a78 100644 (file)
@@ -97,6 +97,8 @@ public:
        void set_audio_stream (boost::shared_ptr<AudioStream>);
        void set_subtitle_stream (boost::shared_ptr<SubtitleStream>);
 
+       void seek (SourceFrame);
+
 private:
 
        bool pass ();
index 45af7a2ec2f86742bd1bea21584ce438c2c6894b..ddc7e371c8aa6bf676673bda979573f8d6f06036 100644 (file)
@@ -847,20 +847,20 @@ Film::set_content (string c)
                shared_ptr<Options> o (new Options ("", "", ""));
                o->out_size = Size (1024, 1024);
                
-               pair<shared_ptr<VideoDecoder>, shared_ptr<AudioDecoder> > d = decoder_factory (shared_from_this(), o, 0);
+               Decoders d = decoder_factory (shared_from_this(), o, 0);
                
-               set_size (d.first->native_size ());
-               set_frames_per_second (d.first->frames_per_second ());
-               set_subtitle_streams (d.first->subtitle_streams ());
-               set_content_audio_streams (d.second->audio_streams ());
+               set_size (d.video->native_size ());
+               set_frames_per_second (d.video->frames_per_second ());
+               set_subtitle_streams (d.video->subtitle_streams ());
+               set_content_audio_streams (d.audio->audio_streams ());
 
                /* Start off with the first audio and subtitle streams */
-               if (!d.second->audio_streams().empty()) {
-                       set_content_audio_stream (d.second->audio_streams().front());
+               if (!d.audio->audio_streams().empty()) {
+                       set_content_audio_stream (d.audio->audio_streams().front());
                }
                
-               if (!d.first->subtitle_streams().empty()) {
-                       set_subtitle_stream (d.first->subtitle_streams().front());
+               if (!d.video->subtitle_streams().empty()) {
+                       set_subtitle_stream (d.video->subtitle_streams().front());
                }
                
                {
index 537b9b66452d6f4b8e30dbe4f824b9983166fb0b..114803fb533892d020a03b64f18b6152cbe08eb0 100644 (file)
@@ -63,18 +63,18 @@ Transcoder::Transcoder (shared_ptr<Film> f, shared_ptr<const Options> o, Job* j,
        }
 
        /* Set up the decoder to use the film's set streams */
-       _decoders.first->set_subtitle_stream (f->subtitle_stream ());
-       _decoders.second->set_audio_stream (f->audio_stream ());
+       _decoders.video->set_subtitle_stream (f->subtitle_stream ());
+       _decoders.audio->set_audio_stream (f->audio_stream ());
 
        if (_matcher) {
-               _decoders.first->connect_video (_matcher);
+               _decoders.video->connect_video (_matcher);
                _matcher->connect_video (_encoder);
        } else {
-               _decoders.first->connect_video (_encoder);
+               _decoders.video->connect_video (_encoder);
        }
        
        if (_matcher && _delay_line) {
-               _decoders.second->connect_audio (_delay_line);
+               _decoders.audio->connect_audio (_delay_line);
                _delay_line->connect_audio (_matcher);
                _matcher->connect_audio (_gain);
                _gain->connect_audio (_encoder);
@@ -93,12 +93,12 @@ Transcoder::go ()
                
                while (1) {
                        if (!done[0]) {
-                               done[0] = _decoders.first->pass ();
-                               _decoders.first->set_progress ();
+                               done[0] = _decoders.video->pass ();
+                               _decoders.video->set_progress ();
                        }
 
-                       if (!done[1] && dynamic_pointer_cast<Decoder> (_decoders.second) != dynamic_pointer_cast<Decoder> (_decoders.first)) {
-                               done[1] = _decoders.second->pass ();
+                       if (!done[1] && dynamic_pointer_cast<Decoder> (_decoders.audio) != dynamic_pointer_cast<Decoder> (_decoders.video)) {
+                               done[1] = _decoders.audio->pass ();
                        } else {
                                done[1] = true;
                        }
index e3ca2bb32085b128bb0c47673bcab7b55fed804a..35aac0b501f5c21381fb4b1eac74af228991d602 100644 (file)
@@ -24,6 +24,8 @@
  *  as a parameter to the constructor.
  */
 
+#include "decoder_factory.h"
+
 class Film;
 class Job;
 class Encoder;
@@ -55,7 +57,7 @@ protected:
        /** The encoder that we will use */
        boost::shared_ptr<Encoder> _encoder;
        /** The decoders that we will use */
-       std::pair<boost::shared_ptr<VideoDecoder>, boost::shared_ptr<AudioDecoder> > _decoders;
+       Decoders _decoders;
        boost::shared_ptr<Matcher> _matcher;
        boost::shared_ptr<DelayLine> _delay_line;
        boost::shared_ptr<Gain> _gain;
diff --git a/src/wx/ffmpeg_player.cc b/src/wx/ffmpeg_player.cc
deleted file mode 100644 (file)
index a8e5806..0000000
+++ /dev/null
@@ -1,432 +0,0 @@
-/*
-    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 <wx/wx.h>
-#include <wx/tglbtn.h>
-#include <iostream>
-#include <stdint.h>
-extern "C" {
-#include <libavcodec/avcodec.h>        
-#include <libavformat/avformat.h>
-#include <libswscale/swscale.h>
-}
-#include "lib/exceptions.h"
-#include "wx/ffmpeg_player.h"
-
-using namespace std;
-
-FFmpegPlayer::FFmpegPlayer (wxWindow* parent)
-       : _format_context (0)
-       , _video_stream (-1)
-       , _frame (0)
-       , _video_codec_context (0)
-       , _video_codec (0)
-       , _scale_context (0)
-       , _frame_valid (false)
-       , _last_frame_in_seconds (0)
-       , _panel (new wxPanel (parent))
-       , _slider (new wxSlider (parent, wxID_ANY, 0, 0, 4096))
-       , _play_button (new wxToggleButton (parent, wxID_ANY, wxT ("Play")))
-       , _panel_width (0)
-       , _panel_height (0)
-       , _full_width (0)
-       , _full_height (0)
-       , _top_crop_in_source (0)
-       , _bottom_crop_in_source (0)
-       , _left_crop_in_source (0)
-       , _right_crop_in_source (0)
-       , _ratio (1.85)
-{
-       _rgb[0] = 0;
-
-       avcodec_register_all ();
-       av_register_all ();
-       
-       _frame = avcodec_alloc_frame ();
-       if (!_frame) {
-               throw DecodeError ("could not allocate frame");
-       }
-       
-       _panel->Bind (wxEVT_PAINT, &FFmpegPlayer::paint_panel, this);
-       _panel->Bind (wxEVT_SIZE, &FFmpegPlayer::panel_sized, this);
-       _slider->Bind (wxEVT_SCROLL_THUMBTRACK, &FFmpegPlayer::slider_moved, this);
-       _slider->Bind (wxEVT_SCROLL_PAGEUP, &FFmpegPlayer::slider_moved, this);
-       _slider->Bind (wxEVT_SCROLL_PAGEDOWN, &FFmpegPlayer::slider_moved, this);
-       _play_button->Bind (wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, &FFmpegPlayer::play_clicked, this);
-       _timer.Bind (wxEVT_TIMER, &FFmpegPlayer::timer, this);
-}
-
-FFmpegPlayer::~FFmpegPlayer ()
-{
-       delete[] _rgb[0];
-       
-       if (_scale_context) {
-               sws_freeContext (_scale_context);
-       }
-       
-       if (_video_codec_context) {
-               avcodec_close (_video_codec_context);
-       }
-       
-       av_free (_frame);
-
-       if (_format_context) {
-               avformat_close_input (&_format_context);
-       }
-}
-
-void
-FFmpegPlayer::timer (wxTimerEvent& ev)
-{
-       if (!can_display ()) {
-               return;
-       }
-       
-       _panel->Refresh ();
-       _panel->Update ();
-       decode_frame ();
-       convert_frame ();
-
-       if (_last_frame_in_seconds) {
-               double const video_length_in_seconds = static_cast<double>(_format_context->duration) / AV_TIME_BASE;
-               int const new_slider_position = 4096 * _last_frame_in_seconds / video_length_in_seconds;
-               if (new_slider_position != _slider->GetValue()) {
-                       _slider->SetValue (new_slider_position);
-               }
-       }
-}
-
-void
-FFmpegPlayer::update_panel ()
-{
-       _panel->Refresh ();
-       _panel->Update ();
-}
-
-void
-FFmpegPlayer::decode_frame ()
-{
-       assert (_format_context);
-       
-       while (1) {
-               int r = av_read_frame (_format_context, &_packet);
-               if (r < 0) {
-                       return;
-               }
-               
-               avcodec_get_frame_defaults (_frame);
-               if (_packet.stream_index == _video_stream) {
-                       int frame_finished;
-                       int const r = avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet);
-                       if (r >= 0 && frame_finished) {
-                               _frame_valid = true;
-                               _last_frame_in_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base)
-                                       * av_frame_get_best_effort_timestamp(_frame);
-                               av_free_packet (&_packet);
-                               return;
-                       }
-               }
-               
-               av_free_packet (&_packet);
-       }
-}
-
-void
-FFmpegPlayer::convert_frame ()
-{
-       if (!_scale_context || !_rgb[0] || !_frame_valid) {
-               return;
-       }
-       
-       sws_scale (
-               _scale_context,
-               _frame->data, _frame->linesize,
-               0, _video_codec_context->height,
-               _rgb, _rgb_stride
-               );
-       
-       uint8_t* in = _rgb[0];
-       uint8_t* out = _rgb[0];
-       
-       in += top_crop_in_view() * _full_width * 3;
-       for (int y = 0; y < cropped_height_in_view(); ++y) {
-               /* in is the start of the appropriate full-width line */
-               memmove (out, in + left_crop_in_view() * 3, cropped_width_in_view() * 3);
-               in += _full_width * 3;
-               out += cropped_width_in_view() * 3;
-       }
-}
-
-void
-FFmpegPlayer::paint_panel (wxPaintEvent& ev)
-{
-       wxPaintDC dc (_panel);
-       
-       wxImage i (cropped_width_in_view(), cropped_height_in_view(), _rgb[0], true);
-       wxBitmap b (i);
-       dc.DrawBitmap (b, 0, 0);
-}
-
-void
-FFmpegPlayer::slider_moved (wxCommandEvent& ev)
-{
-       if (!can_display ()) {
-               return;
-       }
-
-       double const video_length_in_seconds = static_cast<double>(_format_context->duration) / AV_TIME_BASE;
-       double const new_position_in_seconds = video_length_in_seconds * _slider->GetValue() / 4096;
-       int64_t const t = static_cast<int64_t>(new_position_in_seconds) / av_q2d (_format_context->streams[_video_stream]->time_base);
-       av_seek_frame (_format_context, _video_stream, t, 0);
-       avcodec_flush_buffers (_video_codec_context);
-
-       decode_frame ();
-       convert_frame ();
-       update_panel ();
-}
-
-float
-FFmpegPlayer::frames_per_second () const
-{
-       assert (_format_context);
-       
-       AVStream* s = _format_context->streams[_video_stream];
-       
-       if (s->avg_frame_rate.num && s->avg_frame_rate.den) {
-               return av_q2d (s->avg_frame_rate);
-       }
-       
-       return av_q2d (s->r_frame_rate);
-}
-
-void
-FFmpegPlayer::allocate_buffer_and_scaler ()
-{
-       if (!_format_context || !_panel_width || !_panel_height) {
-               return;
-       }
-
-       float const panel_ratio = static_cast<float> (_panel_width) / _panel_height;
-       
-       int new_width;
-       int new_height;
-       if (panel_ratio < _ratio) {
-               /* panel is less widescreen than the target ratio; clamp width */
-               new_width = _panel_width;
-               new_height = new_width / _ratio;
-       } else {
-               /* panel is more widescreen than the target ratio; clamp height */
-               new_height = _panel_height;
-               new_width = new_height * _ratio;
-       }
-
-        if (new_width == 0 || new_height == 0) {
-            return;
-       }
-       
-       _full_height = new_height + ((_top_crop_in_source + _bottom_crop_in_source) * height_source_to_view_scaling());
-       _full_width = new_width + ((_left_crop_in_source + _right_crop_in_source) * width_source_to_view_scaling());
-       
-       delete[] _rgb[0];
-       
-       _rgb[0] = new uint8_t[_full_width * _full_height * 3];
-        memset (_rgb[0], 0, _full_width * _full_height * 3);
-       _rgb_stride[0] = _full_width * 3;
-       
-       if (_scale_context) {
-               sws_freeContext (_scale_context);
-       }
-       
-       _scale_context = sws_getContext (
-               _video_codec_context->width, _video_codec_context->height, _video_codec_context->pix_fmt,
-               _full_width, _full_height, PIX_FMT_RGB24,
-               SWS_BICUBIC, 0, 0, 0
-               );
-
-       if (!_scale_context) {
-               throw DecodeError ("could not create scaler");
-       }
-}
-
-float
-FFmpegPlayer::width_source_to_view_scaling () const
-{
-       return static_cast<float> (_full_width) / _video_codec_context->width;
-}
-
-float
-FFmpegPlayer::height_source_to_view_scaling () const
-{
-       return static_cast<float> (_full_height) / _video_codec_context->height;
-}
-
-int
-FFmpegPlayer::cropped_width_in_view () const
-{
-       return _full_width - ((_left_crop_in_source + _right_crop_in_source) * width_source_to_view_scaling());
-}
-
-int
-FFmpegPlayer::cropped_height_in_view () const
-{
-       return _full_height - ((_top_crop_in_source + _bottom_crop_in_source) * height_source_to_view_scaling());
-}
-
-int
-FFmpegPlayer::left_crop_in_view () const
-{
-       return _left_crop_in_source * width_source_to_view_scaling();
-}
-
-int
-FFmpegPlayer::top_crop_in_view () const
-{
-       return _top_crop_in_source * height_source_to_view_scaling();
-}
-
-void
-FFmpegPlayer::panel_sized (wxSizeEvent& ev)
-{
-       _panel_width = ev.GetSize().GetWidth();
-       _panel_height = ev.GetSize().GetHeight();
-       allocate_buffer_and_scaler ();
-
-       convert_frame ();
-       update_panel ();
-}
-
-void
-FFmpegPlayer::set_file (string f)
-{
-       if (_video_codec_context) {
-               avcodec_close (_video_codec_context);
-       }
-
-       if (_format_context) {
-               avformat_close_input (&_format_context);
-       }
-       
-       if (avformat_open_input (&_format_context, f.c_str(), 0, 0) < 0) {
-               throw OpenFileError (f);
-       }
-       
-       if (avformat_find_stream_info (_format_context, 0) < 0) {
-               throw DecodeError ("could not find stream information");
-       }
-       
-       for (uint32_t i = 0; i < _format_context->nb_streams; ++i) {
-               AVStream* s = _format_context->streams[i];
-               if (s->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
-                       _video_stream = i;
-               } else if (s->codec->codec_type == AVMEDIA_TYPE_SUBTITLE) {
-                       /* XXX */
-               }
-       }
-       
-       if (_video_stream < 0) {
-               throw DecodeError ("could not find video stream");
-       }
-       
-       _video_codec_context = _format_context->streams[_video_stream]->codec;
-       _video_codec = avcodec_find_decoder (_video_codec_context->codec_id);
-       
-       if (_video_codec == 0) {
-               throw DecodeError ("could not find video stream");
-       }
-       
-       if (avcodec_open2 (_video_codec_context, _video_codec, 0) < 0) {
-               throw DecodeError ("could not open video decoder");
-       }
-
-       allocate_buffer_and_scaler ();
-       check_play_state ();
-       update_panel ();
-}
-
-void
-FFmpegPlayer::set_top_crop (int t)
-{
-       _top_crop_in_source = t;
-
-       allocate_buffer_and_scaler ();
-       convert_frame ();
-       update_panel ();
-}
-
-void
-FFmpegPlayer::set_bottom_crop (int b)
-{
-       _bottom_crop_in_source = b;
-
-       allocate_buffer_and_scaler ();
-       convert_frame ();
-       update_panel ();
-}
-
-void
-FFmpegPlayer::set_left_crop (int l)
-{
-       _left_crop_in_source = l;
-
-       allocate_buffer_and_scaler ();
-       convert_frame ();
-       update_panel ();
-}
-
-void
-FFmpegPlayer::set_right_crop (int r)
-{
-       _right_crop_in_source = r;
-
-       allocate_buffer_and_scaler ();
-       convert_frame ();
-       update_panel ();
-}
-
-void
-FFmpegPlayer::set_ratio (float r)
-{
-       _ratio = r;
-
-       allocate_buffer_and_scaler ();
-       convert_frame ();
-       update_panel ();
-}
-
-void
-FFmpegPlayer::play_clicked (wxCommandEvent &)
-{
-       check_play_state ();
-}
-
-void
-FFmpegPlayer::check_play_state ()
-{
-       if (_play_button->GetValue()) {
-               _timer.Start (1000 / frames_per_second());
-       } else {
-               _timer.Stop ();
-       }
-}
-
-bool
-FFmpegPlayer::can_display () const
-{
-       return (_format_context && _scale_context);
-}
diff --git a/src/wx/ffmpeg_player.h b/src/wx/ffmpeg_player.h
deleted file mode 100644 (file)
index 4d38271..0000000
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
-    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 <wx/wx.h>
-#include <stdint.h>
-extern "C" {
-#include <libavcodec/avcodec.h>        
-#include <libavformat/avformat.h>
-#include <libswscale/swscale.h>
-}
-
-class wxToggleButton;
-
-class FFmpegPlayer
-{
-public:
-       FFmpegPlayer (wxWindow* parent);
-       ~FFmpegPlayer ();
-
-       void set_file (std::string);
-
-       wxPanel* panel () const {
-               return _panel;
-       }
-
-       wxSlider* slider () const {
-               return _slider;
-       }
-
-       wxToggleButton* play_button () const {
-               return _play_button;
-       }
-
-       void set_top_crop (int t);
-       void set_bottom_crop (int b);
-       void set_left_crop (int l);
-       void set_right_crop (int r);
-       void set_ratio (float r);
-
-private:
-       void timer (wxTimerEvent& ev);
-       void decode_frame ();
-       void convert_frame ();
-       void paint_panel (wxPaintEvent& ev);
-       void slider_moved (wxCommandEvent& ev);
-       float frames_per_second () const;
-       void allocate_buffer_and_scaler ();
-       float width_source_to_view_scaling () const;
-       float height_source_to_view_scaling () const;
-       int cropped_width_in_view () const;
-       int cropped_height_in_view () const;
-       int left_crop_in_view () const;
-       int top_crop_in_view () const;
-       void panel_sized (wxSizeEvent &);
-       void play_clicked (wxCommandEvent &);
-       void check_play_state ();
-       void update_panel ();
-       bool can_display () const;
-
-       AVFormatContext* _format_context;
-       int _video_stream;
-       AVFrame* _frame;
-       AVCodecContext* _video_codec_context;
-       AVCodec* _video_codec;
-       AVPacket _packet;
-       struct SwsContext* _scale_context;
-       bool _frame_valid;
-       uint8_t* _rgb[1];
-       int _rgb_stride[1];
-       double _last_frame_in_seconds;
-
-       wxPanel* _panel;
-       wxSlider* _slider;
-       wxToggleButton* _play_button;
-       
-       wxTimer _timer;
-       int _panel_width;
-       int _panel_height;
-       int _full_width;
-       int _full_height;
-       int _top_crop_in_source;
-       int _bottom_crop_in_source;
-       int _left_crop_in_source;
-       int _right_crop_in_source;
-       float _ratio;
-};
index edeb8fd3f9e9d2f040fd5e94360fe317717d4485..2dc578e658e5ee9d382a64f8901ddf822eb05e2c 100644 (file)
 #include "lib/job_manager.h"
 #include "lib/options.h"
 #include "lib/subtitle.h"
+#include "lib/image.h"
+#include "lib/scaler.h"
 #include "film_viewer.h"
 #include "wx_util.h"
 #include "ffmpeg_player.h"
+#include "video_decoder.h"
 
 using std::string;
 using std::pair;
@@ -41,69 +44,157 @@ using boost::shared_ptr;
 
 FilmViewer::FilmViewer (shared_ptr<Film> f, wxWindow* p)
        : wxPanel (p)
-       , _player (new FFmpegPlayer (this))
+       , _panel (new wxPanel (this))
+       , _slider (new wxSlider (this, wxID_ANY, 0, 0, 4096))
+       , _play_button (new wxToggleButton (this, wxID_ANY, wxT ("Play")))
+       , _out_width (0)
+       , _out_height (0)
+       , _panel_width (0)
+       , _panel_height (0)
 {
        wxBoxSizer* v_sizer = new wxBoxSizer (wxVERTICAL);
        SetSizer (v_sizer);
 
-       v_sizer->Add (_player->panel(), 1, wxEXPAND);
+       v_sizer->Add (_panel, 1, wxEXPAND);
 
        wxBoxSizer* h_sizer = new wxBoxSizer (wxHORIZONTAL);
-       h_sizer->Add (_player->play_button(), 0, wxEXPAND);
-       h_sizer->Add (_player->slider(), 1, wxEXPAND);
+       h_sizer->Add (_play_button, 0, wxEXPAND);
+       h_sizer->Add (_slider, 1, wxEXPAND);
 
        v_sizer->Add (h_sizer, 0, wxEXPAND);
 
+       _panel->Bind (wxEVT_PAINT, &FilmViewer::paint_panel, this);
+       _panel->Bind (wxEVT_SIZE, &FilmViewer::panel_sized, this);
+       _slider->Bind (wxEVT_SCROLL_THUMBTRACK, &FilmViewer::slider_moved, this);
+       _slider->Bind (wxEVT_SCROLL_PAGEUP, &FilmViewer::slider_moved, this);
+       _slider->Bind (wxEVT_SCROLL_PAGEDOWN, &FilmViewer::slider_moved, this);
+       _play_button->Bind (wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, &FilmViewer::play_clicked, this);
+       _timer.Bind (wxEVT_TIMER, &FilmViewer::timer, this);
+
        set_film (_film);
 }
 
+
 void
-FilmViewer::film_changed (Film::Property p)
+FilmViewer::set_film (shared_ptr<Film> f)
 {
-       ensure_ui_thread ();
+       if (_film == f) {
+               return;
+       }
        
-       switch (p) {
-               
-       case Film::CONTENT:
-               _player->set_file (_film->content_path ());
-               break;
-               
-       case Film::CROP:
-       {
-               Crop c = _film->crop ();
-               _player->set_left_crop (c.left);
-               _player->set_right_crop (c.right);
-               _player->set_top_crop (c.top);
-               _player->set_bottom_crop (c.bottom);
+       _film = f;
+
+       if (!_film) {
+               return;
+       }
+
+       /* XXX: Options is not decoder-specific at all */
+       shared_ptr<Options> o (new Options ("", "", ""));
+       o->decode_audio = false;
+       _decoders = decoder_factory (_film, o, 0);
+       _decoders.video->Video.connect (bind (&FilmViewer::process_video, this, _1, _2));
+}
+
+void
+FilmViewer::timer (wxTimerEvent& ev)
+{
+       _panel->Refresh ();
+       _panel->Update ();
+
+       shared_ptr<Image> last = _display;
+       while (last == _display) {
+               _decoders.video->pass ();
        }
-       break;
 
-       case Film::FORMAT:
-               if (_film->format()) {
-                       _player->set_ratio (_film->format()->ratio_as_float(_film));
+#if 0  
+       if (_last_frame_in_seconds) {
+               double const video_length_in_seconds = static_cast<double>(_format_context->duration) / AV_TIME_BASE;
+               int const new_slider_position = 4096 * _last_frame_in_seconds / video_length_in_seconds;
+               if (new_slider_position != _slider->GetValue()) {
+                       _slider->SetValue (new_slider_position);
                }
-               break;
-               
-       default:
-               break;
        }
+#endif 
 }
 
+
 void
-FilmViewer::set_film (shared_ptr<Film> f)
+FilmViewer::paint_panel (wxPaintEvent& ev)
 {
-       if (_film == f) {
+       wxPaintDC dc (_panel);
+       if (!_display) {
                return;
        }
-       
-       _film = f;
 
-       if (!_film) {
+       wxImage i (_out_width, _out_height, _display->data()[0], true);
+       wxBitmap b (i);
+       dc.DrawBitmap (b, 0, 0);
+}
+
+
+void
+FilmViewer::slider_moved (wxCommandEvent& ev)
+{
+       _decoders.video->seek (_slider->GetValue() * _film->length().get() / 4096);
+       shared_ptr<Image> last = _display;
+       while (last == _display) {
+               _decoders.video->pass ();
+       }
+       _panel->Refresh ();
+       _panel->Update ();
+}
+
+void
+FilmViewer::panel_sized (wxSizeEvent& ev)
+{
+       _panel_width = ev.GetSize().GetWidth();
+       _panel_height = ev.GetSize().GetHeight();
+       calculate_sizes ();
+
+       if (!_raw) {
                return;
        }
 
-       _film->Changed.connect (bind (&FilmViewer::film_changed, this, _1));
-       film_changed (Film::CONTENT);
-       film_changed (Film::CROP);
-       film_changed (Film::FORMAT);
+       _display = _raw->scale_and_convert_to_rgb (Size (_out_width, _out_height), 0, Scaler::from_id ("bicubic"));
+       _panel->Refresh ();
+       _panel->Update ();
+}
+
+void
+FilmViewer::calculate_sizes ()
+{
+       float const panel_ratio = static_cast<float> (_panel_width) / _panel_height;
+       float const film_ratio = _film->format() ? _film->format()->ratio_as_float(_film) : 1.78;
+       if (panel_ratio < film_ratio) {
+               /* panel is less widscreen than the film; clamp width */
+               _out_width = _panel_width;
+               _out_height = _out_width / film_ratio;
+       } else {
+               /* panel is more widescreen than the film; clamp heignt */
+               _out_height = _panel_height;
+               _out_width = _out_height * film_ratio;
+       }
+}
+
+void
+FilmViewer::play_clicked (wxCommandEvent &)
+{
+       check_play_state ();
+}
+
+void
+FilmViewer::check_play_state ()
+{
+       if (_play_button->GetValue()) {
+               _timer.Start (1000 / _film->frames_per_second());
+       } else {
+               _timer.Stop ();
+       }
+}
+
+void
+FilmViewer::process_video (shared_ptr<Image> image, shared_ptr<Subtitle> sub)
+{
+       _raw = image;
+       _display.reset (new CompactImage (_raw->scale_and_convert_to_rgb (Size (_out_width, _out_height), 0, Scaler::from_id ("bicubic"))));
 }
index f290166c738c966f8a1bb8b1690573a50bd88c2a..f5427551db64319413d3407ef1bc666820514215 100644 (file)
 
 #include <wx/wx.h>
 #include "lib/film.h"
+#include "lib/decoder_factory.h"
 
+class wxToggleButton;
 class FFmpegPlayer;
+class Image;
+class Subtitle;
 
 /** @class FilmViewer
  *  @brief A wx widget to view a preview of a Film.
@@ -38,7 +42,28 @@ public:
 
 private:
        void film_changed (Film::Property);
+       void paint_panel (wxPaintEvent &);
+       void panel_sized (wxSizeEvent &);
+       void slider_moved (wxCommandEvent &);
+       void play_clicked (wxCommandEvent &);
+       void timer (wxTimerEvent &);
+       void process_video (boost::shared_ptr<Image>, boost::shared_ptr<Subtitle>);
+       void calculate_sizes ();
+       void check_play_state ();
 
        boost::shared_ptr<Film> _film;
-       FFmpegPlayer* _player;
+       
+       wxPanel* _panel;
+       wxSlider* _slider;
+       wxToggleButton* _play_button;
+       wxTimer _timer;
+
+       Decoders _decoders;
+       boost::shared_ptr<Image> _raw;
+       boost::shared_ptr<Image> _display;
+
+       int _out_width;
+       int _out_height;
+       int _panel_width;
+       int _panel_height;
 };
index 2ab7feee787b7323844fff4233ab92399e697c52..4dbb04eeacaa4cc0da3155f09e6243e5934b7dbb 100644 (file)
@@ -20,7 +20,6 @@ def build(bld):
                  film_viewer.cc
                  filter_dialog.cc
                  filter_view.cc
-                 ffmpeg_player.cc
                  gain_calculator_dialog.cc
                  job_manager_view.cc
                  job_wrapper.cc