#!/bin/bash
#set -n		# read but do not execute commands (check on syntax errors)
#
# File: /usr/local/bin/medialcut
#
# Cut out part of a file without loss.
#
#    Copyright (c) 2020 Juergen Kaesmann (JK)
#
##############################################################################
# GPL NOTICE
#
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, write to the Free Software
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
##############################################################################
# FIND
##############################################################################
# XARGS
##############################################################################
# FILTER
##############################################################################
# AWK
##############################################################################
# SED
##############################################################################
# TREE
##############################################################################
# RSYNC
##############################################################################
# DIALOG
##############################################################################
# EXIT CODES

##############################################################################
# SOURCE FILES
#
. /usr/local/etc/main.conf				# config main
#. $LLIBDIR/libfuncerr-sh				# functions error
. $LLIBDIR/libfuncstd-sh				# functions standard
. $LLIBDIR/libfuncmath-sh				# functions math
#. $LLIBDIR/libfuncip-sh				# functions IP
#. $LLIBDIR/libfunchart-sh				# functions chart
#. $LLIBDIR/libfunccddvd-sh				# functions cd-dvd
#. /usr/local/etc/multimedia.conf			# config multimedia

#if [ ! -e ~/.samplerc ]; then				# install config user
#	update-sample
#	err=$?
#	if [ $err -eq 0 ]; then
#		echo "User ~/.samplerc installed. You should configure it."
#		exit 0
#	else
#		Show_Err $err "From update-sample"
#		exit $err
#	fi
#fi

#if [ -e ~/.samplerc ]; then
#	. ~/.samplerc					# config user
#fi

##############################################################################
# DECLARATION OF STANDARD CONSTANTS AND VARIABLES
#
readonly package=Multimedia				# package name
readonly package_file=ksm-multimedia			# package file name
readonly vinfopath="/usr/local/share/$package_file"	# path to package dir
readonly version=$(/bin/cat $vinfopath/VERSION)		# package version
readonly release=$(/bin/cat $vinfopath/RELEASE)		# package release
readonly scriptname=$(basename $0)			# name of script
#readonly tempfile=$TEMPDIR/$scriptname.$UID.tmp	# temporary file name
tempfile=$(mktemp -u $TEMPDIR/$scriptname.$UID.XXXXXX)	# temporary file name
#readonly lockfile=$TEMPDIR/lock/$scriptname.$UID.lock	# lock file name
readonly debuglog="/usr/bin/logger -p user.debug -t $scriptname -- DEBUG:"	# log dbg msg
readonly errlog="/usr/bin/logger -p user.err -t $scriptname -- ERROR:"		# log err msg
readonly infolog="/usr/bin/logger -p user.info -t $scriptname -- INFO:"		# log inf msg
cmd=''							# sub command
debug=no						# debug=yes/no
verbose=''						# verbose=yes/''/no
err=${_ERR_OK_}						# error code

##############################################################################
# USER FILL IN AT FIRST RUN
##############################################################################
# SYSTEM
##############################################################################
# VLC
##############################################################################
# USER-CONSTANTS
##############################################################################
# USER-VARIABLES
#
# STRINGS
tmpfa=$tempfile.a.mp4					# temp file name A
tmpfb=$tempfile.b.mp4					# temp file name B

#args=''				# all args from cmdline
arg1=''					# 1. arg from cmdline
arg2=''					# 2. arg from cmdline
#opt_o=''				# option -o (yes)
					# option --output (yes)
#opt_i=''				# option -i (arg)
					# option --infile (arg)
#opt_o=''				# option -o (arg)
					# option --outfile (arg)
#opt_p=''				# option -p (arg)
					# option --pages (arg)
opt_c=''				# option -c (yes)
opt_k=''				# option -k (yes)
opt_r=''				# option -r (arg)
opt_adur=''				# option --adur (arg)
opt_probs=''				# option --probs (arg)
opt_pixfmt=''				# option --pixfmt (arg)
opt_s=''				# option -s (arg)
opt_ss=''				# option --ss (arg)
opt_t=''				# option --t (arg)
opt_to=''				# option --to (arg)
s=''

# NUMBERS
#p_first=0				# first page
#p_last=0				# last page
frd='0.04'				# standard frame duration for 25 fps

# ARRAYS
# Indexed arrays are always zero based string arrays.
# If index starts with 1 and all members are assigned then
# count of members of array = last index of array
#declare -a arr
#maxarr="${#arr[@]}"			# get number of elements in arr
#unset arr[@]				# remove the entire array
declare -a iarr
maxiarr="${#iarr[@]}"			# get number of elements in arr
#
##############################################################################
# LANGUAGE SUPPORT
##############################################################################
# SIGNAL HANDLING

