Shell script to trim a video with mencoder or ffmpeg

I'd previously posted a script to calculate clip duration from start/stop times for trimming with mencoder or ffmpeg, but in an effort to improve customer satisfaction (e.g. recently had to trim a bunch of Olivia's videos and couldn't be bothered to run the calc-duration script then copy/paste and edit the mencoder command), wrote a quick script that both calculates and trims.

UPDATE 2015-02-28: there are newer versions of this script:

For example, to trim a video with the start time of 45 seconds (e.g. drop the first 45 sec) and the end time of 3 min 15 sec (e.g. drop anything past that point), do:

$ trim.vid example.avi 00:00:45 00:03:15
checking input..
all good

calculating duration from start/stop times..
clip duration is 00:02:30

constructing file name for output..
trimmed video will be saved as example.trim.000045-000315.avi

trimming video..
==================================================
MEncoder 1.0rc4-4.4.5 (C) 2000-2010 MPlayer Team
success: format: 0  data: 0x0 - 0x38de94a
AVI file format detected.
[aviheader] Video stream found, -vid 0
[aviheader] Audio stream found, -aid 1
VIDEO:  [XVID]  640x480  12bpp  29.970 fps  2054.6 kbps (250.8 kbyte/s)
[V] filefmt:3  fourcc:0x44495658  size:640x480  fps:29.970  ftime:=0.0334
==========================================================================
Opening audio decoder: [mp3lib] MPEG layer-2, layer-3
AUDIO: 32000 Hz, 2 ch, s16le, 192.0 kbit/18.75% (ratio: 24000->128000)
Selected audio codec: [mp3] afm: mp3lib (mp3lib MPEG layer-2, layer-3)
==========================================================================
videocodec: framecopy (640x480 12bpp fourcc=44495658)
audiocodec: framecopy (format=55 chans=2 rate=32000 bits=0 B/s=22671 sample-0)
Writing header...
ODML: Aspect information not (yet?) available or unspecified, not writing vprp header.
Setting audio delay to 0.036s.
Writing header...
ODML: Aspect information not (yet?) available or unspecified, not writing vprp header.
Setting audio delay to 0.036s.
Pos:   0.7s     22f (23%)  0.00fps Trem:   0min   0mb  A-V:0.070 [0:183]
Skipping frame!
Pos:   1.0s     32f (23%)  0.00fps Trem:   0min   1mb  A-V:0.067 [2011:183]
Skipping frame!
Writing index...98f (93%)  0.00fps Trem:   0min  42mb  A-V:0.051 [2035:181]
Writing header...
ODML: Aspect information not (yet?) available or unspecified, not writing vprp header.
Setting audio delay to 0.036s.

Video stream: 2035.115 kbit/s  (254389 B/s)  size: 38162649 bytes  150.017 secs  4498 frames

Audio stream:  181.003 kbit/s  (22625 B/s)  size: 3394080 bytes  150.012 secs
==================================================
all done, have a fantastic day!

The script:

#!/bin/bash
# Usage: trim.vid MyVid.avi 00:30:00 00:40:00

if [ "$#" -ne "3" ]; then
  echo "Error: need some args like which video file to trim and new start/stop times. Usage example:"
  echo "$(basename $0) video.avi 00:30:00 00:40:00"
  exit 1
 else
  InFile=$1
  StartTime=$2
  StopTime=$3
fi

CheckInput()
{
 if ! [ -r $InFile ]; then
   echo "Error: input file \"$InFile\" not readable"
   exit 1
 fi

 for time in StartTime StopTime; do
   if [ "$(echo $(eval echo \$$time) | grep -o ":" | wc -l)" -ne "2" ]; then
     echo "Error: check $time time format, expecting two colons colons, e.g.:"
     echo "00:20:00"
     exit 1
   fi
   if [ "$(echo $(eval echo \$$time) | sed 's/://g' | wc -c)" -ne "7" ]; then
     echo "Error: check $time time format, expecting six digits, e.g.:"
     echo "00:20:00"
     exit 1
   fi  
 done

 if [ "$StartTime" == "$StopTime" ]; then
   echo "Error: same start and stop times don't make sense. I quit."
   exit 1
 fi
}

