/*
 * @(#)CommonParsing
 *
 * Copyright (c) 2005 by dvb.matt, All Rights Reserved.
 * 
 * This file is part of ProjectX, a free Java based demux utility.
 * By the authors, ProjectX is intended for educational purposes only, 
 * as a non-commercial test project.
 * 
 *
 * 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
 *
 */

package net.sourceforge.dvb.projectx.parser;

import java.io.IOException;
import java.io.File;
import java.io.RandomAccessFile;
import java.io.PrintWriter;
import java.io.FileOutputStream;

import java.util.Calendar;
import java.util.TimeZone;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.ArrayList;
import java.util.List;

import java.text.DateFormat;
import java.text.SimpleDateFormat;

import net.sourceforge.dvb.projectx.common.Resource;
import net.sourceforge.dvb.projectx.common.Settings;
import net.sourceforge.dvb.projectx.common.Common;
import net.sourceforge.dvb.projectx.common.Keys;
import net.sourceforge.dvb.projectx.common.JobProcessing;


/**
 * common stuff for parsing functions
 */
public class CommonParsing extends Object {

	public final static boolean BYTEREORDERING = true;

	public final static int SECONDARY_PES_PARSER = 0;
	public final static int PRIMARY_PES_PARSER   = 1;
	public final static int TS_PARSER            = 2;
	public final static int PVA_PARSER           = 3;
	public final static int ES_VIDEO_PARSER      = 4;
	public final static int ES_AUDIO_PARSER      = 5;
	public final static int ES_SUBPICTURE_PARSER = 6;

	public final static int Unsupported   = 0;	//	"unsupported"
	public final static int PES_AV_TYPE   = 1;	//	"PES (Video/Audio/TTX)"
	public final static int MPEG1PS_TYPE  = 2;	//	"MPEG-1 PS/SS (Video/Audio PES)"
	public final static int MPEG2PS_TYPE  = 3;	//	"MPEG-2 PS/SS (Video/Audio PES)"
	public final static int PVA_TYPE      = 4;	//	"PVA (Video/Audio PES)"
	public final static int TS_TYPE       = 5;	//	"DVB/MPEG2 TS"
	public final static int PES_MPA_TYPE  = 6;	//	"PES (MPEG Audio)"
	public final static int PES_PS1_TYPE  = 7;	//	"PES (private stream 1)"
	public final static int ES_MPV_TYPE   = 8;	//	"ES (MPEG Video)"
	public final static int ES_MPA_TYPE   = 9;	//	"ES (MPEG Audio)"
	public final static int ES_AC3_TYPE   = 10;	//	"ES (AC-3 Audio)"
	public final static int ES_AC3_A_TYPE = 11;	//	"ES (AC-3 Audio) (psb. SMPTE)"
	public final static int ES_DTS_TYPE   = 12;	//	"ES (DTS Audio)"
	public final static int ES_DTS_A_TYPE = 13;	//	"ES (DTS Audio) (psb. SMPTE)"
	public final static int ES_RIFF_TYPE  = 14;	//	"ES (RIFF Audio)"
	public final static int ES_cRIFF_TYPE = 15;	//	"ES (compressed RIFF Audio)"
	public final static int ES_SUP_TYPE   = 16;	//	"ES (Subpicture 2-bit RLE)"

	public final static int ES_TYPE   = 1;	//	"ElementaryStream"

	//	"ElementaryStream"
	public final static int UNKNOWN   = -1;
	public final static int AC3_AUDIO  = 0;
	public final static int TELETEXT   = 1;
	public final static int MPEG_AUDIO = 2;
	public final static int MPEG_VIDEO = 3;
	public final static int LPCM_AUDIO = 4;
	public final static int SUBPICTURE = 5;
	public final static int DTS_AUDIO  = 6;	//handled with in AC3_AUDIO so far
	public final static int WAV_AUDIO  = 7;	