##############################################################################
# FUNCTIONS
##############################################################################
#
##############################################################################
# HELP
#
#=============================================================================
function helptext ()
{
# Output helptext
# Uses cat, echo, logger
# Return codes:
# Standard

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG helptext: $# $@" >&2
	$debuglog helptext: $# $@
fi

cat <<EOT
NAME
$scriptname - Cut out part of a file without loss.

SYNOPSIS
$scriptname [options] [inf outf]

DESCRIPTION
This program combines ffmpeg, mediacut and losslesscut. It cuts out
part of a file without loss. Due to the relatively slow 
lossless-function the program needs sometimes a little bit more amount
of time. This depends on the position of the cut.

The process is done with 2 parts (part A, part B):

  Part A (part of the first GOP) is cut with 'losslesscut', 
  part B (rest of file) is cut with 'mediacut'.

Sometimes the calculated start of part B is not correct, f.i. you could
see a still of the video at start of part B. In this case one can
reduce the time of the start frame with -s (try 1 or 2 f.i.).

The input media file has to be of type MP4 (.mp4), with one video
stream encoded as h264 and (if any) one audio stream encoded as aac.
The output media file will be of the same type (.mp4, h264, aac).
Internal search is for IDR-frames.

We require that the input file has at least (-r <range>) secs
(default = 15). The options --ss and (--t|--to) are mandatory but not
both --t and --to.

The output file will always be overwritten!

OPTIONS
Gnu style options:

  -h|--help    = Output help, then exit.
  -V|--version = Output version info, then exit.
  -d|--debug   = Turn on DEBUG mode.
  -q|--quiet   = Be quiet.
  -v|--verbose = Be verbose.
  -c           = Recalc PTS.
  -k           = Debug option (Keep tempfiles).
  -n           = No audio stream in <inf>. <inf> has only one video
                 stream.
  -r range     = Max. range for H264 GOP detection (secs). Default = 15
                 Increase <range> if no IDR-frame found.
  -s fr        = Start-Reduction for Part B, <fr>=numb of frames.
                 Framesize=($frd). This means the start frame is for
                 <fr> * $frd secs earlier than calculated.
  --adur=dur   = Analyzeduration (input).
                 dur in microseconds (100M|...). Default = 5000000
  --probs=siz  = Probesize (input).
                 siz in Bytes (32 - n, 100M|...). Default = 5000000
  --pixfmt=pxf = pxf = +|yuv420p|... (+ = same fmt as input).
  --ss=start   = Start at time <start> (output option).
  		 start=seconds|SS:MM:SS[.dec] (dec = 1-3 digits).
  		 For videos with 50 fps you may set the start 1 or 2
  		 frames later.
  --t=dur      = Duration is <dur> (output option).
                 dur=seconds[.dec]|SS:MM:SS[.dec] (dec = 1-3 digits).
                 Give only --t or --to.
  --to=end     = End at time <end> (output option).
                 end=seconds[.dec]|SS:MM:SS[.dec] (dec = 1-3 digits).
                 Give only --t or --to.
  --           = End of options.

ARGUMENTS
Give if needed:

  inf  = Media Input file (.mp4)
  outf = Media Output file (.mp4)

EXAMPLES
For instance:

  Cut out part of infile.mp4. Chapter start is at 01:00:15 (start is
  somewhere in the initial GOP). Chapter end is at 01:10:00. Output
  goes to outfile.mp4:

  $scriptname --ss=01:00:15 --to=01:10:00 infile.mp4 outfile.mp4

  If program exits with - no IDR-frame found - then set option -r with
  a higher value than 15 (f.i. 20 or 30 or something else):
  
  $scriptname -r 20 --ss=01:00:15 --to=01:10:00 infile.mp4 outfile.mp4
  
EXIT STATUS
On program exit:

  Code 0 = OK

  See output of 'showerrmsg'.

SEE ALSO
Related manpages:

  showerrmsg(1), losslesscut(1), mediacut(1), mediaconcat(1), ffmpeg(1).

AUTHORS
Juergen Kaesmann <juergen@kaesmann-online.de>
EOT
#  -o|--output  = Output to STDOUT.
#  -i f         = Input from file <f>.
#  --infile=f   = Input from file <f>.
#  -o f         = Output to file <f>.
#  --outfile=f  = Output to file <f>.
#  -p s,e       = Pages s=START e=END.
#  --pages=s,e  = Pages s=START e=END.

#FILES
#Used directories and files:
#
#  Config Global = /usr/local/etc/sample.conf
#  Config User   = ~/.samplerc

return
}

