Skip to content

Instantly share code, notes, and snippets.

@dmoo1790
Created July 26, 2010 07:58
Show Gist options
  • Save dmoo1790/490296 to your computer and use it in GitHub Desktop.
Save dmoo1790/490296 to your computer and use it in GitHub Desktop.
#!/bin/bash
# MYTHCUTNZ Version 0.1
# This script tries to transcode and/or cut myth recordings.
# Why? LATM AAC audio used in most NZ broadcasts causes problems for
# programs like ffmpeg when you try to edit/cut them.
# Uses cvlc to transcode the LATM AAC audio if the recording has no AC3 audio.
# If the recording does have AC3 audio then cvlc is not used.
# Uses ffmpeg to cut the recording using cut list and/or commercial skip list.
# Updates the myth database with sql calls except for rebuilding the seek table.
# Output files may NOT be easily editable again.
OVERWRITE=0
OUTPUT_DIR=""
CHANID=-9999
STARTTIME=""
USECUTS=1
USECOMMFLAGS=1
outfile="11test.mpg"
rec_dir=""
basename=""
USER="root"
PASS="" # Change this to "-p your_password" if necessary
USAGE="$0 -c channel_id -s starttime [-d dir] [-f filename] [-o directory] [-h] [-Z] [-n] [-N]
-h Help/usage
-c Channel ID (required, %CHANID% from Mythtv)
-s Start time (required, %STARTTIME% from Mythtv)
-d Directory where the recording is store (optional, %DIR% from Mythtv)
-f Recording file name (optional, %FILE% from Mythtv)
-o Output directory (optional, recording directory if not specified)
-Z Overwrite original recording (optional, default is false, ignored if -o specified)
-n Don't edit the recording using the cut list (optional, default is use cut list)
-N Don't edit the recording using the commercial flag list (optional, default is use comm flags)
Example usage to cut a recording using the cut list and commercial skip list and overwrite original
mythcutnz -c %CHANID% -s %STARTTIME% -Z
Example usage to cut a recording only using the cut list and save the file to a different directory
mythcutnz -c %CHANID% -s %STARTTIME% -o /your/directory/ -N
Warning: The end files may not be editable with ffmpeg and/or playable in vlc. Keep the original
recording if you think you may want to do further edits or conversions in future. This script does
NOT delete the original recording. It is renamed with the extension \".old\"."
while getopts "c:s:o:d:f:hZnN" opt
do
case $opt in
c )
CHANID="$OPTARG"
;;
s )
STARTTIME="$OPTARG"
;;
d )
rec_dir="$OPTARG"
;;
f )
basename="$OPTARG"
;;
h )
echo "Usage: $USAGE" >&2
exit 0
;;
o )
OUTPUT_DIR="$OPTARG"
;;
Z ) OVERWRITE=1 # Will be reset to false if -o directory is specified
;;
n )
USECUTS=0
;;
N )
USECOMMFLAGS=0
;;
? )
echo "Invalid option: -$OPTARG" >&2
echo "Usage: $USAGE" >&2
exit -1
;;
esac
done
if [ "$OUTPUT_DIR" != "" ]; then
OVERWRITE=0
fi
if [ $CHANID == -9999 -o "$STARTTIME" == "" ]; then
echo "Channel ID and/or Start Time missing" >&2
echo "Usage: $USAGE" >&2
exit -1
fi
#############################################################################
# Find where the recording is stored if not specified with -d and -f options
#############################################################################
if [ "$rec_dir" == "" -o "$basename" == "" ]; then
storage_dirs=`echo "select dirname from storagegroup;" | mysql -N -u $USER $PASS mythconverg`
basename=`echo "select basename from recorded where chanid=$CHANID and starttime=\"$STARTTIME\";" \
| mysql -N -u $USER $PASS mythconverg`
#echo $basename
#echo $storage_dirs
found=0
for f in $storage_dirs; do
if [ -e "$f$basename" ]; then
rec_dir=$f
found=1
fi
done
echo $rec_dir$basename
if [ $found == 0 ]; then
echo "Can't find recording, aborting" >&2
exit -1
fi
fi
# Set output directory to recording directory if not specified with -o option
if [ "$OUTPUT_DIR" == "" ]; then
OUTPUT_DIR=$rec_dir
fi
#############################################################################
# Get recording video and audio streams with ffmpeg
#############################################################################
declare -a STREAMS # STREAMS is a 1 dimensional array storing stream info from ffmpeg
AUDIO_STREAM=""
VIDEO_STREAM=""
STREAMS=( `ffmpeg -i $rec_dir$basename 2>&1 | awk '/Stream #/ {print $3 " " $4 " " $2}'` )
i=0
count=${#STREAMS[@]}
while [ $i -lt $count ]
do
# echo ${STREAMS[$i]}
case ${STREAMS[$i]} in
"Audio:")
if [ ${STREAMS[$i+1]} == "ac3," ]; then
STREAM=${STREAMS[$i+2]}
AUDIO_STREAM=${STREAM:1:3}
fi
;;
"Video:")
STREAM=${STREAMS[$i+2]}
VIDEO_STREAM=${STREAM:1:3}
;;
esac
let i=i+3
done
#############################################################################
# If ffmpeg didn't find an AC3 audio stream then transcode the file with cvlc
#############################################################################
if [ "$AUDIO_STREAM" == "" ]; then
thefile=$OUTPUT_DIR$basename".tmp"
ionice -c3 cvlc $rec_dir$basename --sout "#transcode{acodec=a52, ab=384, channels=2, samplerate=48000} \
:std{mux=ps, access=file, dst="$thefile"}" vlc://quit > /dev/null
MAPPING=""
else
thefile=$rec_dir$basename
MAPPING="-map "$VIDEO_STREAM" -map "$AUDIO_STREAM
fi
#############################################################################
# Edit the recording based on cuts and/or commercial flags
#############################################################################
if [ $USECUTS == 1 -o $USECOMMFLAGS == 1 ]; then
MARKTYPES=""
if [ $USECUTS == 1 ]; then
MARKTYPES="0,1"
if [ $USECOMMFLAGS == 1 ]; then
MARKTYPES=$MARKTYPES",4,5"
fi
else
MARKTYPES=$MARKTYPES"4,5"
fi
# Get the cuts from recordedmarkup table and store in an array
cuts=( `echo "select type,mark from recordedmarkup where chanid=$CHANID and starttime=\"$STARTTIME\" \
and type in ($MARKTYPES) order by mark" | mysql -N -u $USER $PASS mythconverg` )
outfile=$OUTPUT_DIR$basename".new"
i=0
part=1
totaltime=0
pieces=${#cuts[@]}
let pieces=pieces-3
#k=0
#while [ $k -lt $pieces ]; do
# echo ${cuts[k]}
# let k++
#done
# Figure out divisor for frames to time calculations
TBC=( `ffmpeg -i $thefile 2>&1 | awk '/tbc/ {print $(NF-1)}'` )
echo $"tbc = "$TBC
if [ $TBC == 100 ]; then
FPS=50
else
if [ $TBC == 50 ]; then
FPS=25
else
FPS=25 # Guess
fi
fi
while [ $i -le $pieces ]; do
if [ ${cuts[i]} == 0 -o ${cuts[i]} == 5 ]; then # Look for a cut end, i.e., the start of a segment we want to keep
# Convert cut points from frames to seconds
cutstart=`echo "scale=3; ${cuts[i+1]}/$FPS" | bc -l`
cutlength=`echo "scale=3; ${cuts[i+3]}/$FPS-$cutstart" | bc -l`
# Cut length needs to be updated to match timestamp update
cutlength2=`echo "scale=3; $cutlength+$totaltime" | bc -l`
echo $cutstart" "$cutlength" "$cutlength2" "${cuts[i+1]}" "${cuts[i+3]}
# Cut the segment and update the timestamps with ffmpeg
ionice -c3 ffmpeg -y -async 1 -itsoffset $totaltime -ss $cutstart -t $cutlength2 -i "$thefile" \
-vcodec copy -acodec copy $MAPPING tmp.mpg
if [ $part -gt 1 ]; then # Concatenate this segment to existing file
ionice -c3 cat tmp.mpg >> "$outfile"
ionice -c3 rm tmp.mpg
else
ionice -c3 mv tmp.mpg "$outfile"
fi
# Total time updated so that timestamps for next segment can be updated
totaltime=`echo "scale=3; $totaltime+$cutlength" | bc -l`
# Increment loop counter by 4 since array has cut start/end pairs plus cut types
let i+=4
let part++
else
# Skips the case where the first cut point is a cut start, i.e., when start of recording is deleted
let i+=2
fi
done
else
mv $thefile $OUTPUT_DIR$basename".new"
fi
#############################################################################
# Reset the cut list, seek table, etc. for final file
#############################################################################
if [ $OVERWRITE == 1 ]; then
# Don't delete original. Just rename.
mv $rec_dir$basename $rec_dir$basename".old"
mv $OUTPUT_DIR$basename".new" $OUTPUT_DIR$basename
# Delete tmp file if it exists
if [ -e $OUTPUT_DIR$basename".tmp" ]; then
ionice -c3 rm $OUTPUT_DIR$basename".tmp"
fi
# Clear the seek table
echo "delete from recordedseek where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
# Clear the markup table
echo "delete from recordedmarkup where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
# Clear bookmark, cutlist, commflagged flags
echo "update recorded set bookmark=0, cutlist=0, commflagged=0 where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
# Update file size
FILESIZE=`du -b $OUTPUT_DIR$basename | awk '{print $1}'`
echo "update recorded set filesize=$FILESIZE where chanid=$CHANID and starttime=\"$STARTTIME\";" | mysql -u $USER $PASS mythconverg
# Rebuild the seek table with mythtranscode (mythcommflag does not seem to work)
ionice -c3 mythtranscode -c $CHANID -s "$STARTTIME" --buildindex
# Fix the seek table if necessary. Seems mythtranscode has a bug which causes frame numbers (marks) to get doubled.
# Something to do with fields per sec vs frames per sec??? Bug in ffmpeg???
if [ $FPS == 25 ]; then
echo "update recordedseek set mark=mark/2 where chanid=$CHANID and starttime=\"$STARTTIME\" order by mark;" | mysql -u $USER $PASS mythconverg
fi
# Seems to be flaky using mythcommflag to fix things
# ionice -c3 mythcommflag -f $OUTPUT_DIR$basename --clearcutlist
# ionice -c3 mythcommflag -f $OUTPUT_DIR$basename --clearskiplist
# ionice -c3 mythcommflag -f $OUTPUT_DIR$basename --rebuild
else
# Don't accidentally overwrite an existing file
if [ -e $OUTPUT_DIR$basename ]; then
i=1
while [ -e $OUTPUT_DIR$basename"."$i".mpg" ]; do
let i++
done
mv $OUTPUT_DIR$basename $OUTPUT_DIR$basename"."$i".mpg"
else
mv $OUTPUT_DIR$basename".new" $OUTPUT_DIR$basename
fi
fi
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment