#!/bin/bash
#Accurate trimmig of .ts (H264-AAC) based in ffmpeg 
#Strategy: re-encode video & parse audio keeping the AV delay
#
#This script requires ffmpeg (ffmpeg, ffprobe, libxml2-utils (xmllint)) 
#
#Version: 1.0
#
#Usage:
# tsaccuratetrim_rv_pa source destTSvideo destTSaudio IN/DUR timeIN
#
#Examples:
# tsaccuratetrim_rv_pa source.ts destvideo.ts destaudio.ts IN 1.32
#
# tsaccuratetrim_rv_pa source.ts destvideo.ts destaudio.ts DUR 2.33

#Set global vars 
#----------------------------
ffmpeg="ffmpeg"
ffprobe="ffprobe"
xmllint="xmllint"
getinfoparams="-print_format xml -show_streams"

#Set in vars
#----------------------------
sourceTS=$1
destTSvideo=$2
destTSaudio=$3
trimtype=$4
timetrim=$5

#Set intermediate files 
#----------------------------
sourceinfoxml=${sourceTS}.xml
rawvideo=${sourceTS}.h264
rawtrimmedaudio=${sourceTS}.aac
rawtrimedvideo=${sourceTS}_trimmed.h264

#Clean
#----------------------------
rm -f $sourceinfoxml  
rm -f $rawvideo
rm -f $rawtrimmedaudio
rm -f $rawtrimedvideo
rm -f $destTSvideo
rm -f $destTSaudio
rm -f $destMP4

#Create video information file
#----------------------------
${ffprobe} $sourceTS ${getinfoparams} > ${sourceinfoxml}
if [ "$?" != "0" ]; then
	echo "Error getting TS information!" 1>&2
	exit 1
fi

#Load information data
#----------------------------
numvideotracks=$(${xmllint} --xpath "count(/ffprobe/streams/stream[@codec_type='video'])" ${sourceinfoxml})
videocoder=$(${xmllint} --xpath "string(//ffprobe/streams/stream[@codec_type='video']/@codec_name)" ${sourceinfoxml})
videocodingprofilesrc=$(${xmllint} --xpath "string(//ffprobe/streams/stream[@codec_type='video']/@profile)" ${sourceinfoxml})
videocodingleveloriginal=$(${xmllint} --xpath "string(//ffprobe/streams/stream[@codec_type='video']/@level)" ${sourceinfoxml})
videobitrate=$(${xmllint} --xpath "string(//ffprobe/streams/stream[@codec_type='video']/@bit_rate)" ${sourceinfoxml})
videoduration=$(${xmllint} --xpath "string(//ffprobe/streams/stream[@codec_type='video']/@duration)" ${sourceinfoxml})
videoindex=$(${xmllint} --xpath "string(//ffprobe/streams/stream[@codec_type='video']/@index)" ${sourceinfoxml})