#=============================================================================
function HMS2SecsNS ()
{
# Transform a time from HH:MM:SS[.ss] to seconds.
# Non strict version - HH, MM, SS may be only 1 digit.
# par1 = HH:MM:SS[.ss]
# Uses echo, logger
# I OK then output result to STDOUT.
# Return codes:
# Standard

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG HMS2SecsNS: $# $@" >&2
	$debuglog HMS2SecsNS: $# $@
fi

local result='' t="$1"
local h=0 m=0 s=0 ss=0 ret=0

if [ -n "$t" ]; then					# if not empty
	if [ "${t#*:}" != "$t" ]; then			# contains :
		if [ $ret -eq 0 ]; then
			# get secs from HH
			#h=${t:0:2}
			h=${t%%:*}
			# secs = h * 3600
			h=$(X_mul_Y "$h" "3600")	# h in seconds
			ret=$?

			# get secs from MM
			#m=${t:3:2}
			m=${t#*:}
			m=${m%:*}
			# secs =  m * 60 + secs
			m=$(X_mul_Y "$m" "60")		# m in seconds
			ret=$?

			# get secs from SS[.ss]
			#s=${t:6}
			s=${t##*:}

			# sum up h+m+s
			ss=$(($h+$m))
			result=$(X_plus_Y "$ss" "$s")
			ret=$?
			echo "$result"
		fi
	else						# secs always in var
		ret=0
	fi
else							# empty
	ret=10
fi

return $ret
}

#=============================================================================
function secs2HMS ()
{
# Transform a time from seconds to HH:MM:SS[.ss].
# par1 = seconds
# Uses echo, logger
# I OK then output result to STDOUT.
# Return codes:
# Standard

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG secs2HMS: $# $@" >&2
	$debuglog secs2HMS: $# $@
fi

local rest='' t="$1"
local bu=0 h=0 m=0 s=0 result=0 ret=0

if [ -n "$t" ]; then					# if not empty
	if [ "${t#*:}" == "$t" ]; then			# secs - contains no :
		if [ $ret -eq 0 ]; then
			Find_Substr "$t" "."
			ret=$?
			if [ $ret -eq 0 ]; then		# . found, rest=ss
				rest=${t##*.}
				t=${t%.*}		# t = int secs
			fi

			if [ -z "$t" ]; then
				t=0
			fi
			if [ "$t" == "-" ]; then
				t=0
			fi
			h=$(($t/3600))			# h = hours

			bu=$(X_mul_Y "$h" 3600)
			ret=$?
			t=$(($t-$bu))
			m=$(($t/60))			# m = minutes

			bu=$(X_mul_Y "$m" 60)
			ret=$?
			t=$(($t-$bu))
			if [ -n "$rest" ]; then		# s = secs.ss
				s="${t}.$rest"
			else				# s = secs
				s=$t
			fi

			# beautify values
			if [ "$h" -lt 10 ]; then h="0$h"; fi
			if [ "$m" -lt 10 ]; then m="0$m"; fi
			bu=$(X_LT_Y "$s" 10)
			ret=$?
			if [ $ret -eq 0 ]; then s="0$s"; fi

			result="${h}:${m}:${s}"
			echo "$result"
		fi
	else					# seems to be HH:MM:SS.ss
		ret=0
	fi
else							# empty
	ret=10
fi

return $ret
}

#=============================================================================
function h264 ()
{
# Get all video IDR-frames from an area of a h264 encoded video file.
# par1 = first time of area to look for IDR-frames
# par2 = last time of area to look for IDR-frames
# Uses echo, logger
# Return codes:
# Standard

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG h264: $# $@" >&2
	$debuglog h264: $# $@
fi

local dts='' pts='' ft='' start=no reply='' s=''
local i=0 i_first="$1" i_last="$2" j=0 kf=0 ret=0
local -a arr
#maxarr="${#arr[@]}"			# get number of elements in arr
#unset arr[@]				# remove the entire array

# put all framedescriptions in tempfile
case "$verbose" in
    yes)
	echo "Run ffprobe"
	# allow STDOUT and STDERR
	ffprobe -i $arg1 -read_intervals ${i_first}%${i_last} \
		-show_frames -select_streams v:0 -print_format flat > $tempfile
	ret=$?
	;;
    '')
	#echo "Run ffprobe"
	# suppress STDERR
	ffprobe -hide_banner -i $arg1 -read_intervals ${i_first}%${i_last} \
		-show_frames -select_streams v:0 -print_format flat > $tempfile 2>/dev/null
	ret=$?
	;;
    no)
	# suppress STDOUT and STDERR
	ffprobe -hide_banner -i $arg1 -read_intervals ${i_first}%${i_last} \
		-show_frames -select_streams v:0 -print_format flat > $tempfile &>/dev/null
	ret=$?
	;;
esac
if [ "$ret" -ne 0 ]; then				# for other progs
	Show_Err ${_ERR_GENERAL_} "$ret from ffprobe !"
	exit $ret
fi

# search start of framedescriptions from tempfile, then put all to arr[]
j=0							# zähler für frame daten
while read; do
	reply=${REPLY#frames.frame.}		# beautify reply - rm [frames.frame.]*
	reply=${reply#*.}			# beautify reply - rm [n]*
	if [ "$start" != yes ] && [ "${reply%=*}" == media_type ]; then # look for start
		start=yes
	fi

	if [ "$start" == yes ]; then			# start of frames
		let ++j					# inc fr.var. cnt
		arr[$j]="$reply"			# put fr var in array
	fi
done < $tempfile

maxarr="${#arr[@]}"				# get number of elements in arr
i=0						# counter for key_frames
j=0
while [ $j -lt $maxarr ]; do
	let ++j
	if [ "${arr[$j]}" == "key_frame=1" ]; then
		let ++i
		pts=${arr[$j+2]}		# pos. of pkt_pts_time
		pts=${pts#pkt_pts_time=}	# get string val
		pts=${pts//\"/}			# rm "
		iarr[$i]=$pts			# pkt_pts_time of key_frame
		if [ "$debug" == "yes" ]; then	# DEBUG
			s=$(secs2HMS ${iarr[$i]})
			echo "DEBUG h264: IDR-frame=$i  iarr[$i]=${s}" >&2
		fi
	fi
done
if [ -z "$opt_k" ]; then
	rm -f $tempfile
fi

return $ret
}

#=============================================================================
function find_gop ()
{
# Find the gop in which the chapter start is in. Get gope
# par1 = 
# Uses echo, logger
# Return codes:
# Standard

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG find_gop: $# $@" >&2
	$debuglog find_gop: $# $@
fi

local chaps=0 first=0 last=0 gope=0 dgope='' s=''
local ret=0

# opt_ss to secs (chaps)
s=${opt_ss#*:}						# chaps
if [ "$s" != "$opt_ss" ]; then				# chaps contain :
	chaps=$(HMS2SecsNS $opt_ss)
	ret=$?
else							# are seconds
	chaps=$opt_ss
fi

first=$chaps						# start of search range
last=$(X_plus_Y ${first} ${opt_r})			# end of search range
ret=$?

# read all IDR-frames up from chaps into iarr
h264 $first $last
ret=$?

gope=''
maxiarr="${#iarr[@]}"				# get number of elements in iarr
i=0
while [ $i -lt $maxiarr ]; do
	let ++i
	X_LE_Y ${iarr[$i]} $chaps
	ret=$?
	if [ ${ret} -ne 0 ]; then			# I-Frame nach chaps
		gope=${iarr[$i]}
		break
	fi
done

dgope=$(secs2HMS $gope)
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG find_gop: gope=$dgope" >&2
fi
if [ -z "$gope" ]; then					# no IDR for gope found
	Show_Err ${_ERR_GENERAL_} "no IDR-frame for end of gop found, try option -r !"
	exit ${_ERR_GENERAL_}
fi

Push $gope
Push $chaps
ret=0

return $ret
}

#=============================================================================
function mainproc ()
{
# Cut lossless part of the 1. gop and the remaining chapter of the given file.
# par1 = 
# Uses echo, ffmpeg, logger, mediaconcat, mediacut, 
# Return codes:
# Standard
#
#	Cut lossless part
#	Example GOP (GOP length = 10):
#
#	orig file:
#	gops	chaps			gope
#	I	 v			I
#	21 22 23 24 25 26 27 28 29  30  31 32 33 ...	pts
#
#	temp GOP:
#	0  1  2  3  4  5  6  7  8   9   10 11 12 ...	pts
#	I	 ^			I
#		 chapns			gopl
#
#	gops=secs(arg1)=21, gope=secs(arg2)=31, chaps=secs(arg3)=24
#	gopl=gope-gops=10, chapl=gope-chaps=7, 	chapns=chaps-gops=3

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG mainproc: $# $@" >&2
	$debuglog mainproc: $# $@
fi

local gops='' gope='' dgope='' rgope='' drgope='' chaps=''
local ff='' s='' mc=''
local mfrd=0 i=0 ret=0
#local -a arr
#maxarr="${#arr[@]}"			# get number of elements in arr
#unset arr[@]				# remove the entire array

if [ "$verbose" != no ]; then
	echo "Search end of 1. GOP"
fi
find_gop						# get gope, chaps
ret=$?
if [ "$ret" -eq 0 ]; then				# all values in secs
	Pop chaps
	Pop gope
fi

dchaps=$(secs2HMS $chaps)				# for debug
dgope=$(secs2HMS $gope)					# for debug
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG mainproc: from find_gop - chaps=$chaps  gope=$gope" >&2
	echo "DEBUG mainproc: dchaps=$dchaps  dgope=$dgope" >&2
fi

# cut out part of GOP and store to tmpfa
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG mainproc: cut out part of GOP and store to tmpfa" >&2
fi
if [ "$cmd" == -n ]; then				# inf has no audio
	case "$verbose" in
	    yes)
		echo "Lossless cut out first part of chapter from GOP to A"
		losslesscut -v -n $opt_adur $opt_probs --ss=$chaps --to=$gope $arg1 $tmpfa
		ret=$?
		;;
	    '')
		echo "Lossless cut out first part of chapter from GOP to A"
		losslesscut -n $opt_adur $opt_probs --ss=$chaps --to=$gope $arg1 $tmpfa
		ret=$?
		;;
	    no)
		losslesscut -q -n $opt_adur $opt_probs --ss=$chaps --to=$gope $arg1 $tmpfa
		ret=$?
		;;
	esac
else							# inf has audio
	case "$verbose" in
	    yes)
		echo "Lossless cut out first part of chapter from GOP to A"
		losslesscut -v $opt_adur $opt_probs --ss=$chaps --to=$gope $arg1 $tmpfa
		ret=$?
		;;
	    '')
		echo "Lossless cut out first part of chapter from GOP to A"
		losslesscut $opt_adur $opt_probs --ss=$chaps --to=$gope $arg1 $tmpfa
		ret=$?
		;;
	    no)
		losslesscut -q $opt_adur $opt_probs --ss=$chaps --to=$gope $arg1 $tmpfa
		ret=$?
		;;
	esac