	public final static int ACTION_DEMUX  = 0;	
	public final static int ACTION_TO_VDR = 1;	
	public final static int ACTION_TO_M2P = 2;	
	public final static int ACTION_TO_PVA = 3;	
	public final static int ACTION_TO_TS  = 4;	
	public final static int ACTION_FILTER = 5;	

	public final static int CUTMODE_BYTE  = 0;	
	public final static int CUTMODE_GOP   = 1;	
	public final static int CUTMODE_FRAME = 2;	
	public final static int CUTMODE_PTS   = 3;	
	public final static int CUTMODE_TIME  = 4;	

	public final static int PICTURE_START_CODE     =    0;
	public final static int SLICE_START_CODE_MIN   =    1;
	public final static int SLICE_START_CODE_MAX   = 0xAF;
	public final static int USER_DATA_START_CODE   = 0xB2;
	public final static int SEQUENCE_HEADER_CODE   = 0xB3;
	public final static int EXTENSION_START_CODE   = 0xB5;
	public final static int SEQUENCE_END_CODE      = 0xB7;
	public final static int GROUP_START_CODE       = 0xB8;
	public final static int SYSTEM_END_CODE        = 0xB9;
	public final static int PACK_START_CODE        = 0xBA;
	public final static int SYSTEM_START_CODE      = 0xBB;
	public final static int PROGRAM_STREAM_MAP_CODE= 0xBC;
	public final static int PRIVATE_STREAM_1_CODE  = 0xBD;
	public final static int PADDING_STREAM_CODE    = 0xBE;
	public final static int PRIVATE_STREAM_2_CODE  = 0xBF;
	public final static int ECM_STREAM_CODE        = 0xF0;
	public final static int EMM_STREAM_CODE        = 0xF1;
	public final static int DSM_CC_STREAM_CODE     = 0xF2;

	public final static int FRAME_I_TYPE = 1;
	public final static int FRAME_P_TYPE = 2;
	public final static int FRAME_B_TYPE = 3;
	public final static int FRAME_D_TYPE = 4;

	public final static int MAX_BITRATE_VALUE = 262143; //3FFFF *400 = 104857200 bps

	private static int Pva_PidToExtract = -1;

	private static boolean Pva_PidExtraction = false;

	private static boolean InfoScan = false;
	private static boolean qbreak = false;
	private static boolean qpause = false;

	private static boolean _bool = false;

	private static int _cutcount = 0;

	private static double _videoframerate = 3600.0;

	private static long AudioProcessingFlags = 0;

	private CommonParsing()
	{}

	/**
	 *
	 */
	public static boolean isInfoScan()
	{
		return InfoScan;
	}

	/**
	 *
	 */
	public static void setInfoScan(boolean b)
	{
		InfoScan = b;
	}

	/**
	 *
	 */
	public static boolean isProcessCancelled()
	{
		return qbreak;
	}

	/**
	 *
	 */
	public static void setProcessCancelled(boolean b)
	{
		qbreak = b;
	}

	/**
	 *
	 */
	public static boolean isProcessPausing()
	{
		return qpause;
	}

	/**
	 *
	 */
	public static void setProcessPausing(boolean b)
	{
		qpause = b;
	}

	/**
	 *
	 */
	public static long getAudioProcessingFlags()
	{
		return AudioProcessingFlags;
	}

	/**
	 *
	 */
	public static void setAudioProcessingFlags(long val)
	{
		AudioProcessingFlags = val;
	}

	/**
	 *
	 */
	public static double getVideoFramerate()
	{
		return _videoframerate;
	}

	/**
	 *
	 */
	public static void setVideoFramerate(double val)
	{
		_videoframerate = val;
	}

	/**
	 *
	 */
	public static int getCutCounter()
	{
		return _cutcount;
	}

	/**
	 *
	 */
	public static void setCutCounter(int val)
	{
		_cutcount = val;
	}

	/**
	 *
	 */
	public static boolean getCutStatus()
	{
		return _bool;
	}

	/**
	 *
	 */
	public static void setCutStatus(boolean b)
	{
		_bool = b;
	}