CalcDuration()
{
 # calculates duration between start time and stop time, e.g. 00:02:00 00:10:10
 # Fix:
 # 1. Currently expects start hour to be less than stop hour,
 # so won't work for a clip that's longer than 24 hrs, not an
 # issue for me as I don't have any of those... 😉
 #---------------------------------------#
 starthh=$(echo $StartTime | cut -d':' -f1)
 startmm=$(echo $StartTime | cut -d':' -f2)
 startss=$(echo $StartTime | cut -d':' -f3)
 #---------------------------------------#
 stophh=$(echo  $StopTime  | cut -d':' -f1)
 stopmm=$(echo  $StopTime  | cut -d':' -f2)
 stopss=$(echo  $StopTime  | cut -d':' -f3)
 #---------------------------------------#
 ### DEBUG
 #echo " Start:" $starthh hrs $startmm min $startss sec
 #echo " Stop: " $stophh hrs $stopmm min $stopss sec
 #---------------------------------------#
 if [[ $((10#$startmm)) -gt $((10#$stopmm)) && $((10#$startss)) -gt $((10#$stopss)) ]]; then
         # start mm is greater than stop mm, so we add 60 to stop mm and
         # substract 1 from stop hh
         newstopmm1=$((10#$stopmm+10#60))
         newstophh=$((10#$stophh-10#1))
         # start ss is greater than stop ss, so we add 60 to stop ss and
         # substract 1 from stop mm
         newstopss=$((10#$stopss+10#60))
         newstopmm=$((10#$newstopmm1-10#1))
         # calculate differences
         diffhh=$((10#$newstophh-10#$starthh))
         diffmm=$((10#$newstopmm-10#$startmm))
         diffss=$((10#$newstopss-10#$startss))
 elif [[ $((10#$startmm)) -gt $((10#$stopmm)) ]]; then
         # start mm is greater than stop mm, so we add 60 to stop mm and
         # substract 1 from stop hh
         newstopmm=$((10#$stopmm+10#60))
         newstophh=$((10#$stophh-10#1))
         # calculate differences
         diffhh=$((10#$newstophh-10#$starthh))
         diffmm=$((10#$newstopmm-10#$startmm))
         diffss=$((10#$stopss-10#$startss))
 elif [[ $((10#$startss)) -gt $((10#$stopss)) ]]; then
         # start ss is greater than stop ss, so we add 60 to stop ss and
         # substract 1 from stop mm
         newstopss=$((10#$stopss+10#60))
         newstopmm=$((10#$stopmm-10#1))
         # calculate differences
         diffhh=$((10#$stophh-10#$starthh))
         diffmm=$((10#$newstopmm-10#$startmm))
         diffss=$((10#$newstopss-10#$startss))
 else
         # all stop values are greater than start values
         # calculate differences
         diffhh=$((10#$stophh-10#$starthh))
         diffmm=$((10#$stopmm-10#$startmm))
         diffss=$((10#$stopss-10#$startss))
 fi
 #---------------------------------------#
 # pad single digit results with a zero (if + signs contain a single digit, add a zero, then remove + signs)
 pdiffhh=$(echo +$diffhh+ | sed 's/+\([0-9]\{1\}\)+/+0\1+/g;s/+//g')
 pdiffmm=$(echo +$diffmm+ | sed 's/+\([0-9]\{1\}\)+/+0\1+/g;s/+//g')
 pdiffss=$(echo +$diffss+ | sed 's/+\([0-9]\{1\}\)+/+0\1+/g;s/+//g')
 #=======================================#
 Duration=$pdiffhh:$pdiffmm:$pdiffss
 #=======================================#
}

ConstructFileName()
{
 ext=$(echo "$InFile" | egrep -io "\.[a-z0-9]{2,4}$")
 main=$(echo "$InFile" | sed "s/$ext$//")
 start=$(echo $StartTime | sed 's/://g')
 stop=$(echo $StopTime | sed 's/://g')
 OutFile=$main.trim.$start-$stop$ext
}

TrimVid()
{
 mencoder=$(which mencoder)
 if [ -x $mencoder ]; then
   mencoder -ss $StartTime -endpos $Duration -oac copy -ovc copy $InFile -o $OutFile
   echo "=================================================="
   echo "all done, have a fantastic day!"
   exit 0
 fi
 ffmpeg=$(which ffmpeg)
 if [ -x $ffmpeg ]; then
   ffmpeg -ss $StartTime -t $Duration -vcodec copy -acodec copy -i $InFile $OutFile
   echo "=================================================="
   echo "all done, have a fantastic day!"
   exit 0
 fi

 echo "Error: need mencoder or ffmpeg, neither where found in your PATH.."
}


echo -e "\nchecking input.."
CheckInput
echo "all good"

echo -e "\ncalculating duration from start/stop times.."
CalcDuration
echo "clip duration is $Duration"

echo -e "\nconstructing file name for output.."
ConstructFileName
echo "trimmed video will be saved as $OutFile"

echo -e "\ntrimming video.."
echo "=================================================="
TrimVid

1 Comment

Leave a comment

NOTE: Enclose quotes in <blockquote></blockquote>. Enclose code in <pre lang="LANG"></pre> (where LANG is one of these).