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 (boost::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) {
+ if (!_first_input || t < _first_input.get()) {
_first_input = 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)
- */
+ bool const this_is_first_video = !_had_first_video;
+ _had_first_video = true;
- /* 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));
- }
-
- if (!_pending_audio.empty() && _video_frames == 1) {
+ if (!_had_first_audio) {
+ /* No audio yet; we must postpone these data until we have some */
+ _pending_video.push_back (VideoRecord (image, same, sub, t));
+ } else 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;
- _last_image = image;
- _last_subtitle = sub;
+ 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; delta %2; first input was at %3", t, delta, _first_input.get()));
+ }
+
+ _last_image = image;
+ _last_subtitle = sub;
+ }
}
void
-Matcher::process_audio (boost::shared_ptr<AudioBuffers> b, double t)
+Matcher::process_audio (boost::shared_ptr<const AudioBuffers> b, double t)
{
_channels = b->channels ();
-
- if (!_first_input) {
+
+ _log->log (String::compose (
+ "Matcher audio (%1 frames) @ %2 [video=%3, audio=%4, pending_video=%5, pending_audio=%6]",
+ b->frames(), t, _video_frames, _audio_frames, _pending_video.size(), _pending_audio.size()
+ )
+ );
+
+ if (!_first_input || t < _first_input.get()) {
_first_input = t;
}
+
+ bool const this_is_first_audio = !_had_first_audio;
+ _had_first_audio = true;
- if (_video_frames == 0) {
+ 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 (_video_frames > 0 && _audio_frames == 0 && _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 ();
} else {
- /* Normal running. We assume audio time stamps are consecutive */
+ /* Normal running. We assume audio time stamps are consecutive, so there's no equivalent of
+ the checking / insertion of repeat frames that there is for video.
+ */
Audio (b);
_audio_frames += b->frames ();
}
void
Matcher::fix_start ()
{
+ assert (!_pending_video.empty ());
assert (!_pending_audio.empty ());
- assert (_first_input);
+
+ _log->log (String::compose ("Fixing start; video at %1, audio at %2", _pending_video.front().time, _pending_audio.front().time));
- _log->log (String::compose ("Fixing start; start at %1, audio at %2", _first_input.get(), _pending_audio.front().time));
+ match (_pending_video.front().time - _pending_audio.front().time);
- /* This will not add any video frames, since the input parameter will always be -ve.
- Indeed, it cannot add any, since we've already started adding "real" video.
- */
- match (_first_input.get() - _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);
+ }
+
+ _pending_video.clear ();
for (list<AudioRecord>::iterator i = _pending_audio.begin(); i != _pending_audio.end(); ++i) {
process_audio (i->audio, i->time);
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 */
/* 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.
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);