Basics of FFmpeg examiner works.
[dcpomatic.git] / src / lib / matcher.cc
1 /*
2     Copyright (C) 2012 Carl Hetherington <cth@carlh.net>
3
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.
8
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.
13
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.
17
18 */
19
20 #include "matcher.h"
21 #include "image.h"
22 #include "log.h"
23
24 #include "i18n.h"
25
26 using std::min;
27 using std::cout;
28 using std::list;
29 using boost::shared_ptr;
30
31 Matcher::Matcher (shared_ptr<Log> log, int sample_rate, float frames_per_second)
32         : Processor (log)
33         , _sample_rate (sample_rate)
34         , _frames_per_second (frames_per_second)
35         , _video_frames (0)
36         , _audio_frames (0)
37         , _had_first_video (false)
38         , _had_first_audio (false)
39 {
40
41 }
42
43 void
44 Matcher::process_video (boost::shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
45 {
46         _pixel_format = image->pixel_format ();
47         _size = image->size ();
48
49         _log->log(String::compose("Matcher video @ %1 [audio=%2, video=%3, pending_audio=%4]", t, _audio_frames, _video_frames, _pending_audio.size()));
50
51         if (!_first_input || t < _first_input.get()) {
52                 _first_input = t;
53         }
54
55         bool const this_is_first_video = !_had_first_video;
56         _had_first_video = true;
57
58         if (!_had_first_audio) {
59                 /* No audio yet; we must postpone these data until we have some */
60                 _pending_video.push_back (VideoRecord (image, same, sub, t));
61         } else if (this_is_first_video && _had_first_audio) {
62                 /* First video since we got audio */
63                 _pending_video.push_back (VideoRecord (image, same, sub, t));
64                 fix_start ();
65         } else {
66                 /* Normal running */
67
68                 /* Difference between where this video is and where it should be */
69                 double const delta = t - _first_input.get() - _video_frames / _frames_per_second;
70                 double const one_frame = 1 / _frames_per_second;
71                 
72                 if (delta > one_frame) {
73                         /* Insert frames to make up the difference */
74                         int const extra = rint (delta / one_frame);
75                         for (int i = 0; i < extra; ++i) {
76                                 repeat_last_video ();
77                                 _log->log (String::compose ("Extra video frame inserted at %1s", _video_frames / _frames_per_second));
78                         }
79                 }
80                 
81                 if (delta > -one_frame) {
82                         Video (image, same, sub);
83                         ++_video_frames;
84                 } else {
85                         /* We are omitting a frame to keep things right */
86                         _log->log (String::compose ("Frame removed at %1s; delta %2; first input was at %3", t, delta, _first_input.get()));
87                 }
88                 
89                 _last_image = image;
90                 _last_subtitle = sub;
91         }
92 }
93
94 void
95 Matcher::process_audio (boost::shared_ptr<const AudioBuffers> b, double t)
96 {
97         _channels = b->channels ();
98
99         _log->log (String::compose (
100                            "Matcher audio (%1 frames) @ %2 [video=%3, audio=%4, pending_video=%5, pending_audio=%6]",
101                            b->frames(), t, _video_frames, _audio_frames, _pending_video.size(), _pending_audio.size()
102                            )
103                 );
104
105         if (!_first_input || t < _first_input.get()) {
106                 _first_input = t;
107         }
108
109         bool const this_is_first_audio = !_had_first_audio;
110         _had_first_audio = true;
111         
112         if (!_had_first_video) {
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 && _had_first_video) {
116                 /* First audio since we got video */
117                 _pending_audio.push_back (AudioRecord (b, t));
118                 fix_start ();
119         } else {
120                 /* Normal running.  We assume audio time stamps are consecutive, so there's no equivalent of
121                    the checking / insertion of repeat frames that there is for video.
122                 */
123                 Audio (b);
124                 _audio_frames += b->frames ();
125         }
126 }
127
128 void
129 Matcher::process_end ()
130 {
131         if (_audio_frames == 0 || !_pixel_format || !_size || !_channels) {
132                 /* We won't do anything */
133                 return;
134         }
135
136         _log->log (String::compose ("Matcher has seen %1 video frames (which equals %2 audio frames) and %3 audio frames",
137                                     _video_frames, video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second), _audio_frames));
138         
139         match ((double (_audio_frames) / _sample_rate) - (double (_video_frames) / _frames_per_second));
140 }
141
142 void
143 Matcher::fix_start ()
144 {
145         assert (!_pending_video.empty ());
146         assert (!_pending_audio.empty ());
147         
148         _log->log (String::compose ("Fixing start; video at %1, audio at %2", _pending_video.front().time, _pending_audio.front().time));
149
150         match (_pending_video.front().time - _pending_audio.front().time);
151
152         for (list<VideoRecord>::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) {
153                 process_video (i->image, i->same, i->subtitle, i->time);
154         }
155
156         _pending_video.clear ();
157
158         for (list<AudioRecord>::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) {
159                 process_audio (i->audio, i->time);
160         }
161         
162         _pending_audio.clear ();
163 }
164
165 void
166 Matcher::match (double extra_video_needed)
167 {
168         _log->log (String::compose ("Match %1", extra_video_needed));
169         
170         if (extra_video_needed > 0) {
171
172                 /* Emit black video frames */
173                 
174                 int const black_video_frames = ceil (extra_video_needed * _frames_per_second);
175
176                 _log->log (String::compose (N_("Emitting %1 frames of black video"), black_video_frames));
177
178                 shared_ptr<Image> black (new SimpleImage (_pixel_format.get(), _size.get(), true));
179                 black->make_black ();
180                 for (int i = 0; i < black_video_frames; ++i) {
181                         Video (black, i != 0, shared_ptr<Subtitle>());
182                         ++_video_frames;
183                 }
184
185                 extra_video_needed -= black_video_frames / _frames_per_second;
186         }
187
188         if (extra_video_needed < 0) {
189                 
190                 /* Emit silence */
191                 
192                 int64_t to_do = -extra_video_needed * _sample_rate;
193                 _log->log (String::compose (N_("Emitting %1 frames of silence"), to_do));
194
195                 /* Do things in half second blocks as I think there may be limits
196                    to what FFmpeg (and in particular the resampler) can cope with.
197                 */
198                 int64_t const block = _sample_rate / 2;
199                 shared_ptr<AudioBuffers> b (new AudioBuffers (_channels.get(), block));
200                 b->make_silent ();
201                 
202                 while (to_do > 0) {
203                         int64_t const this_time = min (to_do, block);
204                         b->set_frames (this_time);
205                         Audio (b);
206                         _audio_frames += b->frames ();
207                         to_do -= this_time;
208                 }
209         }
210 }
211
212 void
213 Matcher::repeat_last_video ()
214 {
215         if (!_last_image) {
216                 shared_ptr<Image> im (new SimpleImage (_pixel_format.get(), _size.get(), true));
217                 im->make_black ();
218                 _last_image = im;
219         }
220
221         Video (_last_image, true, _last_subtitle);
222         ++_video_frames;
223 }
224