	/**
	 *
	 */
	public static boolean getPvaPidExtraction()
	{
		return Pva_PidExtraction;
	}

	/**
	 *
	 */
	public static void setPvaPidExtraction(boolean b)
	{
		Pva_PidExtraction = b;
	}

	/**
	 *
	 */
	public static int getPvaPidToExtract()
	{
		return Pva_PidToExtract;
	}

	/**
	 *
	 */
	public static void setPvaPidToExtract(int val)
	{
		Pva_PidToExtract = val;
	}


	/**
	 * returns pts value from pes_extension
	 *
	 * @param1 - source array
	 * @param2 - array offset
	 * @return - pts
	 */
	public static long getPTSfromBytes(byte[] array, int offset)
	{
		return getPTSfromBytes(array, offset, true);
	}

	/**
	 * returns pts value from pes_extension
	 *
	 * @param1 - source array
	 * @param2 - array offset
	 * @param3 - trim to positive 32bit value
	 * @return - pts
	 */
	public static long getPTSfromBytes(byte[] array, int offset, boolean trim)
	{
		long pts = (6 & array[offset])<<29 | 
			(0xFF & array[offset + 1])<<22 | 
			(0xFE & array[offset + 2])<<14 |
			(0xFF & array[offset + 3])<<7 | 
			(0xFE & array[offset + 4])>>>1;

		if (trim)
			pts &= 0xFFFFFFFFL;

		return pts;
	}

	/**
	 * returns pts value from pes_extension
	 *
	 * @param1 - source array
	 * @param2 - array offset
	 * @param3 - trim to positive 32bit value
	 * @return - pts
	 */
	public static long readPTS(byte[] array, int offset, int length, boolean bytereordering, boolean trim)
	{
		long value = getValue(array, offset, length, bytereordering);

		if (trim)
			value &= 0xFFFFFFFFL;

		return value;
	}

	/**
	 *
	 */
	public static void setValue(byte[] array, int offset, int length, boolean bytereordering, long value)
	{
		for (int i = 0; bytereordering && i < length; i++)
			array[i + offset] = (byte)(0xFFL & (value>>>(i * 8)));

		for (int i = 0, j = length - 1; !bytereordering && i < length; i++, j--)
			array[i + offset] = (byte)(0xFFL & (value>>>(j * 8)));
	}

	/**
	 * returns value
	 *
	 * @param1 - source array
	 * @param2 - array offset
	 * @param3 - 
	 * @return - 
	 */
	public static long getValue(byte[] array, int offset, int length, boolean bytereordering)
	{
		long value = 0;

		for (int i = 0; bytereordering && i < length; i++)
			value |= (0xFFL & array[i + offset])<<(i * 8);

		for (int i = 0, j = length - 1; !bytereordering && i < length; i++, j--)
			value |= (0xFFL & array[i + offset])<<(j * 8);

		return value;
	}

	public static int getIntValue(byte[] array, int offset, int length, boolean bytereordering)
	{
		return ((int) getValue(array, offset, length, bytereordering));
	}

	/**
	 * 
	 */
	public static void setPES_PTSField(byte[] array, int offset, long value)
	{
		array[9 + offset]  = (byte)(0x21 | (0xE & (value>>>29)));
		array[10 + offset] = (byte)(0xFF & (value>>>22));
		array[11 + offset] = (byte)(1 | (0xFE & (value>>>14)));
		array[12 + offset] = (byte)(0xFF & (value>>>7));
		array[13 + offset] = (byte)(1 | (0xFE & (value<<1)));
	}

	/**
	 * 
	 */
	public static void setPES_LengthField(byte[] array, int offset, int value)
	{
		array[4 + offset] = (byte)(0xFF & value>>>8);
		array[5 + offset] = (byte)(0xFF & value);
	}

	/**
	 * 
	 */
	public static void setPES_IdField(byte[] array, int offset, int value)
	{
		array[3 + offset] = (byte)(0xFF & value);
	}

