Skip to content
April 8, 2013 / jpeg729

Lightning fast video splitting script

I had a few TV recordings that I wanted to clean up. You know, cut the commercial breaks and trim the extra on the ends. Here is what I did.

Transcode the video files to reduce their size etc.

I used handbrake for that, I like the easy control it gives over things like image cropping, decomb filters and stuff.

MPlayer edl mode

mplayer has a mode in which you can use keyboard shortcuts while watching a video in order to indicate parts to skip. You use it like this.

mplayer video.file -edlout file.edl

You then press i to start a skip section and i again to finish it. There is no visual feedback except in the terminal you used to launch mplayer.

Careful, nothing is written out for a skip that you don’t finish!

The problem

You should be able to use mplayer (and therefore mencoder) to automatically skip those sections like this.

mplayer video.file -edl file.edl

However my version of mplayer (on Ubuntu 12.10) says

Old EDL functionality using the –edl option is not supported.
MPlayer2 UNKNOWN (C) 2000-2012 MPlayer Team

ffmpeg

I had already looked into using ffmpeg for that sort of work. Basically, you can tell it to skip a lot and take just a certain length. And you can tell it to copy over the video and the audio, and it will go through an hours video in seconds flat. However it isn’t always very accurate about the timings because it can only start extracting from some sorts of keyframes in the video.

It is best if you write the results out to a .ts file because ffmpeg can join them up again without hassle.

ffmpeg -acodec copy -vcodec copy -ss $start -t $length -i input.m4v part1.ts

It complained about my input files saying I had to add -vbsf h264_mp4toannexb to make it work. All my input files are in the same format, so I just wrote it into my script. Works for me.

And to join them up again:

ffmpeg -i "concat:part1.ts|part2.ts|part3.ts" -acodec copy -vcodec copy output.mp4

Using my script

I had dreamed about cooking up a nice script, so I did. I called it edl, but you can call it what you like.

I run it like this:
edl videofile.m4v
It doesn’t much matter what format your videos are in as long as mplayer and ffmpeg can read them.
The output will be called videofile.m4v.mp4. I couldn’t be bothered to figure out how to remove the first file extension. I will be renaming the files by hand afterwards anyway.

First it fires up mplayer with the -edlout option, and I mark my skips using the i key. I tend to skip from the very beginning (starting it within the first second will do) up to the beginning of the bit I want. Then I mark the commercial breaks, and finally I mark from the end of the part I want to a bit furthur on (it doesn’t matter how far furthur on).

The edl format thinks in terms of ranges to skip, but my script thinks more in terms of ranges to keep, so if your first skip starts after 10m, my script will give you the first ten minutes of the video. However it wont give you anything after the beginning of your last skip (I got lazy there).

When you quit mplayer, it shows you the contents of the edl file and asks if you want to continue. If you do want to continue, just press Enter, anything else, even “y”, will cause it to exit straight away. (another moment of laziness)

Then it does the ffmpeg magic and a few seconds later it shows you the rough timestamps of the points the resulting video was joined at so that you can check. And finally it fires up vlc so you can take a look.

Advanced use

You can choose to give it an edl file, which is useful for fine tuning and stuff.

You do that like this:

edl video.file edl.file

It will skip the mplayer step and use the provided edl file instead. Careful though, a bad edl file will cause chaos!

Finally, the script

#!/bin/sh

# Colour for text messages
txtrst='33[0m'  # Color off
txtred='33[1;31m' # Red
txtgrn='33[1;32m' # Green
txtylw='33[1;33m' # Yellow
txtblu='33[1;34m' # Blue

toHHMMSS () {
  # Translates a time in seconds to the format required by ffmpeg. HH:MM:SS.mss
  echo `dc -e "$1 60~r 60~r [0n]SadZ2>an [:]n [0n]SadZ2>an [:]n 1~r [0n]SadZ2>an [n]Sad0!=a" | cut -b -12`
}

filename=`basename "$1"`
infile=$1
outfile="$1.mp4"
if [ "x$2" = "x" ]; then
  edlfile=`tempfile`
  mplayer "$infile" -edlout "$edlfile"
  mplayer=true
  echo "Here is what is in the edl file produced by mplayer."
else
  edlfile="$2"
  mplayer=false
  echo "Here is what is in the edl file you specified."
fi
cat "$edlfile"
echo "Continuer? [Y/n]"
read cont
if [ "x$cont" = "x" ]; then

start=0
i=0
joincmd='concat:'
curlength="0"
joins='Joins at'

# Reads in from $edlfile, see end of loop.
while read line; do
  if [ "x$line" = "x" ]; then
    break # Jump out on empty line
  fi

  i=$(($i+1))
  end=`echo $line | cut -d" " -f1`
  startnext=`echo $line | cut -d" " -f2`

  if [ `dc -e "$end 1/n"` -ne 0 ]; then # if wanted an integer, so let's give it one.
    length=`dc -e "$end $start -n"`
    curlength=`dc -e "$curlength $length + n"`
    start=`toHHMMSS $start`
    length=`toHHMMSS $length`

    echo "\n$txtylw Taking part $i from $start length $length\n$txtrst"
    ffmpeg -acodec copy -vcodec copy -vbsf h264_mp4toannexb -ss $start -t $length -i "$infile" "part$i.ts"

    joins="$joins `toHHMMSS $curlength | cut -b -9`"
    if [ $i -eq 1 ]; then
      joincmd="${joincmd}part$i.ts"
    else
      joincmd="${joincmd}|part$i.ts"
    fi
  else
    # First skip starts at 0, so don't take out a part from 0 to 0.
    i=0
  fi
  start=$startnext
done < "$edlfile"

echo "\n$txtylw Joining up the parts\n$txtrst"
ffmpeg -i "$joincmd" -acodec copy -vcodec copy "${outfile}"
rm part*.ts
echo "\n$txtylw $joins"
vlc "$outfile" 2>&1 >/dev/null

if $mplayer; then
  rm $edlfile
fi

fi
Advertisements

One Comment

Leave a Comment
  1. Menno / Nov 7 2014 2:02 am

    Dear,

    I am using your python version of the above since the one above generates very small file.

    But there is one problem left;

    for line in open(storagedir + edlfile):
    print line

    times = line.split(“\t”)

    begin_second = times[0]
    end_second = times[1]
    action = times[2]

    duration = float(begin_second) – float(previousend)

    This part works in a way that it only takes the first two entries out of my .edl file. I dont know how to program so I cannot correct it. But my EDL file consists of four commercial blocks. How can I ensure your scripts handles all of them?

    Also I fixed some of the script by adding

    cmd = [‘ffmpeg’, ‘-i’, storagedir + filename,’-bsf’, ‘h264_mp4toannexb’, ‘-vcodec’, ‘copy’,
    ‘-acodec’, ‘copy’, ‘-ss’, str(previousend), ‘-t’, str(duration),
    storagedir + ‘part’ + str(count) + ‘.ts’]

    ——–
    ,’-bsf’, ‘h264_mp4toannexb’,

    Last comment,
    This script changes DTS audio to MPEG2, i would like to avoid it if possible!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: