I have found what I consider more or less a solution to this problem. This past weekend
I recorded 4 rounds of golf off Golf Channel each day from Thursday through Sunday. The
first 3 rounds recorded perfectly beginning to end. The final round Sunday had a glitch.
This was a recording of a little over 4 hours that suddenly went bad around the 3:59
mark. The tournament was pretty much over but there were a few last putts & post-round
interviews left at the moment the problem happened. This meant that I could test out my
solution on conveniently small files.
At the fatal moment, the video sped up to approximately double speed while the audio
remained correct. So here's the roadmap of what I was going to try to do:
1. Split the original broadcast file into separate video-only & audio-only files.
2. Process the video-only file, which had too-fast motion, into a video-only file with natural motion.
3. Play the processed video-only file synchronously with the audio-only file in VLC.
4. As a proof of concept & to compare with step 3, aggregate the corrected video track with the audio track & play that in VLC.
I did all of this in a .Bat script so I will give you a line by line description of what
I did.
First I executed this command:
Call G:\ffmpeg\Setffmpeg
I have created a script named Setffmpeg.Bat in directory G:\ffmpeg. That directory on my
system contains the directory tree of the distribution of ffmpeg from
ffmpeg.org, as I
have discussed elsewhere in this forum. Setffmpeg.Bat consists of 1 line:
SET ffmpeg=G:\ffmpeg\ffmpeg-2021-07-11-git-79ebdbb9b9-full_build\bin
This creates an environment variable named ffmpeg that is accessible for the duration of
execution of my script. The variable disappears when the script ends. That's fine.
That's what I want. I could integrate this durably into my Windows environment but I
find this approach more flexible. The value I assign to the environment variable named
ffmpeg is the directory path to the location of ffmpeg & ffprobe (& ffplay, but that's
irrelevant here). All my scripts that invoke ffmpeg & ffprobe begin with this invocation
of Setffmpeg.Bat. You will see the primary usefulness of this as we go along here. The
secondary usefulness will come at some future date when I update ffmpeg to a new release.
I will do the installation of that new release into a sub-directory within G:\ffmpeg but
in a different sub-directory than my current ffmpeg. In order to automatically get all
my scripts to use the newer ffmpeg, all I will have to do is change the one line in
Setffmpeg.Bat. I'll use it a few times to verify that the new release works, then I will
delete the directory tree of the old release of ffmpeg. On the other hand, if I observe
problems with the new ffmpeg, reverting to the old release will be a simple matter of
putting Setffmpeg.Bat back to the way it had been.
With that housekeeping out of the way, here's the first step of my process. (Throughout
this discussion, Google line wraps some of my commands. Just remember that each
command is actually just a single line in my .Bat file.)
"%ffmpeg%\ffmpeg" -hwaccel auto -ss 3:59:40 -i "Q:\Golf\LPGA Round #4.mp4" -map 0:v -codec: copy -y "Q:\Golf\Video Only.mp4" 1>"Q:\Golf\SlowDownVideo.err" 2>"Q:\Golf\SlowDownVideo.log"
Here's the breakdown of this command, piece by piece.
The %ffmpeg% reference uses the environment variable named ffmpeg. It shortens what I
have to code for each invocation of ffmpeg & ffprobe. Without the environment variable,
I would have to code this minor monstrosity every time:
G:\ffmpeg\ffmpeg-2021-07-11-git-79ebdbb9b9-full_build\bin\ffmpeg
-hwaccel is supposed to take advantage of my GPU to speed up processing. I don't know if
it helps but it doesn't cause problems so I keep it.
-ss specifies the time index where the problem of too-fast video began.
-i specifies the input file, the file name of the original recording of the round of golf.
-map selects only the video track from the input file (0:v means from the first file,
files being numbered from 0, select the video track), ignoring whatever other tracks
might be there. In this case, there were only the video & audio tracks.
-codec: copy tells ffmpeg to simply copy the data bit for bit without reencoding it.
-y says replace the output file if it already exists, a convenience in this case, but
something you should give a few moments of careful thought to every time you code it.
The output file name is the next bit. It should be self-explanatory.
The 2 items with > at the end of the command capture the output of ffmpeg in files for
careful inspection after the fact. This is standard Windows redirection. If you are
unfamiliar with it, do a web search on the subject. Coding > causes the file to be
created if it does not already exist, or erases its current contents & starts adding new
content from the beginning if it does already exist. In subsequent commands, I coded >>
instead of > to tell Windows to append new output to the end of the existing files. In
practice, the 1> file is always empty, but I keep that specification for completeness,
just in case. 2> is where ffmpeg puts its log file. I won't be paying any further
attention to this. You'll see it in the rest of this post but I won't comment on it.
This command is intended to split the video track of the original broadcast into a
separate file.
It actually did not work. I added debugging output (parameter -loglevel trace) to try to
figure this out. I got a log file of about 1.8 million lines that I had to page through
because I didn't know what I was looking for. Painful & time consuming. I eventually
figured out that ffmpeg was dropping duplicated frames. As I've mentioned several times
before, these allegedly livestreams from NBC Sports actually seem to be coming from a
stored file on a server, not directly from a live TV feed. When I record a golf
broadcast, the beginning of the recording reverts back to some time before the broadcast
began. The recording starts with anything from a half hour to an hour & a half of a
screen that just says:
Coverage will begin shortly
In the case of this particular round of golf, they put something over 50 minutes of this
uselessness on the front of the livestream, which VDH dutifully recorded. I discovered
from the command that failed that ffmpeg sees this mass of duplicated frames & decides to
stop processing the input. The command I show above kept generating an empty output
file. The log file seemed to indicate that it was giving up at about 17 minutes into the
broadcast.
I searched the ffmpeg documentation for anything that might give me a clue & as usual,
found no help. No point in repeating how lousy I think their documentation is. Multiple
web searches looking for help turned up the opposite of what I wanted to do. Everybody
seems to be concerned with getting ffmpeg to remove duplicated video frames from files.
Nobody seems to want to keep them, which is what I needed to do. Nobody showed any
examples of how to stop ffmpeg from bothering to detect duplicate video frames. If
anybody reading this can tell me how to do that, I would welcome that with great
enthusiasm. What I did should have worked. In coming up with that command, I tried it
on a few other files I have on my system & it worked perfectly to strip the video track
out of the original file. So the fact that it failed on this one was a big surprise to
me. I am quite convinced that the command should have worked but does not because of the
idiosyncratic nature of the NBC Sports livestreams, specifically the initial long stretch
of unchanging silent video that is the hallmark of their content.
So I dropped back 10 & punted. I used the Convert function of VLC. I set the starting
point for conversion to 3:59:40.000. They allow you to specify thousandths of a second
in the time stamp. I left the ending point for conversion at 0, meaning process the file
from the starting point to the end of the file. In the subsequent conversion dialog (I
had to click the little wrench icon), I made sure that the Video Codec & Audio Codec tabs
of the dialog specified to keep the original track. I then selected a file named
Shortened.mp4 as my output. This did create a file of a few minutes duration containing
just the end part of the broadcast with the fast video & normal audio. VLC did the
conversion so fast I thought it had failed. I'm sure if I had been dealing with
something of a more substantial duration, it would have taken longer.
Then I executed this command:
"%ffmpeg%\ffmpeg" -hwaccel auto -i "Q:\Golf\Shortened.mp4" -map 0:v codec: copy -y "Q:\Golf\Video Only.mp4" 1>"Q:\Golf\SlowDownVideo.err" 2>"Q:\Golf\SlowDownVideo.log"
This also failed, but with a specific error message that told me there was some sort of
incompatibility between the input file & my requested output file. I don't know what it
was. The input file looked like a regular mp4 to ffprobe. It played like a regular mp4
in VLC. But something about it was upsetting ffmpeg. It appears that VLC creates a
strange type of mp4. At least, ffmpeg thinks it's strange. So I tried this:
"%ffmpeg%\ffmpeg" -hwaccel auto -i "Q:\Golf\Shortened.mp4" -map 0:v -y "Q:\Golf\Video Only.mp4" 1>"Q:\Golf\SlowDownVideo.err" 2>"Q:\Golf\SlowDownVideo.log"
The only difference here is that I left off the -codec: parameter. This got ffmpeg to
reencode the file into what it considers is a standard mp4. This finally gave me a short
file containing only the sped-up video track from the end of the original broadcast.
This command is quite CPU-intensive. My case fans were blowing up a hurricane while it
was executing. Fortunately, I was dealing with input of a short duration, barely 15
minutes, most of which was taken up by the usual "Coverage has concluded" tail that NBC
Sports likes to tack on the end of their livestreams. I'm sure if I had been dealing
with a couple of hours' worth of content, it would have taken a considerable time to
complete.
One rather annoying thing that happens with this is that ffmpeg severely degrades the
quality of the video. The original had bit rates in the 4,000kbps range while the output
was around 1,500kbps. This was quite visible in the playback. I guess ffmpeg just
figures out what it can get away with & does it. I suppose I could fiddle around with
certain ffmpeg parameters to get it to preserve the bit rates. That will be an exercise
for another day.
So once I finally had my video track, I did this to get the audio track:
"%ffmpeg%\ffmpeg" -hwaccel auto -ss 3:59:40 -i "Q:\Golf\LPGA Round #4.mp4" -map 0:a -codec: copy -y "Q:\Golf\Audio Only.mp4" 1>>"Q:\Golf\SlowDownVideo.err" 2>>"Q:\Golf\SlowDownVideo.log"
This worked perfectly on the first attempt. The only difference between this one & the
one above is the -map parameter value 0:a. Above, I selected the video track. Here, I'm
selecting the audio track.
At this point, I verified the properties of the 4 files I had: the original broadcast,
the tail end of the original broadcast as massaged by VLC, the video track of the end of
the original broadcast extracted from the VLC output file, the audio track extracted from
the end of the original broadcast:
"%ffmpeg%\ffprobe" "Q:\Golf\LPGA Round #4.mp4" 1>>"Q:\Golf\SlowDownVideo.err" 2>>"Q:\Golf\SlowDownVideo.log"
"%ffmpeg%\ffprobe" "Q:\Golf\Shortened.mp4" 1>>"Q:\Golf\SlowDownVideo.err" 2>>"Q:\Golf\SlowDownVideo.log"
"%ffmpeg%\ffprobe" "Q:\Golf\Video Only.mp4" 1>>"Q:\Golf\SlowDownVideo.err" 2>>"Q:\Golf\SlowDownVideo.log"
"%ffmpeg%\ffprobe" "Q:\Golf\Audio Only.mp4" 1>>"Q:\Golf\SlowDownVideo.err" 2>>"Q:\Golf\SlowDownVideo.log"
Finally, I could get to trying to correct the speed of the video track. When I played
Video Only.mp4, I tried using VLC to slow it down to 0.5 speed. To my eye, this still
looked a bit fast. There is a way to adjust the playback speed factor in the VLC GUI.
But it is in increments of 0.02. I decided after trying a few different playback speeds
that I needed a finer adjustment. So I looked at the program shortcut in my Start Menu
to discover where VLC resides on my system. Its location is this:
"C:\Program Files\VideoLAN\VLC\vlc.exe"
As far as I know, this is the default configuration, so you will find it in the same
place. But that's not a guarantee. I opened a command window & changed directories to
that location. Then I executed this command:
vlc --rate=0.455
This opened VLC with the speed factor set to the indicated value. Then I played
Video Only.mp4 in that window. It looked reasonably good.
Now that I had a speed factor for converting the video, I could tell ffmpeg to slow the
video down. I have done quite a bit of web searching to find this information. The page
where I found the advice I'm using here (sorry, I didn't make note of it) contained
suggestions for several different possible approaches. I tried them all & found only
this one worked. I have to believe that the guy who posted this advice had tried the
other things & they had worked for him. But it's possible I wasn't doing it right. It's
possible that his advice is applicable only in an environment similar to his, & mine is
not similar. It's possible that his advice is no longer valid due to changes in ffmpeg
since he posted. Whatever the reason, only this is what worked for me:
"%ffmpeg%\ffmpeg" -hwaccel auto -i "Q:\Golf\Video Only.mp4" -filter:v "setpts=2.1978*PTS" -y "Q:\Golf\Slow Video Only.mp4" 1>>"Q:\Golf\SlowDownVideo.err" 2>>"Q:\Golf\SlowDownVideo.log"
The interesting bit here is the -filter parameter on the output file. It defines a video
filter (the :v part) to apply to the output. The filter is setpts. PTS is an
abbreviation for Presentation Time Stamp. I only barely understand what I've done here
so don't press me on these things. The weird number 2.1978 is the reciprocal of 0.455.
1/0.455=2.1978. This is how the filter works. If you want to slow down the video to
half speed, you code setpts=2*PTS. If you want to speed it up to double speed, you code
setpts=0.5*PTS. That's just the way it works. So this command created an output file
named Slow Video Only.mp4. When played at normal speed, it looked like the original
broadcast slowed down by a factor of 0.455, which produced reasonably natural looking
motion.
At this point, I stopped & played 2 files synchronously in VLC: Slow Video Only.mp4,
Audio Only.mp4. The audio & the video were out of synch. I used the usual VLC controls
(the j & k keys) to advance & delay the audio track. I found that once I had gotten them
reasonably in synch at one point, they were out of synch again a few seconds later. I
believe this is because 0.455 was not the perfect speed for slowing down the video. I
could have experimented further with other speeds to try to find what would be better. I
should have observed how much the audio was out of synch & by how much it was drifting,
whether ahead or behind the video. But by this point, I had spent about 6 hours only to
try to watch about 5 minutes of what was left of the broadcast. I was out of energy.
I'll try that next time I encounter this problem. I am quite certain I will encounter it
again. It seems to be a frequent occurrence with these Golf Channel broadcasts.
Keeping the video & audio in separate files simplifies the trial & error process for
trying different speed factors for the video. But I also tried aggregating the 2 files
together:
"%ffmpeg%\ffmpeg" -hwaccel auto -i "Q:\Golf\Slow Video Only.mp4" -i "Q:\Golf\Audio Only.mp4" -codec: copy -y "Q:\Golf\Video with Audio.mp4" 1>>"Q:\Golf\SlowDownVideo.err" 2>>"Q:\Golf\SlowDownVideo.log"
"%ffmpeg%\ffprobe" "Q:\Golf\Video with Audio.mp4" 1>>"Q:\Golf\SlowDownVideo.err" 2>>"Q:\Golf\SlowDownVideo.log"
Since each of the 2 input files consists of only a single track, this command is this
simple. If the input files contained the usual video & audio tracks, I would have to add
-map parameters to do this.
In any case, this aggregated file was no different from what I got playing the 2 files
synchronously. I still had all the trouble synching the audio with the video. I also
had another problem. This was a problem both when I played the 2 files synchronously as
well as when I played the aggregated file. Skipping forward & backward in the file in
VLC was very sluggish. This was not something I observed with the original broadcast.
The sluggishness was the same no matter whether I had tried to synch the audio ahead or
behind the video by substantial amounts or I had left the audio offset from the video at
0, meaning with no attempt to synch. The file was of a very short duration. I have
noticed with longer files that sometimes, VLC gets progressively more sluggish the larger
the time index within the file that I'm playing it back. But this file was only about 15
minutes long, & the sluggishness was observable from the start. I don't know if I will
observe this in the future or if it was just something weird about this case. Experience
will tell.
So this is how I dealt with the issue without getting any fancy software, free or
expensive, to edit the file.