	/**
	 * 
	 */
	public static void setPES_SubIdField(byte[] array, int offset, int pes_headerlength, int pes_extensionlength, int value)
	{
		array[pes_headerlength + pes_extensionlength + offset] = (byte)(0xFF & value);
	}

	/**
	 * 
	 */
	public static int getPES_LengthField(byte[] array, int offset)
	{
		int value = (0xFF & array[4 + offset])<<8 | (0xFF & array[5 + offset]);

		return value;
	}

	/**
	 * 
	 */
	public static int getPES_IdField(byte[] array, int offset)
	{
		int value = 0xFF & array[3 + offset];

		return value;
	}

	/**
	 * 
	 */
	public static int getPES_ExtensionLengthField(byte[] array, int offset)
	{
		int value = 0xFF & array[8 + offset];

		return value;
	}

	/**
	 * 
	 */
	public static boolean clearBit33ofPTS(byte[] array, int offset)
	{
		if ((0x80 & array[7 + offset]) != 0)
		{
			array[9 + offset] &= ~8;
			return true;
		}

		return false;
	}

	/**
	 * 
	 */
	public static boolean clearBit33ofDTS(byte[] array, int offset)
	{
		if ((0x40 & array[7 + offset]) != 0)
		{
			array[9 + 5 + offset] &= ~8;
			return true;
		}

		return false;
	}

	/**
	 * 
	 */
	public static int nextBits(byte buffer[], int BitPos, int N)
	{
		int Pos, Val;

		Pos = BitPos>>>3;

		Val =   (0xFF & buffer[Pos])<<24 |
			(0xFF & buffer[Pos + 1])<<16 |
			(0xFF & buffer[Pos + 2])<<8 |
			(0xFF & buffer[Pos + 3]);

		Val <<= BitPos & 7;
		Val >>>= 32-N;

		return Val;
	}

	/**
	 * check startcode
	 * return int of skip'able data (negative)
	 */
	public static int validateStartcode(byte[] pes_packet, int offset)
	{
		if (pes_packet[2 + offset] == 1)
		{
			if (pes_packet[1 + offset] == 0)
				if (pes_packet[offset] == 0)
					return 0;
		}

		else if (pes_packet[2 + offset] == 0)
		{
			if (pes_packet[1 + offset] == 0)
				return -1;
			else
				return -2;
		}

		return -3;
	}

	/**
	 * skip leading bytes before first valid startcodes and return fixed array
	 */
	public static boolean alignSyncword(byte[] pes_packet, int pes_offset, int es_streamtype)
	{
		int pes_payloadlength = getPES_LengthField(pes_packet, pes_offset);
		int pes_headerlength = 9;
		int pes_packetlength = pes_headerlength - 3 + pes_payloadlength;
		int pes_extensionlength = getPES_ExtensionLengthField(pes_packet, pes_offset);

		int offset = pes_headerlength + pes_extensionlength;
		int i;
		int j;
		int k;

		boolean found = false;

		packloop:
		for (i = offset, j = pes_packetlength - 3, k = offset + pes_offset; i < j; i++, k++)
		{
			switch (es_streamtype)
			{
			case MPEG_VIDEO:
				if (pes_packet[k] != 0 || pes_packet[1 + k] != 0  || pes_packet[2 + k] != 1 || pes_packet[3 + k] != (byte)SEQUENCE_HEADER_CODE) 
					continue packloop;

				found = true;
				break packloop;

			case AC3_AUDIO:
			case DTS_AUDIO:
				if ((pes_packet[k] != 0xB || pes_packet[1 + k] != 0x77) && (pes_packet[k] != 0x7F || pes_packet[1 + k] != (byte)0xFE || pes_packet[2 + k] != (byte)0x80 || pes_packet[3 + k] != 1)) 
					continue packloop;

				found = true; 
				break packloop;

			case MPEG_AUDIO:
				if ( pes_packet[k] != (byte)0xFF || (0xF0 & pes_packet[1 + k]) != 0xF0)
					continue packloop;

				found = true; 
				break packloop;
			}
		}

		if (found) 
		{
			System.arraycopy(pes_packet, i + pes_offset, pes_packet, offset + pes_offset, pes_packetlength - i);

			pes_payloadlength -= (i - offset);

			setPES_LengthField(pes_packet, pes_offset, pes_payloadlength);
		}

		return found;
	}

