Bash script I wrote to assist in clipping home movies. Still some more to do, but here's a summary.

Particularly useful was the code I used to find the difference under bash between start and end points using date...and keeping the result in a format ffmpeg could understand.

#!/bin/bash

#

# Title: HomeMoviesClipper.sh

# Description: Use ffmpeg to split raw home movie captures into clips

# Original Author: Joseph Gullo (surfrock66) (surfrock66@surfrock66.com)

#

# This script works with a very specific set of constraints and formats to

# take raw video captures and split them into clips of events. Each of

# these movies was captured, using a Dazzle USB video capture device,

# into h264 video and mp3 audio, using VLC. These original tapes were

# typically close to 2 hours long, however capture was often left to run,

# creating videos that were 8+ hours long. The capture device produced

# video that was 480x480 and audio that only contains 1 channel (recorded

# through stereo), and the output video will retain this. The desired

# format is high-bitrate h264 and aac audio, encapsulated in .mp4 files.

# This script also works within a predictable directory environment

# because of how specific the task is, and because I'm a lazy bastard

# that doesn't want to code bunch of directory logic. The script first

# queries the input file, then works on this file recursing the steps

# for each clip. Each clip requires an output name, a start timecode

# (formatted hh:mm:ss.nnn) and an ending timecode. This is part of

# some of the headache, as ffmpeg processes clips with a start time

# and a duration, meaning you have to do the difference math yourself.

# I take care of this in there. After producing a clip, the script

# will ask the user if they want to create another clip from the same

# source file, at which it recurses through a prompting loop. The

# script also can be run from the command line...however in CLI mode,

# the intent is to NOT allow the script to re-prompt for additional cips

# from the same source video.

#

# The script can optionally take 4 command line arguments:

# 1) The filename for the source video, without extension

# 2) The desired resulting clip name, without extension

# 3) The start timecode for the clip, format hh:mm:ss.nnn

# 4) The end timecode for the clip, format hh:mm:ss.nnn

#

# Variables:

# INPUTFILE - Input video filename, without extension

# OUTPUTFILE - Clip output filename, without extension

# STARTTIME - Clip starting timecode, format hh:mm:ss.nnn

# ENDTIME - Clip ending timecode, format hh:mm:ss.nnn

# NANODIFF - Difference between start and end timecode, in milliseconds

# NUMSECS - Timecoe difference in whole seconds

# NUMNANO - Timecode difference remainder in milliseconds

# REPLY - Flag for checking if the script should create another clip

# CLIFLAG - Flag set if command line mode is detected, to prevent prompting

#



# Prompt the user for all parameters, by asking for the source clip name, then calling

# the function which prompts for the clip info. This can safely call the clip-info

# prompting function, as this should only be used once per execution, if at all.

PROMPTFULL() {

echo "---What is the source video name? (No Extension) (Inside /home/surfrock66/Videos/HomeMoviesRaw/):"

# First point of possible declaration for INPUTFILE

read INPUTFILE

# Execute the PROMPCLIP function

PROMPTCLIP

}



# Prompt the user for the clip name, start time, and end time.

PROMPTCLIP() {

echo "---What is the output filename for this clip? (No Extension):"

# First point of possible declaration for OUTPUTFILE

read OUTPUTFILE

echo "---What is the timecode for the start of this clip? (##:##:##.###):"

# First point of possible declaration for STARTTIME

read STARTTIME

echo "---What is the timecode for the end of this clip? (##:##:##.###):"

# First point of possible declaration for ENDTIME

read ENDTIME

}



# This function handles the timecode calculations to find the clip duration,

# then executes the actial ffmpeg command.