fi

if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG mainproc: losslesscut $opt_adur $opt_probs --ss=$dchaps --to=$dgope <arg1> <tmpfa>" >&2
	echo "DEBUG mainproc: losslesscut ret=$ret" >&2
fi
if [ "$ret" -ne 0 ]; then
	Show_Err $ret "from losslesscut - Abort!"
	return $ret
fi

# cut out rest of chap to tmpfb
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG mainproc: cut out rest of chap and store to tmpfb" >&2
fi
if [ -n "$opt_t" ]; then				# for ffmpeg
	ff="-t $opt_t"
	mc="--t=$opt_t"
fi
if [ -n "$opt_to" ]; then				# for ffmpeg
	ff="-to $opt_to"
	mc="--to=$opt_to"
fi
if [ -z "$opt_c" ]; then				# normal cut
	mfrd=$(X_mul_Y $frd $opt_s)			# num of frd to reduce
	ret=$?
	rgope=$(X_minus_Y "$gope" $mfrd)
	ret=$?
	drgope=$(secs2HMS $rgope)			# for debug
	if [ "$debug" == "yes" ]; then			# DEBUG
		echo "DEBUG mainproc: gope=$gope  dgope=$dgope  rgope=$rgope  drgope=$drgope" >&2
	fi
	case "$verbose" in
	    yes)
		echo "Cut out rest of chapter after gop to B"
		mediacut -v $opt_adur $opt_probs --ss=$rgope $mc $opt_pixfmt $arg1 $tmpfb
		ret=$?
		;;
	    '')
		echo "Cut out rest of chapter after gop to B"
		mediacut $opt_adur $opt_probs --ss=$rgope $mc $opt_pixfmt $arg1 $tmpfb
		#ffmpeg -hide_banner $opt_adur $opt_probs -i $arg1 -ss $gope $ff $opt_pixfmt -filter:v "setpts=PTS-STARTPTS" $tmpfb 2>/dev/null
		ret=$?
		;;
	    no)
		mediacut -q $opt_adur $opt_probs --ss=$rgope $mc $opt_pixfmt $arg1 $tmpfb
		ret=$?
		;;
	esac
	if [ "$debug" == "yes" ]; then			# DEBUG
		echo "DEBUG mainproc: mediacut $opt_adur $opt_probs --ss=$drgope $mc $opt_pixfmt <arg1> <tmpfb>" >&2
		echo "DEBUG mainproc: mediacut ret=$ret" >&2
	fi
	if [ "$ret" -ne 0 ]; then
		Show_Err $ret "Error from mediacut - Abort!"
		return $ret
	fi
