Replace dropped-frame silence in vdi-caps


I have a recording made of a recent school event which we are hoping to give to parents as they were unable to attend due to Covid. Unfortunately, the camera (an old DV camcorder) obviously had a problem with the record heads and there is frame corruption about every 1s which has led to 1-frame of dropped audio (samples go to 0) which amounts to 960 samples @ 48KHZ. I have attached a short (~4s) sample which shows it quite clearly (I think).

I have tried the Audacity repair feature (that only works up to 128 samples) and am still experimenting with more detailed plugins etc. but this is way above my skillset and I was hoping someone here might have some advice. I’m quite happy learning new tools and generally technically minded but don’t have much experience with audio editing and would greatly appreciate any assistance here.


(Please ensure that it is a “lossless” format such as WAV or FLAC)

My apologies - keyboard error resulted in me hitting ENTER before I’d finished which caused it to get submitted. Moderation was so fast I didn’t have time to fix :slight_smile:

File is attached now.

There will still be some clicks and crackles, but this will remove the gaps:


Thanks Steve.
I tried this and it does chop the silence quite well. Unfortunately it then shrinks the length of the audio relative to the video. I tried changing the tempo so that it went back to the original length on the basis that the silences are short and spaced out over the whole thing and a bit of drift between them would be OK. Unfortunately, the spacing is obviously not regular enough and parts of the video become horribly unsynced.
However, it gave me an idea and I got some bash scripts together to use ffmpeg to locate frames with silence and delete the frame and the audio - as it happens some of the related frames are also corrupt so this worked for both and being only 1 or 2 frames at a time the results wasn’t too choppy (compared to the corrupt original anyway). I’ll post eventual solution once I’m happy (enough) with it.

In the end I used FFMPEG for a lot of the work, but did use Audacity to clean up the final audio track with clip-fixing, click-removal and LP-filter so, in case this may give ideas to others and since I’ve got a solution that is not perfect, but about all I can achieve in the time I have spare, this is what I did:

An old and apparently worn DV camcorder was used to record a school play, but the heads have a problem causing 1-2 frames to get corrupted every 1 second or so. Manifested as the odd frame with missed/garbled pixels and hard-silence (i.e. samples = 0) on the audio track because, I’m guessing, of corrupt bitstream rather than actually sampling 0s. The glitchy video was distracting but not intolerable whereas the skipped audio was making it unintelligible at points.

Eventual solution:
Since the skipped audio as a stream of hard-0s, the ffmpeg detect-silence filter was very effective at finding the start and end points by looking for -80dB for > 39ms (1 frame = 40ms). I then turned these start and end points into a list of cuts to select the good(ish) video between each clip, extracted these clips and then concatenated them back together which results in the original video getting the silenced frames cut out.

How I did it:
This is undoubtedly NOT the best way, and the result is far from perfect (silences drifted throughout video w.r.t. frame boundaries, not all video frames had silence, skipped silences still lead to some “popping” etc.) but it was good enough for a one-off solution and produced tolerable output. The BASH script I eventually came up with is below (don’t judge me - it’s very Heath-Robinson/Rube-Goldberg) for anyone who’s interested but I can’t attach any example of the corrupt files as they’re from a school play and it wouldn’t be appropriate.


for f in ${files[*]} ; do

  b=`basename $f`
  # Recoding just to clear up stream errors that crept in somewhere (save as .dv instead of .avi)
  echo "Working on $f..."
  if [[ ! -r $r ]] ; then
    echo "  Recoding $f to $r (clean up stream errors)..."
    rm -f $r
    ffmpeg -i $f -s pal -r pal -aspect 16:9 -ar 48000 -ac 2 $r 2>$r.recode.log
  # Find silences in terms of start and end times, convert these to whole frames and then output
  # an ffmpeg extract commnad from after previous silence to start of current one.
  # NOTE: Silences initially aligned on frame boundaries but seem to drift. I opted to cut the last
  #       frame with any silence in it and the number of frames containing silence which sometimes left
  #       a bit of silence at end of last uncut frame - trade off between audio and video skips.
  # NOTE: Needed one ffmpeg command per cut as large number of cuts resulted in "too-many open files"
  #       error when done in one go. Very slow at end of long files - could be optimised.
  echo "  Finding silence..."
  rm $r.split.ffs
  ffmpeg -i $r -af silencedetect=noise=-80dB:d=0.035 -f null - 2>&1 | \
    tr '\r' '\n' | \
    awk 'BEGIN {s=0}; /silence_end/ {d=int(($8+0.039)/0.04)*0.04; e=int($5/0.04)*0.04; printf "%8.2f %8.2f\n", s, e-d+0.04; s=e+0.04}' |
    awk 'BEGIN {n=1}; {printf "printf .; ffmpeg -i <fname> -c:v copy -c:a copy -ss %s -to %s clips/%06d.dv 2>/dev/null\n",$1,$2,n++}' |
    sed "s/<fname>/$r/" >> $r.split.ffs
  echo 'printf "\n"' >> $r.split.ffs
  # Run the ffmpeg commands created above to generate sequentially-named files
  rm -rf clips; mkdir -p clips
  . $r.split.ffs
  # Prepare list of files to be concatenated and do it.
  rm -f $r.concat-files.txt
  for clip in `ls -1 clips/*.dv` ; do
    echo "file '$clip'" >> $r.concat-files.txt
  rm -f fixed.$r
  ffmpeg -f concat -i $r.concat-files.txt -c copy fixed.$r
  # Extract audio for audio-cleanup in audacity.
  ffmpeg -i fixed.$r -c:a pcm_s16le -vn fixed.$r.wav
  # TODO: Once complete, clean up audio in audacity (clip-fix, click-removal, low-pass filter) and write out to fixed.$r.clean.wav

  # Recombine video and audio into DV encoded AVI
  ffmpeg -i fixed.$r -i fixed.$r.clean.wav -map 0:0 -map 1:0 -c:v copy -c:a pcm_s16le fixed.$

  echo "Finished $r."

Interesting solution Flyball1788. Thanks for posting the details.