MythDVBcut

From MythTV Official Wiki
Jump to: navigation, search

This script is based on one I've been using for several years to process my recordings from dvb-t, and another, less often, for recordings from dvb-t2.

http://www.mythtv.org/wiki/Mythcutprojectx http://www.mythtv.org/wiki/Mythpycutter

This updated script works for me with 0.25-fixes and 0.26-fixes for both dvb-t and dvb-t2. It provides more info about what it has done and, although restricted to cutting at keyframes, usually cuts where I would like it to. It might well be useful for other formats, at least for removing pre-and-post run.

The user interface is largely unchanged; I use it from the keyboard.

The link between the editor-based cutlist and the cutpoints actually used differs slightly from both the earlier versions. I find that edits at the last/first keyframes that show a wanted picture usually give acceptable results. This may well depend on the editing processes at source.

--Johnp 12:08, 8 March 2013 (UTC)

Soon after this was posted, Michael Stucky reported that the call to mplex failed when the script was used on an HD recording from an HDHR device. He gave a workaround and comments that I reproduce here for convenience:

"Further tests indicate that a trial-and-error process is/maybe required for each HD recording (channel??) to get a clean run of mplex. The only way I have been able to get consistent results is to use mythffmpeg to remux into an mkv container.

mythffmpeg -fflags +genpts -i $DEMUXPREF.m2v -i $DEMUXAUDIO -vcodec copy -acodec copy "$OUTFILE"

Where $OUTFILE is the recording name with an .mkv extension. I move the resulting file to my Video storage group, the file is playable on my MythFEs and all my other media players."

Here's the original thread:

http://www.gossamer-threads.com/lists/mythtv/users/540360#540360 --Johnp 20:52, 10 March 2013 (UTC)

Two 'features' have become apparent since the script was first posted:

MythTV 0.26 filenames are based on UTC, while the Mythfrontend display uses local time. I'm still using mental arithmetic to convert, but users not in the UK may find this more inconvenient.

An unannounced change at my local transmitter makes mythffmpeg find multiple 'Video' streams in some DVB-T channels; this causes an error exit. In the 'grep' lines for stream count and video stream selection replace 'mpeg2video' or 'Video' by 'mpeg2video (Main)'.

--Johnp 07:51, 3 July 2013 (UTC)

I'm posting a new version that can create either 'DVD format' or matroska files after cutting by ProjectX. The choice is made by editing the MAKEMKV Boolean variable in the script. Thanks to Michael Stucky for his suggestions.

http://www.gossamer-threads.com/lists/mythtv/users/546679

--Johnp 10:59, 4 July 2013 (UTC)

Script.png mythDVBcut_20130704.sh

#!/bin/bash

# Copyright (C) 2013 John Pilkington 
# Uses ideas from scripts posted by Tino Keitel and Kees Cook in the Mythtv lists.
# The suggestion to convert HD files to .mkv, and code to do it, came from Michael Stucky.

# **** I have not tried running this script on an .mkv file that it has created *****

# Usage: "$0" <recording>   ...or...
# ionice -c3 ./"$0" <recording> will reduce io priority and is recommended.
# <recording> is a file recorded by MythTV with a valid DB entry and seektable.
# e.g. 1234_20100405123400.mpg in one of the defined RECDIRs
# The output file replaces the input file which is renamed to <recording>.old

# This script is essentially a terminal-based replacement for the 'lossless' mpeg2 mythtranscode.
# It was developed from mythcutprojectx but will now cut some recording formats that defeat Project-X.
# All cuts are made at keyframes.  

# Project-X is used here to apply a cutlist, and also to remove all but one video and one audio stream.
# If the audio is initially in .wav format it will be converted to .mp2; mp2 or ac3 will be unchanged.
# For non-mpeg2video all streams are passed through unchanged, but with cuts applied at the video keyframes.  
  
# It then clears the cutlist, updates the filesize in the database, rebuilds the seek table and creates a new preview.

# If the script is edited to have MAKEMKV=false, the result should be acceptable as a recording within MythTV
# and perhaps as an input to MythArchive.  After MAKEMKV=true the file must be treated as a Video.

# The logfile includes the positions in the new file at which deletions have been made. 

# The script needs to be edited to define some local variables and folders.  
# It will not recognise Storage Groups.

# A tarball of the most recent version of Project-X can be downloaded from the link at the bottom of this page:

# http://project-x.cvs.sourceforge.net/viewvc/project-x/Project-X/

# Some of the comments in this script have been left as reminders of things that might be useful.
 
####################

#. ~/.mythtv/mysql.txt # for DB access info
#PASSWD=`grep "^DBPassword" ~/.mythtv/mysql.txt | cut -d '=' -f 2-`
#PASSWD=mythtv

# mysql.txt is no longer referenced in 0.26+
# The values are now defined within ~/.mythtv/config.xml but it seems easier to
# get them into the script by editing.  They are:

# DBUserName, DBPassword, DBLocalHostName, DBName

# Define the variables used for DB access
#
DBUserName="mythtv"
DBPassword="mythtv" 
DBLocalHostName="localhost" 
DBName="mythconverg"

 
BN="$1"     # "Basename" of file as given in the command line but without special attributes.

MAKEMKV=false   # after ProjectX use mplex to create a DVD profile file
# mplex fails with higher bitrates, but mkv can cope.
# MAKEMKV=true     # after ProjectX create an .mkv file

# If TESTRUN is set to true, no changes will be made to the input file.

# TESTRUN=true    # cutlists will be shown but the recording will be unchanged 
TESTRUN=false   # the recording will be processed
if [ $TESTRUN != "false" ] ; then TESTRUN=true
fi
#CUTMODE="FRAMECOUNT"  # Not used here.
CUTMODE="BYTECOUNT"

USEPJX=true  # Will be set to false if not mpeg2video (Main)
TIMEOUT=20   # Longest 'thinking time' in seconds allowed before adopting the automatically selected audio stream.

# Variables RECDIR1, TEMPDIR1, RECDIR2, TEMPDIR2, LOGDIR, PROJECTX, HOST1, HOST2 need to be customised.
# The TEMPDIRs are used to hold the demuxed recording, so should have a few GiB available. 

HOST1="PresV5000"  # laptop with single disk, SL6, MythTV 0.26-fixes FE/BE
HOST2="gateway12"  # twin-disk box, Fedora 17, MythTV 0.26-fixes FE/BE

