Tweak rounding. Pass pending video / audio back through so it is treated the same...
[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 (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 {
38
39 }
40
41 void
42 Matcher::process_video (boost::shared_ptr<Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
43 {
44         _pixel_format = image->pixel_format ();
45         _size = image->size ();
46
47         _log->log(String::compose("Matcher video @ %1 (same=%2)", t, same));
48
49         if (!_first_input) {
50                 _first_input = t;
51         }
52         
53         if (_audio_frames == 0 && _pending_audio.empty ()) {
54                 /* No audio yet; we must postpone this frame until we have some */
55                 _pending_video.push_back (VideoRecord (image, same, sub, t));
56         } else if (!_pending_audio.empty() && _pending_video.empty ()) {
57                 /* First video since we got audio */
58                 _pending_video.push_back (VideoRecord (image, same, sub, t));
59                 fix_start ();
60         } else {
61                 /* Normal running */
62
63                 /* Difference between where this video is and where it should be */
64                 double const delta = t - _first_input.get() - _video_frames / _frames_per_second;
65                 double const one_frame = 1 / _frames_per_second;
66
67                 if (delta > one_frame) {
68                         /* Insert frames to make up the difference */
69                         int const extra = rint (delta / one_frame);
70                         for (int i = 0; i < extra; ++i) {
71                                 repeat_last_video ();
72                                 _log->log (String::compose ("Extra video frame inserted at %1s", _video_frames / _frames_per_second));
73                         }
74                 }
75
76                 if (delta > -one_frame) {
77                         Video (image, same, sub);
78                         ++_video_frames;
79                 } else {
80                         /* We are omitting a frame to keep things right */
81                         _log->log (String::compose ("Frame removed at %1s", t));
82                 }
83         }
84                 
85         _last_image = image;
86         _last_subtitle = sub;
87 }
88
89 void
90 Matcher::process_audio (boost::shared_ptr<AudioBuffers> b, double t)
91 {
92         _channels = b->channels ();
93         
94         if (!_first_input) {
95                 _first_input = t;
96         }
97         
98         if (_video_frames == 0 && _pending_video.empty ()) {
99                 /* No video yet; we must postpone these data until we have some */
100                 _pending_audio.push_back (AudioRecord (b, t));
101         } else if (!_pending_video.empty() && _pending_audio.empty ()) {
102                 /* First audio since we got video */
103                 _pending_audio.push_back (AudioRecord (b, t));
104                 fix_start ();
105         } else {
106                 /* Normal running.  We assume audio time stamps are consecutive */
107                 Audio (b);
108                 _audio_frames += b->frames ();
109         }
110 }
111
112 void
113 Matcher::process_end ()
114 {
115         if (_audio_frames == 0 || !_pixel_format || !_size || !_channels) {
116                 /* We won't do anything */
117                 return;
118         }
119         
120         match ((double (_audio_frames) / _sample_rate) - (double (_video_frames) / _frames_per_second));
121 }
122
123 void
124 Matcher::fix_start ()
125 {
126         assert (!_pending_video.empty ());
127         assert (!_pending_audio.empty ());
128
129         _log->log (String::compose ("Fixing start; video at %1, audio at %2", _pending_video.front().time, _pending_audio.front().time));
130
131         match (_pending_video.front().time - _pending_audio.front().time);
132
133         for (list<VideoRecord>::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) {
134                 process_video (i->image, i->same, i->subtitle, i->time);
135         }
136
137         for (list<AudioRecord>::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) {
138                 process_audio (i->audio, i->time);
139         }
140         
141         _pending_video.clear ();
142         _pending_audio.clear ();
143 }
144
145 void
146 Matcher::match (double extra_video_needed)
147 {
148         if (extra_video_needed > 0) {
149
150                 /* Emit black video frames */
151                 
152                 int const black_video_frames = ceil (extra_video_needed * _frames_per_second);
153
154                 _log->log (String::compose (N_("Emitting %1 frames of black video"), black_video_frames));
155
156                 shared_ptr<Image> black (new SimpleImage (_pixel_format.get(), _size.get(), true));
157                 black->make_black ();
158                 for (int i = 0; i < black_video_frames; ++i) {
159                         Video (black, i != 0, shared_ptr<Subtitle>());
160                         ++_video_frames;
161                 }
162
163                 extra_video_needed -= black_video_frames / _frames_per_second;
164         }
165
166         if (extra_video_needed < 0) {
167                 
168                 /* Emit silence */
169                 
170                 int64_t to_do = -extra_video_needed * _sample_rate;
171                 _log->log (String::compose (N_("Emitted %1 frames of silence"), to_do));
172
173                 /* Do things in half second blocks as I think there may be limits
174                    to what FFmpeg (and in particular the resampler) can cope with.
175                 */
176                 int64_t const block = _sample_rate / 2;
177                 shared_ptr<AudioBuffers> b (new AudioBuffers (_channels.get(), block));
178                 b->make_silent ();
179                 
180                 while (to_do > 0) {
181                         int64_t const this_time = min (to_do, block);
182                         b->set_frames (this_time);
183                         Audio (b);
184                         _audio_frames += b->frames ();
185                         to_do -= this_time;
186                 }
187         }
188 }
189
190 void
191 Matcher::repeat_last_video ()
192 {
193         if (!_last_image) {
194                 _last_image.reset (new SimpleImage (_pixel_format.get(), _size.get(), true));
195                 _last_image->make_black ();
196         }
197
198         Video (_last_image, true, _last_subtitle);
199         ++_video_frames;
200 }
201