Skip to content

Instantly share code, notes, and snippets.

@hfossli
Last active December 27, 2020 21:47
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save hfossli/5996260 to your computer and use it in GitHub Desktop.
Save hfossli/5996260 to your computer and use it in GitHub Desktop.
A bash / shell script for splitting videos / movies into several / multiple files using ffmpeg
#!/bin/bash
# Created by Håvard Fossli <hfossli@gmail.com> in 2013
# Derived from Alexis Bezverkhyy <alexis@grapsus.net> in 2011
# This is free and unencumbered software released into the public domain.
# For more information, please refer to <http://unlicense.org/>
#
# Description
# A bash script for splitting videos into several files using ffmpeg.
#
# Keywords
# Terminal, bash, unix, mac, shell, script, video, movie, split, time interval, multiple files, ffmpeg.
#
# Usefull links when working with ffmpeg
# - https://sites.google.com/site/linuxencoding/ffmpeg-tips
# - http://pvdm.xs4all.nl/wiki/index.php/Convert_an_AVCHD_/_MTS_file_to_MP4_using_ffmpeg
# - http://rodrigopolo.com/ffmpeg/cheats.php
# - http://www.catswhocode.com/blog/19-ffmpeg-commands-for-all-needs
#
# arg1: name of program
function assert_program_exists() {
command -v $1 >/dev/null 2>&1 || {
echo >&2 "You must have $1 installed in order to run this script. Try:";
echo "\$ brew install $1"
exit 1;
}
}
# arg1: duration in format 01:23:45.67
# arg2: offset for group 1 gives hours, 2 gives minutes, 3 gives seconds, 4 gives milliseconds
function parse_duration_info() {
if [[ $1 ]] && [[ $2 -gt 0 ]] && [[ $2 -lt 5 ]] ; then
__OFFSET=$2
__DURATION_PATTERN='\([0-9][0-9]\):\([0-9][0-9]\):\([0-9][0-9]\)\.\([0-9][0-9]\)'
echo "$1" | sed "s/$__DURATION_PATTERN/\\$__OFFSET/"
else
echo "Bad input to parse_duration_info()"
echo "Givven duration $1"
echo "Givven offset $2"
echo "Exiting..."
exit 1
fi
}
# arg1: file
function get_video_duration_in_seconds_from_file() {
if [[ -z "$1" ]] ; then
echo "Bad input to get_video_duration_in_seconds_from_file()"
echo "Givven argument: '$1'"
exit 1
else
__FILE=$1
__DURATION_HMS=$(ffmpeg -i "$__FILE" 2>&1 | grep Duration | grep '\d\d:\d\d:\d\d.\d\d' -o)
__DURATION_H=$(parse_duration_info "$__DURATION_HMS" 1)
__DURATION_M=$(parse_duration_info "$__DURATION_HMS" 2)
__DURATION_S=$(parse_duration_info "$__DURATION_HMS" 3)
let "__SECONDS = ( __DURATION_H * 60 + __DURATION_M ) * 60 + __DURATION_S"
echo $__SECONDS
fi
}
# arg1: path to input file / source
function fallback_out_file_format() {
if [[ -z "$1" ]] ; then
echo "Bad input to get_video_duration_in_seconds_from_ffmpeg_info()"
echo "Input givven $1"
exit 1
else
__FILE_NAME=`rev <<< "$1" | cut -d"." -f2- | rev`
__FILE_EXT=`rev <<< "$1" | cut -d"." -f1 | rev`
__OUT_FILE_FORMAT="${__FILE_NAME}-part-%03d.${__FILE_EXT}"
echo $__OUT_FILE_FORMAT
exit 1
fi
}
function echo_variables {
echo
echo "### Variables ###"
echo "- SOURCE $SOURCE"
echo "- OFFSET $OFFSET"
echo "- N_FILES $N_FILES"
echo "- DURATION $DURATION"
echo "- CHUNK_LEN $CHUNK_LEN"
echo "- OUT_FILE_FORMAT $OUT_FILE_FORMAT"
echo
}
function usage {
echo "For help and detailed guide type:"
echo "\$ $0 -h"
}
function detailed_guide {
echo "
Example:
\$ $0 input.file chunk-duration output-filename-format
Info:
- input file may be any kind of file reconginzed by ffmpeg
- chunk duration must be in seconds
- output filename format must be printf-like, for example myvideo-part-%04d.avi
- if no output filename format is given, it will be computed
automatically from input filename
Flags:
-s Path to video source (string)
-o Path to output file with format (string)
Format must be printf-like, for example ./path/video-part-%03d.avi
-c Chunk length in seconds (integer)
-v Takes no arguments
Will be Verbose
-a Takes no arguments
Will remove audiostream
-p Takes no arguments
Will play sound on done
-i Offset for file format (integer)
-h Takes no arguments
Help - detailed guide
-d Takes no arguments
Dry-run (no actual writing)
"
}
# Allow to be terminated with ctrl + c
trap "exit" INT
# Assert ffmpeg is installed
assert_program_exists "ffmpeg"
# Set variables and default values
typeset -i CHUNK_LEN
CHUNK_LEN=60
SOURCE=''
OUT_FILE_FORMAT=''
REMOVE_AUDIO_STREAM=false
VERBOSE=false
PLAY_SOUND=false
DRY_RUN=false
FILE_OFFSET='1'
OFFSET='0'
HELP=false
# Grab input arguments
while getopts “s:o:c:vapi:hd” OPTION
do
case $OPTION in
s) SOURCE=$(echo "$OPTARG" | sed 's/ /\\ /g' ) ;;
o) OUT_FILE_FORMAT=$(echo "$OPTARG" | sed 's/ /\\ /g' ) ;;
c) CHUNK_LEN="$OPTARG" ;;
v) VERBOSE=true ;;
a) REMOVE_AUDIO_STREAM=true ;;
p) PLAY_SOUND=true ;;
i) FILE_OFFSET=$OPTARG ;;
h) HELP=true ;;
d) DRY_RUN=true ;;
?) usage
exit 1
;;
esac
done
if $HELP ; then
detailed_guide
exit 1
fi
if [[ -z $SOURCE ]] ; then
echo "Invalid source"
usage
exit 1
fi
if [ -z "$OUT_FILE_FORMAT" ] ; then
OUT_FILE_FORMAT=$(fallback_out_file_format $SOURCE)
fi
FFMPEG_INFO=$(ffmpeg -i "$__FILE" 2>&1)
DURATION=$(get_video_duration_in_seconds_from_file $SOURCE)
let 'N_FILES = (DURATION - 1) / CHUNK_LEN + 1'
if $VERBOSE ; then
echo_variables
fi
if [ "$DURATION" = '0' ] ; then
echo "Invalid input video"
usage
exit 1
fi
if [ "$CHUNK_LEN" = "0" ] ; then
usage
exit 2
fi
while [[ $OFFSET -lt $DURATION ]] ; do
OUT_FILE=$(printf "$OUT_FILE_FORMAT" "$FILE_OFFSET")
FFMPEG_PARAMETERS="-i $SOURCE -ss $OFFSET -t $CHUNK_LEN"
if $REMOVE_AUDIO_STREAM ; then
FFMPEG_PARAMETERS+=" -vcodec copy -an $OUT_FILE"
else
FFMPEG_PARAMETERS+=" -vcodec copy -acodec copy $OUT_FILE"
fi
echo "### Writing file $FILE_OFFSET of $N_FILES to $OUT_FILE ###"
if $DRY_RUN ; then
echo "Would call ffmpeg $FFMPEG_PARAMETERS"
else
ffmpeg $FFMPEG_PARAMETERS
fi
let "FILE_OFFSET = FILE_OFFSET + 1"
let "OFFSET = OFFSET + CHUNK_LEN"
echo
done
if $PLAY_SOUND ; then
# & means async
say "Done! ." &
fi
@jhbowling
Copy link

You need to change the pattern in get_video_duration_in_seconds_from_file to:

__DURATION_HMS=$(ffmpeg -i "${FILE}" 2>&1 | grep Duration | grep -e '[0-9]\{2\}:[0-9]\{2\}:[0-9]\{2\}' -o)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment