Merge master.
[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 using std::min;
25 using boost::shared_ptr;
26
27 Matcher::Matcher (Log* log, int sample_rate, float frames_per_second)
28         : AudioVideoProcessor (log)
29         , _sample_rate (sample_rate)
30         , _frames_per_second (frames_per_second)
31         , _video_frames (0)
32         , _audio_frames (0)
33 {
34
35 }
36
37 void
38 Matcher::process_video (boost::shared_ptr<Image> i, boost::shared_ptr<Subtitle> s)
39 {
40         Video (i, s);
41         _video_frames++;
42
43         _pixel_format = i->pixel_format ();
44         _size = i->size ();
45 }
46
47 void
48 Matcher::process_audio (boost::shared_ptr<AudioBuffers> b)
49 {
50         Audio (b);
51         _audio_frames += b->frames ();
52
53         _channels = b->channels ();
54 }
55
56 void
57 Matcher::process_end ()
58 {
59         if (_audio_frames == 0 || !_pixel_format || !_size || !_channels) {
60                 /* We won't do anything */
61                 return;
62         }
63         
64         int64_t audio_short_by_frames = video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second) - _audio_frames;
65
66         _log->log (
67                 String::compose (
68                         "Matching processor has seen %1 video frames (which equals %2 audio frames) and %3 audio frames",
69                         _video_frames,
70                         video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second),
71                         _audio_frames
72                         )
73                 );
74         
75         if (audio_short_by_frames < 0) {
76                 
77                 _log->log (String::compose ("%1 too many audio frames", -audio_short_by_frames));
78                 
79                 /* We have seen more audio than video.  Emit enough black video frames so that we reverse this */
80                 int const black_video_frames = ceil (-audio_short_by_frames * _frames_per_second / _sample_rate);
81                 
82                 _log->log (String::compose ("Emitting %1 frames of black video", black_video_frames));
83
84                 shared_ptr<Image> black (new CompactImage (_pixel_format.get(), _size.get()));
85                 black->make_black ();
86                 for (int i = 0; i < black_video_frames; ++i) {
87                         Video (black, shared_ptr<Subtitle>());
88                 }
89                 
90                 /* Now recompute our check value */
91                 audio_short_by_frames = video_frames_to_audio_frames (_video_frames, _sample_rate, _frames_per_second) - _audio_frames;
92         }
93         
94         if (audio_short_by_frames > 0) {
95                 _log->log (String::compose ("Emitted %1 too few audio frames", audio_short_by_frames));
96
97                 /* Do things in half second blocks as I think there may be limits
98                    to what FFmpeg (and in particular the resampler) can cope with.
99                 */
100                 int64_t const block = _sample_rate / 2;
101                 shared_ptr<AudioBuffers> b (new AudioBuffers (_channels.get(), block));
102                 b->make_silent ();
103                 
104                 int64_t to_do = audio_short_by_frames;
105                 while (to_do > 0) {
106                         int64_t const this_time = min (to_do, block);
107                         b->set_frames (this_time);
108                         Audio (b);
109                         _audio_frames += b->frames ();
110                         to_do -= this_time;
111                 }
112         }
113 }