if [ $HOSTNAME = ${HOST1} ] ; then

   RECDIR1=/home/john/Mythrecs
   TEMPDIR1=/home/john/MythTmp

   RECDIR2=${RECDIR1}
   TEMPDIR2=${TEMPDIR1}

   LOGDIR=/home/john/Logs

   #PROJECTX=/path/to/ProjectX.jar (or to a link to it)
   PROJECTX=~/projectx_link

elif [ $HOSTNAME = ${HOST2} ] ; then

   # Section below for twin-disk setup using Project-X. 

   # RECDIR1 and TEMPDIR1 should if possible be on different drive spindles.  Likewise RECDIR2 and TEMPDIR2.

   RECDIR1=/mnt/f10store/myth/reca
   TEMPDIR1=/mnt/sam1/tempb

   RECDIR2=/mnt/sam1/recb
   TEMPDIR2=/mnt/f10store/myth/tempa

   LOGDIR=/home/John/Documents/PXcutlogs

   #PROJECTX=/path/to/ProjectX.jar (or to a link to it)
   PROJECTX=~/projectx_link

else
   echo "Hostname $HOSTNAME not recognised."
   exit 1
fi 

if [ "$BN" = "-h" ] || [ "$BN" = "--help" ] ; then
echo "Usage: "$0" <recording>"
echo "<recording> is a file recorded by MythTV with a valid DB entry and seektable."
echo "e.g. 1234_20100405123400.mpg in one of the defined RECDIRs"
echo "The output file replaces the input file which is renamed to <recording>.old"
exit 0
fi

# exit if .old file exists

if  [ -f ${RECDIR1}/"$BN".old ] ; then 
    echo " ${RECDIR1}/"$BN".old exists: giving up." ; exit 1
fi

if  [ -f ${RECDIR2}/"$BN".old ] ; then 
    echo " ${RECDIR2}/"$BN".old exists: giving up." ; exit 1
fi
 
# Customize with paths to alternative recording and temp folders

cd ${RECDIR1}
RECDIR=${RECDIR1}
TEMP=${TEMPDIR1}
if  [ ! -f "$BN" ] ; then
  cd ${RECDIR2}
  RECDIR=${RECDIR2}
  TEMP=${TEMPDIR2} 
     if  [ ! -f "$BN" ] ; then 
       echo " "$BN" not found.  Giving up"
       cd ~
       exit 1
     fi
fi

# Is it an mpeg-2 recording?
echo
echo
#mythffmpeg -i "$BN" 2>&1 | grep -C 4 Video | tee "temp$$.txt"
#mythffmpeg -i "$BN" 2>&1 | grep -B 2 -A 4 Video | tee "temp$$.txt"
mythffmpeg -i "$BN" 2>&1 | grep -B 2 -A 4 "mpeg2video (Main)" | tee "temp$$.txt"

mpeg=$(grep -c "mpeg2video (Main)" temp$$.txt) 
echo "mpeg2video (Main) stream count:  $mpeg"
if [ $mpeg != 1 ] ; then 
  echo "Not mpeg2video (Main), or no or multiple video streams"
  if [ $mpeg = 0 ] ; then
     USEPJX=false  # really ought to see if it's a radio channel.
  else
     exit 1
  fi
fi

