From 59de84a29b81ac32477a4b804ca8bb9ec2760e67 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 00:46:10 +0000 Subject: First cut. --- src/lib/ffmpeg_decoder.cc | 126 ++++++---------------------------------------- 1 file changed, 16 insertions(+), 110 deletions(-) (limited to 'src/lib/ffmpeg_decoder.cc') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 32c8e224a..f801821e9 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -80,10 +80,6 @@ FFmpegDecoder::FFmpegDecoder (shared_ptr f, DecodeOptions o) setup_video (); setup_audio (); setup_subtitle (); - - if (!o.video_sync) { - _first_video = 0; - } } FFmpegDecoder::~FFmpegDecoder () @@ -240,7 +236,7 @@ FFmpegDecoder::pass () if (_opt.decode_video) { while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - filter_and_emit_video (_frame); + filter_and_emit_video (); } } @@ -265,16 +261,12 @@ FFmpegDecoder::pass () _film->log()->log (String::compose (N_("Used only %1 bytes of %2 in packet"), r, _packet.size)); } - if (_opt.video_sync) { - out_with_sync (); - } else { - filter_and_emit_video (_frame); - } + filter_and_emit_video (); } } else if (ffa && _packet.stream_index == ffa->id() && _opt.decode_audio) { decode_audio_packet (); - } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream->id() && _opt.decode_subtitles && _first_video) { + } else if (_subtitle_stream && _packet.stream_index == _subtitle_stream->id() && _opt.decode_subtitles) { int got_subtitle; AVSubtitle sub; @@ -504,29 +496,29 @@ FFmpegDecoder::set_subtitle_stream (shared_ptr s) } void -FFmpegDecoder::filter_and_emit_video (AVFrame* frame) +FFmpegDecoder::filter_and_emit_video () { boost::mutex::scoped_lock lm (_filter_graphs_mutex); shared_ptr graph; list >::iterator i = _filter_graphs.begin(); - while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (frame->width, frame->height), (AVPixelFormat) frame->format)) { + while (i != _filter_graphs.end() && !(*i)->can_process (libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)) { ++i; } if (i == _filter_graphs.end ()) { - graph.reset (new FilterGraph (_film, this, libdcp::Size (frame->width, frame->height), (AVPixelFormat) frame->format)); + graph.reset (new FilterGraph (_film, this, libdcp::Size (_frame->width, _frame->height), (AVPixelFormat) _frame->format)); _filter_graphs.push_back (graph); - _film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), frame->width, frame->height, frame->format)); + _film->log()->log (String::compose (N_("New graph for %1x%2, pixel format %3"), _frame->width, _frame->height, _frame->format)); } else { graph = *i; } - list > images = graph->process (frame); + list > images = graph->process (_frame); for (list >::iterator i = images.begin(); i != images.end(); ++i) { - emit_video (*i, frame_time ()); + emit_video (*i, av_frame_get_best_effort_timestamp (_frame) * av_q2d (_format_context->streams[_video_stream]->time_base)); } } @@ -613,53 +605,6 @@ FFmpegAudioStream::to_string () const return String::compose (N_("ffmpeg %1 %2 %3 %4"), _id, _sample_rate, _channel_layout, _name); } -void -FFmpegDecoder::out_with_sync () -{ - /* Where we are in the output, in seconds */ - double const out_pts_seconds = video_frame() / frames_per_second(); - - /* Where we are in the source, in seconds */ - double const source_pts_seconds = av_q2d (_format_context->streams[_packet.stream_index]->time_base) - * av_frame_get_best_effort_timestamp(_frame); - - _film->log()->log ( - String::compose (N_("Source video frame ready; source at %1, output at %2"), source_pts_seconds, out_pts_seconds), - Log::VERBOSE - ); - - if (!_first_video) { - _first_video = source_pts_seconds; - } - - /* Difference between where we are and where we should be */ - double const delta = source_pts_seconds - _first_video.get() - out_pts_seconds; - double const one_frame = 1 / frames_per_second(); - - /* Insert frames if required to get out_pts_seconds up to pts_seconds */ - if (delta > one_frame) { - int const extra = rint (delta / one_frame); - for (int i = 0; i < extra; ++i) { - /* XXX: timestamp is wrong */ - repeat_last_video (source_pts_seconds); - _film->log()->log ( - String::compose ( - N_("Extra video frame inserted at %1s; source frame %2, source PTS %3 (at %4 fps)"), - out_pts_seconds, video_frame(), source_pts_seconds, frames_per_second() - ) - ); - } - } - - if (delta > -one_frame) { - /* Process this frame */ - filter_and_emit_video (_frame); - } else { - /* Otherwise we are omitting a frame to keep things right */ - _film->log()->log (String::compose (N_("Frame removed at %1s"), out_pts_seconds)); - } -} - void FFmpegDecoder::film_changed (Film::Property p) { @@ -685,12 +630,6 @@ FFmpegDecoder::length () const return (double(_format_context->duration) / AV_TIME_BASE) * frames_per_second(); } -double -FFmpegDecoder::frame_time () const -{ - return av_frame_get_best_effort_timestamp(_frame) * av_q2d (_format_context->streams[_video_stream]->time_base); -} - void FFmpegDecoder::decode_audio_packet () { @@ -707,54 +646,21 @@ FFmpegDecoder::decode_audio_packet () int frame_finished; int const decode_result = avcodec_decode_audio4 (_audio_codec_context, _frame, &frame_finished, ©_packet); - if (decode_result >= 0 && frame_finished) { - - /* Where we are in the source, in seconds */ - double const source_pts_seconds = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base) - * av_frame_get_best_effort_timestamp(_frame); - - /* We only decode audio if we've had our first video packet through, and if it - was before this packet. Until then audio is thrown away. - */ + if (decode_result >= 0) { + if (frame_finished) { - if ((_first_video && _first_video.get() <= source_pts_seconds) || !_opt.decode_video) { - - if (!_first_audio && _opt.decode_video) { - _first_audio = source_pts_seconds; - - /* This is our first audio frame, and if we've arrived here we must have had our - first video frame. Push some silence to make up any gap between our first - video frame and our first audio. - */ - - /* frames of silence that we must push */ - int const s = rint ((_first_audio.get() - _first_video.get()) * ffa->sample_rate ()); - - _film->log()->log ( - String::compose ( - N_("First video at %1, first audio at %2, pushing %3 audio frames of silence for %4 channels (%5 bytes per sample)"), - _first_video.get(), _first_audio.get(), s, ffa->channels(), bytes_per_audio_sample() - ) - ); - - if (s) { - shared_ptr audio (new AudioBuffers (ffa->channels(), s)); - audio->make_silent (); - /* XXX: this time stamp is wrong */ - Audio (audio, source_pts_seconds); - } - } + /* Where we are in the source, in seconds */ + double const source_pts_seconds = av_q2d (_format_context->streams[copy_packet.stream_index]->time_base) + * av_frame_get_best_effort_timestamp(_frame); int const data_size = av_samples_get_buffer_size ( 0, _audio_codec_context->channels, _frame->nb_samples, audio_sample_format (), 1 ); assert (_audio_codec_context->channels == _film->audio_channels()); - Audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds ); + Audio (deinterleave_audio (_frame->data, data_size), source_pts_seconds); } - } - - if (decode_result >= 0) { + copy_packet.data += decode_result; copy_packet.size -= decode_result; } -- cgit v1.2.3 From be9c85c16c19b6107c33e23eae1cc9405d5203a2 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 14:38:00 +0000 Subject: Drop frames without PTS and only try to fish out leftover frames for codecs with CAP_DELAY. --- src/lib/ffmpeg_decoder.cc | 47 +++++++++++++++++++++++++++++------------------ 1 file changed, 29 insertions(+), 18 deletions(-) (limited to 'src/lib/ffmpeg_decoder.cc') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index f801821e9..30972cbf3 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -224,26 +224,32 @@ FFmpegDecoder::pass () av_strerror (r, buf, sizeof(buf)); _film->log()->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r)); } - - /* Get any remaining frames */ - - _packet.data = 0; - _packet.size = 0; - - /* XXX: should we reset _packet.data and size after each *_decode_* call? */ - - int frame_finished; - if (_opt.decode_video) { - while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - filter_and_emit_video (); + if (_video_codec->capabilities & CODEC_CAP_DELAY) { + + /* Get any remaining frames */ + + _packet.data = 0; + _packet.size = 0; + + /* XXX: should we reset _packet.data and size after each *_decode_* call? */ + + int frame_finished; + + if (_opt.decode_video) { + while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { + filter_and_emit_video (); + } } + + if (_audio_stream && _opt.decode_audio) { + decode_audio_packet (); + } + } else { + _film->log()->log("Codec does not have CAP_DELAY"); } - - if (_audio_stream && _opt.decode_audio) { - decode_audio_packet (); - } - + + return true; } @@ -518,7 +524,12 @@ FFmpegDecoder::filter_and_emit_video () list > images = graph->process (_frame); for (list >::iterator i = images.begin(); i != images.end(); ++i) { - emit_video (*i, av_frame_get_best_effort_timestamp (_frame) * av_q2d (_format_context->streams[_video_stream]->time_base)); + int64_t const bet = av_frame_get_best_effort_timestamp (_frame); + if (bet != AV_NOPTS_VALUE) { + emit_video (*i, bet * av_q2d (_format_context->streams[_video_stream]->time_base)); + } else { + _film->log()->log ("Dropping frame without PTS"); + } } } -- cgit v1.2.3 From bfea14c1655bc4bbadbe3d9e89f4bd2ddf037659 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 22:20:10 +0000 Subject: It appears that it may not be just CAP_DELAY decoders that can produce extra frames at the end. --- src/lib/ffmpeg_decoder.cc | 38 ++++++++++++++++---------------------- 1 file changed, 16 insertions(+), 22 deletions(-) (limited to 'src/lib/ffmpeg_decoder.cc') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 30972cbf3..e1834d7f6 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -225,30 +225,24 @@ FFmpegDecoder::pass () _film->log()->log (String::compose (N_("error on av_read_frame (%1) (%2)"), buf, r)); } - if (_video_codec->capabilities & CODEC_CAP_DELAY) { - - /* Get any remaining frames */ - - _packet.data = 0; - _packet.size = 0; - - /* XXX: should we reset _packet.data and size after each *_decode_* call? */ - - int frame_finished; - - if (_opt.decode_video) { - while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { - filter_and_emit_video (); - } - } - - if (_audio_stream && _opt.decode_audio) { - decode_audio_packet (); + /* Get any remaining frames */ + + _packet.data = 0; + _packet.size = 0; + + /* XXX: should we reset _packet.data and size after each *_decode_* call? */ + + int frame_finished; + + if (_opt.decode_video) { + while (avcodec_decode_video2 (_video_codec_context, _frame, &frame_finished, &_packet) >= 0 && frame_finished) { + filter_and_emit_video (); } - } else { - _film->log()->log("Codec does not have CAP_DELAY"); } - + + if (_audio_stream && _opt.decode_audio) { + decode_audio_packet (); + } return true; } -- cgit v1.2.3 From b0802a7644c12bc039c070367440439f7afe133a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 22:55:11 +0000 Subject: Hopefully fix up still-image generation. --- src/lib/ffmpeg_decoder.cc | 2 +- src/lib/imagemagick_decoder.cc | 15 +++++---- src/lib/imagemagick_decoder.h | 6 +--- src/lib/matcher.cc | 72 ++++++++++++++++++++---------------------- src/lib/matcher.h | 16 ---------- src/lib/video_decoder.cc | 4 +-- src/lib/video_decoder.h | 2 +- 7 files changed, 47 insertions(+), 70 deletions(-) (limited to 'src/lib/ffmpeg_decoder.cc') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index e1834d7f6..2d7092789 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -520,7 +520,7 @@ FFmpegDecoder::filter_and_emit_video () for (list >::iterator i = images.begin(); i != images.end(); ++i) { int64_t const bet = av_frame_get_best_effort_timestamp (_frame); if (bet != AV_NOPTS_VALUE) { - emit_video (*i, bet * av_q2d (_format_context->streams[_video_stream]->time_base)); + emit_video (*i, false, bet * av_q2d (_format_context->streams[_video_stream]->time_base)); } else { _film->log()->log ("Dropping frame without PTS"); } diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index 119f05792..5ce22c296 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -31,8 +31,6 @@ using std::cout; using boost::shared_ptr; using libdcp::Size; -/* XXX: reads a directory and then ignores it */ - ImageMagickDecoder::ImageMagickDecoder ( boost::shared_ptr f, DecodeOptions o) : Decoder (f, o) @@ -79,8 +77,7 @@ ImageMagickDecoder::pass () return true; } - /* XXX: timestamp */ - emit_video (_image, 0); + emit_video (_image, true, double (video_frame()) / frames_per_second()); return false; } @@ -105,8 +102,7 @@ ImageMagickDecoder::pass () _image = image->crop (_film->crop(), true); - /* XXX: timestamp */ - emit_video (_image, 0); + emit_video (_image, false, double (video_frame()) / frames_per_second()); ++_iter; return false; @@ -134,7 +130,6 @@ ImageMagickDecoder::seek_to_last () bool ImageMagickDecoder::seek (double t) { - /* XXX: frames_per_second == 0 */ int const f = t * frames_per_second(); _iter = _files.begin (); @@ -155,3 +150,9 @@ ImageMagickDecoder::film_changed (Film::Property p) OutputChanged (); } } + +float +ImageMagickDecoder::frames_per_second () const +{ + return _film->source_frame_rate (); +} diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index ef550c651..ca8e819d3 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -28,10 +28,7 @@ class ImageMagickDecoder : public VideoDecoder public: ImageMagickDecoder (boost::shared_ptr, DecodeOptions); - float frames_per_second () const { - /* We don't know */ - return 0; - } + float frames_per_second () const; libdcp::Size native_size () const; @@ -88,5 +85,4 @@ private: std::list::iterator _iter; boost::shared_ptr _image; - }; diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 93531dbc5..fbd3e3e76 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -49,37 +49,35 @@ Matcher::process_video (boost::shared_ptr image, bool same, boost::shared if (!_first_input) { _first_input = t; } + + /* Video before audio is fine, since we can make up an arbitrary difference + with audio samples (contrasting with video which is quantised to frames) + */ + + /* Difference between where this video is and where it should be */ + double const delta = t - _first_input.get() - _video_frames / _frames_per_second; + double const one_frame = 1 / _frames_per_second; + + if (delta > one_frame) { + /* Insert frames to make up the difference */ + int const extra = rint (delta / one_frame); + for (int i = 0; i < extra; ++i) { + repeat_last_video (); + _log->log (String::compose ("Extra video frame inserted at %1s", _video_frames / _frames_per_second)); + } + } + + if (delta > -one_frame) { + Video (image, same, sub); + ++_video_frames; + } else { + /* We are omitting a frame to keep things right */ + _log->log (String::compose ("Frame removed at %1s", t)); + } - if (_audio_frames == 0 && _pending_audio.empty ()) { - /* No audio yet; we must postpone this frame until we have some */ - _pending_video.push_back (VideoRecord (image, same, sub, t)); - } else if (!_pending_audio.empty() && _pending_video.empty ()) { + if (!_pending_audio.empty() && _video_frames == 1) { /* First video since we got audio */ - _pending_video.push_back (VideoRecord (image, same, sub, t)); fix_start (); - } else { - /* Normal running */ - - /* Difference between where this video is and where it should be */ - double const delta = t - _first_input.get() - _video_frames / _frames_per_second; - double const one_frame = 1 / _frames_per_second; - - if (delta > one_frame) { - /* Insert frames to make up the difference */ - int const extra = rint (delta / one_frame); - for (int i = 0; i < extra; ++i) { - repeat_last_video (); - _log->log (String::compose ("Extra video frame inserted at %1s", _video_frames / _frames_per_second)); - } - } - - if (delta > -one_frame) { - Video (image, same, sub); - ++_video_frames; - } else { - /* We are omitting a frame to keep things right */ - _log->log (String::compose ("Frame removed at %1s", t)); - } } _last_image = image; @@ -95,10 +93,10 @@ Matcher::process_audio (boost::shared_ptr b, double t) _first_input = t; } - if (_video_frames == 0 && _pending_video.empty ()) { + if (_video_frames == 0) { /* No video yet; we must postpone these data until we have some */ _pending_audio.push_back (AudioRecord (b, t)); - } else if (!_pending_video.empty() && _pending_audio.empty ()) { + } else if (_video_frames > 0 && _pending_audio.empty ()) { /* First audio since we got video */ _pending_audio.push_back (AudioRecord (b, t)); fix_start (); @@ -126,22 +124,20 @@ Matcher::process_end () void Matcher::fix_start () { - assert (!_pending_video.empty ()); assert (!_pending_audio.empty ()); + assert (_first_input); - _log->log (String::compose ("Fixing start; video at %1, audio at %2", _pending_video.front().time, _pending_audio.front().time)); - - match (_pending_video.front().time - _pending_audio.front().time); + _log->log (String::compose ("Fixing start; start at %1, audio at %2", _first_input.get(), _pending_audio.front().time)); - for (list::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) { - process_video (i->image, i->same, i->subtitle, i->time); - } + /* This will not add any video frames, since the input parameter will always be -ve. + Indeed, it cannot add any, since we've already started adding "real" video. + */ + match (_first_input.get() - _pending_audio.front().time); for (list::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) { process_audio (i->audio, i->time); } - _pending_video.clear (); _pending_audio.clear (); } diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 2f580b589..84ae2f73e 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -42,22 +42,6 @@ private: boost::optional _size; boost::optional _channels; - struct VideoRecord { - VideoRecord (boost::shared_ptr i, bool s, boost::shared_ptr sub, double t) - : image (i) - , same (s) - , subtitle (sub) - , time (t) - {} - - boost::shared_ptr image; - bool same; - boost::shared_ptr subtitle; - double time; - }; - - std::list _pending_video; - struct AudioRecord { AudioRecord (boost::shared_ptr a, double t) : audio (a) diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 9c0d2bbe3..16a076698 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -45,14 +45,14 @@ VideoDecoder::VideoDecoder (shared_ptr f, DecodeOptions o) * @param t Time of the frame within the source, in seconds. */ void -VideoDecoder::emit_video (shared_ptr image, double t) +VideoDecoder::emit_video (shared_ptr image, bool same, double t) { shared_ptr sub; if (_timed_subtitle && _timed_subtitle->displayed_at (t)) { sub = _timed_subtitle->subtitle (); } - Video (image, false, sub, t); + Video (image, same, sub, t); ++_video_frame; _last_source_time = t; diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 1a02272a5..6e4fd48c0 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -65,7 +65,7 @@ protected: virtual PixelFormat pixel_format () const = 0; - void emit_video (boost::shared_ptr, double); + void emit_video (boost::shared_ptr, bool, double); void emit_subtitle (boost::shared_ptr); /** Subtitle stream to use when decoding */ -- cgit v1.2.3 From 98499a61e17e68c438e56fd8854081a4c98b15ad Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 20 Apr 2013 02:26:23 +0100 Subject: Somewhat hacky but seemingly functional frame back/forward (rest of #68). --- src/lib/decoder.h | 2 ++ src/lib/ffmpeg_decoder.cc | 46 +++++++++++++++++++++++++++++++++++++++++----- src/lib/ffmpeg_decoder.h | 4 +++- src/lib/format.cc | 2 +- src/wx/film_viewer.cc | 40 ++++++++++++++++++++++++++++++++++++---- src/wx/film_viewer.h | 6 +++++- 6 files changed, 88 insertions(+), 12 deletions(-) (limited to 'src/lib/ffmpeg_decoder.cc') diff --git a/src/lib/decoder.h b/src/lib/decoder.h index f2f523516..2bc462c33 100644 --- a/src/lib/decoder.h +++ b/src/lib/decoder.h @@ -59,6 +59,8 @@ public: virtual bool pass () = 0; virtual bool seek (double); virtual bool seek_to_last (); + virtual void seek_back () {} + virtual void seek_forward () {} boost::signals2::signal OutputChanged; diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 2d7092789..7c88c3c35 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -530,7 +530,7 @@ FFmpegDecoder::filter_and_emit_video () bool FFmpegDecoder::seek (double p) { - return do_seek (p, false); + return do_seek (p, false, false); } bool @@ -540,21 +540,57 @@ FFmpegDecoder::seek_to_last () (used when we change decoder parameters and want to re-fetch the frame) we end up going forwards rather than staying in the same place. */ - return do_seek (last_source_time(), true); + return do_seek (last_source_time(), true, false); +} + +void +FFmpegDecoder::seek_back () +{ + do_seek (last_source_time() - 2.5 / frames_per_second (), true, true); +} + +void +FFmpegDecoder::seek_forward () +{ + do_seek (last_source_time() - 0.5 / frames_per_second(), true, true); } bool -FFmpegDecoder::do_seek (double p, bool backwards) +FFmpegDecoder::do_seek (double p, bool backwards, bool accurate) { int64_t const vt = p / av_q2d (_format_context->streams[_video_stream]->time_base); int const r = av_seek_frame (_format_context, _video_stream, vt, backwards ? AVSEEK_FLAG_BACKWARD : 0); - + avcodec_flush_buffers (_video_codec_context); if (_subtitle_codec_context) { avcodec_flush_buffers (_subtitle_codec_context); } - + + if (accurate) { + while (1) { + int r = av_read_frame (_format_context, &_packet); + if (r < 0) { + return true; + } + + avcodec_get_frame_defaults (_frame); + + if (_packet.stream_index == _video_stream) { + int finished = 0; + int const r = avcodec_decode_video2 (_video_codec_context, _frame, &finished, &_packet); + if (r >= 0 && finished) { + int64_t const bet = av_frame_get_best_effort_timestamp (_frame); + if (bet > vt) { + break; + } + } + } + + av_free_packet (&_packet); + } + } + return r < 0; } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 2a4d40b1d..0c89b973d 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -102,11 +102,13 @@ public: bool seek (double); bool seek_to_last (); + void seek_forward (); + void seek_back (); private: bool pass (); - bool do_seek (double p, bool); + bool do_seek (double p, bool, bool); PixelFormat pixel_format () const; AVSampleFormat audio_sample_format () const; int bytes_per_audio_sample () const; diff --git a/src/lib/format.cc b/src/lib/format.cc index 640eee167..8c3d0d8ad 100644 --- a/src/lib/format.cc +++ b/src/lib/format.cc @@ -50,7 +50,7 @@ FixedFormat::name () const s << _nickname << N_(" ("); } - s << setprecision(3) << (_ratio / 100.0) << N_(":1"); + s << setprecision(3) << _ratio << N_(":1"); if (!_nickname.empty ()) { s << N_(")"); diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 00f895285..5770c5b70 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -51,6 +51,8 @@ FilmViewer::FilmViewer (shared_ptr f, wxWindow* p) : wxPanel (p) , _panel (new wxPanel (this)) , _slider (new wxSlider (this, wxID_ANY, 0, 0, 4096)) + , _back_button (new wxButton (this, wxID_ANY, wxT("<"))) + , _forward_button (new wxButton (this, wxID_ANY, wxT(">"))) , _frame (new wxStaticText (this, wxID_ANY, wxT(""))) , _timecode (new wxStaticText (this, wxID_ANY, wxT(""))) , _play_button (new wxToggleButton (this, wxID_ANY, _("Play"))) @@ -72,14 +74,18 @@ FilmViewer::FilmViewer (shared_ptr f, wxWindow* p) wxBoxSizer* time_sizer = new wxBoxSizer (wxVERTICAL); time_sizer->Add (_frame, 0, wxEXPAND); time_sizer->Add (_timecode, 0, wxEXPAND); - + + h_sizer->Add (_back_button, 0, wxALL, 2); h_sizer->Add (time_sizer, 0, wxEXPAND); + h_sizer->Add (_forward_button, 0, wxALL, 2); h_sizer->Add (_play_button, 0, wxEXPAND); h_sizer->Add (_slider, 1, wxEXPAND); _v_sizer->Add (h_sizer, 0, wxEXPAND | wxALL, 6); _frame->SetMinSize (wxSize (84, -1)); + _back_button->SetMinSize (wxSize (32, -1)); + _forward_button->SetMinSize (wxSize (32, -1)); _panel->Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (FilmViewer::paint_panel), 0, this); _panel->Connect (wxID_ANY, wxEVT_SIZE, wxSizeEventHandler (FilmViewer::panel_sized), 0, this); @@ -88,6 +94,8 @@ FilmViewer::FilmViewer (shared_ptr f, wxWindow* p) _slider->Connect (wxID_ANY, wxEVT_SCROLL_PAGEDOWN, wxScrollEventHandler (FilmViewer::slider_moved), 0, this); _play_button->Connect (wxID_ANY, wxEVT_COMMAND_TOGGLEBUTTON_CLICKED, wxCommandEventHandler (FilmViewer::play_clicked), 0, this); _timer.Connect (wxID_ANY, wxEVT_TIMER, wxTimerEventHandler (FilmViewer::timer), 0, this); + _back_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmViewer::back_clicked), 0, this); + _forward_button->Connect (wxID_ANY, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler (FilmViewer::forward_clicked), 0, this); set_film (f); @@ -121,7 +129,7 @@ FilmViewer::film_changed (Film::Property p) if (_decoders.video == 0) { break; } - _decoders.video->Video.connect (bind (&FilmViewer::process_video, this, _1, _2, _3)); + _decoders.video->Video.connect (bind (&FilmViewer::process_video, this, _1, _2, _3, _4)); _decoders.video->OutputChanged.connect (boost::bind (&FilmViewer::decoder_changed, this)); _decoders.video->set_subtitle_stream (_film->subtitle_stream()); calculate_sizes (); @@ -392,7 +400,7 @@ FilmViewer::check_play_state () } void -FilmViewer::process_video (shared_ptr image, bool, shared_ptr sub) +FilmViewer::process_video (shared_ptr image, bool, shared_ptr sub, double t) { _raw_frame = image; _raw_sub = sub; @@ -401,7 +409,6 @@ FilmViewer::process_video (shared_ptr image, bool, shared_ptr s _got_frame = true; - double const t = _decoders.video->last_source_time (); double const fps = _decoders.video->frames_per_second (); _frame->SetLabel (wxString::Format ("%d", int (rint (t * fps)))); @@ -465,3 +472,28 @@ FilmViewer::active_jobs_changed (bool a) _play_button->Enable (!a); } +void +FilmViewer::back_clicked (wxCommandEvent &) +{ + if (!_decoders.video) { + return; + } + + _decoders.video->seek_back (); + get_frame (); + _panel->Refresh (); + _panel->Update (); +} + +void +FilmViewer::forward_clicked (wxCommandEvent &) +{ + if (!_decoders.video) { + return; + } + + _decoders.video->seek_forward (); + get_frame (); + _panel->Refresh (); + _panel->Update (); +} diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 859bf7ede..a78c772a4 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -48,7 +48,7 @@ private: void slider_moved (wxScrollEvent &); void play_clicked (wxCommandEvent &); void timer (wxTimerEvent &); - void process_video (boost::shared_ptr, bool, boost::shared_ptr); + void process_video (boost::shared_ptr, bool, boost::shared_ptr, double); void calculate_sizes (); void check_play_state (); void update_from_raw (); @@ -56,12 +56,16 @@ private: void raw_to_display (); void get_frame (); void active_jobs_changed (bool); + void back_clicked (wxCommandEvent &); + void forward_clicked (wxCommandEvent &); boost::shared_ptr _film; wxSizer* _v_sizer; wxPanel* _panel; wxSlider* _slider; + wxButton* _back_button; + wxButton* _forward_button; wxStaticText* _frame; wxStaticText* _timecode; wxToggleButton* _play_button; -- cgit v1.2.3 From 2c1533e4e5568a8bb4f538cfb845d07b0637380c Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 20 Apr 2013 02:50:09 +0100 Subject: Be more careful about the validity of s->metadata. --- src/lib/ffmpeg_decoder.cc | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) (limited to 'src/lib/ffmpeg_decoder.cc') diff --git a/src/lib/ffmpeg_decoder.cc b/src/lib/ffmpeg_decoder.cc index 7c88c3c35..8e09810cb 100644 --- a/src/lib/ffmpeg_decoder.cc +++ b/src/lib/ffmpeg_decoder.cc @@ -453,18 +453,20 @@ string FFmpegDecoder::stream_name (AVStream* s) const { stringstream n; - - AVDictionaryEntry const * lang = av_dict_get (s->metadata, N_("language"), 0, 0); - if (lang) { - n << lang->value; - } - - AVDictionaryEntry const * title = av_dict_get (s->metadata, N_("title"), 0, 0); - if (title) { - if (!n.str().empty()) { - n << N_(" "); + + if (s->metadata) { + AVDictionaryEntry const * lang = av_dict_get (s->metadata, N_("language"), 0, 0); + if (lang) { + n << lang->value; + } + + AVDictionaryEntry const * title = av_dict_get (s->metadata, N_("title"), 0, 0); + if (title) { + if (!n.str().empty()) { + n << N_(" "); + } + n << title->value; } - n << title->value; } if (n.str().empty()) { -- cgit v1.2.3