else							# recalc pts
	if [ "$cmd" == -n ]; then			# inf has no audio
		case "$verbose" in
		    yes)
			echo "Cut out rest of chapter to B"
			ffmpeg $opt_adur $opt_probs -i $arg1 -ss $rgope $ff $opt_pixfmt -filter:v "setpts=PTS-STARTPTS" $tmpfb
			ret=$?
			;;
		    '')
			echo "Cut out rest of chapter to B"
			ffmpeg -hide_banner -y $opt_adur $opt_probs -i $arg1 -ss $rgope $ff $opt_pixfmt -filter:v "setpts=PTS-STARTPTS" $tmpfb 2>/dev/null
			ret=$?
			;;
		    no)
			ffmpeg -hide_banner -y $opt_adur $opt_probs -i $arg1 -ss $rgope $ff $opt_pixfmt -filter:v "setpts=PTS-STARTPTS" $tmpfb &>/dev/null
			ret=$?
			;;
		esac
	else						# inf has audio
		case "$verbose" in
		    yes)
			echo "Cut out rest of chapter to B"
			ffmpeg $opt_adur $opt_probs -i $arg1 -ss $rgope $ff -acodec copy $opt_pixfmt -filter:v "setpts=PTS-STARTPTS" $tmpfb
			ret=$?
			;;
		    '')
			echo "Cut out rest of chapter to B"
			ffmpeg -hide_banner -y $opt_adur $opt_probs -i $arg1 -ss $rgope $ff -acodec copy $opt_pixfmt -filter:v "setpts=PTS-STARTPTS" $tmpfb 2>/dev/null
			ret=$?
			;;
		    no)
			ffmpeg -hide_banner -y $opt_adur $opt_probs -i $arg1 -ss $rgope $ff -acodec copy $opt_pixfmt -filter:v "setpts=PTS-STARTPTS" $tmpfb &>/dev/null
			ret=$?
			;;
		esac
	fi

	if [ "$debug" == "yes" ]; then			# DEBUG
		echo "DEBUG mainproc: ffmpeg $opt_adur $opt_probs <arg1> -ss=$drgope $ff $opt_pixfmt -filter:v setpts=PTS-STARTPTS <tmpfb>" >&2
		echo "DEBUG mainproc: ffmpeg ret=$ret" >&2
	fi
	if [ "$ret" -ne 0 ]; then
		Show_Err 1 "Error $ret from ffmpeg - Abort!"
		return $ret
	fi