	/**
	 * create PTS alias 
	 */
	public static void logAlias(JobProcessing job_processing, String _vptslog, String _datalog)
	{
		File vpts = new File(_vptslog); 

		try {
			RandomAccessFile log = new RandomAccessFile(_datalog, "rw");

			log.seek(0);

			if (vpts.exists() && vpts.length() > 0)
			{
				RandomAccessFile vlog = new RandomAccessFile(_vptslog, "r");
				long p = vlog.readLong();

				if (!job_processing.hasElementaryVideoStream() && job_processing.getSplitPart() == 0)
					job_processing.setFirstAudioPts(p);

				log.writeLong(job_processing.getFirstAudioPts() + (Common.getSettings().getBooleanProperty(Keys.KEY_additionalOffset) ? 90L * Common.getSettings().getIntProperty(Keys.KEY_ExportPanel_additionalOffset_Value) : 0));

				vlog.close();
			}
			else 
				log.writeLong(0L + (Common.getSettings().getBooleanProperty(Keys.KEY_additionalOffset) ? Common.getSettings().getIntProperty(Keys.KEY_ExportPanel_additionalOffset_Value) : 0));

			log.writeLong(0L); 
			log.close();

			Common.setMessage("");
			Common.setMessage(Resource.getString("all.msg.pts.faked"));

		} catch (IOException e) {

			Common.setExceptionMessage(e);
		}
	}

	/**
	 * split reset
	 */
	public static void resetSplitMode(JobProcessing job_processing, String vptslog)
	{
		if ( vptslog.equals("-1") && job_processing.getSplitSize() > 0 )
		{ 
			job_processing.setSplitSize(0);
			Common.setMessage(Resource.getString("splitreset.novideo"));
		}
	}


	/**
	 * parse cut long value
	 */
	public static String parseCutValue(long value)
	{
		if (Common.getSettings().getIntProperty(Keys.KEY_CutMode) != CUTMODE_TIME)
			return String.valueOf(value);

		String str = "";
		long frametime = 1;
		value /= 90;

		DateFormat time = new SimpleDateFormat("H:mm:ss:");
		time.setTimeZone(TimeZone.getTimeZone("GMT+0:00"));

		Calendar cal = Calendar.getInstance();
		cal.setTimeZone(TimeZone.getTimeZone("GMT+0:00"));
		cal.setTime(new Date(value));

		int frame = (cal.get(14) / ((int)frametime));
		str = time.format(new Date(value)) + (frame < 10 ? "0" : "") + frame;

		return str;
	}

	/**
	 * parse cut field value
	 */
	public static long parseCutValue(String value, boolean demux)
	{
		if (value == null)
			return 0;

		value = value.trim();

		try {
			if (Common.getSettings().getIntProperty(Keys.KEY_CutMode) != CUTMODE_TIME)
				return Long.parseLong(value);

		} catch (Exception e) {
			return -1L;
		}

		int i;
		if ( (i = value.indexOf(" ")) > -1)
			value = value.substring(0, i);

		value = value.replace('.',':');

		if (value.indexOf(":") < 0)
			return Long.parseLong(value);

		StringTokenizer st = new StringTokenizer(value, ":");
		String str = null;
		long val = 0;
		long frametime = !demux ? 90 : (long)_videoframerate;
		long mp[] = { 324000000L, 5400000L, 90000L, frametime }; //h,m,s,f

		for (int a=0; st.hasMoreTokens() && a < 4; a++)
		{
			str = st.nextToken();
			val += (mp[a] * Long.parseLong(str));
		}

		return val;
	}