if [ $# -lt 3  ]
then
   echo "Needs one or three arguments." 
   echo  
   cat temp$$.txt
   echo

#    Examples
#    Stream #0.0[0xe0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 15000 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc
#    Stream #0.1[0xc0](deu): Audio: mp2, 48000 Hz, 2 channels, s16, 256 kb/s

   # Thanks to Christopher Meredith for the basic parsing magic here. 
   VPID=`grep "mpeg2video (Main)"  temp$$.txt | head -n1 | cut -f 1,1 -d']' | sed 's+.*\[++g'`
   # It has to be tweaked for multiple audio streams.  This (with head -n1 ) selects the first listed by ffmpeg.
   # You may alternatively wish to select for language, format, etc.   May be channel, programme, user dependent.
   APID=`grep Audio  temp$$.txt | head -n1 | cut -f 1,1 -d']' | sed 's+.*\[++g'`

   echo -e "Choosing the first audio track listed by \" mythffmpeg -i \".  It may not be the one you want."
   echo -e "\nThe selected values would be "$VPID" and "$APID".  The track info for these is \n"

   grep "$VPID" temp$$.txt
   grep "$APID" temp$$.txt

   echo -e "\nTo accept these values press \"a\", or wait....\n"  
   echo  "If you want to select other values, or quit to think about it, press another key within $TIMEOUT seconds."
   echo -e "If the format is not mpeg2video all streams will be passed unchanged.\n"
 
   read -t $TIMEOUT -n 1 RESP
   if  [ $? -gt 128 ] ; then    
       RESP="a"
   fi

   if [ "$RESP" != "a" ] ; then
       echo -e "Quitting: if you want to select the PIDs from the command line its expected form is   \n"
       echo " "$0" 1234_20070927190000.mpg  0xvvv 0xaaa " 
       echo -e "                    filename_in_DB           vPID  aPID \n" 
       cd ~
       exit 1
   fi

   echo -e "Going on: processing with suggested values $VPID  $APID \n"
   grep "$VPID" temp$$.txt
   grep "$APID" temp$$.txt
   echo
else
   VPID="$2"
   APID="$3"
fi

# Now do the actual processing
# chanid and starttime identify the recording in the DB
chanid=$(echo "select chanid from recorded where basename=\"$BN\";" |
mysql -N -u${DBUserName} -p${DBPassword} -h${DBLocalHostName} $DBName )

starttime=$(echo "select starttime from recorded where basename=\"$BN\";" |
mysql -N -u${DBUserName} -p${DBPassword} -h${DBLocalHostName} $DBName )

#exit
echo -e "\nLogfile listing:\n" > log$$
echo -e "Logfile is log$log$$ \n" | tee -a log$$

echo "chanid ${chanid}   starttime ${starttime} "  >> log$$
starttime=$(echo  ${starttime} | tr -d ': -')

echo "Reformatted starttime ${starttime} " >> log$$

command="mythutil --getcutlist --chanid  $chanid --starttime $starttime -q"
echo "Running: "${command}"" >> log$$
mythutilcutlist=$($(echo ${command}))
echo "${mythutilcutlist}" |tee -a  log$$

shortlist=$(echo ${mythutilcutlist} | sed 's/Cutlist://' )

echo -e "\nIf you want to reset this cutlist, after restoring the .old file 
and resetting the DB with mythsetsize, you could try: \n
mythutil --setcutlist ${shortlist} --chanid  $chanid --starttime $starttime \n
-but seektables from different tools may differ!!" | tee -a log$$

#exit 0

if [ "${mythutilcutlist}" = "Cutlist: " ] ; then
  echo "Cutlist was empty; inserting dummy EOF" >> log$$
  mythutilcutlist=" 9999999 "
fi

echo -e "\nCutframe list from editor: " >> log$$
echo "${mythutilcutlist}" | tr -d [:alpha:] | tr [:punct:] " " | tee  edlist$$ >> log$$

#cat edlist$$ | tee -a log$$
echo

#
#Reverse the sense of the cutlist 
#
echo -n > revedlist$$
for i in  $(cat edlist$$) ; 
do
   if  [ $i -eq 0 ] ;  then
      for j in  $(cat edlist$$) ; 
      do
         if  [ $j != 0 ] ; then echo -n "$j " >> revedlist$$
         fi
      done
   else
      echo -n " 0 " >> revedlist$$
      for j in  $(cat edlist$$) ; 
      do
          echo -n "$j " >> revedlist$$
      done
   fi
   break
done
echo >> revedlist$$
echo -e "\nPassframe (reversed cutframe) list from editor: " >> log$$
cat revedlist$$ | tee -a log$$
 
# For a byte-count cutlist, (PX CutMode=0)
# mark is the frame count in the seektable and is compared here with the editpoint
# mark type 9 is MARK_GOP_BYFRAME (see eg trac ticket #1088) with adjacent values typically separated by 12 or more,
# so that is presumably the cutpoint frame granularity in bytecount mode. 
# Subjectively the granularity seems smaller, but this may not apply to locally encoded recordings; 
# in dvb-t and similar systems the spacing of keyframes apparently depends on pre-transmission edits of content. 
#
# Find the next keyframe (mark type 9) after a position slightly before the cutmark found by the editor.
# The original version, which I have been using for years, effectively had lag=0.

lag=4        #  In frames.  Best value might depend on recording source, MythTV version and seektable history.   
scope=2000   #  Sometimes h264 keyframes in the wild are much more widely spaced than expected.

for frame in $(cat revedlist$$) 
do
    i=$((${frame} - ${lag})) 
    j=$((${i})) 
    k=$((${i} + ${scope}))
    echo  "select offset, mark from recordedseek
    where chanid=$chanid and starttime='$starttime' and type=9 
    and mark >= ${j} and mark < ${k}  order by offset limit 3 ;" |
    mysql -N -u${DBUserName} -p${DBPassword} -h${DBLocalHostName} ${DBName}          
done > tmp0$$

echo "Full results of DB read:"
cat tmp0$$
cat tmp0$$  | sed -n '1,${p;n;n;}' > tmp1$$  # select lines 1,4,7...
cat tmp0$$  | sed -n '2,${p;n;n;}' > tmp2$$
cat tmp0$$  | sed -n '3,${p;n;n;}' > tmp3$$
rm tmp0$$
             
echo

# write the byte offset and frame number into one-line cutlists.

echo -e "\nActive keyframe passlist via DB.  First is a cut-in:" >> log$$
cut -f2 tmp1$$ | tr "\n" " "  | tee -a log$$ > keylist$$
echo >> keylist$$
echo >> log$$

echo -e "\n2nd keyframes, via DB.  For info only." >> log$$
cut -f2 tmp2$$ |  tr "\n" " "  >> log$$
echo >> log$$

echo -e "\n3rd keyframes, via DB.  For info only." >> log$$
cut -f2 tmp3$$ |  tr "\n" " "  >> log$$
echo >> log$$

echo -e "\nByte offsets of switchpoints in original file, via DB.  First is a cut-in:" >> log$$
cut -f1 tmp1$$ |  tr "\n" " "  >> log$$     
echo >> log$$

echo -e "\nByte offsets of 2nd keyframes, via DB.  Compare with PjX log." >> log$$
cut -f1 tmp2$$ |  tr "\n" " "  >> log$$ 
echo >> log$$

mv tmp1$$ tmp$$  # for use

rm  tmp2$$  tmp3$$ 

# With this one-packet-length adjustment in place, the byte-positions that PjX reports 
# having used are exactly equal to those held in the myth DB for 
# the keyframe that next follows the one selected:- i.e. it seems to report the 
# end-of-GOP position when that GOP has been processed. 

# Editing may be frame-accurate if edit-points are the first or last keyframes
# for which a wanted picture is displayed.

FILESIZE=$( du -bL "$BN" | cut -f 1 )

echo -e "\nCreating the cutlist for Project-X with 188 byte adjustment" >> log$$
cut -f1 tmp$$ > PXraw$$
for i in $( cat PXraw$$ ) ;
do 
 echo $(( $i + 188 ))
done > PXadj$$

rm tmp$$

echo "PXraw$$"
cat PXraw$$
echo "EOF"
echo "PXadj$$"
cat PXadj$$
echo "EOF"

# Add a limiting filesize value for use if EOF will be reached
J=$( cat PXraw$$ | wc -l )
echo "PXraw$$ has $J lines"
if [ $(( $J % 2 )) -eq 1 ] ; then
   echo "Adding EOF endmark"
   echo "$FILESIZE" >> PXraw$$
   echo "$FILESIZE" >> PXadj$$
fi

echo "###########  Start of recording " | tee -a log$$
hexdump -C -n 40 -s 0 ${1} | tee -a log$$

echo "###########  PXraw$$ ### " | tee -a log$$
for i in $( cat PXraw$$ ) ;
do 
  echo ${i} | tee -a log$$
  hexdump -C -n 40 -s  $i ${1} | tee -a log$$
done

echo "##########  PXadj$$ ###" | tee -a log$$
for i in $( cat PXadj$$ ) ;
do 
  echo ${i}  | tee -a log$$
  hexdump -C -n 40 -s  $i ${1}  | tee -a log$$
done

echo "CollectionPanel.CutMode=0" > projx$$
cat PXadj$$ >> projx$$

echo
echo >> log$$

cat projx$$ | tee -a log$$

cat PXadj$$ | tr "\n" " " > bytelist$$
echo >> bytelist$$

# Prepare pyscript$$, the script that would run pycut.py
echo -e " #!/bin/bash\n mv  '$RECDIR/$BN' '$RECDIR/$BN.old' "  > pyscript$$
echo -en " ionice -c3 ~/pycut.py '$RECDIR/$BN.old' '$RECDIR/$BN' " >> pyscript$$
cat bytelist$$  >>  pyscript$$  

rm PXraw$$ PXadj$$ 

echo -e "\nSwitchbyte positions in new file:" >> log$$
J=0
S=0                           # 0 or 1 for cut or pass lists
for i in  $(cat bytelist$$) ;  
do 
  if [ $S -eq 0 ] ; then
      J=$((J - i))           
      S=1
  else
      J=$((J + i))
      S=0
      echo -n "$J " >> log$$
   fi
done 
echo >> log$$

echo -e "\nSwitchframe positions in new file:" >> log$$
J=0
S=0                           # 0 or 1 for cut or pass lists
for i in  $(cat keylist$$) ;  
do 
  if [ $S -eq 0 ] ; then
      J=$((J - i))           
      S=1
  else
      J=$((J + i))
      S=0
      echo -n "$J " >> log$$
   fi
done 
echo >> log$$
echo
cat log$$

# create script to be run
echo -e "\nThis is pyscript$$, the script that will be run if TESTRUN is false 
and you are not trying to use PjX instead:\n"
cat pyscript$$
chmod +x pyscript$$
echo -e "\nTo run this use ./pyscript$$. \n"

if $TESTRUN ; then
   echo "Quitting because TESTRUN is ${TESTRUN}"
   rm -f cutlist$$
   rm -f temp$$   
   cd ~
   exit 0
fi

# Now do the actual cutting and concatenation

TEMPHEAD=$TEMP/tempcut${$}
OUTFILE="$BN"

if ${USEPJX} ; then

   #################

   # For mpeg2 format only....
   # use ProjectX to de-multiplex selected streams with the created cutlist

   mv  "$BN" "$BN".old

   CMD="ionice -c3 java -jar "$PROJECTX" -name tempcut$$ -id ${VPID},${APID} \
   -out $TEMP -cut projx$$ "$BN".old"
   echo "running: "${CMD}""
   ${CMD}
   # 

   # if demuxed audio is in .wav format (maybe direct camera output) convert it to mp2
   if [ -f $TEMPHEAD.wav ] ; then
      twolame $TEMPHEAD.wav
   fi

   if [ -f $TEMPHEAD.mp2 ] ; then
       TEMPAUDIO=$TEMPHEAD.mp2
   else
       TEMPAUDIO=$TEMPHEAD.ac3
   fi

   if ! ${MAKEMKV} ; then

     # Now remux.  mplex -f 8, mplex -f 9 both use DVD profile. 
     # mplex -f 8 inserts blank DVD-nav sectors, -f 9 does not.  
     # Neither plays as a 'Recording' via uPnP on my Panasonic TV.

     CMD="ionice -c3  mplex -o "$OUTFILE" -V -f 9 $TEMPHEAD.m2v $TEMPAUDIO" 
     echo "running: ${CMD}"
     ${CMD}
   else
     # Remux to .mkv
     # This does play as a recording via uPnP but won't seek or skip in Mythfrontend.

     OUTFILE=$( echo $BN | sed 's/mpg/mkv/')
     echo "Newfile name will be:  $OUTFILE "
     CMD="ionice -c3 mythffmpeg -fflags +genpts -i $TEMPHEAD.m2v -i $TEMPAUDIO -vcodec copy -acodec copy $OUTFILE " 
     echo "running: ${CMD}"
     ${CMD}

     # tell mythDB about new filename
     echo "update recorded set basename='${OUTFILE}' where chanid=$chanid and starttime='$starttime';" |
     mysql -N -u${DBUserName} -p${DBPassword} -h${DBLocalHostName} ${DBName}
   fi

   CMD="rm  $TEMPHEAD.m2v   $TEMPAUDIO "  # Large; usually best to remove these. 
   echo "running: "${CMD}""
   ${CMD}

   ################

else

   ################
   # Use pyscript, which isn't restricted to mpeg2; but PjX can repair some mpeg2 defects.
   echo "Running : " 
   cat pyscript$$
   echo
   echo
   ./pyscript$$
   echo
   ###############
fi

# Cutting completed.  Now clean up.
CMD="ionice -c3 mythutil  --clearcutlist  --chanid "$chanid" --starttime "$starttime" -q "
echo "running: "${CMD}""
${CMD}
echo -e "Cutlist has been cleared.\n" 

#rebuild seek table; this now also resets filesize in DB 
CMD="ionice -c3 mythcommflag -q --rebuild --file $OUTFILE "
echo "running: "${CMD}""
${CMD}
#echo -e "Seek table has been rebuilt.\n"

#echo  "The cutlist was applied in ** "$CUTMODE" ** mode."
echo -e "Output file is $OUTFILE. \n" 

# Get tech details of output file into the log.
echo -e "\nRunning:  mythffmpeg -i "$OUTFILE" 2>&1 | grep -C 4 Video" | tee -a log$$
echo
mythffmpeg -i "$OUTFILE" 2>&1 | grep -C 4 Video | tee -a log$$
echo

CMD="grep -A 2 Switch log$$ " # put outfile switchpoint info onto terminal
echo "running: "${CMD}""
${CMD}

# The PjX-listed switchpoints are usually 2 frames further apart than those calculated above.
# The editor often sees them about 1 GOP earlier.  Dropped frames and/or seektable problems?  
echo "outfile switchpoints listed by PjX"
grep 'cut-' ${TEMPHEAD}_log.txt 
cat log$$ >> ${TEMPHEAD}_log.txt

echo -e "\nWhile the .old file still exists you can examine it with\n
hexdump -C -n 40 -s {byteoffset} ${RECDIR}/$BN.old\n
On my 32-bit installation, hexdump misinterprets start offsets > 2 GiB.\n "

CMD="mv ${TEMPHEAD}_log.txt  ${LOGDIR}/$OUTFILE.PXcut$$.txt"
echo "running: "${CMD}""
${CMD}

rm bytelist$$
rm edlist$$
rm keylist$$
rm revedlist$$
rm pyscript$$
rm projx$$
rm temp$$.txt
rm log$$ 
cd ~

# mythpreviewgen isn't essential here so put it where failure won't cause other problems.
# Creates a blank frame from .mkv file.

CMD="ionice -c3 mythpreviewgen --chanid "$chanid" --starttime "$starttime" -q "
echo "running: "${CMD}""
${CMD}
echo -e "Preview created.\n"

exit 0


Script.png mythDVBcut_20130305.sh


#!/bin/bash

# Copyright (C) 2013 John Pilkington 
# Uses ideas from scripts posted by Tino Keitel and Kees Cook in the Mythtv lists.

# Usage: "$0" <recording>   ...or...
# ionice -c3 ./"$0" <recording> will reduce io priority and is recommended.
# <recording> is a file recorded by MythTV with a valid DB entry and seektable.
# e.g. 1234_20100405123400.mpg in one of the defined RECDIRs
# The output file replaces the input file which is renamed to <recording>.old

# This script is essentially a terminal-based replacement for the 'lossless' mpeg2 mythtranscode.
# It was developed from mythcutprojectx but will now cut some recording formats that defeat Project-X.
# All cuts are made at keyframes.  

# Project-X is used here to apply a cutlist, and also to remove all but one video and one audio stream.
# If the audio is initially in .wav format it will be converted to .mp2; mp2 or ac3 will be unchanged.
# For non-mpeg2video all streams are passed through unchanged, but with cuts applied at the video keyframes.  
  
# It then clears the cutlist, updates the filesize in the database, rebuilds the seek table and creates a new preview.
# The result should be acceptable as a recording within MythTV and perhaps as an input to MythArchive.
# The logfile includes the positions in the new file at which deletions have been made. 

# The script needs to be edited to define some local variables and folders.  
# It will not recognise Storage Groups.

# A tarball of the most recent version of Project-X can be downloaded from the link at the bottom of this page:

# http://project-x.cvs.sourceforge.net/viewvc/project-x/Project-X/

# Some of the comments in this script have been left as reminders of things that might be useful.
 
####################

#. ~/.mythtv/mysql.txt # for DB access info
#PASSWD=`grep "^DBPassword" ~/.mythtv/mysql.txt | cut -d '=' -f 2-`
#PASSWD=mythtv

# mysql.txt is no longer referenced in 0.26+
# The values are now defined within ~/.mythtv/config.xml but it seems easier to
# get them into the script by editing.  They are:

# DBUserName, DBPassword, DBLocalHostName, DBName

# Define the variables used for DB access
#
DBUserName="mythtv"
DBPassword="mythtv" 
DBLocalHostName="localhost" 
DBName="mythconverg"

# TESTRUN is initially set to true.  No changes will be made to the input file.

TESTRUN=true    # cutlists will be shown but the recording will be unchanged 
#TESTRUN=false   # the recording will be processed
if [ $TESTRUN != "false" ] ; then TESTRUN=true
fi
#CUTMODE="FRAMECOUNT"  # Not used here.
CUTMODE="BYTECOUNT"

USEPJX=true  # Will be set to false if not mpeg2video
TIMEOUT=20   # Longest 'thinking time' in seconds allowed before adopting the automatically selected audio stream.

# Variables RECDIR1, TEMPDIR1, RECDIR2, TEMPDIR2, LOGDIR, PROJECTX, HOST1, HOST2 need to be customised.
# The TEMPDIRs are used to hold the demuxed recording, so should have a few GiB available. 

HOST1="PresV5000"  # laptop with single disk, SL6, MythTV 0.26-fixes FE/BE
HOST2="gateway12"  # twin-disk box, Fedora 16, MythTV 0.25.3 FE/BE

if [ $HOSTNAME = ${HOST1} ] ; then

   RECDIR1=/home/john/Mythrecs
   TEMPDIR1=/home/john/MythTmp

   RECDIR2=${RECDIR1}
   TEMPDIR2=${TEMPDIR1}

   LOGDIR=/home/john/Logs

   #PROJECTX=/path/to/ProjectX.jar (or to a link to it)
   PROJECTX=~/projectx_link

elif [ $HOSTNAME = ${HOST2} ] ; then

   # Section below for twin-disk setup using Project-X. 

   # RECDIR1 and TEMPDIR1 should if possible be on different drive spindles.  Likewise RECDIR2 and TEMPDIR2.

   RECDIR1=/mnt/f10store/myth/reca
   TEMPDIR1=/mnt/sam1/tempb

   RECDIR2=/mnt/sam1/recb
   TEMPDIR2=/mnt/f10store/myth/tempa

   LOGDIR=/home/John/Documents/PXcutlogs

   #PROJECTX=/path/to/ProjectX.jar (or to a link to it)
   PROJECTX=~/projectx_link

else
   echo "Hostname $HOSTNAME not recognised."
   exit 1
fi 

if [ "$1" = "-h" ] || [ "$1" = "--help" ] ; then
echo "Usage: "$0" <recording>"
echo "<recording> is a file recorded by MythTV with a valid DB entry and seektable."
echo "e.g. 1234_20100405123400.mpg in one of the defined RECDIRs"
echo "The output file replaces the input file which is renamed to <recording>.old"
exit 0
fi

# exit if .old file exists

if  [ -f ${RECDIR1}/"$1".old ] ; then 
    echo " ${RECDIR1}/"$1".old exists: giving up." ; exit 1
fi

if  [ -f ${RECDIR2}/"$1".old ] ; then 
    echo " ${RECDIR2}/"$1".old exists: giving up." ; exit 1
fi
 
# Customize with paths to alternative recording and temp folders

cd ${RECDIR1}
RECDIR=${RECDIR1}
TEMP=${TEMPDIR1}
if  [ ! -f "$1" ] ; then
  cd ${RECDIR2}
  RECDIR=${RECDIR2}
  TEMP=${TEMPDIR2} 
     if  [ ! -f "$1" ] ; then 
       echo " "$1" not found.  Giving up"
       cd ~
       exit 1
     fi
fi

# Is it an mpeg-2 recording?
echo
echo
mythffmpeg -i "$1" 2>&1 | grep -C 4 Video | tee "temp$$.txt"

mpeg=$(grep -c "mpeg2video" temp$$.txt) 
echo "mpeg2video stream count:  $mpeg"
if [ $mpeg != 1 ] ; then 
  echo "Not mpeg2, or no or multiple video streams"
  if [ $mpeg = 0 ] ; then
     USEPJX=false  # really ought to see if it's a radio channel.
  else
     exit 1
  fi
fi

if [ $# -lt 3  ]
then
   echo "Needs one or three arguments." 
   echo  
   cat temp$$.txt
   echo

#    Examples
#    Stream #0.0[0xe0]: Video: mpeg2video, yuv420p, 720x576 [PAR 64:45 DAR 16:9], 15000 kb/s, 25 fps, 25 tbr, 90k tbn, 50 tbc
#    Stream #0.1[0xc0](deu): Audio: mp2, 48000 Hz, 2 channels, s16, 256 kb/s

   # Thanks to Christopher Meredith for the basic parsing magic here. 
   VPID=`grep Video  temp$$.txt | head -n1 | cut -f 1,1 -d']' | sed 's+.*\[++g'`
   # It has to be tweaked for multiple audio streams.  This (with head -n1 ) selects the first listed by ffmpeg.
   # You may alternatively wish to select for language, format, etc.   May be channel, programme, user dependent.
   APID=`grep Audio  temp$$.txt | head -n1 | cut -f 1,1 -d']' | sed 's+.*\[++g'`

   echo -e "Choosing the first audio track listed by \" mythffmpeg -i \".  It may not be the one you want."
   echo -e "\nThe selected values would be "$VPID" and "$APID".  The track info for these is \n"

   grep "$VPID" temp$$.txt
   grep "$APID" temp$$.txt

   echo -e "\nTo accept these values press \"a\", or wait....\n"  
   echo  "If you want to select other values, or quit to think about it, press another key within $TIMEOUT seconds."
   echo -e "If the format is not mpeg2video all streams will be passed unchanged.\n"
 
   read -t $TIMEOUT -n 1 RESP
   if  [ $? -gt 128 ] ; then    
       RESP="a"
   fi

   if [ "$RESP" != "a" ] ; then
       echo -e "Quitting: if you want to select the PIDs from the command line its expected form is   \n"
       echo " "$0" 1234_20070927190000.mpg  0xvvv 0xaaa " 
       echo -e "                    filename_in_DB           vPID  aPID \n" 
       cd ~
       exit 1
   fi

   echo -e "Going on: processing with suggested values $VPID  $APID \n"
   grep "$VPID" temp$$.txt
   grep "$APID" temp$$.txt
   echo
else
   VPID="$2"
   APID="$3"
fi

# Now do the actual processing
# chanid and starttime identify the recording in the DB
chanid=$(echo "select chanid from recorded where basename=\"$1\";" |
mysql -N -u${DBUserName} -p${DBPassword} -h${DBLocalHostName} $DBName )

starttime=$(echo "select starttime from recorded where basename=\"$1\";" |
mysql -N -u${DBUserName} -p${DBPassword} -h${DBLocalHostName} $DBName )

#exit
echo -e "\nLogfile listing:\n" > log$$
echo -e "Logfile is log$log$$ \n" | tee -a log$$

echo "chanid ${chanid}   starttime ${starttime} "  >> log$$
starttime=$(echo  ${starttime} | tr -d ': -')

echo "Reformatted starttime ${starttime} " >> log$$

command="mythutil --getcutlist --chanid  $chanid --starttime $starttime -q"
echo "Running: "${command}"" >> log$$
mythutilcutlist=$($(echo ${command}))
echo "${mythutilcutlist}" |tee -a  log$$

shortlist=$(echo ${mythutilcutlist} | sed 's/Cutlist://' )

echo -e "\nIf you want to reset this cutlist, after restoring the .old file 
and resetting the DB with mythsetsize, you could try: \n
mythutil --setcutlist ${shortlist} --chanid  $chanid --starttime $starttime \n
-but seektables from different tools may differ!!" | tee -a log$$

#exit 0

if [ "${mythutilcutlist}" = "Cutlist: " ] ; then
  echo "Cutlist was empty; inserting dummy EOF" >> log$$
  mythutilcutlist=" 9999999 "
fi

echo -e "\nCutframe list from editor: " >> log$$
echo "${mythutilcutlist}" | tr -d [:alpha:] | tr [:punct:] " " | tee  edlist$$ >> log$$

#cat edlist$$ | tee -a log$$
echo

#
#Reverse the sense of the cutlist 
#
echo -n > revedlist$$
for i in  $(cat edlist$$) ; 
do
   if  [ $i -eq 0 ] ;  then
      for j in  $(cat edlist$$) ; 
      do
         if  [ $j != 0 ] ; then echo -n "$j " >> revedlist$$
         fi
      done
   else
      echo -n " 0 " >> revedlist$$
      for j in  $(cat edlist$$) ; 
      do
          echo -n "$j " >> revedlist$$
      done
   fi
   break
done
echo >> revedlist$$
echo -e "\nPassframe (reversed cutframe) list from editor: " >> log$$
cat revedlist$$ | tee -a log$$
 
# For a byte-count cutlist, (PX CutMode=0)
# mark is the frame count in the seektable and is compared here with the editpoint
# mark type 9 is MARK_GOP_BYFRAME (see eg trac ticket #1088) with adjacent values typically separated by 12 or more,
# so that is presumably the cutpoint frame granularity in bytecount mode. 
# Subjectively the granularity seems smaller, but this may not apply to locally encoded recordings; 
# in dvb-t and similar systems the spacing of keyframes apparently depends on pre-transmission edits of content. 
#
# Find the next keyframe (mark type 9) after a position slightly before the cutmark found by the editor.
# The original version, which I have been using for years, effectively had lag=0.

lag=4        #  In frames.  Best value might depend on recording source, MythTV version and seektable history.   
scope=2000   #  Sometimes h264 keyframes in the wild are much more widely spaced than expected.

for frame in $(cat revedlist$$) 
do
    i=$((${frame} - ${lag})) 
    j=$((${i})) 
    k=$((${i} + ${scope}))
    echo  "select offset, mark from recordedseek
    where chanid=$chanid and starttime='$starttime' and type=9 
    and mark >= ${j} and mark < ${k}  order by offset limit 3 ;" |
    mysql -N -u${DBUserName} -p${DBPassword} -h${DBLocalHostName} ${DBName}          
done > tmp0$$

echo "Full results of DB read:"
cat tmp0$$
cat tmp0$$  | sed -n '1,${p;n;n;}' > tmp1$$  # select lines 1,4,7...
cat tmp0$$  | sed -n '2,${p;n;n;}' > tmp2$$
cat tmp0$$  | sed -n '3,${p;n;n;}' > tmp3$$
rm tmp0$$
             
echo

# write the byte offset and frame number into one-line cutlists.

echo -e "\nActive keyframe passlist via DB.  First is a cut-in:" >> log$$
cut -f2 tmp1$$ | tr "\n" " "  | tee -a log$$ > keylist$$
echo >> keylist$$
echo >> log$$

echo -e "\n2nd keyframes, via DB.  For info only." >> log$$
cut -f2 tmp2$$ |  tr "\n" " "  >> log$$
echo >> log$$

echo -e "\n3rd keyframes, via DB.  For info only." >> log$$
cut -f2 tmp3$$ |  tr "\n" " "  >> log$$
echo >> log$$

echo -e "\nByte offsets of switchpoints in original file, via DB.  First is a cut-in:" >> log$$
cut -f1 tmp1$$ |  tr "\n" " "  >> log$$     
echo >> log$$

echo -e "\nByte offsets of 2nd keyframes, via DB.  Compare with PjX log." >> log$$
cut -f1 tmp2$$ |  tr "\n" " "  >> log$$ 
echo >> log$$

mv tmp1$$ tmp$$  # for use

rm  tmp2$$  tmp3$$ 

# With this one-packet-length adjustment in place, the byte-positions that PjX reports 
# having used are exactly equal to those held in the myth DB for 
# the keyframe that next follows the one selected:- i.e. it seems to report the 
# end-of-GOP position when that GOP has been processed. 

# Editing may be frame-accurate if edit-points are the first or last keyframes
# for which a wanted picture is displayed.

FILESIZE=$( du -bL "$1" | cut -f 1 )

echo -e "\nCreating the cutlist for Project-X with 188 byte adjustment" >> log$$
cut -f1 tmp$$ > PXraw$$
for i in $( cat PXraw$$ ) ;
do 
 echo $(( $i + 188 ))
done > PXadj$$

rm tmp$$

echo "PXraw$$"
cat PXraw$$
echo "EOF"
echo "PXadj$$"
cat PXadj$$
echo "EOF"

# Add a limiting filesize value for use if EOF will be reached
J=$( cat PXraw$$ | wc -l )
echo "PXraw$$ has $J lines"
if [ $(( $J % 2 )) -eq 1 ] ; then
   echo "Adding EOF endmark"
   echo "$FILESIZE" >> PXraw$$
   echo "$FILESIZE" >> PXadj$$
fi

echo "###########  Start of recording " | tee -a log$$
hexdump -C -n 40 -s 0 ${1} | tee -a log$$

echo "###########  PXraw$$ ### " | tee -a log$$
for i in $( cat PXraw$$ ) ;
do 
  echo ${i} | tee -a log$$
  hexdump -C -n 40 -s  $i ${1} | tee -a log$$
done

echo "##########  PXadj$$ ###" | tee -a log$$
for i in $( cat PXadj$$ ) ;
do 
  echo ${i}  | tee -a log$$
  hexdump -C -n 40 -s  $i ${1}  | tee -a log$$
done

echo "CollectionPanel.CutMode=0" > projx$$
cat PXadj$$ >> projx$$

echo
echo >> log$$

cat projx$$ | tee -a log$$

cat PXadj$$ | tr "\n" " " > bytelist$$
echo >> bytelist$$

# Prepare pyscript$$, the script that would run pycut.py
echo -e " #!/bin/bash\n mv  '$RECDIR/$1' '$RECDIR/$1.old' "  > pyscript$$
echo -en " ionice -c3 ~/pycut.py '$RECDIR/$1.old' '$RECDIR/$1' " >> pyscript$$
cat bytelist$$  >>  pyscript$$  

rm PXraw$$ PXadj$$ 

echo -e "\nSwitchbyte positions in new file:" >> log$$
J=0
S=0                           # 0 or 1 for cut or pass lists
for i in  $(cat bytelist$$) ;  
do 
  if [ $S -eq 0 ] ; then
      J=$((J - i))           
      S=1
  else
      J=$((J + i))
      S=0
      echo -n "$J " >> log$$
   fi
done 
echo >> log$$

echo -e "\nSwitchframe positions in new file:" >> log$$
J=0
S=0                           # 0 or 1 for cut or pass lists
for i in  $(cat keylist$$) ;  
do 
  if [ $S -eq 0 ] ; then
      J=$((J - i))           
      S=1
  else
      J=$((J + i))
      S=0
      echo -n "$J " >> log$$
   fi
done 
echo >> log$$
echo
cat log$$

# create script to be run
echo -e "\nThis is pyscript$$, the script that will be run if TESTRUN is false 
and you are not trying to use PjX instead:\n"
cat pyscript$$
chmod +x pyscript$$
echo -e "\nTo run this use ./pyscript$$. \n"

if $TESTRUN ; then
   echo "Quitting because TESTRUN is ${TESTRUN}"
   rm -f cutlist$$
   rm -f temp$$   
   cd ~
   exit 0
fi

# Now do the actual cutting and concatenation

TEMPHEAD=$TEMP/tempcut${$}

if ${USEPJX} ; then

   #################

   # For mpeg2 format only....
   # use ProjectX to de-multiplex selected streams with the created cutlist

   mv  "$1" "$1".old
   
   CMD="ionice -c3 java -jar "$PROJECTX" -name tempcut$$ -id ${VPID},${APID} \
   -out $TEMP -cut projx$$ "$1".old"

   echo "running: "${CMD}""
   ${CMD}
   # 

   # if demuxed audio is in .wav format (maybe direct camera output) convert it to mp2
   if [ -f $TEMPHEAD.wav ] ; then
      twolame $TEMPHEAD.wav
   fi

   if [ -f $TEMPHEAD.mp2 ] ; then
       TEMPAUDIO=$TEMPHEAD.mp2
   else
       TEMPAUDIO=$TEMPHEAD.ac3
   fi

   # Now remux.  mplex -f 8, mplex -f 9 both use DVD profile. 
   # mplex -f 8 inserts blank DVD-nav sectors, -f 9 does not.  
   # Neither plays as a 'Recording' via uPnP on my Panasonic TV.

   CMD="ionice -c3  mplex -o "$1" -V -f 9 $TEMPHEAD.m2v $TEMPAUDIO" 
   echo "running: ${CMD}"
   ${CMD}

   CMD="rm  $TEMPHEAD.m2v   $TEMPAUDIO "  # Large; usually best to remove these. 
   echo "running: "${CMD}""
   ${CMD}

   ################

else

   ################
   # Use pyscript, which isn't restricted to mpeg2; but PjX can repair some mpeg2 defects.
   echo "Running : " 
   cat pyscript$$
   echo
   echo
   ./pyscript$$
   echo
   ###############
fi

# Cutting completed.  Now clean up.
CMD="ionice -c3 mythutil  --clearcutlist  --chanid "$chanid" --starttime "$starttime" -q "
echo "running: "${CMD}""
${CMD}
echo -e "Cutlist has been cleared.\n" 

#rebuild seek table; this now also resets filesize in DB 
CMD="ionice -c3 mythcommflag -q --rebuild --file $1 "
echo "running: "${CMD}""
${CMD}
#echo -e "Seek table has been rebuilt.\n"

#echo  "The cutlist was applied in ** "$CUTMODE" ** mode."
echo -e "Output file is $1. \n" 

# Get tech details of output file into the log.
echo -e "\nRunning:  mythffmpeg -i "$1" 2>&1 | grep -C 4 Video" | tee -a log$$
echo
mythffmpeg -i "$1" 2>&1 | grep -C 4 Video | tee -a log$$
echo

CMD="grep -A 2 Switch log$$ " # put outfile switchpoint info onto terminal
echo "running: "${CMD}""
${CMD}

# The PjX-listed switchpoints are usually 2 frames further apart than those calculated above.
# The editor often sees them about 1 GOP earlier.  Dropped frames and/or seektable problems?  
echo "outfile switchpoints listed by PjX"
grep 'cut-' ${TEMPHEAD}_log.txt 
cat log$$ >> ${TEMPHEAD}_log.txt

echo -e "\nWhile the .old file still exists you can examine it with\n
hexdump -C -n 40 -s {byteoffset} ${RECDIR}/$1.old\n
On my 32-bit installation, hexdump truncates start offsets > 2 GiB.\n "

CMD="mv ${TEMPHEAD}_log.txt  ${LOGDIR}/$1.PXcut$$.txt"
echo "running: "${CMD}""
${CMD}

rm bytelist$$
rm edlist$$
rm keylist$$
rm revedlist$$
rm pyscript$$
rm projx$$
rm temp$$.txt
rm log$$ 
cd ~

# mythpreviewgen isn't essential here so put it where failure won't cause other problems.
CMD="ionice -c3 mythpreviewgen --chanid "$chanid" --starttime "$starttime" -q "
echo "running: "${CMD}""
${CMD}
echo -e "Preview created.\n"

exit 0




Script.png pycut_20130305.py


#! /usr/bin/env python
# -*- coding: utf-8 -*-

# Cut out and concatenate sections of a file
# access as pycut.py from mythDVBcut.sh

import sys, os
#print sys.argv


######################
## For tests
##
## echo "0123456789A123456789B123456789C123456789D123456789E123456789F123456789" > ~/test.txt 
## 
## fn1 = './test.txt'
## fn2 = './temp.txt'
## chunks = [ 3, 12, 35, 47, 53, 68  ]
## buflen = 5

## Doesn't recognise '~/test.txt' but this, or full path, seems ok
## python pycut.py './test.txt' './temp.txt' 3 12 35 47 53 68  

# Zambesi HD

#./printcutlist /home/john/Mythrecs/1054_20120328222600.mpg
# Generates byte-mode cutlist for use with Project-X  - and here
#CollectionPanel.CutMode=0

#fn1 = '/mnt/f10store/myth/reca/1054_20120323002600old.mpg'
#fn2 = '/mnt/sam1/recb/1054_20120323002600.mpg'
#chunks = [ 390284804, 4556742872 ]
#buflen = 1024*1024
#
########################

fn1 = sys.argv[1]  # input file
fn2 = sys.argv[2]  # output file
chunks = map( int, sys.argv [ 3 : ] )  # start and end bytes of chunks in infile
buflen = 1024*1024
#bignum = 10000000000                   # for use as EOF if needed
# less likely to be surprised if we use the actual filesize here

print "infile        ", fn1
print "outfile       ", fn2
print "switchpoints  ", chunks

#######################

# sanity checks

chunklen = len(chunks)
if chunklen != 2 * ( chunklen / 2 ) :
#    chunks.append(bignum)
    chunks.append( 1 + os.path.getsize(fn1))
    chunklen = len(chunks)

# adjust chunk-endpoints in the hope of keeping chain linkage in the data intact
n = 1
while n < chunklen :
  chunks[n] += -1
  n += 2
  
n=0
while n < chunklen - 2 :
   if chunks[n] > chunks[n+1] :
      print "Quitting: switchpoints out of order"
      sys.exit(98)
   n += 1

print "Adjusted switchpoints  ", chunks

n = 0
m = 0
offset = [ 0 ]
while n < chunklen - 1 :
   m += 1 + chunks[ n+1 ] - chunks[ n ]
   offset.append( m )
   n += 2

print
print "Byte offsets of cutpoints in output file: ",  offset
print "DB table is recordedseek, mark (framecount) is type 9."
##################################
# Don't touch stuff below here 
## byte numbering starts at 0 and output includes both chunk-endpoints
i=0
j=0
imax = 40     # buffers per star
jmax = 25     # stars per line 
print         # for progress display

chnklim = len(chunks) - 1 
nchnk = 0
chstart=chunks[nchnk]
chend=chunks[nchnk + 1]
bufstart = 0

f1 = open(fn1, 'rb')
f2 = open(fn2, 'wb')

while True :
  data = f1.read(buflen)
  lendat = len(data)
  if lendat == 0 :
       break
  bufend = bufstart + lendat 
  while chstart < bufend :
       if chend <  bufend :
           f2.write(data[chstart - bufstart : chend - bufstart + 1 ])
           nchnk += 2
           if nchnk > chnklim :             # job done
               chstart = bufend + buflen*2  # kill further looping
               break
           
           chstart = chunks[nchnk]
           chend   = chunks[nchnk + 1]
       else :
           f2.write(data[chstart - bufstart :  ])
           chstart = bufend 
 
  bufstart += lendat
  i += 1           # progress display          
  if i > imax :
     sys.stdout.write("*")
     sys.stdout.flush()
     i = 0
     j += 1
     if j > jmax :
        print
        j = 0
 
f1.close()
f2.close()
print