209 lines
11 KiB
HTML
209 lines
11 KiB
HTML
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
|
<html xmlns="http://www.w3.org/1999/xhtml"><head>
|
|
<meta http-equiv="Content-type" content="text/html;charset=UTF-8" />
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
<link rel="stylesheet" href="main.css" type="text/css" />
|
|
<link rel="stylesheet" href="blog.css" type="text/css" />
|
|
<link rel="alternate" type="application/rss+xml" title="Subscribe to this page..." href="feed.rss" />
|
|
<title>Video editing from the command line</title>
|
|
</head><body>
|
|
<div id="divbodyholder">
|
|
<div class="headerholder"><div class="header">
|
|
<div id="title">
|
|
<h1 class="nomargin"><a class="ablack" href="http://zigford.org/index.html">zigford.org</a></h1>
|
|
<div id="description"><a href="about.html">About</a><a href="links.html"> | Links</a><a href="scripts.html"> | Scripts</a><br>Sharing linux/windows scripts and tips</br></div>
|
|
</div></div></div>
|
|
<div id="divbody"><div class="content">
|
|
<!-- entry begin -->
|
|
<h3><a class="ablack" href="video-editing-from-the-command-line.html">
|
|
Video editing from the command line
|
|
</a></h3>
|
|
<!-- bashblog_timestamp: #201910051203.16# -->
|
|
<div class="subtitle">October 05, 2019 —
|
|
Jesse Harris
|
|
</div>
|
|
<!-- text begin -->
|
|
<p>I previously posted about <a href="converting-vhs-and-dv-to-modern-formats---part-1.html">capturing video in
|
|
Linux</a>. While this isn't
|
|
exactly part 2 of that post there is enough crossover to warrant a link. This
|
|
post is about how I have strangely found video editing to be much easier from
|
|
the command line than a gui app.</p>
|
|
<hr />
|
|
<p>Firstly, there isn't a shortage of gui apps for editing video on Linux for very
|
|
basic editing. My requirements are very simple. I need to be able to cut and
|
|
join various video files, and product 2 output files. A file for hosting on the
|
|
web and a file for archival purposes.</p>
|
|
<h2 id="problem-1-audio-sync">Problem 1 - Audio Sync</h2>
|
|
<p>I started out myself with <a href="http://www.pitivi.org/">pitivi</a> and tried a few others.
|
|
My trouble with each editor was speed and accuracy (also my brain). On some
|
|
videos I had to adjust the audio sync and was driven mad by moving audio tracks
|
|
around. I had alot of trouble knowing if the audio was starting too early or too
|
|
late and which way to move the audio to correct it.</p>
|
|
<p>With a gui editor, this involves click and dragging an audio track one way or
|
|
another (for accuracy, you have to zoom in very far). Once zoomed in and you
|
|
move the track, you then need to zoom out, place the play head, click play and
|
|
then make a judgement if you made it better or worse.</p>
|
|
<p>For some reason, my brain could never tell exactly which way to move the audio
|
|
until it was way too far in the wrong direction. This was a source of
|
|
frustration that I initially solved using ffmpeg on the command line.</p>
|
|
<p>Using ffmpeg I wrote a very simple sh script to output a small time slice of
|
|
the video many times with different audio offsets. Then it was simply a matter
|
|
of watching each video and picking the right one. The resulting audio offset
|
|
that was used for that video can then be used on the entire clip.</p>
|
|
<p>Firstly, start by creating a small snippet of video where you will clearly be
|
|
able to see if the video is synced up.</p>
|
|
<pre><code> ffmpeg -i bigclip.mp4 -c copy -ss 00:01:55 -to 00:02:00 Small.mp4
|
|
</code></pre>
|
|
<p>Now we will use the following script</p>
|
|
<pre><code> #!/bin/sh
|
|
|
|
# fixaudiodelay.sh
|
|
|
|
Offset="${2}"
|
|
inputFile="${1}"
|
|
outputFile="${inputFile%%.*}-delayed${Offset}.mp4"
|
|
out="${3:-$outputFile}"
|
|
ffmpeg -i "${inputFile}" -itsoffset "${Offset}" \
|
|
-i "${inputFile}" -map 0:v -map 1:a -c copy "${out}"
|
|
</code></pre>
|
|
<p>The script will take parameter 1 is the clip, and parameter 2 is the audio
|
|
offset. We can use <code>seq</code> to generate a list of offsets to try and <code>xargs</code> to run
|
|
them.</p>
|
|
<pre><code> seq -2.0 .1 2.0 | xargs -n1 ./fixaudiodelay.sh Small.mp4
|
|
</code></pre>
|
|
<p>The result is 41 clips with .1 second offset difference. Play them all and find
|
|
the best offset. Then when you could use <code>./fixaudiodelay.sh BigClip.mp4 -1.7</code>
|
|
(or whatever was the best offset) to correct the entire file.</p>
|
|
<h2 id="problem-2-perfect-splitting">Problem 2 - Perfect splitting</h2>
|
|
<p>The second problem I had with gui editors, is that many home videos are
|
|
completely different scenes butted up against each other on the tape. When
|
|
transferring these to a new modern format, we want our audience to be able to
|
|
skip the scenes.</p>
|
|
<p>With the gui editors, again we have the problem of accuracy along with the
|
|
inability to at once, save all the clips from the single file. Say you have 4
|
|
distinct clips in a file. How can you mark each clip as a separate file then
|
|
press encode and walk away? You can't you would have to create 3 separate
|
|
projects and encode each one separately.</p>
|
|
<p>Solving it with ffmpeg is easier than you might think. When I showed cutting a
|
|
clip earlier, you can use the <code>-ss</code> parameter to declare where to start from in
|
|
the clip, and the <code>-to</code> parameter to declare the end. How I've solved it is like
|
|
this.</p>
|
|
<ol>
|
|
<li><p>Create a track file which lists all the clips start, end and titles. I chose
|
|
to separate each clip by a line, and each parameter by a <code>-</code> dash. Eg</p>
|
|
<pre><code> 00:00:00-00:10:05-SnowTrip
|
|
00:10:05.6-00:13:02-PlayingAtThePark
|
|
</code></pre>
|
|
</li>
|
|
<li><p>I came up with the start and end positions, simply by marking down the times
|
|
while watching the clip using mpv or vlc. However, the proof is in the
|
|
pudding, so I use ffmpeg with some parameters to produce test video files.
|
|
These won't be good enough quality to upload to the web, but are sufficient
|
|
for verifying the time stamps and are very quick to encode and see the
|
|
results.</p>
|
|
<p><strong>Note</strong> <em>You can't use the <code>copy</code> codec when cutting clips. As the keyframes
|
|
are not recreated</em></p>
|
|
<pre><code> while IFS=- read START END TITLE
|
|
do
|
|
OUTFILE="${TITLE}-Test.mp4"
|
|
ffmpeg -nostdin -i BigClip.mp4 -c:v libx264 \
|
|
-c:a aac -ss $START -to $END -preset ultrafast \
|
|
$OUTFILE
|
|
done < tracks.txt
|
|
</code></pre>
|
|
</li>
|
|
<li><p>After viewing the resulting files, I can make minute adjustments to the track
|
|
file so the clips are perfect. Then I adjust for a better file for the web by
|
|
changing the preset to <code>veryslow</code> and adding the following parameters</p>
|
|
<pre><code> -crf 23 -movflags faststart
|
|
</code></pre>
|
|
</li>
|
|
<li><p>Additionally in some cases, I add some video filters. I've found the
|
|
following work well:</p>
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<td>Desired affect</td>
|
|
<td>Parameter</td>
|
|
<tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr>
|
|
<td>Increase volume</td>
|
|
<td>-filter:a "volume=2.0"</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Deinterlace</td>
|
|
<td>-vf "yadif"</td>
|
|
</tr>
|
|
<tr>
|
|
<td>Archival Quality</td>
|
|
<td>Replace libx264 with libx265</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</li>
|
|
</ol>
|
|
<h2 id="problem-3-joining-videos">Problem 3 - Joining videos</h2>
|
|
<p>The last problem I will discuss may only be a problem on Linux. While many of my
|
|
video files are converted from VHS using a composite to usb converter, some are
|
|
coming from DV tape. DV Tape stores it's data digitally in Mpeg-2 on a magnetic
|
|
tape. On Linux, I've been able to transfer these files using FireWire and a
|
|
command line tool <code>dvgrab</code>. The resultant content is many 1gb .dv files.</p>
|
|
<p>While these files can individually be processed by ffmpeg, it is more ideal to
|
|
join them into a single clip for processing so that you can grab clips that span
|
|
multiple files and use a single set of timestamps for processing. To adjust
|
|
ffmpeg for this use, we use another file which tells ffmpeg all the files to
|
|
concatenate.</p>
|
|
<p>Use the following sh to generate a file that ffmpeg can understand:</p>
|
|
<pre><code> for i in *.dv; do echo "file '$i'" >> files.txt; done
|
|
</code></pre>
|
|
<p><strong>TIP</strong> <em>Did you know bash has a shorthand non-posix for loop?</em></p>
|
|
<pre><code> for i in *.dv; { echo "file '$i' >> files.txt; }
|
|
</code></pre>
|
|
<p><strong>NOTE</strong> <em>The space after the <code>{</code> is needed</em></p>
|
|
<p>Now use the resultant files.txt to create a single test clip of the whole lot:</p>
|
|
<pre><code> ffmpeg -f concat -safe 0 -i files.txt \
|
|
-c:v libx264 -c:a aac -crf 35 \
|
|
-preset ultrafast testconcat.mp4
|
|
</code></pre>
|
|
<p>You can now use this file to create timestamps relative to the whole
|
|
concatenated lot.</p>
|
|
<h2 id="problem-4-lp-vs-sp">Problem 4 - LP vs SP</h2>
|
|
<p>DV Camera's in the early 2000s started featuring a LP (Long Play) feature which
|
|
allowed them to drop the sample rate on the audio (and maybe video?) significantly
|
|
in order to fit more on the tape. This introduces a problem for ffmpeg.</p>
|
|
<p>If multiple clips on a single tape switch between LP and SP (Short Play or
|
|
standard play) then ffmpeg would only <strong>see</strong> what it encountered when it
|
|
first probed the file. The result could be video/audio playing either very fast
|
|
(chipmunk voice) or very slow. The trick here is to slice the .dv file far
|
|
enough in that ffmpeg's probe matches the clip of audio you begin to transcode.
|
|
It is not enough to simple specify the start position using <code>-ss</code>.
|
|
As ffmpeg will probe the start of the file, not the start specified by
|
|
<code>-ss</code>.</p>
|
|
<p>So we come now to the next tool in the unix tool belt <code>dd</code>. We can use <code>dd</code> to copy
|
|
a file but tell it to <strong>skip</strong> x bytes. Eg</p>
|
|
<pre><code> dd if=dvgrab-001.dv of=testdv.dv bs=1M skip=100
|
|
</code></pre>
|
|
<p>Using <code>bs=1M</code> and <code>skip=100</code> will create a new file that will skip roughly 100
|
|
megabytes of video at the start. Depending on where you clip begins/ends, you
|
|
will have to fiddle with the <code>skip</code> option and if you need more granular
|
|
control, you could reduce the <code>bs</code> (byte size) down into the kilobyte range.</p>
|
|
<p>If the resulting output file starts at the clip you are wanting to produce, you
|
|
could now replace the original dvgrab file from your tracks list file with the
|
|
newly generated file.</p>
|
|
<p>Tags: <a href='tag_ffmpeg.html'>ffmpeg</a>, <a href='tag_dd.html'>dd</a>, <a href='tag_cli.html'>cli</a></p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<!-- text end -->
|
|
<!-- entry end -->
|
|
</div>
|
|
<div id="footer">© <a href="http://twitter.com/zigford_org">Jesse Harris</a> — <a href="mailto:jesse@zigford.org">jesse@zigford.org</a><br/>
|
|
Generated with <a href="https://github.com/cfenollosa/bashblog">bashblog</a>, a single bash script to easily create blogs like this one</div>
|
|
</div></div>
|
|
</body></html>
|