	/**
	 * video timelength read from ptslogfile
	 */
	public static long calcVideoTime(String logfile)
	{
		long vtime = 0;

		try {
			long vlogsize = new File(logfile).length();
			RandomAccessFile vlog = new RandomAccessFile(logfile, "r");

			vlog.seek(vlogsize - 8);

			vtime = vlog.readLong();

			vlog.close();
		}

		catch (IOException e) {

			Common.setExceptionMessage(e);
		}

		return vtime;
	}


	/**
	 * make cut
	 */
	public static boolean makecut(JobProcessing job_processing, long comparePoint, List ctemp)
	{
		return makecut(job_processing, null, 0, comparePoint, new ArrayList(), 0, ctemp, 0, new ArrayList());
	}

	/**
	 * make cut
	 */
	public static boolean makecut(JobProcessing job_processing, String cuts_filename, long startPTS, long comparePoint, List newcut, int lastframes, List ctemp, int gopnumber, List cell)
	{
		if (ctemp.isEmpty())
			return true;

		long[] abc;

		if ( _cutcount < ctemp.size() )
		{ 
			if ( comparePoint > parseCutValue(ctemp.get(_cutcount).toString(), true) )
			{
				/**
				 * ungerade == cutout
				 */
				if ((_cutcount & 1) == 1)
				{
					_bool = false; 
					for (int c = newcut.size() - 1; c >- 1; c--)
					{
						abc = (long[])newcut.get(c);

						if ( abc[0] < parseCutValue(ctemp.get(_cutcount).toString(), true) )
						{ 
							_bool = true; 

							Common.setMessage(Resource.getString("msg.cuts.cutin", "" + gopnumber, "" + lastframes, "" + Common.formatTime_1((long)(lastframes * (double)(_videoframerate / 90.0f)) )));

							saveCuts(comparePoint, startPTS, lastframes, cuts_filename);

						//	if (lastframes > 0) 
						//		cell.add("" + lastframes); // celltimes for cutin

							break;
						}
					}

					if (!_bool)
					{
						Common.setMessage(Resource.getString("msg.cuts.cutout", "" + gopnumber));

						saveCuts(comparePoint, startPTS, lastframes, cuts_filename);
					}

					_cutcount++;

					return _bool;
				}

				/**
				 * gerade == cutin
				 */
				else
				{

					_bool = true;
					_cutcount++;

					Common.setMessage(Resource.getString("msg.cuts.cutin", "" + gopnumber, "" + lastframes, "" + Common.formatTime_1((long)(lastframes * (double)(_videoframerate / 90.0f)) )));

					saveCuts(comparePoint, startPTS, lastframes, cuts_filename);

				//	if (lastframes > 0) 
				//		cell.add("" + lastframes); // celltimes for cutin

					if (_cutcount >= ctemp.size()) 
						return _bool;

					for (int c = newcut.size() - 1; c >- 1; c--)
					{
						abc = (long[])newcut.get(c);

						if ( abc[0] < parseCutValue(ctemp.get(_cutcount).toString(), true) )
						{ 
							_cutcount++;

							break;
						}
					}

					return _bool;
				}
			}
		}

		else
		{ 
			if (!_bool && job_processing.getCutComparePoint() == 10000000) 
				job_processing.setCutComparePoint(comparePoint);
		}

		return _bool;
	}

