/*
* @(#)StreamParser
*
* Copyright (c) 2005-2006 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.File;
import java.io.InputStream;
import java.io.IOException;
import java.io.PushbackInputStream;
import java.io.FileOutputStream;
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
import java.util.Hashtable;
import net.sourceforge.dvb.projectx.common.Common;
import net.sourceforge.dvb.projectx.common.Resource;
import net.sourceforge.dvb.projectx.common.Keys;
import net.sourceforge.dvb.projectx.common.JobCollection;
import net.sourceforge.dvb.projectx.common.JobProcessing;
import net.sourceforge.dvb.projectx.io.StandardBuffer;
import net.sourceforge.dvb.projectx.xinput.XInputFile;
import net.sourceforge.dvb.projectx.xinput.StreamInfo;
import net.sourceforge.dvb.projectx.parser.CommonParsing;
import net.sourceforge.dvb.projectx.parser.StreamConverter;
import net.sourceforge.dvb.projectx.parser.StreamDemultiplexer;
import net.sourceforge.dvb.projectx.parser.StreamParserBase;
/**
* main thread
*/
public class StreamParserPESSecondary extends StreamParserBase {
/**
*
*/
public StreamParserPESSecondary()
{
super();
}
/**
* secondary PES Parser
*/
public String parseStream(JobCollection collection, XInputFile aXInputFile, int _pes_streamtype, int action, String vptslog)
{
String fchild = collection.getOutputName(aXInputFile.getName());
String fparent = collection.getOutputNameParent(fchild);
if (collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_appendExtension))
fparent = collection.getOutputDirectory() + collection.getFileSeparator() + fchild;
JobProcessing job_processing = collection.getJobProcessing();
job_processing.clearStatusVariables();
int[] clv = job_processing.getStatusVariables();
/**
* split part
*/
fparent += job_processing.getSplitSize() > 0 ? "(" + job_processing.getSplitPart() + ")" : "" ;
String paname = fparent + ".ma1";
String file_id = aXInputFile.getFileID();
List tempfiles = job_processing.getTemporaryFileList();
if (!tempfiles.isEmpty())
{
for (int i = 0; i < tempfiles.size(); i += 4 )
{
if ( tempfiles.get(i + 1).toString().equals(aXInputFile.toString()) )
{
Common.renameTo(tempfiles.get(i).toString(), paname);
tempfiles.set(i, paname);
Common.setMessage(Resource.getString("parseSecondaryPES.continue") + " " + aXInputFile);
String str = tempfiles.get(i + 3).toString();
if (str.equals("tt")) //vtx
new StreamProcess(CommonParsing.TELETEXT, collection, tempfiles.get(i).toString(), tempfiles.get(i + 2).toString(), tempfiles.get(i + 3).toString(), vptslog);
else if (str.equals("sp")) //subpics
new StreamProcess(CommonParsing.SUBPICTURE, collection, tempfiles.get(i).toString(), tempfiles.get(i + 2).toString(), tempfiles.get(i + 3).toString(), vptslog);
else if (str.equals("pc")) //lpcm
new StreamProcess(CommonParsing.LPCM_AUDIO, collection, tempfiles.get(i).toString(), tempfiles.get(i + 2).toString(), tempfiles.get(i + 3).toString(), vptslog);
else //other audio
new StreamProcess(CommonParsing.MPEG_AUDIO, collection, tempfiles.get(i).toString(), tempfiles.get(i + 2).toString(), tempfiles.get(i + 3).toString(), vptslog);
return vptslog;
}
}
}
boolean Message_2 = collection.getSettings().getBooleanProperty(Keys.KEY_MessagePanel_Msg2);
boolean Debug = collection.getSettings().getBooleanProperty(Keys.KEY_DebugLog);
boolean SimpleMPG = collection.getSettings().getBooleanProperty(Keys.KEY_simpleMPG);
boolean GetEnclosedPackets = collection.getSettings().getBooleanProperty(Keys.KEY_Input_getEnclosedPackets);
boolean IgnoreScrambledPackets = collection.getSettings().getBooleanProperty(Keys.KEY_TS_ignoreScrambled);
boolean isTeletext = false;
boolean missing_startcode = false;
boolean scrambling_messaged = false;
boolean pes_isMpeg2;
boolean pes_alignment;
boolean pes_scrambled;
boolean containsPts = false;
boolean foundObject;
int pes_streamtype;
int pes_payloadlength;
int pes_packetlength;
int pes_extensionlength;
int pes_headerlength = 9;
int pes_packetoffset = 6;
int pes_extension2_id;
int pesID;
int subID;
int offset;
int returncode = 0;
int tmp_value1 = 0;
byte[] pes_packet = new byte[0x10006];
byte[] buffered_data;
long count = 0;
long size;
long qexit;
job_processing.setMinBitrate(CommonParsing.MAX_BITRATE_VALUE);
job_processing.setMaxBitrate(0);
job_processing.setExportedVideoFrameNumber(0);
job_processing.setEndPtsOfGop(-10000);
job_processing.setSequenceHeader(true);
job_processing.setAllMediaFilesExportLength(0);
job_processing.setProjectFileExportLength(0);
Hashtable substreams = new Hashtable();
StandardBuffer sb;
demuxList = job_processing.getSecondaryPESDemuxList();
demuxList.clear();
try {
PushbackInputStream in = new PushbackInputStream(aXInputFile.getInputStream(), pes_packet.length);
size = aXInputFile.length();
Common.updateProgressBar(Resource.getString("parseSecondaryPES.demux.pes") + " " + aXInputFile.getName(), 0, 0);
qexit = count + (0x100000L * Integer.parseInt(collection.getSettings().getProperty(Keys.KEY_ExportPanel_Infoscan_Value)));
bigloop:
while (true)
{
loop:
while (count < size)
{
pes_streamtype = _pes_streamtype; // reset to original type
Common.updateProgressBar(count, size);
//yield();
while (pause())
{}
if (CommonParsing.isProcessCancelled() || (CommonParsing.isInfoScan() && count > qexit))
{
CommonParsing.setProcessCancelled(false);
job_processing.setSplitSize(0);
break bigloop;
}
in.read(pes_packet, 0, pes_packetoffset);
pesID = CommonParsing.getPES_IdField(pes_packet, 0);
if ((returncode = CommonParsing.validateStartcode(pes_packet, 0)) < 0 || pesID < CommonParsing.SYSTEM_END_CODE)
{
returncode = returncode < 0 ? -returncode : 4;
if (Message_2 && !missing_startcode)
Common.setMessage(Resource.getString("parseSecondaryPES.missing.startcode") + " " + count);
in.read(pes_packet, pes_packetoffset, pes_packet.length - pes_packetoffset);
int i = returncode;
for (; i < pes_packet.length - 3; )
{
returncode = CommonParsing.validateStartcode(pes_packet, i);
if (returncode < 0)
{
i += -returncode;
continue;
}
else
{
in.unread(pes_packet, i, pes_packet.length - i);
count += i;
missing_startcode = true;
continue loop;
}
}
in.unread(pes_packet, i, pes_packet.length - i);
count += i;
missing_startcode = true;
continue loop;
}
if (Message_2 && missing_startcode)
Common.setMessage(Resource.getString("parseSecondaryPES.found.startcode") + " " + count);
missing_startcode = false;
if (pes_streamtype == CommonParsing.MPEG1PS_TYPE || pes_streamtype == CommonParsing.MPEG2PS_TYPE || SimpleMPG)
{
switch (pesID)
{
case CommonParsing.SYSTEM_END_CODE:
in.unread(pes_packet, 4, 2);
Common.setMessage("-> skip system_end_code @ " + count);
count += 4;
continue loop;
case CommonParsing.PACK_START_CODE:
if ((0xC0 & pes_packet[4]) == 0) //mpg1
{
in.skip(6);
count += 12;
continue loop;
}
else if ((0xC0 & pes_packet[4]) == 0x40) //mpg2
{
in.read(pes_packet, 6, 8);
offset = 7 & pes_packet[13];
count += 14;
in.read(pes_packet, 14, offset);
if (offset > 0)
{
for (int i = 0; i < offset; i++)
if (pes_packet[14 + i] != -1)
{
in.unread(pes_packet, 14, offset);
Common.setMessage("!> wrong pack header stuffing @ " + count);
missing_startcode = true;
continue loop;
}
}
count += offset;
continue loop;
}
else
{
in.unread(pes_packet, 4, 2);
count += 4;
continue loop;
}
case CommonParsing.SYSTEM_START_CODE:
case CommonParsing.PROGRAM_STREAM_MAP_CODE:
case CommonParsing.PADDING_STREAM_CODE:
case CommonParsing.PRIVATE_STREAM_2_CODE:
case CommonParsing.ECM_STREAM_CODE:
case CommonParsing.EMM_STREAM_CODE:
case CommonParsing.DSM_CC_STREAM_CODE:
case 0xF3:
case 0xF4:
case 0xF5:
case 0xF6:
case 0xF7:
case 0xF8:
case 0xF9:
case 0xFA:
case 0xFB:
case 0xFC:
case 0xFD:
case 0xFE:
case 0xFF:
pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, 0);
in.skip(pes_payloadlength);
count += (pes_packetoffset + pes_payloadlength);
continue loop;
}
}
if ( (0xF0 & pesID) != 0xE0 && (0xE0 & pesID) != 0xC0 && pesID != CommonParsing.PRIVATE_STREAM_1_CODE)
{
in.unread(pes_packet, 3, pes_packetoffset - 3);
count += 3;
continue loop;
}
pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, 0);
if (pes_payloadlength == 0)
{
Common.setMessage(Resource.getString("parseSecondaryPES.packet.length") + " " + count);
count += pes_packetoffset;
continue loop;
}
in.read(pes_packet, pes_packetoffset, pes_payloadlength + 4);
pes_packetlength = pes_packetoffset + pes_payloadlength;
if (GetEnclosedPackets && CommonParsing.validateStartcode(pes_packet, pes_packetlength) < 0)
{
if (count + pes_packetlength < size)
{
if (Message_2 && !missing_startcode)
Common.setMessage(Resource.getString("parseSecondaryPES.miss.next.startcode", String.valueOf(count + pes_packetlength), String.valueOf(count), Integer.toHexString(pesID).toUpperCase()));
missing_startcode = true;
in.unread(pes_packet, pes_packetoffset, pes_payloadlength + 4);
count += pes_packetoffset;
continue loop;
}
}
else
in.unread(pes_packet, pes_packetlength, 4);
clv[5]++;
if (Debug)
System.out.print("\r"+Resource.getString("parseSecondaryPES.packs", String.valueOf(clv[5]), String.valueOf((count * 100 / size)), String.valueOf(count)));
pes_extensionlength = CommonParsing.getPES_ExtensionLengthField(pes_packet, 0);
pes_isMpeg2 = (0xC0 & pes_packet[6]) == 0x80;
pes_alignment = pes_isMpeg2 && (4 & pes_packet[6]) != 0;
pes_scrambled = pes_isMpeg2 && (0x30 & pes_packet[6]) != 0;
count += pes_packetlength;
/**
* check scrambling
*/
if (IgnoreScrambledPackets)
{
// cannot work with scrambled data
if (pes_scrambled)
{
if (!scrambling_messaged)
{
scrambling_messaged = true;
Common.setMessage(Resource.getString("parseTS.scrambled", Integer.toHexString(pesID).toUpperCase(), String.valueOf(clv[5]), String.valueOf(count - pes_packetlength)));
}
continue loop;
}
else if (scrambling_messaged)
{
Common.setMessage(Resource.getString("parseTS.clear", Integer.toHexString(pesID).toUpperCase(), String.valueOf(clv[5]), String.valueOf(count - pes_packetlength)));
scrambling_messaged = false;
}
}
/**
* vdr_dvbsub determination
*/
pes_extension2_id = CommonParsing.getExtension2_Id(pes_packet, pes_headerlength, pes_payloadlength, pesID, pes_isMpeg2, count - pes_packetlength);
isTeletext = false;
subID = 0;
if (pesID == CommonParsing.PRIVATE_STREAM_1_CODE && pes_payloadlength > 2)
{
offset = pes_headerlength + pes_extensionlength;
if (offset < pes_packetlength)
{
subID = 0xFF & pes_packet[offset];
isTeletext = pes_extensionlength == 0x24 && subID>>>4 == 1;
// vdr 1.5.x dvb-subs container
if (pes_payloadlength >= 4 && subID>>>4 == 2)
{
tmp_value1 = CommonParsing.getIntValue(pes_packet, offset, 4, !CommonParsing.BYTEREORDERING);
//vdr 1.5.x start packet of dvb-sub || subsequent packet
//if ((pes_alignment && tmp_value1 == 0x20010000) || (!pes_alignment && tmp_value1 == 0x20010001))
if ((pes_alignment && (0xF0FFFFFF & tmp_value1) == 0x20010000) || (!pes_alignment && (0xF0FFFFFF & tmp_value1) == 0x20010001))
{
for (int i = offset, j = offset + 4; i < j; i++)
pes_packet[i] = (byte) 0xFF;
pes_extensionlength += 4;
pes_packet[8] = (byte)(pes_extensionlength);
pes_payloadlength -= 4;
//pes_extension2_id = 1;
pes_extension2_id = subID = tmp_value1>>>24;
pes_streamtype = CommonParsing.MPEG2PS_TYPE; //will be resetted before next packet
if (pes_alignment)
pes_packet[offset + 4] = (byte)(subID); }
}
//subpic in vdr_pes before 1.5.x
if (pes_alignment && !isTeletext && (subID>>>4 == 2 || subID>>>4 == 3))
pes_streamtype = CommonParsing.MPEG2PS_TYPE; //will be resetted before next packet
if (pes_streamtype != CommonParsing.MPEG1PS_TYPE && pes_streamtype != CommonParsing.MPEG2PS_TYPE && !isTeletext)
subID = 0; //disables LPCM too
}
else if (pes_streamtype != CommonParsing.MPEG1PS_TYPE) //?
{
pes_extensionlength = pes_payloadlength - 3;
pes_packet[8] = (byte)(pes_extensionlength);
}
/**
* packet buffering esp. of subpics from vdr or other pes
*/
if (pes_extension2_id != -1)
{
String str = String.valueOf(pes_extension2_id);
offset = pes_headerlength + pes_extensionlength;
if ( !substreams.containsKey(str))
substreams.put(str, new StandardBuffer());
sb = (StandardBuffer) substreams.get(str);
// buffer raw packet data
if (!pes_alignment)
{
sb.write(pes_packet, offset, pes_packetlength - offset);
continue loop;
}
// start packet, buffer this and get last completed packet
else
{
buffered_data = sb.getData();
sb.reset();
sb.write(pes_packet, 0, pes_packetlength);
if (buffered_data == null || buffered_data.length < 10)
continue loop;
pes_packetlength = buffered_data.length;
if (pes_packetlength > 0x10005)
{
Common.setMessage("!> sub packet too long: 0x" + Integer.toHexString(pesID).toUpperCase() + " /ext2_id " + pes_extension2_id);
pes_packetlength = 0x10005;
}
pes_payloadlength = pes_packetlength - pes_packetoffset;
System.arraycopy(buffered_data, 0, pes_packet, 0, pes_packetlength);
CommonParsing.setPES_LengthField(pes_packet, 0, pes_payloadlength);
buffered_data = null;
}
}
}
foundObject = false;
/**
* find ID object
*/
for (int i = 0; i < demuxList.size(); i++)
{
streamdemultiplexer = (StreamDemultiplexer) demuxList.get(i);
foundObject = pesID == streamdemultiplexer.getID() && subID == streamdemultiplexer.subID() && isTeletext == streamdemultiplexer.isTTX();
if (foundObject)
break;
}
/**
* create new ID object
*/
if (!foundObject)
{
String IDtype = "";
switch (0xF0 & pesID)
{
case 0xE0:
IDtype = Resource.getString("idtype.mpeg.video.ignored");
streamdemultiplexer = new StreamDemultiplexer(collection);
streamdemultiplexer.setID(pesID);
streamdemultiplexer.setsubID(0);
streamdemultiplexer.setType(CommonParsing.MPEG_VIDEO);
streamdemultiplexer.setStreamType(pes_streamtype);
demuxList.add(streamdemultiplexer);
break;
case 0xC0:
case 0xD0:
IDtype = Resource.getString("idtype.mpeg.audio");
streamdemultiplexer = new StreamDemultiplexer(collection);
streamdemultiplexer.setID(pesID);
streamdemultiplexer.setsubID(0);
streamdemultiplexer.setType(CommonParsing.MPEG_AUDIO);
streamdemultiplexer.setStreamType(pes_streamtype);
demuxList.add(streamdemultiplexer);
streamdemultiplexer.init(collection, fparent, MainBufferSize / demuxList.size(), demuxList.size(), CommonParsing.SECONDARY_PES_PARSER);
break;
}
switch (pesID)
{
case CommonParsing.PRIVATE_STREAM_1_CODE:
IDtype = Resource.getString("idtype.private.stream");
IDtype += (isTeletext ? " TTX ": "") + (subID != 0 ? " (SubID 0x" + Integer.toHexString(subID).toUpperCase() + ")" : "");
streamdemultiplexer = new StreamDemultiplexer(collection);
streamdemultiplexer.setID(pesID);
streamdemultiplexer.setsubID(subID);
switch (subID>>>4)
{
case 1:
streamdemultiplexer.setType(CommonParsing.TELETEXT);
break;
case 2:
case 3:
streamdemultiplexer.setType(CommonParsing.SUBPICTURE);
break;
case 8:
streamdemultiplexer.setType(CommonParsing.AC3_AUDIO);
break;
case 0xA:
streamdemultiplexer.setType(CommonParsing.LPCM_AUDIO);
}
streamdemultiplexer.setTTX(isTeletext);
streamdemultiplexer.setStreamType(pes_streamtype);
demuxList.add(streamdemultiplexer);
streamdemultiplexer.init(collection, fparent, MainBufferSize / demuxList.size(), demuxList.size(), CommonParsing.SECONDARY_PES_PARSER);
break;
}
Common.setMessage(Resource.getString("parseSecondaryPES.found.pesid", Integer.toHexString(pesID).toUpperCase(), IDtype, "" + (count - 6 - pes_payloadlength)));
}
if (!streamdemultiplexer.StreamEnabled())
continue loop;
if (streamdemultiplexer.getType() == CommonParsing.MPEG_VIDEO)
continue loop;
else
streamdemultiplexer.write(job_processing, pes_packet, 0, pes_packetlength, true);
}
/**
* loop not yet used
*/
break bigloop;
}
Common.setMessage(Resource.getString("parseSecondaryPES.packs", String.valueOf(clv[5]), String.valueOf(count * 100 / size), String.valueOf(count)));
in.close();
processNonVideoElementaryStreams(vptslog, action, clv, collection, job_processing, tempfiles, aXInputFile);
} catch (IOException e2) {
Common.setExceptionMessage(e2);
}
return vptslog;
}
}