fi

# concat tmpfa + tmpfb and store to arg2
case "$verbose" in
    yes)
	echo "Concat A B"
	mediaconcat -v -o $arg2 $tmpfa $tmpfb
	ret=$?
	;;
    '')
	echo "Concat A B"
	mediaconcat -q -o $arg2 $tmpfa $tmpfb
	ret=$?
	;;
    no)
	mediaconcat -q -o $arg2 $tmpfa $tmpfb
	ret=$?
	;;
esac
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG mainproc: mediaconcat tmpfa tmpfb - ret=$ret" >&2
fi
if [ "$ret" -ne 0 ]; then
	Show_Err $ret "from mediaconcat!"
fi

if [ -z "$opt_k" ]; then
	rm -f $tmpfa
	rm -f $tmpfb
fi

return $ret
}

##############################################################################
# GET OPTIONS
##############################################################################
# Give 2 strings and all cmdline_args to func GetOpt
# (POSIX_options Gnu_long_options cmdline_args):
# GetOpt 'hVdqvi:m:' 'help version debug quiet verbose infile= myval=' $@
#
# A ':' after a POSIX option means this option needs an arg.
# A '=' after a Gnu long option means this option needs an arg.
#
# All cmd/options have to be initialised to ''!
#
# POSIX options are classified in 2 categories:
#   a) cmds (mutually exclusive). All cmds are given to var 'cmd'.
#   b) options (as many as you like). Each option is given to it's own var.
# As a general rule: If cmds/options are given multiple then last given wins.
#
# Options can take 1 argument (no whitespace in arg). If you want to give more
# than 1 arg or want to use multiple strings as 1 arg then concatenate them
# together with a delimiter like ',' or ';' or '.'
# Use a delimiter which will not be part of the arg:
# sampleprog -m val1,val2
# sampleprog --myval=val1,val2
# It's at the programmers responsibility to do meaningfull things with the
# content of the options.
#
# If err != 0 then all args are processed, else continue to look for options.
# When GetOpt finishes then all options are removed from cmdline.
#
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG Parse Commandline: $# $@" >&2
	$debuglog Parse Commandline: $# $@