	/**
	 * saves the cutpoint list from coll specials
	 */
	private static void saveCuts(long cutposition, long startPTS, long lastframes, String cuts_filename)
	{
		if ( Common.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_exportPts) && cuts_filename != null)
		{
			try {
				cuts_filename += ".Xpl";

				PrintWriter pts_writer = new PrintWriter(new FileOutputStream(cuts_filename, lastframes > 0));

				if (new File(cuts_filename).length() == 0)
					pts_writer.println(Keys.KEY_CutMode[0] + "=3"); // PTS cut mode fixed for file

				pts_writer.println(startPTS);
				pts_writer.close();

				Common.setMessage(Resource.getString("msg.savecut", "" + startPTS));

			} catch (IOException e) {

				Common.setExceptionMessage(e);
			}
		}
	}


	/**
	 * set 1. videoheader
	 */
	public static void setVideoHeader(JobProcessing job_processing, String videofile, String logfile, int[] clv, int[] MPGVideotype)
	{
		long time = 0;
		String videotype[] = { "(m1v)", "(m2v)" };

		String frames_used[] = { 
			Resource.getString("video.msg.io.non"), 
			Resource.getString("video.msg.io.int"), 
			Resource.getString("video.msg.io.pro"), 
			Resource.getString("video.msg.io.int_pro")
		};

		if (new File(logfile).exists())
		{
			time = (calcVideoTime(logfile) / 90);
			String vt = Common.formatTime_1( time / 10 * 10 );

			Common.setMessage(Resource.getString("video.msg.length", "" + job_processing.getExportedVideoFrameNumber()) + " " + vt);
			Common.setMessage(Resource.getString("video.msg.gop.summary", "" + (0x7FFF & clv[9]>>>15), "" + (0x7FFF & clv[9]), "" + frames_used[clv[9]>>>30]));

			if (clv[8] > 0)
				Common.setMessage(Resource.getString("video.error.pts.same", "" + clv[8]));

			job_processing.getSummaryInfo().add(Resource.getString("video.summary", videotype[MPGVideotype[0]], "" + job_processing.getExportedVideoFrameNumber(), "" + vt) + "'" + videofile + "'");
		}

		try {

			RandomAccessFile pv2 = new RandomAccessFile(videofile, "rw");

			if (Common.getSettings().getIntProperty(Keys.KEY_ChangeBitrateInAllSequences) > 0)
			{
				if (time == 0) 
					Common.setMessage(Resource.getString("video.msg.bitrate.avg", "" + ((job_processing.getMinBitrate() + job_processing.getMaxBitrate()) / 2 * 400), "" + (job_processing.getMinBitrate() * 400) + "/" + (job_processing.getMaxBitrate() * 400)));

				else 
					Common.setMessage(Resource.getString("video.msg.bitrate.avgnom", "" + ((pv2.length() * 8000L) / time), "" + (job_processing.getMinBitrate() * 400) + "/" + (job_processing.getMaxBitrate() * 400)));

			}

			else if (Common.getSettings().getBooleanProperty(Keys.KEY_DebugLog)) 
				System.out.println();

			if (!Common.getSettings().getBooleanProperty(Keys.KEY_WriteOptions_writeVideo))
			{
				pv2.close(); 

				return; 
			}

			int max_bitrate_value = 22500; // 9.0Mbit, (video only)

			/**
			 * bitraten 
			 */
			if (Common.getSettings().getIntProperty(Keys.KEY_ChangeBitrateInFirstSequence) > 0)
			{
				int newmux = (Common.getSettings().getIntProperty(Keys.KEY_ChangeBitrateInFirstSequence) == 3) ? job_processing.getMaxBitrate() : max_bitrate_value;

				if (Common.getSettings().getIntProperty(Keys.KEY_ChangeBitrateInFirstSequence) == 2 && job_processing.getMaxBitrate() < max_bitrate_value) 
					newmux = job_processing.getMaxBitrate();

				if (Common.getSettings().getIntProperty(Keys.KEY_ChangeBitrateInFirstSequence) == 1)
				{
					if (time == 0) 
						newmux = (job_processing.getMinBitrate() + job_processing.getMaxBitrate()) / 2;   // old calcul. avg.

					else
					{
						newmux = (int)(((pv2.length() * 8000L) / time) / 400);

						if (newmux < 0 || newmux > max_bitrate_value) 
							newmux = (job_processing.getMinBitrate() + job_processing.getMaxBitrate()) / 2;   // old calcul. avg.
					}
				}

				if (Common.getSettings().getIntProperty(Keys.KEY_ChangeBitrateInFirstSequence) == 4)
				{ 
					newmux = 0x3FFFF;

					Common.setMessage(Resource.getString("video.msg.bitrate.vbr"));
				}

				else 
					Common.setMessage(Resource.getString("video.msg.bitrate.val", "" + (newmux*400)));

				pv2.seek(8);
				newmux = (newmux<<14) | ((pv2.readInt()<<18)>>>18);

				pv2.seek(8);
				pv2.writeInt(newmux);
			}

			/**
			 * patch resolution DVD-conform 
			 */
			//JLA14082003+
			//0: no patch, 1:patch unconditionally, 2:patch if <>720|352, 3:pach if <>720|704|352
			if (Common.getSettings().getIntProperty(Keys.KEY_ConditionalHorizontalPatch) != 0)
			{
				pv2.seek(4);

				int resolutionOrig = pv2.readInt();
				int hresOrg = (resolutionOrig>>>20);
				int resolution = (0xFFFFF & resolutionOrig) | Common.getSettings().getIntProperty(Keys.KEY_ConditionalHorizontalResolution)<<20;
				boolean doPatch;

				switch (Common.getSettings().getIntProperty(Keys.KEY_ConditionalHorizontalPatch))
				{
				case 2: 
					doPatch = hresOrg != 720 && hresOrg != 352; 
					break;

				case 3: 
					doPatch = hresOrg != 720 && hresOrg != 704 && hresOrg != 352; 
					break;

				default: 
					doPatch = true; 
				}

				if(doPatch)
				{
					pv2.seek(4);
					pv2.writeInt(resolution);

					Common.setMessage(Resource.getString("video.msg.resolution", "" + (resolutionOrig>>>20)+"*"+((0xFFF00&resolutionOrig)>>>8)) + " " + (resolution>>>20)+"*"+((0xFFF00&resolution)>>>8));
				}
			}
			//JLA14082003-

			pv2.close();

			Common.setMessage(Resource.getString("msg.newfile") + " " + videofile);

		} catch (IOException e) {

			Common.setExceptionMessage(e);
		}
	}

	/**
	 * vdr_dvbsub determination
	 */
	public static int getExtension2_Id(byte[] pes_packet, int pes_headerlength, int pes_payloadlength, int pesID, boolean pes_isMpeg2)
	{
		int pes_extension2_id = -1;

		if (pesID != PRIVATE_STREAM_1_CODE || !pes_isMpeg2)
			return pes_extension2_id;

		//read flags
		int pes_shift = pes_headerlength; //points to 1st appendix

		pes_shift += (0x80 & pes_packet[7]) != 0 ? 5 : 0; //pes_pts
		pes_shift += (0x40 & pes_packet[7]) != 0 ? 5 : 0; //pes_dts
		pes_shift += (0x20 & pes_packet[7]) != 0 ? 6 : 0; //pes_escr
		pes_shift += (0x10 & pes_packet[7]) != 0 ? 3 : 0; //pes_esrate
		pes_shift += (4 & pes_packet[7]) != 0 ? 1 : 0; //pes_copy
		pes_shift += (2 & pes_packet[7]) != 0 ? 2 : 0; //pes_crc

		boolean pes_ext1 = (1 & pes_packet[7]) != 0; //ext1

		if (pes_ext1 && pes_payloadlength > pes_shift + 2)
		{
			int shift = pes_shift;

			pes_shift += (0x80 & pes_packet[shift]) != 0 ? 16 : 0; //pes_private
			pes_shift += (0x40 & pes_packet[shift]) != 0 ? 1 : 0; //pes_packfield
			pes_shift += (0x20 & pes_packet[shift]) != 0 ? 2 : 0; //pes_sequ_counter
			pes_shift += (0x10 & pes_packet[shift]) != 0 ? 2 : 0; //pes_P-STD

			boolean pes_ext2 = (1 & pes_packet[shift]) != 0; //ext2

			pes_shift++; //skip flag_fields of ext1

			if (pes_ext2 && pes_payloadlength > pes_shift + 2)
			{
				pes_shift++; //skip ext2 length field
				pes_extension2_id = 0xFF & pes_packet[pes_shift]; //read byte0 (res.) of ext2
			}
		}

		return pes_extension2_id;
	}
}