2 Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 using boost::shared_ptr;
31 Matcher::Matcher (shared_ptr<Log> log, int sample_rate, float frames_per_second)
33 , _sample_rate (sample_rate)
34 , _frames_per_second (frames_per_second)
37 , _had_first_audio (false)
43 Matcher::process_video (boost::shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
45 _pixel_format = image->pixel_format ();
46 _size = image->size ();
48 _log->log(String::compose("Matcher video @ %1 [audio=%2, video=%3, pending_audio=%4]", t, _audio_frames, _video_frames, _pending_audio.size()));
54 bool const this_is_first_video = !_first_video;
60 if (this_is_first_video && _had_first_audio) {
61 /* First video since we got audio */
65 /* Video before audio is fine, since we can make up an arbitrary difference
66 with audio samples (contrasting with video which is quantised to frames)
69 /* Difference between where this video is and where it should be */
70 double const delta = t - _first_input.get() - _video_frames / _frames_per_second;
71 double const one_frame = 1 / _frames_per_second;
73 if (delta > one_frame) {
74 /* Insert frames to make up the difference */
75 int const extra = rint (delta / one_frame);
76 for (int i = 0; i < extra; ++i) {
78 _log->log (String::compose ("Extra video frame inserted at %1s", _video_frames / _frames_per_second));
82 if (delta > -one_frame) {
83 Video (image, same, sub);
86 /* We are omitting a frame to keep things right */
87 _log->log (String::compose ("Frame removed at %1s", t));
95 Matcher::process_audio (boost::shared_ptr<const AudioBuffers> b, double t)
97 _channels = b->channels ();
99 _log->log (String::compose (
100 "Matcher audio (%1 frames) @ %2 [video=%3, audio=%4, pending_audio=%5]",
101 b->frames(), t, _video_frames, _audio_frames, _pending_audio.size()
109 bool const this_is_first_audio = !_had_first_audio;
110 _had_first_audio = true;
113 /* No video yet; we must postpone these data until we have some */
114 _pending_audio.push_back (AudioRecord (b, t));
115 } else if (this_is_first_audio && _first_video) {
116 /* First audio since we got video */
117 _pending_audio.push_back (AudioRecord (b, t));
118 fix_start (_first_input.get ());
120 /* Normal running. We assume audio time stamps are consecutive */
122 _audio_frames += b->frames ();
127 Matcher::process_end ()
129 if (_audio_frames == 0 || !_pixel_format || !_size || !_channels) {
130 /* We won't do anything */
134 _log->log (String::compose ("Matcher has seen %1 video frames (which equals %2 audio frames) and %3 audio frames",
135 _video_frames, video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second), _audio_frames));
137 match ((double (_audio_frames) / _sample_rate) - (double (_video_frames) / _frames_per_second));
141 Matcher::fix_start (double first_video)
143 assert (!_pending_audio.empty ());
145 _log->log (String::compose ("Fixing start; video at %1, audio at %2", first_video, _pending_audio.front().time));
147 match (first_video - _pending_audio.front().time);
149 for (list<AudioRecord>::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) {
150 if (_audio_starts_with_video) {
151 assert (_first_video);
152 assert (_first_audio);
153 i->time += _first_video.get() - _first_audio.get();
155 process_audio (i->audio, i->time);
158 _pending_audio.clear ();
162 Matcher::match (double extra_video_needed)
164 _log->log (String::compose ("Match %1", extra_video_needed));
166 if (extra_video_needed > 0) {
168 /* Emit black video frames */
170 int const black_video_frames = ceil (extra_video_needed * _frames_per_second);
172 _log->log (String::compose (N_("Emitting %1 frames of black video"), black_video_frames));
174 shared_ptr<Image> black (new SimpleImage (_pixel_format.get(), _size.get(), true));
175 black->make_black ();
176 for (int i = 0; i < black_video_frames; ++i) {
177 Video (black, i != 0, shared_ptr<Subtitle>());
181 extra_video_needed -= black_video_frames / _frames_per_second;
184 if (extra_video_needed < 0) {
188 int64_t to_do = -extra_video_needed * _sample_rate;
189 _log->log (String::compose (N_("Emitting %1 frames of silence"), to_do));
191 /* Do things in half second blocks as I think there may be limits
192 to what FFmpeg (and in particular the resampler) can cope with.
194 int64_t const block = _sample_rate / 2;
195 shared_ptr<AudioBuffers> b (new AudioBuffers (_channels.get(), block));
199 int64_t const this_time = min (to_do, block);
200 b->set_frames (this_time);
202 _audio_frames += b->frames ();
209 Matcher::repeat_last_video ()
212 shared_ptr<Image> im (new SimpleImage (_pixel_format.get(), _size.get(), true));
217 Video (_last_image, true, _last_subtitle);
221 /** Set `audio starts with video' behaviour. If this is enabled,
222 * audio time stamps are adjusted so that the first audio is synced
223 * with the first video.
226 Matcher::set_audio_starts_with_video (bool a)
228 _audio_starts_with_video = a;