PROCESSVID() {

# Calculate the actuall difference, in milliseconds (but we'll call them

# nanoseconds, because that's how the date command works) between the

# start time and end time. This relies heavily on the "date" command.

# It can convert formatted timecodes between different format

# conventions, but to go from something resembling a standard video

# editing timecode to a raw nanosecond count is a PAIN IN THE ASS.

# This strips out the amount of hours in milliseconds, then the number

# of minutes in milliseconds, then the number of seconds in milliseconds,

# then the number of nanoseconds in milliseconds. It then adds them

# together, creates a full millisecond count for the start an end time,

# and then finds the difference, in milliseconds.

NANODIFF=$(($(($(($(date -d $ENDTIME +%-H) * 3600000)) + $(($(date -d $ENDTIME +%-M) * 60000)) + $(($(date -d $ENDTIME +%-S) * 1000)) + $(($(date -d $ENDTIME +%-N) / 1000000)))) - $(($(($(date -d $STARTTIME +%-H) * 3600000)) + $(($(date -d $STARTTIME +%-M) * 60000)) + $(($(date -d $STARTTIME +%-S) * 1000)) + $(($(date -d $STARTTIME +%-N) / 1000000))))))

# Convert the resulting difference, in millisecodns, into seconds

NUMSECS=$(($NANODIFF / 1000))

# Convert the reaminder of the resulting difference, in milliseconds,

# to milliseconds

NUMNANO=$(($NANODIFF % 1000))

# Print out the ffmpeg command that is generated, mostly for debugging

echo "

Command: ffmpeg -i \"/home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE.mp4\" -ss $STARTTIME -t $NUMSECS.$NUMNANO -vcodec libx264 -b 1500k -s 480x480 -acodec libfaac -ab 192k -ac 1 -threads 8 \"/home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE/$OUTPUTFILE.mp4\"

"

# Execute the ffmpeg command, this is the big kahuna.

ffmpeg -i "/home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE.mp4" -ss $STARTTIME -t $NUMSECS.$NUMNANO -vcodec libx264 -b 1500k -s 480x480 -acodec libfaac -ab 192k -ac 1 -threads 8 "/home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE/$OUTPUTFILE.mp4"

}



# Start of the main script. Section 1: Input validation

# Check that there is a first parameter. If not, Prompt the user for the

# parameters. If there is one, validate the rest of the params, then

# either prompt the user, or use the passed params.

if [ -z "$1" ]

then

# Call the prompt function, including asking for the source video

PROMPTFULL

else

# Assuming the first parameter exists, check for parameters 2,3, and

# 4. I'm not validating these strings, mostly because ffmpeg will

# flip out if there's something wrong with them...as you know, it's

# quite verbose.

if [ -z "$2" -o -z "$3" -o -z "$4" ]

then

# If any parameters are invalid, prompt for them all. Even if param 1

# is successfully detected, re-ask for it.

echo "--Parameters are incomplete, switching to prompt mode."

PROMPTFULL

else

# Pass all the command line parameters to the variables

INPUTFILE="$1"

OUTPUTFILE="$2"

STARTTIME="$3"

ENDTIME="$4"

# Change the flag to indicate later that this initiated by CLI

CLIFLAG="yes"

fi

fi



# Create the default output directory, if it's not already created

if [ ! -d /home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE ]; then

mkdir /home/surfrock66/Videos/HomeMoviesRaw/$INPUTFILE

fi



# Now...initiate the final video processing

# Declare and initiate the REPLY flag...can be set to anything byt n/N

REPLY="y"

# As long as the REPLY flag isn't n or N, repeat the loop

while [ "$REPLY" != "n" ] && [ "$REPLY" != "N" ]

do

# Call the function PROCESSVID

PROCESSVID

# Check to see if the script was run with command line parameters,

# If not, issue a bunch of logic to see if we want to re-run the

# code for new clips

if [ -z "$CLIFLAG" ]

then

# Now that we know the script was run in prompt mode, ask the user

# if they want to make another clip, repeating this loop

echo "---Would you like to create another clip from this input video? {Y/n}"

read REPLY

if [ "$REPLY" != "n" ] && [ "$REPLY" != "N" ]

then

PROMPTCLIP

fi

# If it was run by command line, essentially abort the loop,

# and the script.

else

REPLY="n"

fi

done

