H264 commercial remover and remuxer

From MythTV Official Wiki
Jump to: navigation, search

Author Ian Thiele
Description This script uses mythcommflag, ffmpeg, and mkvmerge to remove commercials from an h264 encoded input file and outputs them into an mkv container.
Supports


Usage

/path/h264cut.sh /path/to/file


Disclaimer

I wrote this specifically to get a friend's .mpg files, that were encoded with h.264 from a hd-pvr, commercial-free and into a Matroska container. I have little,read: no, knowledge of video encoding, containers, and whatnot. Built this just from ffmpeg and mkvmerge man pages. Might work for you, might not. That said, feel free to take it and modify it to your needs, etc., etc.

Update

Added a little database bookkeeping and logfile support. Will stop broken seeking when playing inside myth. Also confirmed that this works with files from both a hdr-pvr and hdhomerun.


Script.png h264cut.sh

#!/bin/bash
# Ian Thiele
# icthiele@gmail.com
#Uses mythcommflag, ffmpeg, and mkvmerge to cut commercials out of h.264 encoded mpg files#


workdir="/tmp/h264cut"

if [ -z "`ls ${workdir}`" ]; then
    mkdir -p ${workdir}
fi
if [ ! -f $1 ]; then
    echo "file doesn't exist, aborting."
    exit 1
fi

#see if we have the DB info
test -f /etc/mythtv/mysql.txt && . /etc/mythtv/mysql.txt
test -f ~/.mythtv/mysql.txt && . ~/.mythtv/mysql.txt

#let's make sure we have a sane environment
if [ -z "`which ffmpeg`" ]; then
    echo "FFMpeg not present in the path. Adjust environment or install ffmpeg"
    exit 1
elif [ -z "`which  mkvmerge`" ]; then
    echo "mkvmerge is not present in the path. Adjust environment or install mkvtoolnix"
    exit 1
fi

#connect to DB
mysqlconnect="mysql -N -h$DBHostName -u$DBUserName -p$DBPassword $DBName"
export mysqlconnect

#deteremine directory and filename
RECDIR=`dirname $1`
BASENAME=`basename $1`
tmp=`basename $BASENAME .mpg`

logfile=/store/LeBaugh/mythold/${BASENAME}.log
>${logfile}

#determine chanid and starttime from recorded table
chanid=`echo "select chanid from recorded where basename=\"$BASENAME\";" | $mysqlconnect`
starttime=`echo "select starttime from recorded where basename=\"$BASENAME\";" | $mysqlconnect`
#determine FPS (frames per second) from db, which is used to determine the seek time to extract video clips
fps=$(echo "scale=10;`echo "select data from recordedmarkup where chanid=$chanid AND starttime='$starttime' and type=32" | $mysqlconnect` / 1000.0" | bc)

if [ -z "$chanid" ] || [ -z "$starttime" ]
then
    echo "Recording not found in MythTV database, script aborted."
    exit 1
elif [ -z "$fps" ]
then
    echo "Frames per second value not found in MythTV database, script aborted."
    exit 1
fi

#lets make sure we have a cutlist before we proceed
if [ -z "`mythcommflag --getcutlist -c $chanid -s "$starttime" | grep Cutlist | sed 's/Cutlist: $//'`" ]; then
    echo "no cutlist found....generating new cutlist"
    mythcommflag -c $chanid -s "$starttime" --gencutlist &>${logfile}
fi   

#cutlist provides a list of frames in the format start-end,[start1-end1,....] to cut 
#we swap this list so that it provides the ranges of video we want in the format
#	start-end start1:end1 ....
CUTLIST=`mythcommflag -c $chanid -s "$starttime" --getcutlist | grep Cutlist | sed 's/Cutlist: //' | \
    sed 's/-/ /g' | sed 's/^\|$/-/g' | sed 's/,/-/g'`


clipcount=0
for i in ${CUTLIST}
do
    start=`echo $i | sed 's/ //g' | sed 's/^\(.*\)-.*$/\1/'`
    end=`echo $i | sed 's/ //g' | sed 's/^.*-\(.*\)$/\1/'`

    #if $start is empty, deal with it
    if [ -z $start ]; then
	#set $start to 0
	start=0
	if [ $start -eq $end ]; then
	    continue 
	fi
    fi
    #convert start into time in seconds (divide frames by frames per second)    
    start=$(echo "scale=8; $start / $fps" | bc -l)
    #if $end is not null, we can do things
    if [ -n "$end" ]; then
	clipcount=$((++clipcount))
	end=$(echo "scale=8; $end / $fps" | bc -l)
	duration=`echo "$end - $start" | bc -l`	

	ffmpeg -i ${RECDIR}/${BASENAME} -acodec copy -vcodec copy -f matroska -ss $start -t $duration \
	    ${workdir}/${tmp}_${clipcount}.mkv &>>${logfile}
    elif [ -z "$end" ]; then
	clipcount=$((++clipcount))
	ffmpeg -i ${RECDIR}/${BASENAME} -acodec copy -vcodec copy -f matroska -ss ${start} \
	    ${workdir}/${tmp}_${clipcount}.mkv &>>${logfile}
    fi

    
done

mergestr=""
for i in `ls ${workdir}/${tmp}_* | sort`
do
    if [ -z "$mergestr" ]; then
	mergestr="$i"
	continue
    fi
    mergestr="$mergestr +$i"
done

mkvmerge --append-mode track $mergestr -o ${RECDIR}/${tmp}.mkv &>>${logfile}



#clear out the old cutlist
mythcommflag --clearcutlist -c $chanid -s "$starttime" &>>${logfile}


#we'll need a new filesize to update the db with
filesize=$(du ${RECDIR}/${tmp}.mkv | awk '{print $1}') 

#update db with new filesize and filename
cat <<EOF | $mysqlconnect
UPDATE
	recorded
SET
	cutlist = 0,
	filesize = ${filesize},
	basename = "${tmp}.mkv"
WHERE
	chanid = ${chanid} AND
	starttime = "${starttime}";
EOF

#delete the old commercial skip info
cat <<EOF | $mysqlconnect
DELETE FROM
	recordedmarkup
WHERE	
	chanid = ${chanid} AND
	starttime = "${starttime}" AND
	(type = 4 OR type = 5);
EOF
 
cat <<EOF | $mysqlconnect
DELETE FROM
	recordedseek
WHERE
	chanid = ${chanid} AND
	starttime = "${starttime}";

EOF

mv ${RECDIR}/${BASENAME} /store/LeBaugh/mythold/${BASENAME}.old

#cleanup $workdir
rm -rf ${workdir}/*${tmp}*