Merge master; fix crash on new film.
[dcpomatic.git] / src / lib / matcher.cc
index 93531dbc5adc6dd2584fb2d86b3d98bafeefc8d9..c56a563015b6eb617bf38535ff81be81c7cffeb0 100644 (file)
@@ -28,80 +28,92 @@ using std::cout;
 using std::list;
 using boost::shared_ptr;
 
-Matcher::Matcher (Log* log, int sample_rate, float frames_per_second)
+Matcher::Matcher (shared_ptr<Log> log, int sample_rate, float frames_per_second)
        : Processor (log)
        , _sample_rate (sample_rate)
        , _frames_per_second (frames_per_second)
        , _video_frames (0)
        , _audio_frames (0)
+       , _had_first_video (false)
+       , _had_first_audio (false)
 {
 
 }
 
 void
-Matcher::process_video (boost::shared_ptr<Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
+Matcher::process_video (shared_ptr<const Image> image, bool same, boost::shared_ptr<Subtitle> sub, double t)
 {
        _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 (_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 ()) {
+
+       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 */
-               _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));
-                       }
-               }
+               fix_start (t);
+       }
 
-               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));
+       /* 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));
+       }
+       
        _last_image = image;
        _last_subtitle = sub;
 }
 
 void
-Matcher::process_audio (boost::shared_ptr<AudioBuffers> b, double t)
+Matcher::process_audio (shared_ptr<const AudioBuffers> b, double t)
 {
        _channels = b->channels ();
-       
+
+       _log->log (String::compose (
+                          "Matcher audio (%1 frames) @ %2 [video=%3, audio=%4, pending_audio=%5]",
+                          b->frames(), 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 && _pending_video.empty ()) {
+       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 (!_pending_video.empty() && _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 ();
+               fix_start (_first_input.get ());
        } else {
                /* Normal running.  We assume audio time stamps are consecutive */
                Audio (b);
@@ -124,30 +136,26 @@ Matcher::process_end ()
 }
 
 void
-Matcher::fix_start ()
+Matcher::fix_start (double first_video)
 {
-       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);
+       _log->log (String::compose ("Fixing start; video at %1, audio at %2", first_video, _pending_audio.front().time));
 
-       for (list<VideoRecord>::iterator i = _pending_video.begin(); i != _pending_video.end(); ++i) {
-               process_video (i->image, i->same, i->subtitle, i->time);
-       }
+       match (first_video - _pending_audio.front().time);
 
        for (list<AudioRecord>::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) {
                process_audio (i->audio, i->time);
        }
        
-       _pending_video.clear ();
        _pending_audio.clear ();
 }
 
 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 */
@@ -171,7 +179,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.
@@ -194,8 +202,9 @@ void
 Matcher::repeat_last_video ()
 {
        if (!_last_image) {
-               _last_image.reset (new SimpleImage (_pixel_format.get(), _size.get(), true));
-               _last_image->make_black ();
+               shared_ptr<Image> im (new SimpleImage (_pixel_format.get(), _size.get(), true));
+               im->make_black ();
+               _last_image = im;
        }
 
        Video (_last_image, true, _last_subtitle);