#Correct the level string from XX to X.X (Ex: 31 to 3.1)
#----------------------------
if [ ${#videocodingleveloriginal} == "2" ]; then
	videocodinglevel=${videocodingleveloriginal:0:1}.${videocodingleveloriginal:1:1}
else
	videocodinglevel=$videocodingleveloriginal
fi

#Adapt the profile name
videocodingprofile=${videocodingprofilesrc,,}
if [[ $videocodingprofile == *"baseline"* ]]; then
	videocodingprofile="baseline"
fi

#Check allowed profiles
if [ ${videocodingprofile} != "baseline" ] && [ ${videocodingprofile} != "main" ] && [ ${videocodingprofile} != "high" ]; then
	echo "Error the profile is ${videocodingprofile} is not allowed!" 1>&2
	exit 1
fi

#Approximate the video bitrate using the file length
#----------------------------
if [ ${#videobitrate} == "0" ]; then
	sourcefilesize=$(stat -c %s ${sourceTS})
	videobitrate=$(bc <<< "scale=0;(${sourcefilesize}*8)/${videoduration}")
fi

#To check
#----------------------------
echo "Num video tracks: $numvideotracks" 1>&2
echo "Video codec is: $videocoder" 1>&2
echo "Video bitrate is: $videobitrate" 1>&2
echo "Video duration is: $videoduration" 1>&2
echo "Profile is: $videocodingprofile" 1>&2
echo "Level is: $videocodingleveloriginal" 1>&2
echo "Level length: ${#videocodingleveloriginal}" 1>&2
echo "Level modified is: $videocodinglevel" 1>&2
echo "Video index is: $videoindex" 1>&2

#Validate input TS
#----------------------------
if [ $numvideotracks != "1" ]; then
	echo "Error number of video tracks. Must be 1 video track in input TS!" 1>&2
	exit 1
fi
if [ $videocoder != "h264" ]; then
	echo "Error video codec. Must be h264" 1>&2
	exit 1
fi

#Set video encoding modifiers
#----------------------------
#This must be copied from input file video stream (OK from xml, bitrate approx from filesize / duration)
videoencodingparams="-vcodec libx264 -profile:v ${videocodingprofile} -level ${videocodinglevel} -b:v ${videobitrate}" 

#To check
#echo "videoencodingparams is: $videoencodingparams"

#Get precise trim points (PTS based)
#----------------------------
#Get video trim PTS
timetrimvideo=$(./getnextpts $sourceTS $timetrim video)
#Get audio trim PTS (it will be after the video trim point)
timetrimaudio=$(./getnextpts $sourceTS $timetrimvideo audio)

#To check
echo "Trim point video: $timetrimvideo" 1>&2
echo "Trim point audio: $timetrimaudio" 1>&2

#Use FFMPEG to extract video track in raw format (h264)
#----------------------------
${ffmpeg} -i $sourceTS -vcodec copy -f mpeg2video $rawvideo

#Use FFMPEG to extract audio track (and parse it)
#----------------------------
if [ $trimtype == "IN" ]; then
	${ffmpeg} -i $sourceTS -ss ${timetrimaudio} -acodec copy -f mp2 ${rawtrimmedaudio}
else
	${ffmpeg} -i $sourceTS -t ${timetrimaudio} -acodec copy -f mp2 ${rawtrimmedaudio}
fi

#Use FFMPEG to trim and re-encode the video track (re-encoding uses similar parameters as original file)
#----------------------------
if [ $trimtype == "IN" ]; then
	${ffmpeg} -i $rawvideo -ss ${timetrimvideo} ${videoencodingparams} ${rawtrimedvideo}
else
	${ffmpeg} -i $rawvideo -t ${timetrimvideo} ${videoencodingparams} ${rawtrimedvideo}
fi

#Create TSs for the video & audio streams
#----------------------------
#Comment: perhaps the PIDs changes!!
#Comment: Compute avdelay parameterin order to preserve the AV delay
avdelay=$(printf "%f\n" $(bc -q <<< scale=0\;${timetrimaudio}-${timetrimvideo}))

#To check
#echo "Audio delay appliied: $avdelay" 1>&2

#Comment: It does not work with h264 main profile. See https://trac.ffmpeg.org/ticket/1598
${ffmpeg} -i ${rawtrimedvideo} -vcodec copy -mpegts_copyts 1 -f mpegts -copyts ${destTSvideo}
${ffmpeg} -i ${rawtrimmedaudio} -acodec copy -mpegts_copyts 1 -f mpegts -copyts ${destTSaudio}

#Create final MP4 for test purposes (remux)
#----------------------------
#if [ $trimtype == "IN" ]; then
#	./rewraptomp4 ${destTSvideo} ${destTSaudio} ${avdelay} ${destMP4}
#else
#	./rewraptomp4 ${destTSvideo} ${destTSaudio} 0 ${destMP4}
#fi

#Return audio delay
#----------------------------
echo "$avdelay"	