fi
#
err=${_ERR_OK_}
while [ "$err" -eq 0 ]; do
	GetOpt 'hVdqvcknr:s:' 'help version debug quiet verbose adur= probs= pixfmt= ss= t= to=' $@
	err=$?
	#echo "DEBUG GET OPTIONS: err=${err}  _OPTNAME_=${_OPTNAME_}  _OPTVAL_=${_OPTVAL_}  _OPTPOS_=${_OPTPOS_}  _OPTIDX_=${_OPTIDX_}" >&2
	if [ $err -eq 0 ]; then
		case "$_OPTNAME_" in
		    h|help)	cmd="-h";;
		    V|version)	cmd="-V";;
		    n)		cmd="-${_OPTNAME_}";;
		    d|debug)	debug=yes;;
		    q|quiet)	verbose=no;;
		    v|verbose)	verbose=yes;;
		    c)		opt_c=yes;;
		    k)		opt_k=yes;;
#		    o|output)	opt_o=yes;;
#		    i|infile)	opt_i="${_OPTVAL_}";;
#		    o|outfile)	opt_o="${_OPTVAL_}";;
#		    p|pages)	opt_p="${_OPTVAL_}";;
		    r)		opt_r="${_OPTVAL_}";;
		    s)		opt_s="${_OPTVAL_}";;
		    adur)	opt_adur="--adur=${_OPTVAL_}";;
		    probs)	opt_probs="--probs=${_OPTVAL_}";;
		    pixfmt)	opt_pixfmt="--pixfmt=${_OPTVAL_}";;
		    ss)		opt_ss="${_OPTVAL_}";;
		    t)		opt_t="${_OPTVAL_}";;
		    to)		opt_to="${_OPTVAL_}";;
		    --)		break;;
		    :)	Show_Err ${_ERR_MISSING_ARG_FOR_OPT_} "-${_OPTVAL_}"
			exit ${_ERR_MISSING_ARG_FOR_OPT_};;
		    ?)	Show_Err ${_ERR_UNKNOWN_OPT_} "-${_OPTVAL_}"
			exit ${_ERR_UNKNOWN_OPT_};;
		    *)	Show_Err ${_ERR_UNKNOWN_ERR_} "_OPTNAME_=${_OPTNAME_} _OPTVAL_=${_OPTVAL_}"
			exit ${_ERR_UNKNOWN_ERR_};;
		esac
	fi
done
err=${_ERR_OK_}
shift ${_OPTIDX_}				# rm all opts from cmdline
#
# Uncomment if giving a cmd is mandatory
#if [ -z "$cmd" ]; then					# error no cmd given
#	Show_Err ${_ERR_MISSING_SUB_CMD_} ""
#	exit ${_ERR_MISSING_SUB_CMD_}
#fi

##############################################################################
# CHECK LINK NAMES
##############################################################################
# CHECK OPTIONS
#
#-----------------------------------------------------------------------------
# check not allowed option for sub commands
#case "$cmd" in
#    -l|-p)						# for these cmds
#	if [ -n "$opt_z" ]; then			# option -z not allowed
#		if [ "$verbose" != no ]; then
#			echo "${ErrMsg[${_ERR_OPT_NOT_ALLOWED_}]} -z" >&2
#		else
#			$errlog ${ErrMsg[${_ERR_OPT_NOT_ALLOWED_}]} -z
#		fi
#		exit ${_ERR_OPT_NOT_ALLOWED_}
#	fi
#	;;
#    *)							# for all other cmds
#	:
#	;;
#esac
#
#-----------------------------------------------------------------------------
# check allowed option for sub commands
#if [ -n "$opt_z" ]; then
#	case "$cmd" in
#	    -l|-p)					# for these cmds
#		:					# option -z allowed
#		;;
#	    *)						# for all other cmds
#		if [ "$verbose" != no ]; then
#			echo "${ErrMsg[${_ERR_OPT_NOT_ALLOWED_}]} -z" >&2
#		else
#			$errlog ${ErrMsg[${_ERR_OPT_NOT_ALLOWED_}]} -z
#		fi
#		exit ${_ERR_OPT_NOT_ALLOWED_}
#		;;
#	esac
#fi
#
#-----------------------------------------------------------------------------
# check needed option for sub commands
#case "$cmd" in
#    '')						# for these cmds
#	if [ -z "$opt_x" ]; then			# option needed
#		if [ "$verbose" != no ]; then
#			echo "${ErrMsg[${_ERR_MISSING_ARG_FOR_OPT_}]} -x" >&2
#		else
#			$errlog ${ErrMsg[${_ERR_MISSING_ARG_FOR_OPT_}]} -x
#		fi
#		exit ${_ERR_MISSING_ARG_FOR_OPT_}
#	fi
#	;;
#    *)							# for all other cmds
#	:
#	;;
#esac
#
#-----------------------------------------------------------------------------
# check options which are mutually exclusive
if [ -n "$opt_t" ] && [ -n "$opt_to" ]; then		# don't give both opts
		if [ "$verbose" != no ]; then
			echo "${ErrMsg[${_ERR_OPT_NOT_ALLOWED_}]} -t and -to given" >&2
		else
			$errlog ${ErrMsg[${_ERR_OPT_NOT_ALLOWED_}]} -t and -to given
		fi
		exit ${_ERR_OPT_NOT_ALLOWED_}
