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 +++++------------------------------ src/lib/ffmpeg_decoder.h | 7 +- src/lib/imagemagick_decoder.cc | 14 ++-- src/lib/imagemagick_decoder.h | 3 + src/lib/matcher.cc | 148 ++++++++++++++++++++++++++++++++--------- src/lib/matcher.h | 36 ++++++++++ src/lib/video_decoder.cc | 19 +----- src/lib/video_decoder.h | 4 -- 8 files changed, 182 insertions(+), 175 deletions(-) (limited to 'src') 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; } diff --git a/src/lib/ffmpeg_decoder.h b/src/lib/ffmpeg_decoder.h index 1bb14ce9c..2a4d40b1d 100644 --- a/src/lib/ffmpeg_decoder.h +++ b/src/lib/ffmpeg_decoder.h @@ -111,9 +111,7 @@ private: AVSampleFormat audio_sample_format () const; int bytes_per_audio_sample () const; - void out_with_sync (); - void filter_and_emit_video (AVFrame *); - double frame_time () const; + void filter_and_emit_video (); void setup_general (); void setup_video (); @@ -143,9 +141,6 @@ private: AVPacket _packet; - boost::optional _first_video; - boost::optional _first_audio; - std::list > _filter_graphs; boost::mutex _filter_graphs_mutex; }; diff --git a/src/lib/imagemagick_decoder.cc b/src/lib/imagemagick_decoder.cc index 38dace6de..119f05792 100644 --- a/src/lib/imagemagick_decoder.cc +++ b/src/lib/imagemagick_decoder.cc @@ -31,6 +31,8 @@ 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) @@ -77,8 +79,8 @@ ImageMagickDecoder::pass () return true; } - /* XXX: timestamp is wrong */ - repeat_last_video (0); + /* XXX: timestamp */ + emit_video (_image, 0); return false; } @@ -101,9 +103,10 @@ ImageMagickDecoder::pass () delete magick_image; - image = image->crop (_film->crop(), true); - - emit_video (image, 0); + _image = image->crop (_film->crop(), true); + + /* XXX: timestamp */ + emit_video (_image, 0); ++_iter; return false; @@ -131,6 +134,7 @@ ImageMagickDecoder::seek_to_last () bool ImageMagickDecoder::seek (double t) { + /* XXX: frames_per_second == 0 */ int const f = t * frames_per_second(); _iter = _files.begin (); diff --git a/src/lib/imagemagick_decoder.h b/src/lib/imagemagick_decoder.h index 0e375f6e9..ef550c651 100644 --- a/src/lib/imagemagick_decoder.h +++ b/src/lib/imagemagick_decoder.h @@ -86,4 +86,7 @@ private: std::list _files; std::list::iterator _iter; + + boost::shared_ptr _image; + }; diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 3a513b24e..70a9b2a85 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -24,6 +24,8 @@ #include "i18n.h" using std::min; +using std::cout; +using std::list; using boost::shared_ptr; Matcher::Matcher (Log* log, int sample_rate, float frames_per_second) @@ -37,22 +39,72 @@ Matcher::Matcher (Log* log, int sample_rate, float frames_per_second) } void -Matcher::process_video (boost::shared_ptr i, bool same, boost::shared_ptr s, double) +Matcher::process_video (boost::shared_ptr image, bool same, boost::shared_ptr sub, double t) { - Video (i, same, s); - _video_frames++; + _pixel_format = image->pixel_format (); + _size = image->size (); - _pixel_format = i->pixel_format (); - _size = i->size (); + if (!_first_input) { + _first_input = 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 ()) { + /* 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; + _last_subtitle = sub; } void -Matcher::process_audio (boost::shared_ptr b, double) +Matcher::process_audio (boost::shared_ptr b, double t) { - Audio (b); - _audio_frames += b->frames (); - _channels = b->channels (); + + if (!_first_input) { + _first_input = t; + } + + if (_video_frames == 0 && _pending_video.empty ()) { + /* 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 ()) { + /* First audio since we got video */ + _pending_audio.push_back (AudioRecord (b, t)); + fix_start (); + } else { + /* Normal running. We assume audio time stamps are consecutive */ + Audio (b); + _audio_frames += b->frames (); + } } void @@ -63,38 +115,58 @@ Matcher::process_end () return; } - int64_t audio_short_by_frames = video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second) - _audio_frames; - - _log->log ( - String::compose ( - N_("Matching processor has seen %1 video frames (which equals %2 audio frames) and %3 audio frames"), - _video_frames, - video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second), - _audio_frames - ) - ); + match ((double (_audio_frames) / _sample_rate) - (double (_video_frames) / _frames_per_second)); +} + +void +Matcher::fix_start () +{ + assert (!_pending_video.empty ()); + assert (!_pending_audio.empty ()); + + _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); + + for (list::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) { + Video (i->image, i->same, i->subtitle); + } + + for (list::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) { + Audio (i->audio); + } - if (audio_short_by_frames < 0) { - - _log->log (String::compose (N_("%1 too many audio frames"), -audio_short_by_frames)); - - /* We have seen more audio than video. Emit enough black video frames so that we reverse this */ - int const black_video_frames = ceil (-audio_short_by_frames * _frames_per_second / _sample_rate); + _pending_video.clear (); + _pending_audio.clear (); +} + +void +Matcher::match (double extra_video_needed) +{ + if (extra_video_needed) { + + /* Emit black video frames */ + int const black_video_frames = ceil (extra_video_needed * _frames_per_second); + _log->log (String::compose (N_("Emitting %1 frames of black video"), black_video_frames)); shared_ptr black (new SimpleImage (_pixel_format.get(), _size.get(), true)); black->make_black (); for (int i = 0; i < black_video_frames; ++i) { Video (black, i != 0, shared_ptr()); + ++_video_frames; } - - /* Now recompute our check value */ - audio_short_by_frames = video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second) - _audio_frames; + + extra_video_needed -= black_video_frames / _frames_per_second; } - - if (audio_short_by_frames > 0) { - _log->log (String::compose (N_("Emitted %1 too few audio frames"), audio_short_by_frames)); + + if (extra_video_needed < 0) { + + /* Emit silence */ + + int64_t to_do = rint (-extra_video_needed * _sample_rate); + _log->log (String::compose (N_("Emitted %1 frames of silence"), to_do)); /* Do things in half second blocks as I think there may be limits to what FFmpeg (and in particular the resampler) can cope with. @@ -103,7 +175,6 @@ Matcher::process_end () shared_ptr b (new AudioBuffers (_channels.get(), block)); b->make_silent (); - int64_t to_do = audio_short_by_frames; while (to_do > 0) { int64_t const this_time = min (to_do, block); b->set_frames (this_time); @@ -113,3 +184,16 @@ Matcher::process_end () } } } + +void +Matcher::repeat_last_video () +{ + if (!_last_image) { + _last_image.reset (new SimpleImage (_pixel_format.get(), _size.get(), true)); + _last_image->make_black (); + } + + Video (_last_image, true, _last_subtitle); + ++_video_frames; +} + diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 4a66f4e70..2f580b589 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -30,6 +30,10 @@ public: void process_end (); private: + void fix_start (); + void match (double); + void repeat_last_video (); + int _sample_rate; float _frames_per_second; int _video_frames; @@ -37,4 +41,36 @@ private: boost::optional _pixel_format; 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) + , time (t) + {} + + boost::shared_ptr audio; + double time; + }; + + std::list _pending_audio; + + boost::optional _first_input; + boost::shared_ptr _last_image; + boost::shared_ptr _last_subtitle; }; diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 773688b34..7fff93c45 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -27,6 +27,7 @@ #include "i18n.h" +using std::cout; using boost::shared_ptr; using boost::optional; @@ -55,21 +56,6 @@ VideoDecoder::emit_video (shared_ptr image, double t) _last_source_time = t; } -/** Called by subclasses to repeat the last video frame that we - * passed to emit_video(). If emit_video hasn't yet been called, - * we will generate a black frame. - */ -void -VideoDecoder::repeat_last_video (double t) -{ - if (!_last_image) { - _last_image.reset (new SimpleImage (pixel_format(), native_size(), true)); - _last_image->make_black (); - } - - signal_video (_last_image, true, _last_subtitle, t); -} - /** Emit our signal to say that some video data is ready. * @param image Video frame. * @param same true if `image' is the same as the last one we emitted. @@ -81,9 +67,6 @@ VideoDecoder::signal_video (shared_ptr image, bool same, shared_ptr, double); void emit_subtitle (boost::shared_ptr); - void repeat_last_video (double t); /** Subtitle stream to use when decoding */ boost::shared_ptr _subtitle_stream; @@ -81,9 +80,6 @@ private: double _last_source_time; boost::shared_ptr _timed_subtitle; - - boost::shared_ptr _last_image; - boost::shared_ptr _last_subtitle; }; #endif -- cgit v1.2.3 From a0fedb33cab65a1be1c49319b7f14319897991f3 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 01:04:23 +0000 Subject: Small simplification. --- src/lib/video_decoder.cc | 15 ++------------- src/lib/video_decoder.h | 2 -- 2 files changed, 2 insertions(+), 15 deletions(-) (limited to 'src') diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 7fff93c45..8461b1635 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -52,21 +52,10 @@ VideoDecoder::emit_video (shared_ptr image, double t) sub = _timed_subtitle->subtitle (); } - signal_video (image, false, sub, t); - _last_source_time = t; -} - -/** Emit our signal to say that some video data is ready. - * @param image Video frame. - * @param same true if `image' is the same as the last one we emitted. - * @param sub Subtitle for this frame, or 0. - */ -void -VideoDecoder::signal_video (shared_ptr image, bool same, shared_ptr sub, double t) -{ - TIMING (N_("Decoder emits %1"), _video_frame); Video (image, same, sub, t); ++_video_frame; + + _last_source_time = t; } /** Set up the current subtitle. This will be put onto frames that diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index 7d43c9e82..1a02272a5 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -74,8 +74,6 @@ protected: std::vector > _subtitle_streams; private: - void signal_video (boost::shared_ptr, bool, boost::shared_ptr, double); - int _video_frame; double _last_source_time; -- cgit v1.2.3 From bb6ceb671f08e0294556036af7ca5f88d1db2fca Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 01:08:31 +0000 Subject: Fix thinko. --- src/lib/matcher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 70a9b2a85..749acf463 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -143,7 +143,7 @@ Matcher::fix_start () void Matcher::match (double extra_video_needed) { - if (extra_video_needed) { + if (extra_video_needed > 0) { /* Emit black video frames */ -- cgit v1.2.3 From 3f30952afbda7d945998f62fee836afc8bc8a48f Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 01:11:01 +0000 Subject: Fix mistake in previous simplification. --- src/lib/video_decoder.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/lib/video_decoder.cc b/src/lib/video_decoder.cc index 8461b1635..9c0d2bbe3 100644 --- a/src/lib/video_decoder.cc +++ b/src/lib/video_decoder.cc @@ -52,7 +52,7 @@ VideoDecoder::emit_video (shared_ptr image, double t) sub = _timed_subtitle->subtitle (); } - Video (image, same, sub, t); + Video (image, false, sub, t); ++_video_frame; _last_source_time = t; -- cgit v1.2.3 From b3dfeeb7f960b6d3418005351f52c5f6b99e9636 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 11:18:06 +0000 Subject: Tweak rounding. Pass pending video / audio back through so it is treated the same as the first time. --- src/lib/matcher.cc | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 749acf463..2c697157f 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -44,6 +44,8 @@ Matcher::process_video (boost::shared_ptr image, bool same, boost::shared _pixel_format = image->pixel_format (); _size = image->size (); + _log->log(String::compose("Matcher video @ %1 (same=%2)", t, same)); + if (!_first_input) { _first_input = t; } @@ -129,11 +131,11 @@ Matcher::fix_start () match (_pending_video.front().time - _pending_audio.front().time); for (list::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) { - Video (i->image, i->same, i->subtitle); + process_video (i->image, i->same, i->subtitle, i->time); } for (list::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) { - Audio (i->audio); + process_audio (i->audio, i->time); } _pending_video.clear (); @@ -165,7 +167,7 @@ Matcher::match (double extra_video_needed) /* Emit silence */ - int64_t to_do = rint (-extra_video_needed * _sample_rate); + int64_t to_do = -extra_video_needed * _sample_rate; _log->log (String::compose (N_("Emitted %1 frames of silence"), to_do)); /* Do things in half second blocks as I think there may be limits -- cgit v1.2.3 From da9ab56f39b1d658ccd0e40de2df8e18c9e4cd89 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 14:20:39 +0000 Subject: A little logging. --- src/lib/matcher.cc | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 2c697157f..93531dbc5 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -116,6 +116,9 @@ Matcher::process_end () /* We won't do anything */ return; } + + _log->log (String::compose ("Matcher has seen %1 video frames (which equals %2 audio frames) and %3 audio frames", + _video_frames, video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second), _audio_frames)); match ((double (_audio_frames) / _sample_rate) - (double (_video_frames) / _frames_per_second)); } -- 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') 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') 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 085d9d4966c32aa1f3661c597b4bc2b47eaefa40 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 22:41:59 +0000 Subject: Catch exception thrown by checking the space on a filesystem using a non-existant file. --- src/lib/job.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'src') diff --git a/src/lib/job.cc b/src/lib/job.cc index 8c1612a55..78a7a7577 100644 --- a/src/lib/job.cc +++ b/src/lib/job.cc @@ -69,11 +69,15 @@ Job::run_wrapper () set_state (FINISHED_ERROR); string m = String::compose (_("An error occurred whilst handling the file %1."), boost::filesystem::path (e.filename()).leaf()); - - boost::filesystem::space_info const s = boost::filesystem::space (e.filename()); - if (s.available < pow (1024, 3)) { - m += N_("\n\n"); - m += _("The drive that the film is stored on is low in disc space. Free some more space and try again."); + + try { + boost::filesystem::space_info const s = boost::filesystem::space (e.filename()); + if (s.available < pow (1024, 3)) { + m += N_("\n\n"); + m += _("The drive that the film is stored on is low in disc space. Free some more space and try again."); + } + } catch (...) { + } set_error (e.what(), m); -- 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') 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 e3ce330c4dc3f59f4e2b942bb6111c308a3d83eb Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 22:56:51 +0000 Subject: Remove believed never-failing checks for audio streams. --- src/lib/transcoder.cc | 33 +++++++++++---------------------- 1 file changed, 11 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index 3beda2b8b..ae88116a0 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -55,33 +55,22 @@ Transcoder::Transcoder (shared_ptr f, DecodeOptions o, Job* j, shared_ptr< { assert (_encoder); - if (f->audio_stream()) { - shared_ptr st = f->audio_stream(); - _matcher.reset (new Matcher (f->log(), st->sample_rate(), f->source_frame_rate())); - _delay_line.reset (new DelayLine (f->log(), st->channels(), f->audio_delay() * st->sample_rate() / 1000)); - _gain.reset (new Gain (f->log(), f->audio_gain())); - } + shared_ptr st = f->audio_stream(); + _matcher.reset (new Matcher (f->log(), st->sample_rate(), f->source_frame_rate())); + _delay_line.reset (new DelayLine (f->log(), st->channels(), f->audio_delay() * st->sample_rate() / 1000)); + _gain.reset (new Gain (f->log(), f->audio_gain())); /* Set up the decoder to use the film's set streams */ _decoders.video->set_subtitle_stream (f->subtitle_stream ()); - if (_decoders.audio) { - _decoders.audio->set_audio_stream (f->audio_stream ()); - } + _decoders.audio->set_audio_stream (f->audio_stream ()); - if (_matcher) { - _decoders.video->connect_video (_matcher); - _matcher->connect_video (_encoder); - } else { - /* Discard timestamps here */ - _decoders.video->Video.connect (boost::bind (&Encoder::process_video, _encoder, _1, _2, _3)); - } + _decoders.video->connect_video (_matcher); + _matcher->connect_video (_encoder); - if (_matcher && _delay_line && _decoders.audio) { - _decoders.audio->connect_audio (_delay_line); - _delay_line->connect_audio (_matcher); - _matcher->connect_audio (_gain); - _gain->connect_audio (_encoder); - } + _decoders.audio->connect_audio (_delay_line); + _delay_line->connect_audio (_matcher); + _matcher->connect_audio (_gain); + _gain->connect_audio (_encoder); } /** Run the decoder, passing its output to the encoder, until the decoder -- cgit v1.2.3 From 7a9a8229b57139ece3f9848910087310c06169c7 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 23:08:07 +0000 Subject: Try doing delay line by fiddling timestamps. Fix up confusion in A/B transcoder similar to that in standard one. --- src/lib/ab_transcoder.cc | 30 ++++++--------- src/lib/delay_line.cc | 67 ++++++++------------------------- src/lib/delay_line.h | 11 ++---- src/lib/transcoder.cc | 17 +++------ test/test.cc | 98 ------------------------------------------------ 5 files changed, 36 insertions(+), 187 deletions(-) (limited to 'src') diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index 373549b57..3af32f988 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -58,12 +58,10 @@ ABTranscoder::ABTranscoder ( _da = decoder_factory (_film_a, o); _db = decoder_factory (_film_b, o); - if (_film_a->audio_stream()) { - shared_ptr st = _film_a->audio_stream(); - _matcher.reset (new Matcher (_film_a->log(), st->sample_rate(), _film_a->source_frame_rate())); - _delay_line.reset (new DelayLine (_film_a->log(), st->channels(), _film_a->audio_delay() * st->sample_rate() / 1000)); - _gain.reset (new Gain (_film_a->log(), _film_a->audio_gain())); - } + shared_ptr st = _film_a->audio_stream(); + _matcher.reset (new Matcher (_film_a->log(), st->sample_rate(), _film_a->source_frame_rate())); + _delay_line.reset (new DelayLine (_film_a->log(), _film_a->audio_delay() / 1000.0f)); + _gain.reset (new Gain (_film_a->log(), _film_a->audio_gain())); /* Set up the decoder to use the film's set streams */ _da.video->set_subtitle_stream (_film_a->subtitle_stream ()); @@ -73,20 +71,14 @@ ABTranscoder::ABTranscoder ( _da.video->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2, _3, _4)); _db.video->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2, _3, _4)); - if (_matcher) { - _combiner->connect_video (_matcher); - _matcher->connect_video (_encoder); - } else { - /* Remove timestamp from the output of the combiner */ - _combiner->Video.connect (bind (&Encoder::process_video, _encoder, _1, _2, _3)); - } + _combiner->connect_video (_delay_line); + _delay_line->connect_video (_matcher); + _matcher->connect_video (_encoder); - if (_matcher && _delay_line) { - _da.audio->connect_audio (_delay_line); - _delay_line->connect_audio (_matcher); - _matcher->connect_audio (_gain); - _gain->connect_audio (_encoder); - } + _da.audio->connect_audio (_delay_line); + _delay_line->connect_audio (_matcher); + _matcher->connect_audio (_gain); + _gain->connect_audio (_encoder); } void diff --git a/src/lib/delay_line.cc b/src/lib/delay_line.cc index 84785cfc6..924a1f082 100644 --- a/src/lib/delay_line.cc +++ b/src/lib/delay_line.cc @@ -27,68 +27,31 @@ using std::min; using boost::shared_ptr; -/** @param channels Number of channels of audio. - * @param frames Delay in frames, +ve to move audio later. +/* @param seconds Delay in seconds, +ve to move audio later. */ -DelayLine::DelayLine (Log* log, int channels, int frames) +DelayLine::DelayLine (Log* log, double seconds) : Processor (log) - , _negative_delay_remaining (0) - , _frames (frames) + , _seconds (seconds) { - if (_frames > 0) { - /* We need a buffer to keep some data in */ - _buffers.reset (new AudioBuffers (channels, _frames)); - _buffers->make_silent (); - } else if (_frames < 0) { - /* We can do -ve delays just by chopping off - the start, so no buffer needed. - */ - _negative_delay_remaining = -_frames; - } + } -/* XXX: can we just get rid of all this and fiddle with the timestamp? */ void DelayLine::process_audio (shared_ptr data, double t) { - if (_buffers) { - /* We have some buffers, so we are moving the audio later */ - - /* Copy the input data */ - AudioBuffers input (*data.get ()); - - int to_do = data->frames (); - - /* Write some of our buffer to the output */ - int const from_buffer = min (to_do, _buffers->frames()); - data->copy_from (_buffers.get(), from_buffer, 0, 0); - to_do -= from_buffer; - - /* Write some of the input to the output */ - int const from_input = to_do; - data->copy_from (&input, from_input, 0, from_buffer); - - int const left_in_buffer = _buffers->frames() - from_buffer; - - /* Shuffle our buffer down */ - _buffers->move (from_buffer, 0, left_in_buffer); - - /* Copy remaining input data to our buffer */ - _buffers->copy_from (&input, input.frames() - from_input, from_input, left_in_buffer); - - } else { + if (_seconds > 0) { + t += _seconds; + } - /* Chop the initial data off until _negative_delay_remaining - is zero, then just pass data. - */ + Audio (data, t); +} - int const to_do = min (data->frames(), _negative_delay_remaining); - if (to_do) { - data->move (to_do, 0, data->frames() - to_do); - data->set_frames (data->frames() - to_do); - _negative_delay_remaining -= to_do; - } +void +DelayLine::process_video (boost::shared_ptr image, bool same, boost::shared_ptr sub, double t) +{ + if (_seconds < 0) { + t += _seconds; } - Audio (data, t); + Video (image, same, sub, t); } diff --git a/src/lib/delay_line.h b/src/lib/delay_line.h index 8c4a3953c..a52fb981c 100644 --- a/src/lib/delay_line.h +++ b/src/lib/delay_line.h @@ -20,18 +20,15 @@ #include #include "processor.h" -class AudioBuffers; - /** A delay line for audio */ -class DelayLine : public Processor, public TimedAudioSink, public TimedAudioSource +class DelayLine : public Processor, public TimedAudioSink, public TimedAudioSource, public TimedVideoSink, public TimedVideoSource { public: - DelayLine (Log* log, int channels, int frames); + DelayLine (Log* log, double); + void process_video (boost::shared_ptr, bool, boost::shared_ptr, double); void process_audio (boost::shared_ptr, double); private: - boost::shared_ptr _buffers; - int _negative_delay_remaining; ///< number of frames of negative delay that remain to emit - int _frames; + double _seconds; }; diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index ae88116a0..8e5e15e7f 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -57,14 +57,15 @@ Transcoder::Transcoder (shared_ptr f, DecodeOptions o, Job* j, shared_ptr< shared_ptr st = f->audio_stream(); _matcher.reset (new Matcher (f->log(), st->sample_rate(), f->source_frame_rate())); - _delay_line.reset (new DelayLine (f->log(), st->channels(), f->audio_delay() * st->sample_rate() / 1000)); + _delay_line.reset (new DelayLine (f->log(), f->audio_delay() / 1000.0f)); _gain.reset (new Gain (f->log(), f->audio_gain())); /* Set up the decoder to use the film's set streams */ _decoders.video->set_subtitle_stream (f->subtitle_stream ()); _decoders.audio->set_audio_stream (f->audio_stream ()); - _decoders.video->connect_video (_matcher); + _decoders.video->connect_video (_delay_line); + _delay_line->connect_video (_matcher); _matcher->connect_video (_encoder); _decoders.audio->connect_audio (_delay_line); @@ -107,14 +108,8 @@ Transcoder::go () throw; } - if (_delay_line) { - _delay_line->process_end (); - } - if (_matcher) { - _matcher->process_end (); - } - if (_gain) { - _gain->process_end (); - } + _delay_line->process_end (); + _matcher->process_end (); + _gain->process_end (); _encoder->process_end (); } diff --git a/test/test.cc b/test/test.cc index b2af8ab22..5e85e0dd5 100644 --- a/test/test.cc +++ b/test/test.cc @@ -28,7 +28,6 @@ #include "job_manager.h" #include "util.h" #include "exceptions.h" -#include "delay_line.h" #include "image.h" #include "log.h" #include "dcp_video_frame.h" @@ -251,103 +250,6 @@ public: void do_log (string) {} }; -void -do_positive_delay_line_test (int delay_length, int data_length) -{ - NullLog log; - - DelayLine d (&log, 6, delay_length); - shared_ptr data (new AudioBuffers (6, data_length)); - - int in = 0; - int out = 0; - int returned = 0; - int zeros = 0; - - for (int i = 0; i < 64; ++i) { - for (int j = 0; j < data_length; ++j) { - for (int c = 0; c < 6; ++c ) { - data->data(c)[j] = in; - ++in; - } - } - - /* This only works because the delay line modifies the parameter */ - /* XXX: timestamp is wrong */ - d.process_audio (data, 0); - returned += data->frames (); - - for (int j = 0; j < data->frames(); ++j) { - if (zeros < delay_length) { - for (int c = 0; c < 6; ++c) { - BOOST_CHECK_EQUAL (data->data(c)[j], 0); - } - ++zeros; - } else { - for (int c = 0; c < 6; ++c) { - BOOST_CHECK_EQUAL (data->data(c)[j], out); - ++out; - } - } - } - } - - BOOST_CHECK_EQUAL (returned, 64 * data_length); -} - -void -do_negative_delay_line_test (int delay_length, int data_length) -{ - NullLog log; - - DelayLine d (&log, 6, delay_length); - shared_ptr data (new AudioBuffers (6, data_length)); - - int in = 0; - int out = -delay_length * 6; - int returned = 0; - - for (int i = 0; i < 256; ++i) { - data->set_frames (data_length); - for (int j = 0; j < data_length; ++j) { - for (int c = 0; c < 6; ++c) { - data->data(c)[j] = in; - ++in; - } - } - - /* This only works because the delay line modifies the parameter */ - /* XXX: timestamp is wrong */ - d.process_audio (data, 0); - returned += data->frames (); - - for (int j = 0; j < data->frames(); ++j) { - for (int c = 0; c < 6; ++c) { - BOOST_CHECK_EQUAL (data->data(c)[j], out); - ++out; - } - } - } - - returned += -delay_length; - BOOST_CHECK_EQUAL (returned, 256 * data_length); -} - -BOOST_AUTO_TEST_CASE (delay_line_test) -{ - do_positive_delay_line_test (64, 128); - do_positive_delay_line_test (128, 64); - do_positive_delay_line_test (3, 512); - do_positive_delay_line_test (512, 3); - - do_positive_delay_line_test (0, 64); - - do_negative_delay_line_test (-64, 128); - do_negative_delay_line_test (-128, 64); - do_negative_delay_line_test (-3, 512); - do_negative_delay_line_test (-512, 3); -} - BOOST_AUTO_TEST_CASE (md5_digest_test) { string const t = md5_digest ("test/md5.test"); -- cgit v1.2.3 From 4de9464d7547954bcc74d4c1337c202dc50f588a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 23:25:15 +0000 Subject: Hopefully fix thinko. --- src/lib/matcher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index fbd3e3e76..b2bef0269 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -96,7 +96,7 @@ Matcher::process_audio (boost::shared_ptr b, double t) 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 (_video_frames > 0 && _pending_audio.empty ()) { + } else if (_video_frames > 0 && _audio_frames == 0) { /* First audio since we got video */ _pending_audio.push_back (AudioRecord (b, t)); fix_start (); -- cgit v1.2.3 From 3994ec6aeb7b1dd07aeac2cd6086f6d0a68352de Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 23:37:59 +0000 Subject: Fix infinite loop. --- src/lib/matcher.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index b2bef0269..83a39b6ee 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -96,7 +96,7 @@ Matcher::process_audio (boost::shared_ptr b, double t) 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 (_video_frames > 0 && _audio_frames == 0) { + } else if (_video_frames > 0 && _audio_frames == 0 && _pending_audio.empty()) { /* First audio since we got video */ _pending_audio.push_back (AudioRecord (b, t)); fix_start (); -- cgit v1.2.3 From 070e62d79b895c0b1ce31d69594973ced72f699b Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 6 Mar 2013 23:46:04 +0000 Subject: Another attempt to fix up matching. --- src/lib/matcher.cc | 22 +++++++++------------- src/lib/matcher.h | 2 +- 2 files changed, 10 insertions(+), 14 deletions(-) (limited to 'src') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 83a39b6ee..a751e7297 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -50,6 +50,11 @@ Matcher::process_video (boost::shared_ptr image, bool same, boost::shared _first_input = t; } + if (!_pending_audio.empty() && _video_frames == 0) { + /* First video since we got audio */ + fix_start (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) */ @@ -75,11 +80,6 @@ Matcher::process_video (boost::shared_ptr image, bool same, boost::shared _log->log (String::compose ("Frame removed at %1s", t)); } - if (!_pending_audio.empty() && _video_frames == 1) { - /* First video since we got audio */ - fix_start (); - } - _last_image = image; _last_subtitle = sub; } @@ -99,7 +99,7 @@ Matcher::process_audio (boost::shared_ptr b, double t) } else if (_video_frames > 0 && _audio_frames == 0 && _pending_audio.empty()) { /* First audio since we got video */ _pending_audio.push_back (AudioRecord (b, t)); - fix_start (); + fix_start (_first_input.get ()); } else { /* Normal running. We assume audio time stamps are consecutive */ Audio (b); @@ -122,17 +122,13 @@ Matcher::process_end () } void -Matcher::fix_start () +Matcher::fix_start (double first_video) { assert (!_pending_audio.empty ()); - assert (_first_input); - _log->log (String::compose ("Fixing start; start at %1, audio at %2", _first_input.get(), _pending_audio.front().time)); + _log->log (String::compose ("Fixing start; video at %1, audio at %2", first_video, _pending_audio.front().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); + match (first_video - _pending_audio.front().time); for (list::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) { process_audio (i->audio, i->time); diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 84ae2f73e..4a387a3f9 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -30,7 +30,7 @@ public: void process_end (); private: - void fix_start (); + void fix_start (double); void match (double); void repeat_last_video (); -- cgit v1.2.3 From b9fb4a402b411ddf84c10587187e187bcea34a5e Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Thu, 7 Mar 2013 10:11:52 +0000 Subject: Another attempt to fix up matching. --- src/lib/matcher.cc | 26 +++++++++++++++++++------- src/lib/matcher.h | 3 +++ 2 files changed, 22 insertions(+), 7 deletions(-) (limited to 'src') diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index a751e7297..a74eeabbb 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -34,6 +34,8 @@ Matcher::Matcher (Log* log, int sample_rate, float frames_per_second) , _frames_per_second (frames_per_second) , _video_frames (0) , _audio_frames (0) + , _had_first_video (false) + , _had_first_audio (false) { } @@ -44,17 +46,20 @@ Matcher::process_video (boost::shared_ptr image, bool same, boost::shared _pixel_format = image->pixel_format (); _size = image->size (); - _log->log(String::compose("Matcher video @ %1 (same=%2)", t, same)); + _log->log(String::compose("Matcher video @ %1 [audio=%2, video=%3, pending_audio=%4]", t, _audio_frames, _video_frames, _pending_audio.size())); if (!_first_input) { _first_input = t; } - if (!_pending_audio.empty() && _video_frames == 0) { + bool const this_is_first_video = !_had_first_video; + _had_first_video = true; + + if (this_is_first_video && _had_first_audio) { /* First video since we got audio */ fix_start (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) */ @@ -88,15 +93,20 @@ void Matcher::process_audio (boost::shared_ptr b, double t) { _channels = b->channels (); - + + _log->log (String::compose ("Matcher audio @ %1 [video=%2, audio=%3, pending_audio=%4]", t, _video_frames, _audio_frames, _pending_audio.size())); + if (!_first_input) { _first_input = t; } + + bool const this_is_first_audio = _had_first_audio; + _had_first_audio = true; - if (_video_frames == 0) { + if (!_had_first_video) { /* No video yet; we must postpone these data until we have some */ _pending_audio.push_back (AudioRecord (b, t)); - } else if (_video_frames > 0 && _audio_frames == 0 && _pending_audio.empty()) { + } else if (this_is_first_audio && !_had_first_video) { /* First audio since we got video */ _pending_audio.push_back (AudioRecord (b, t)); fix_start (_first_input.get ()); @@ -140,6 +150,8 @@ Matcher::fix_start (double first_video) void Matcher::match (double extra_video_needed) { + _log->log (String::compose ("Match %1", extra_video_needed)); + if (extra_video_needed > 0) { /* Emit black video frames */ @@ -163,7 +175,7 @@ Matcher::match (double extra_video_needed) /* Emit silence */ int64_t to_do = -extra_video_needed * _sample_rate; - _log->log (String::compose (N_("Emitted %1 frames of silence"), to_do)); + _log->log (String::compose (N_("Emitting %1 frames of silence"), to_do)); /* Do things in half second blocks as I think there may be limits to what FFmpeg (and in particular the resampler) can cope with. diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 4a387a3f9..a7054f540 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -57,4 +57,7 @@ private: boost::optional _first_input; boost::shared_ptr _last_image; boost::shared_ptr _last_subtitle; + + bool _had_first_video; + bool _had_first_audio; }; -- cgit v1.2.3 From 26c6f510ca23f1d21e41e3df65726ffa0852d745 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Wed, 10 Apr 2013 15:12:09 +0100 Subject: Try to fix rounding problems with ratios due to old unused integer ops. --- src/lib/format.cc | 40 +++++++++++++++++----------------------- src/lib/format.h | 29 +++++++++-------------------- src/wx/film_viewer.cc | 2 +- test/test.cc | 6 ++++-- 4 files changed, 31 insertions(+), 46 deletions(-) (limited to 'src') diff --git a/src/lib/format.cc b/src/lib/format.cc index faadcd797..2f9f87ba9 100644 --- a/src/lib/format.cc +++ b/src/lib/format.cc @@ -72,51 +72,51 @@ Format::setup_formats () { /// TRANSLATORS: these are film picture aspect ratios; "Academy" means 1.37, "Flat" 1.85 and "Scope" 2.39. _formats.push_back ( - new FixedFormat (119, libdcp::Size (1285, 1080), N_("119"), _("1.19"), N_("F") + new FixedFormat (1.19, libdcp::Size (1285, 1080), N_("119"), _("1.19"), N_("F") )); _formats.push_back ( - new FixedFormat (133, libdcp::Size (1436, 1080), N_("133"), _("1.33"), N_("F") + new FixedFormat (4.0 / 3.0, libdcp::Size (1436, 1080), N_("133"), _("4:3"), N_("F") )); _formats.push_back ( - new FixedFormat (138, libdcp::Size (1485, 1080), N_("138"), _("1.375"), N_("F") + new FixedFormat (1.38, libdcp::Size (1485, 1080), N_("138"), _("1.375"), N_("F") )); _formats.push_back ( - new FixedFormat (133, libdcp::Size (1998, 1080), N_("133-in-flat"), _("4:3 within Flat"), N_("F") + new FixedFormat (4.0 / 30, libdcp::Size (1998, 1080), N_("133-in-flat"), _("4:3 within Flat"), N_("F") )); _formats.push_back ( - new FixedFormat (137, libdcp::Size (1480, 1080), N_("137"), _("Academy"), N_("F") + new FixedFormat (1.37, libdcp::Size (1480, 1080), N_("137"), _("Academy"), N_("F") )); _formats.push_back ( - new FixedFormat (166, libdcp::Size (1793, 1080), N_("166"), _("1.66"), N_("F") + new FixedFormat (1.66, libdcp::Size (1793, 1080), N_("166"), _("1.66"), N_("F") )); _formats.push_back ( - new FixedFormat (166, libdcp::Size (1998, 1080), N_("166-in-flat"), _("1.66 within Flat"), N_("F") + new FixedFormat (1.66, libdcp::Size (1998, 1080), N_("166-in-flat"), _("1.66 within Flat"), N_("F") )); _formats.push_back ( - new FixedFormat (178, libdcp::Size (1998, 1080), N_("178-in-flat"), _("16:9 within Flat"), N_("F") + new FixedFormat (1.78, libdcp::Size (1998, 1080), N_("178-in-flat"), _("16:9 within Flat"), N_("F") )); _formats.push_back ( - new FixedFormat (178, libdcp::Size (1920, 1080), N_("178"), _("16:9"), N_("F") + new FixedFormat (1.78, libdcp::Size (1920, 1080), N_("178"), _("16:9"), N_("F") )); _formats.push_back ( - new FixedFormat (185, libdcp::Size (1998, 1080), N_("185"), _("Flat"), N_("F") + new FixedFormat (1.85, libdcp::Size (1998, 1080), N_("185"), _("Flat"), N_("F") )); _formats.push_back ( - new FixedFormat (178, libdcp::Size (2048, 858), N_("178-in-scope"), _("16:9 within Scope"), N_("S") + new FixedFormat (1.78, libdcp::Size (2048, 858), N_("178-in-scope"), _("16:9 within Scope"), N_("S") )); _formats.push_back ( - new FixedFormat (239, libdcp::Size (2048, 858), N_("239"), _("Scope"), N_("S") + new FixedFormat (2.39, libdcp::Size (2048, 858), N_("239"), _("Scope"), N_("S") )); _formats.push_back ( @@ -181,12 +181,12 @@ Format::all () return _formats; } -/** @param r Ratio multiplied by 100 (e.g. 185) +/** @param r Ratio * @param dcp Size (in pixels) of the images that we should put in a DCP. * @param id ID (e.g. 185) * @param n Nick name (e.g. Flat) */ -FixedFormat::FixedFormat (int r, libdcp::Size dcp, string id, string n, string d) +FixedFormat::FixedFormat (float r, libdcp::Size dcp, string id, string n, string d) : Format (dcp, id, n, d) , _ratio (r) { @@ -199,7 +199,7 @@ FixedFormat::FixedFormat (int r, libdcp::Size dcp, string id, string n, string d int Format::dcp_padding (shared_ptr f) const { - int p = rint ((_dcp_size.width - (_dcp_size.height * ratio_as_float(f))) / 2.0); + int p = rint ((_dcp_size.width - (_dcp_size.height * ratio(f))) / 2.0); /* This comes out -ve for Scope; bodge it */ if (p < 0) { @@ -210,7 +210,7 @@ Format::dcp_padding (shared_ptr f) const } float -Format::container_ratio_as_float () const +Format::container_ratio () const { return static_cast (_dcp_size.width) / _dcp_size.height; } @@ -221,14 +221,8 @@ VariableFormat::VariableFormat (libdcp::Size dcp, string id, string n, string d) } -int -VariableFormat::ratio_as_integer (shared_ptr f) const -{ - return rint (ratio_as_float (f) * 100); -} - float -VariableFormat::ratio_as_float (shared_ptr f) const +VariableFormat::ratio (shared_ptr f) const { libdcp::Size const c = f->cropped_size (f->size ()); return float (c.width) / c.height; diff --git a/src/lib/format.h b/src/lib/format.h index 783ff25ce..e95306232 100644 --- a/src/lib/format.h +++ b/src/lib/format.h @@ -38,16 +38,8 @@ public: , _dci_name (d) {} - /** @return the aspect ratio multiplied by 100 - * (e.g. 239 for Cinemascope 2.39:1) - */ - virtual int ratio_as_integer (boost::shared_ptr f) const = 0; - - /** @return the ratio as a floating point number */ - virtual float ratio_as_float (boost::shared_ptr f) const = 0; - - /** @return the ratio of the container (including any padding) as a floating point number */ - float container_ratio_as_float () const; + /** @return the ratio of the container (including any padding) */ + float container_ratio () const; int dcp_padding (boost::shared_ptr f) const; @@ -84,6 +76,9 @@ public: static void setup_formats (); protected: + /** @return the ratio */ + virtual float ratio (boost::shared_ptr f) const = 0; + /** libdcp::Size in pixels of the images that we should * put in a DCP for this ratio. This size will not correspond * to the ratio when we are doing things like 16:9 in a Flat frame. @@ -107,22 +102,17 @@ private: class FixedFormat : public Format { public: - FixedFormat (int, libdcp::Size, std::string, std::string, std::string); + FixedFormat (float, libdcp::Size, std::string, std::string, std::string); - int ratio_as_integer (boost::shared_ptr) const { + float ratio (boost::shared_ptr) const { return _ratio; } - float ratio_as_float (boost::shared_ptr) const { - return _ratio / 100.0; - } - std::string name () const; private: - /** Ratio expressed as the actual ratio multiplied by 100 */ - int _ratio; + float _ratio; }; class VariableFormat : public Format @@ -130,8 +120,7 @@ class VariableFormat : public Format public: VariableFormat (libdcp::Size, std::string, std::string, std::string); - int ratio_as_integer (boost::shared_ptr f) const; - float ratio_as_float (boost::shared_ptr f) const; + float ratio (boost::shared_ptr f) const; std::string name () const; }; diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index dbbff3713..40b74ac39 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -332,7 +332,7 @@ FilmViewer::calculate_sizes () Format const * format = _film->format (); float const panel_ratio = static_cast (_panel_size.width) / _panel_size.height; - float const film_ratio = format ? format->container_ratio_as_float () : 1.78; + float const film_ratio = format ? format->container_ratio () : 1.78; if (panel_ratio < film_ratio) { /* panel is less widscreen than the film; clamp width */ diff --git a/test/test.cc b/test/test.cc index d1bb400f9..741f36d5a 100644 --- a/test/test.cc +++ b/test/test.cc @@ -222,11 +222,13 @@ BOOST_AUTO_TEST_CASE (format_test) Format const * f = Format::from_nickname ("Flat"); BOOST_CHECK (f); - BOOST_CHECK_EQUAL (f->ratio_as_integer(shared_ptr ()), 185); + BOOST_CHECK_EQUAL (f->dcp_size().width, 1998); + BOOST_CHECK_EQUAL (f->dcp_size().height, 1080); f = Format::from_nickname ("Scope"); BOOST_CHECK (f); - BOOST_CHECK_EQUAL (f->ratio_as_integer(shared_ptr ()), 239); + BOOST_CHECK_EQUAL (f->dcp_size().width, 2048); + BOOST_CHECK_EQUAL (f->dcp_size().height, 858); } /* Test VariableFormat-based scaling of content */ -- cgit v1.2.3 From 6fe31ddd16a0819fe38ffb7e9520c6a049af078a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 12 Apr 2013 00:21:30 +0100 Subject: Fix typo. --- src/lib/format.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/lib/format.cc b/src/lib/format.cc index 2f9f87ba9..640eee167 100644 --- a/src/lib/format.cc +++ b/src/lib/format.cc @@ -84,7 +84,7 @@ Format::setup_formats () )); _formats.push_back ( - new FixedFormat (4.0 / 30, libdcp::Size (1998, 1080), N_("133-in-flat"), _("4:3 within Flat"), N_("F") + new FixedFormat (4.0 / 3.0, libdcp::Size (1998, 1080), N_("133-in-flat"), _("4:3 within Flat"), N_("F") )); _formats.push_back ( -- cgit v1.2.3 From 3f2365fcf3a1ffc50901eb2e4af246aa4c83e272 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Mon, 15 Apr 2013 16:24:03 +0100 Subject: Repair very bad merge. Mysterious. --- src/lib/ab_transcoder.cc | 4 ++-- src/lib/audio_decoder.h | 2 +- src/lib/audio_sink.h | 7 +++++++ src/lib/audio_source.cc | 6 ++++++ src/lib/audio_source.h | 12 ++++++++++++ src/lib/combiner.cc | 8 ++++---- src/lib/combiner.h | 6 +++--- src/lib/delay_line.cc | 4 ++-- src/lib/delay_line.h | 4 ++-- src/lib/matcher.cc | 2 +- src/lib/matcher.h | 6 +++--- src/lib/processor.h | 17 +++++++++++++++++ src/lib/sndfile_decoder.cc | 5 +++-- src/lib/transcoder.cc | 9 --------- src/lib/video_decoder.h | 2 +- src/lib/video_sink.h | 12 ++++++++++++ src/lib/video_source.cc | 6 ++++++ src/lib/video_source.h | 23 +++++++++++++++++++++-- 18 files changed, 103 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/lib/ab_transcoder.cc b/src/lib/ab_transcoder.cc index 7db13afcc..6eef397c2 100644 --- a/src/lib/ab_transcoder.cc +++ b/src/lib/ab_transcoder.cc @@ -70,8 +70,8 @@ ABTranscoder::ABTranscoder ( _db.video->set_subtitle_stream (_film_a->subtitle_stream ()); _da.audio->set_audio_stream (_film_a->audio_stream ()); - _da.video->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2, _3)); - _db.video->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2, _3)); + _da.video->Video.connect (bind (&Combiner::process_video, _combiner, _1, _2, _3, _4)); + _db.video->Video.connect (bind (&Combiner::process_video_b, _combiner, _1, _2, _3, _4)); _combiner->connect_video (_delay_line); _delay_line->connect_video (_matcher); diff --git a/src/lib/audio_decoder.h b/src/lib/audio_decoder.h index 9bef8e0e7..cfe94b528 100644 --- a/src/lib/audio_decoder.h +++ b/src/lib/audio_decoder.h @@ -31,7 +31,7 @@ /** @class AudioDecoder. * @brief Parent class for audio decoders. */ -class AudioDecoder : public AudioSource, public virtual Decoder +class AudioDecoder : public TimedAudioSource, public virtual Decoder { public: AudioDecoder (boost::shared_ptr, DecodeOptions); diff --git a/src/lib/audio_sink.h b/src/lib/audio_sink.h index 11d578a60..f34b24f88 100644 --- a/src/lib/audio_sink.h +++ b/src/lib/audio_sink.h @@ -27,4 +27,11 @@ public: virtual void process_audio (boost::shared_ptr) = 0; }; +class TimedAudioSink +{ +public: + /** Call with some audio data */ + virtual void process_audio (boost::shared_ptr, double t) = 0; +}; + #endif diff --git a/src/lib/audio_source.cc b/src/lib/audio_source.cc index 53b0dda15..bca3562cf 100644 --- a/src/lib/audio_source.cc +++ b/src/lib/audio_source.cc @@ -28,3 +28,9 @@ AudioSource::connect_audio (shared_ptr s) { Audio.connect (bind (&AudioSink::process_audio, s, _1)); } + +void +TimedAudioSource::connect_audio (shared_ptr s) +{ + Audio.connect (bind (&TimedAudioSink::process_audio, s, _1, _2)); +} diff --git a/src/lib/audio_source.h b/src/lib/audio_source.h index 5a1510d3c..3dc998cca 100644 --- a/src/lib/audio_source.h +++ b/src/lib/audio_source.h @@ -28,6 +28,7 @@ class AudioBuffers; class AudioSink; +class TimedAudioSink; /** A class that emits audio data */ class AudioSource @@ -39,4 +40,15 @@ public: void connect_audio (boost::shared_ptr); }; + +/** A class that emits audio data with timestamps */ +class TimedAudioSource +{ +public: + /** Emitted when some audio data is ready */ + boost::signals2::signal, double)> Audio; + + void connect_audio (boost::shared_ptr); +}; + #endif diff --git a/src/lib/combiner.cc b/src/lib/combiner.cc index 12ce4a96e..006dd2697 100644 --- a/src/lib/combiner.cc +++ b/src/lib/combiner.cc @@ -23,7 +23,7 @@ using boost::shared_ptr; Combiner::Combiner (shared_ptr log) - : VideoProcessor (log) + : TimedVideoProcessor (log) { } @@ -33,7 +33,7 @@ Combiner::Combiner (shared_ptr log) * @param image Frame image. */ void -Combiner::process_video (shared_ptr image, bool, shared_ptr) +Combiner::process_video (shared_ptr image, bool, shared_ptr, double t) { _image = image; } @@ -43,7 +43,7 @@ Combiner::process_video (shared_ptr image, bool, shared_ptr) * @param sub Subtitle (which will be put onto the whole frame) */ void -Combiner::process_video_b (shared_ptr image, bool, shared_ptr sub) +Combiner::process_video_b (shared_ptr image, bool, shared_ptr sub, double t) { /* Copy the right half of this image into our _image */ /* XXX: this should probably be in the Image class */ @@ -62,6 +62,6 @@ Combiner::process_video_b (shared_ptr image, bool, shared_ptr s } } - Video (_image, false, sub); + Video (_image, false, sub, t); _image.reset (); } diff --git a/src/lib/combiner.h b/src/lib/combiner.h index 68026eaff..a8f1fa804 100644 --- a/src/lib/combiner.h +++ b/src/lib/combiner.h @@ -28,13 +28,13 @@ * one image used for the left half of the screen and the other for * the right. */ -class Combiner : public VideoProcessor +class Combiner : public TimedVideoProcessor { public: Combiner (boost::shared_ptr log); - void process_video (boost::shared_ptr i, bool, boost::shared_ptr s); - void process_video_b (boost::shared_ptr i, bool, boost::shared_ptr s); + void process_video (boost::shared_ptr i, bool, boost::shared_ptr s, double); + void process_video_b (boost::shared_ptr i, bool, boost::shared_ptr s, double); private: /** The image that we are currently working on */ diff --git a/src/lib/delay_line.cc b/src/lib/delay_line.cc index c8e593a18..9e6baeba8 100644 --- a/src/lib/delay_line.cc +++ b/src/lib/delay_line.cc @@ -30,14 +30,14 @@ using boost::shared_ptr; /* @param seconds Delay in seconds, +ve to move audio later. */ DelayLine::DelayLine (shared_ptr log, double seconds) - : Processor (log) + : TimedAudioVideoProcessor (log) , _seconds (seconds) { } void -DelayLine::process_audio (shared_ptr data) +DelayLine::process_audio (shared_ptr data, double t) { if (_seconds > 0) { t += _seconds; diff --git a/src/lib/delay_line.h b/src/lib/delay_line.h index 7a8b11c69..90f1dcfa7 100644 --- a/src/lib/delay_line.h +++ b/src/lib/delay_line.h @@ -20,8 +20,8 @@ #include #include "processor.h" -/** A delay line for audio */ -class DelayLine : public Processor, public TimedAudioSink, public TimedAudioSource, public TimedVideoSink, public TimedVideoSource +/** A delay line */ +class DelayLine : public TimedAudioVideoProcessor { public: DelayLine (boost::shared_ptr log, double); diff --git a/src/lib/matcher.cc b/src/lib/matcher.cc index 69d12e2c4..34ddc86d6 100644 --- a/src/lib/matcher.cc +++ b/src/lib/matcher.cc @@ -29,7 +29,7 @@ using std::list; using boost::shared_ptr; Matcher::Matcher (shared_ptr log, int sample_rate, float frames_per_second) - : AudioVideoProcessor (log) + : Processor (log) , _sample_rate (sample_rate) , _frames_per_second (frames_per_second) , _video_frames (0) diff --git a/src/lib/matcher.h b/src/lib/matcher.h index 4ec0a3e96..f54aa4b6a 100644 --- a/src/lib/matcher.h +++ b/src/lib/matcher.h @@ -21,12 +21,12 @@ #include "processor.h" #include "ffmpeg_compatibility.h" -class Matcher : public AudioVideoProcessor +class Matcher : public Processor, public TimedAudioSink, public TimedVideoSink, public AudioSource, public VideoSource { public: Matcher (boost::shared_ptr log, int sample_rate, float frames_per_second); - void process_video (boost::shared_ptr i, bool, boost::shared_ptr s); - void process_audio (boost::shared_ptr); + void process_video (boost::shared_ptr i, bool, boost::shared_ptr s, double); + void process_audio (boost::shared_ptr, double); void process_end (); private: diff --git a/src/lib/processor.h b/src/lib/processor.h index 1ba396f2f..603239f8f 100644 --- a/src/lib/processor.h +++ b/src/lib/processor.h @@ -67,6 +67,15 @@ public: {} }; +class TimedAudioVideoProcessor : public Processor, public TimedVideoSource, public TimedVideoSink, public TimedAudioSource, public TimedAudioSink +{ +public: + TimedAudioVideoProcessor (boost::shared_ptr log) + : Processor (log) + {} +}; + + /** @class AudioProcessor * @brief A processor which handles just audio data. */ @@ -95,4 +104,12 @@ public: {} }; +class TimedVideoProcessor : public Processor, public TimedVideoSource, public TimedVideoSink +{ +public: + TimedVideoProcessor (boost::shared_ptr log) + : Processor (log) + {} +}; + #endif diff --git a/src/lib/sndfile_decoder.cc b/src/lib/sndfile_decoder.cc index 0e3e5e234..af59c049c 100644 --- a/src/lib/sndfile_decoder.cc +++ b/src/lib/sndfile_decoder.cc @@ -113,8 +113,8 @@ SndfileDecoder::pass () to what FFmpeg (and in particular the resampler) can cope with. */ sf_count_t const block = _audio_stream->sample_rate() / 2; - shared_ptr audio (new AudioBuffers (_audio_stream->channels(), block)); + sf_count_t done = 0; while (frames > 0) { sf_count_t const this_time = min (block, frames); for (size_t i = 0; i < sndfiles.size(); ++i) { @@ -126,7 +126,8 @@ SndfileDecoder::pass () } audio->set_frames (this_time); - Audio (audio); + Audio (audio, double(done) / _audio_stream->sample_rate()); + done += this_time; frames -= this_time; } diff --git a/src/lib/transcoder.cc b/src/lib/transcoder.cc index 8046080de..23fb5b788 100644 --- a/src/lib/transcoder.cc +++ b/src/lib/transcoder.cc @@ -63,18 +63,9 @@ Transcoder::Transcoder (shared_ptr f, DecodeOptions o, Job* j, shared_ptr< _decoders.video->set_subtitle_stream (f->subtitle_stream ()); _decoders.audio->set_audio_stream (f->audio_stream ()); -<<<<<<< HEAD _decoders.video->connect_video (_delay_line); _delay_line->connect_video (_matcher); _matcher->connect_video (_encoder); -======= - if (_matcher) { - _decoders.video->connect_video (_matcher); - _matcher->connect_video (_encoder); - } else { - _decoders.video->connect_video (_encoder); - } ->>>>>>> master _decoders.audio->connect_audio (_delay_line); _delay_line->connect_audio (_matcher); diff --git a/src/lib/video_decoder.h b/src/lib/video_decoder.h index f8612dff2..6e4fd48c0 100644 --- a/src/lib/video_decoder.h +++ b/src/lib/video_decoder.h @@ -24,7 +24,7 @@ #include "stream.h" #include "decoder.h" -class VideoDecoder : public VideoSource, public virtual Decoder +class VideoDecoder : public TimedVideoSource, public virtual Decoder { public: VideoDecoder (boost::shared_ptr, DecodeOptions); diff --git a/src/lib/video_sink.h b/src/lib/video_sink.h index 7c128cf73..32c7f3b38 100644 --- a/src/lib/video_sink.h +++ b/src/lib/video_sink.h @@ -37,4 +37,16 @@ public: virtual void process_video (boost::shared_ptr i, bool same, boost::shared_ptr s) = 0; }; +class TimedVideoSink +{ +public: + /** Call with a frame of video. + * @param i Video frame image. + * @param same true if i is the same as last time we were called. + * @param s A subtitle that should be on this frame, or 0. + * @param t Source timestamp. + */ + virtual void process_video (boost::shared_ptr i, bool same, boost::shared_ptr s, double t) = 0; +}; + #endif diff --git a/src/lib/video_source.cc b/src/lib/video_source.cc index 56742e2b4..af6f941fd 100644 --- a/src/lib/video_source.cc +++ b/src/lib/video_source.cc @@ -28,3 +28,9 @@ VideoSource::connect_video (shared_ptr s) { Video.connect (bind (&VideoSink::process_video, s, _1, _2, _3)); } + +void +TimedVideoSource::connect_video (shared_ptr s) +{ + Video.connect (bind (&TimedVideoSink::process_video, s, _1, _2, _3, _4)); +} diff --git a/src/lib/video_source.h b/src/lib/video_source.h index 893629160..705b0023a 100644 --- a/src/lib/video_source.h +++ b/src/lib/video_source.h @@ -29,11 +29,12 @@ #include "util.h" class VideoSink; +class TimedVideoSink; class Subtitle; class Image; -/** @class VideoSink - * @param A class that emits video data. +/** @class VideoSource + * @param A class that emits video data without timestamps. */ class VideoSource { @@ -49,4 +50,22 @@ public: void connect_video (boost::shared_ptr); }; +/** @class TimedVideoSource + * @param A class that emits video data with timestamps. + */ +class TimedVideoSource +{ +public: + + /** Emitted when a video frame is ready. + * First parameter is the video image. + * Second parameter is true if the image is the same as the last one that was emitted. + * Third parameter is either 0 or a subtitle that should be on this frame. + * Fourth parameter is the source timestamp of this frame. + */ + boost::signals2::signal, bool, boost::shared_ptr, double)> Video; + + void connect_video (boost::shared_ptr); +}; + #endif -- cgit v1.2.3 From 4a3d017ef63c1442e00954c0c222b66ab51a4ee9 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 19 Apr 2013 00:28:10 +0100 Subject: Fix warning. --- src/lib/combiner.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src') diff --git a/src/lib/combiner.cc b/src/lib/combiner.cc index 006dd2697..0a9eaf6b6 100644 --- a/src/lib/combiner.cc +++ b/src/lib/combiner.cc @@ -33,7 +33,7 @@ Combiner::Combiner (shared_ptr log) * @param image Frame image. */ void -Combiner::process_video (shared_ptr image, bool, shared_ptr, double t) +Combiner::process_video (shared_ptr image, bool, shared_ptr, double) { _image = image; } -- cgit v1.2.3 From c9375d572f6e508f4d6a3c039d3827ef44407c1a Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 19 Apr 2013 21:20:00 +0100 Subject: Write metadata on new film to go with the directory that is created. --- src/lib/film.cc | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src') diff --git a/src/lib/film.cc b/src/lib/film.cc index a42b874e8..227f8557b 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -143,6 +143,8 @@ Film::Film (string d, bool must_exist) if (must_exist) { read_metadata (); + } else { + write_metadata (); } _log.reset (new FileLog (file ("log"))); -- cgit v1.2.3 From f2989966b2871ac5fc5f78c2a3ce7867f344b7cd Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Fri, 19 Apr 2013 22:10:10 +0100 Subject: Basic frame index and timecode (part of #68). --- ChangeLog | 2 ++ src/wx/film_viewer.cc | 24 ++++++++++++++++++++++++ src/wx/film_viewer.h | 2 ++ 3 files changed, 28 insertions(+) (limited to 'src') diff --git a/ChangeLog b/ChangeLog index fb526efd1..12890135c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,7 @@ 2013-04-19 Carl Hetherington + * Add basic frame index and timecode to viewer (part of #68). + * Version 0.84beta4 released. 2013-04-19 Carl Hetherington diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 40b74ac39..00f895285 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)) + , _frame (new wxStaticText (this, wxID_ANY, wxT(""))) + , _timecode (new wxStaticText (this, wxID_ANY, wxT(""))) , _play_button (new wxToggleButton (this, wxID_ANY, _("Play"))) , _display_frame_x (0) , _got_frame (false) @@ -66,11 +68,19 @@ FilmViewer::FilmViewer (shared_ptr f, wxWindow* p) _v_sizer->Add (_panel, 1, wxEXPAND); wxBoxSizer* h_sizer = new wxBoxSizer (wxHORIZONTAL); + + wxBoxSizer* time_sizer = new wxBoxSizer (wxVERTICAL); + time_sizer->Add (_frame, 0, wxEXPAND); + time_sizer->Add (_timecode, 0, wxEXPAND); + + h_sizer->Add (time_sizer, 0, wxEXPAND); 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)); + _panel->Connect (wxID_ANY, wxEVT_PAINT, wxPaintEventHandler (FilmViewer::paint_panel), 0, this); _panel->Connect (wxID_ANY, wxEVT_SIZE, wxSizeEventHandler (FilmViewer::panel_sized), 0, this); _slider->Connect (wxID_ANY, wxEVT_SCROLL_THUMBTRACK, wxScrollEventHandler (FilmViewer::slider_moved), 0, this); @@ -390,6 +400,20 @@ FilmViewer::process_video (shared_ptr image, bool, shared_ptr s raw_to_display (); _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)))); + + double w = t; + int const h = (w / 3600); + w -= h * 3600; + int const m = (w / 60); + w -= m * 60; + int const s = floor (w); + w -= s; + int const f = rint (w * fps); + _timecode->SetLabel (wxString::Format ("%02d:%02d:%02d:%02d", h, m, s, f)); } void diff --git a/src/wx/film_viewer.h b/src/wx/film_viewer.h index 784434f6b..859bf7ede 100644 --- a/src/wx/film_viewer.h +++ b/src/wx/film_viewer.h @@ -62,6 +62,8 @@ private: wxSizer* _v_sizer; wxPanel* _panel; wxSlider* _slider; + wxStaticText* _frame; + wxStaticText* _timecode; wxToggleButton* _play_button; wxTimer _timer; -- cgit v1.2.3 From 45747c8dce6448cc6b6c797ae04f3e5fc3b741e8 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 20 Apr 2013 02:18:23 +0100 Subject: Fix for 2.8. --- src/wx/film_viewer.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src') diff --git a/src/wx/film_viewer.cc b/src/wx/film_viewer.cc index 00f895285..fe2025b78 100644 --- a/src/wx/film_viewer.cc +++ b/src/wx/film_viewer.cc @@ -403,7 +403,7 @@ FilmViewer::process_video (shared_ptr image, bool, shared_ptr s 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)))); + _frame->SetLabel (wxString::Format (wxT("%d"), int (rint (t * fps)))); double w = t; int const h = (w / 3600); @@ -413,7 +413,7 @@ FilmViewer::process_video (shared_ptr image, bool, shared_ptr s int const s = floor (w); w -= s; int const f = rint (w * fps); - _timecode->SetLabel (wxString::Format ("%02d:%02d:%02d:%02d", h, m, s, f)); + _timecode->SetLabel (wxString::Format (wxT("%02d:%02d:%02d:%02d"), h, m, s, f)); } void -- 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') 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') 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 From f20cd70a9afc28f785ef4a50c875ccf6c3729462 Mon Sep 17 00:00:00 2001 From: Carl Hetherington Date: Sat, 20 Apr 2013 20:45:32 +0100 Subject: Import Ardour's LocaleGuard to fix problems with saving decimals to metadata (#119). --- ChangeLog | 5 +++++ src/lib/film.cc | 3 +++ src/lib/util.cc | 19 +++++++++++++++++++ src/lib/util.h | 11 +++++++++++ 4 files changed, 38 insertions(+) (limited to 'src') diff --git a/ChangeLog b/ChangeLog index 85cdb48a6..0fbb73773 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2013-04-20 Carl Hetherington + + * Fix bad saving of metadata in locales which use + commas to separate decimals (#119). + 2013-04-19 Carl Hetherington * Add basic frame index and timecode to viewer, and previous/next diff --git a/src/lib/film.cc b/src/lib/film.cc index 227f8557b..b0785df34 100644 --- a/src/lib/film.cc +++ b/src/lib/film.cc @@ -204,6 +204,7 @@ string Film::video_state_identifier () const { assert (format ()); + LocaleGuard lg; pair f = Filter::ffmpeg_strings (filters()); @@ -428,6 +429,7 @@ void Film::write_metadata () const { boost::mutex::scoped_lock lm (_state_mutex); + LocaleGuard lg; boost::filesystem::create_directories (directory()); @@ -515,6 +517,7 @@ void Film::read_metadata () { boost::mutex::scoped_lock lm (_state_mutex); + LocaleGuard lg; _external_audio.clear (); _content_audio_streams.clear (); diff --git a/src/lib/util.cc b/src/lib/util.cc index 557e9a34b..e43b598ab 100644 --- a/src/lib/util.cc +++ b/src/lib/util.cc @@ -1018,3 +1018,22 @@ FrameRateConversion::FrameRateConversion (float source, int dcp) } } } + +LocaleGuard::LocaleGuard () + : _old (0) +{ + char const * old = setlocale (LC_NUMERIC, 0); + + if (old) { + _old = strdup (old); + if (strcmp (_old, "POSIX")) { + setlocale (LC_NUMERIC, "POSIX"); + } + } +} + +LocaleGuard::~LocaleGuard () +{ + setlocale (LC_NUMERIC, _old); + free (_old); +} diff --git a/src/lib/util.h b/src/lib/util.h index 3d251cf06..31d0fc967 100644 --- a/src/lib/util.h +++ b/src/lib/util.h @@ -293,5 +293,16 @@ extern int64_t video_frames_to_audio_frames (SourceFrame v, float audio_sample_r extern bool still_image_file (std::string); extern std::pair cpu_info (); +class LocaleGuard +{ +public: + LocaleGuard (); + ~LocaleGuard (); + +private: + char* _old; +}; + + #endif -- cgit v1.2.3