setup_subtitle ();
_film_connection = f->Changed.connect (bind (&FFmpegDecoder::film_changed, this, _1));
+
+ if (!o->video_sync) {
+ _first_video = 0;
+ }
}
FFmpegDecoder::~FFmpegDecoder ()
avcodec_get_frame_defaults (_frame);
shared_ptr<FFmpegAudioStream> ffa = dynamic_pointer_cast<FFmpegAudioStream> (_audio_stream);
-
+
if (_packet.stream_index == _video_stream) {
int frame_finished;
{
VideoDecoder::set_subtitle_stream (s);
setup_subtitle ();
+ OutputChanged ();
}
void
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;
}
}
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);
}
-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());
}
-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());
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);
+}
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 ();
{
public:
AlignedImage (AVPixelFormat, Size);
- AlignedImage (boost::shared_ptr<Image>);
+ AlignedImage (boost::shared_ptr<const Image>);
};
/** @class CompactImage
{
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
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 ();
}
{
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;
}
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
_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();
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);
+ }
}
if (_decoders.video->seek (f)) {
return;
}
-
- shared_ptr<Image> last = _display;
- while (last == _display) {
- _decoders.video->pass ();
- }
+
+ get_frame ();
_panel->Refresh ();
_panel->Update ();
}
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 ()
{
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 ();
}
}
class wxToggleButton;
class FFmpegPlayer;
class Image;
+class RGBPlusAlphaImage;
class Subtitle;
/** @class FilmViewer
void update_from_raw ();
void decoder_changed ();
void seek_and_update (SourceFrame);
+ void raw_to_display ();
+ void get_frame ();
boost::shared_ptr<Film> _film;
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;