diff options
| author | Carl Hetherington <cth@carlh.net> | 2012-12-16 12:18:08 +0000 |
|---|---|---|
| committer | Carl Hetherington <cth@carlh.net> | 2012-12-16 12:18:08 +0000 |
| commit | 9c58fcdb6fd8131c17456dd71c5c277a6b0ae053 (patch) | |
| tree | b18c58859a4b2365be252253e47c578bb1c33bf7 /src | |
| parent | 6375f7268d0acd53c6dc7968e17a15471fba3a45 (diff) | |
Integrated FFmpeg player (slow).
Diffstat (limited to 'src')
| -rw-r--r-- | src/lib/ab_transcoder.cc | 20 | ||||
| -rw-r--r-- | src/lib/ab_transcoder.h | 5 | ||||
| -rw-r--r-- | src/lib/decoder.cc | 6 | ||||
| -rw-r--r-- | src/lib/decoder.h | 1 | ||||
| -rw-r--r-- | src/lib/decoder_factory.cc | 9 | ||||
| -rw-r--r-- | src/lib/decoder_factory.h | 19 | ||||
| -rw-r--r-- | src/lib/examine_content_job.cc | 6 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.cc | 9 | ||||
| -rw-r--r-- | src/lib/ffmpeg_decoder.h | 2 | ||||
| -rw-r--r-- | src/lib/film.cc | 18 | ||||
| -rw-r--r-- | src/lib/transcoder.cc | 18 | ||||
| -rw-r--r-- | src/lib/transcoder.h | 4 | ||||
| -rw-r--r-- | src/wx/ffmpeg_player.cc | 432 | ||||
| -rw-r--r-- | src/wx/ffmpeg_player.h | 102 | ||||
| -rw-r--r-- | src/wx/film_viewer.cc | 163 | ||||
| -rw-r--r-- | src/wx/film_viewer.h | 27 | ||||
| -rw-r--r-- | src/wx/wscript | 1 |
17 files changed, 231 insertions, 611 deletions
diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index 537cb4dd7..d65fcbd4e 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -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; diff --git a/src/lib/ab_transcoder.h b/src/lib/ab_transcoder.h index 9b57e4f73..86094301b 100644 --- a/src/lib/ab_transcoder.h +++ b/src/lib/ab_transcoder.h @@ -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; diff --git a/src/lib/decoder.cc b/src/lib/decoder.cc index 2bacf58e7..577894996 100644 --- a/src/lib/decoder.cc +++ b/src/lib/decoder.cc @@ -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"); +} diff --git a/src/lib/decoder.h b/src/lib/decoder.h index e757e5401..96d3a2014 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -58,6 +58,7 @@ public: virtual ~Decoder () {} virtual bool pass () = 0; + virtual void seek (SourceFrame); protected: /** our Film */ diff --git a/src/lib/decoder_factory.cc b/src/lib/decoder_factory.cc index b2118ef74..8674c6262 100644 --- a/src/lib/decoder_factory.cc +++ b/src/lib/decoder_factory.cc @@ -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))); } diff --git a/src/lib/decoder_factory.h b/src/lib/decoder_factory.h index 1f3690611..88f719ba2 100644 --- a/src/lib/decoder_factory.h +++ b/src/lib/decoder_factory.h @@ -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 diff --git a/src/lib/examine_content_job.cc b/src/lib/examine_content_job.cc index 7e0f7692b..5c565cd88 100644 --- a/src/lib/examine_content_job.cc +++ b/src/lib/examine_content_job.cc @@ -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())); diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 74eb8934c..f4c7d3d85 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -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); } + diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 87eebe1ec..de0a6a67c 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -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 (); diff --git a/src/lib/film.cc b/src/lib/film.cc index 45af7a2ec..ddc7e371c 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -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()); } { diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index 537b9b664..114803fb5 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -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; } diff --git a/src/lib/transcoder.h b/src/lib/transcoder.h index e3ca2bb32..35aac0b50 100644 --- a/src/lib/transcoder.h +++ b/src/lib/transcoder.h @@ -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 index a8e580617..000000000 --- a/src/wx/ffmpeg_player.cc +++ /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 index 4d382717b..000000000 --- a/src/wx/ffmpeg_player.h +++ /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; -}; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index edeb8fd3f..2dc578e65 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -30,9 +30,12 @@ #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")))); } diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index f290166c7..f5427551d 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -23,8 +23,12 @@ #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; }; diff --git a/src/wx/wscript b/src/wx/wscript index 2ab7feee7..4dbb04eea 100644 --- a/src/wx/wscript +++ b/src/wx/wscript @@ -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 |