fi
#
#-----------------------------------------------------------------------------
# check option which needs another option
#if [ -n "$opt_x" ]; then			# opt_x given
#	if [ -z "$opt_y" ]; then		# error - opt_x needs opt_y
#		if [ "$verbose" != no ]; then
#			echo "${ErrMsg[${_ERR_GENERAL_}]} -x needs -y" >&2
#		else
#			$errlog ${ErrMsg[${_ERR_GENERAL_}]} -x needs -y
#		fi
#		exit ${_ERR_GENERAL_}
#	fi
#fi
#
#-----------------------------------------------------------------------------
# check options which needs to give at least one of them
case "$cmd" in
    ''|-n)
	if [ -z "$opt_t" ] && [ -z "$opt_to" ]; then	# no option given
		if [ "$verbose" != no ]; then
			echo "${ErrMsg[${_ERR_GENERAL_}]} give -t and/or -to" >&2
		else
			$errlog ${ErrMsg[${_ERR_GENERAL_}]} give -t and/or -to
		fi
		exit ${_ERR_GENERAL_}
	fi
esac
#
#-----------------------------------------------------------------------------
# get values from arg of option -p to vars p_first p_last
#if [ -n "$opt_p" ]; then
#	p_first="${opt_p%,*}"
#	p_last="${opt_p#*,}"
#fi
#
##############################################################################
# CHECK VERSION
##############################################################################
# CHECK ARGUMENTS
#
case "$cmd" in
    ''|-n)
	if [ "$#" -eq 2 ]; then				# parameter count ok
		arg1="$1"
		arg2="$2"
		shift 2
		#args="$@"
		#shift $#
	else						# error in param count
		Show_Err ${_ERR_WRONG_PAR_COUNT_} ""
		exit ${_ERR_WRONG_PAR_COUNT_}
	fi
	;;
    *)
	if [ "$#" -ne 0 ]; then				# error in param count
		Show_Err ${_ERR_WRONG_PAR_COUNT_} ""
		exit ${_ERR_WRONG_PAR_COUNT_}
	fi
	;;
esac
#
##############################################################################
# LOCK

##############################################################################
# MAINPROGRAM
##############################################################################
#
if [ "$debug" == "yes" ]; then				# DEBUG
	echo "DEBUG Main: $# $@" >&2
	$debuglog Main: $# $@
fi

if [ -z "$opt_s" ]; then
	opt_s=0
fi

# Check what to do (sub commands, options and args are removed from cmdline)
case "$cmd" in
    ''|-n)
	if [ -z "$opt_r" ]; then
		opt_r=15
	fi

	if [ -z "$opt_ss" ]; then
		opt_ss=0
	fi

	if [ "${arg1##*.}" != mp4 ] || [ "${arg2##*.}" != mp4 ]; then # is not an .mp4
		Show_Err ${_ERR_GENERAL_} "Infile and outfile must be an mpeg4 !"
		exit ${_ERR_GENERAL_}
	fi

	s=y
	if [ -e "$arg2" ]; then
		echo -n "Overwrite file (y/N)? "
		read s
	fi
	if [ "$s" == y ] || [ "$s" == Y ]; then
		mainproc
		err=$?
	fi
	;;
    -h)
	helptext
	exit 0
	;;
    -V)
	echo "${scriptname}: (${package}) $package_file ${version}-${release}"
	exit 0
	;;
    *)							# error
	Show_Err ${_ERR_SUB_CMD_NOT_FOUND_} ""
	err=${_ERR_SUB_CMD_NOT_FOUND_}
	;;
esac

#rm -f $lockfile

exit $err
