/*
* @(#)StreamParser
*
* 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.File;
import java.io.InputStream;
import java.io.IOException;
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.xinput.XInputFile;
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.StreamProcess;
import java.util.List;
import java.util.Hashtable;
/**
* main thread
*/
public class StreamParserBase extends Object {
public int ERRORCODE = 0;
public int MainBufferSize = 8192000;
//
public static StreamDemultiplexer streamdemultiplexer;
public static StreamConverter streamconverter;
public static List demuxList;
public static Hashtable streamobjects;
public static boolean CreateD2vIndex;
public static boolean SplitProjectFile;
public static String fchild;
public static String fparent;
public static long OverheadSize;
//
/**
*
*/
public StreamParserBase()
{
init();
}
/**
*
*/
public void init()
{
MainBufferSize = Integer.parseInt(Common.getSettings().getProperty(Keys.KEY_MainBuffer));
if (MainBufferSize <= 0)
MainBufferSize = 4096000;
streamdemultiplexer = null;
streamconverter = null;
demuxList = null;
streamobjects = null;
CreateD2vIndex = false;
SplitProjectFile = false;
OverheadSize = 2048000;
fchild = "";
fparent = "";
}
/**
*
*/
public String parseStream(JobCollection collection, XInputFile aXInputFile, int pes_streamtype, int action, String vptslog)
{
return null;
}
/**
*
*/
public void setFileName(JobCollection collection, JobProcessing job_processing, XInputFile aXInputFile)
{
setFileName(collection, job_processing, aXInputFile, "");
}
/**
*
*/
public void setFileName(JobCollection collection, JobProcessing job_processing, XInputFile aXInputFile, String extension)
{
String str = aXInputFile.getName();
str = str.replace('?', '_');
str = str.replace('*', '_');
str = str.replace(':', '_');
str = str.replace('/', '_');
str = str.replace('"', '_');
str = str.replace('<', '_');
str = str.replace('>', '_');
str = str.replace('|', '_');
fchild = collection.getOutputName(str);
// fchild = collection.getOutputName(aXInputFile.getName());
fparent = collection.getOutputNameParent(fchild);
// split part
fparent += job_processing.getSplitSize() > 0 ? "(" + job_processing.getSplitPart() + ")" : extension;
}
/**
*
*/
public boolean pause()
{
return Common.waitingMainProcess();
}
/**
* nextfile PTS check
*
* returns new pts offset to append
*/
public long nextFilePTS(JobCollection collection, int parser_type, int pes_streamtype, long lastpts, int file_number)
{
return nextFilePTS(collection, parser_type, pes_streamtype, lastpts, file_number, 0L);
}
/**
* nextfile PTS check
*
* returns new pts offset to append
*/
public long nextFilePTS(JobCollection collection, int parser_type, int pes_streamtype, long lastpts, int file_number, long startPoint)
{
return nextFilePTS(collection, parser_type, pes_streamtype, lastpts, 0L, file_number, 0L);
}
/**
* nextfile PTS check
*
* returns new pts offset to append
*/
public long nextFilePTS(JobCollection collection, int parser_type, int pes_streamtype, long lastpts, long ptsoffset, int file_number, long startPoint)
{
JobProcessing job_processing = collection.getJobProcessing();
byte[] data;
long pts = 0;
int position = 0;
int buffersize = collection.getSettings().getIntProperty(Keys.KEY_ScanBuffer);
int pes_ID;
boolean PVA_Audio = collection.getSettings().getBooleanProperty(Keys.KEY_PVA_Audio);
boolean containsPts;
lastpts &= 0xFFFFFFFFL; //ignore bit33 of lastpts
if (collection.getPrimaryInputFileSegments() > file_number)
{
try {
XInputFile aXinputFile = ((XInputFile) collection.getInputFile(file_number)).getNewInstance();
if (aXinputFile == null)
return -1L;
data = new byte[buffersize];
aXinputFile.randomAccessSingleRead(data, startPoint);
switch (parser_type)
{
case CommonParsing.PVA_PARSER: // pva
int pva_pid;
int ptsflag;
int pva_payloadlength;
loop:
while (position < buffersize - 20)
{
if (data[position] != 0x41 || data[position + 1] != 0x56 || data[position + 4] != 0x55)
{
position++;
continue loop;
}
pva_pid = 0xFF & data[position + 2];
ptsflag = 0xFF & data[position + 5];
pva_payloadlength = (0xFF & data[position + 6])<<8 | (0xFF & data[position + 7]);
containsPts = (0x10 & ptsflag) != 0;
if (pva_pid == 1) //video
{
if (containsPts)
{
pts = CommonParsing.readPTS(data, position + 8, 4, !CommonParsing.BYTEREORDERING, true);
break loop;
}
}
else if (pva_pid != 0) //mainaudio mpa and other pids
{
ptsloop:
for (int i = position + 8, j = (PVA_Audio && !containsPts) ? position + 8: position + 11; i < j; i++)
{
if (CommonParsing.validateStartcode(data, i) < 0)
continue ptsloop;
pes_ID = CommonParsing.getPES_IdField(data, i);
if (pes_ID != 0xBD && (0xF0 & pes_ID) != 0xC0)
continue ptsloop;
if ((0x80 & data[i + 7]) == 0)
break ptsloop;
pts = CommonParsing.getPTSfromBytes(data, i + 9); //returns 32bit
break loop;
}
}
position += 8 + pva_payloadlength;
}
break;
case CommonParsing.PRIMARY_PES_PARSER: // mpg
int pes_payloadlength = 0;
int ptslength = 0;
int returncode;
int pes_offset = 0;
boolean nullpacket = false;
loop:
while (position < buffersize - 20)
{
if ((returncode = CommonParsing.validateStartcode(data, position)) < 0)
{
position += -returncode;
continue loop;
}
pes_ID = CommonParsing.getPES_IdField(data, position);
if (pes_ID < 0xBA)
{
position += 3;
continue loop;
}
if (pes_ID == 0xBA)
{
position += 12;
continue loop;
}
pes_payloadlength = CommonParsing.getPES_LengthField(data, position);
if (pes_payloadlength == 0)
nullpacket = true;
if ((0xF0 & pes_ID) != 0xE0 && (0xF0 & pes_ID) != 0xC0 && pes_ID != 0xBD )
{
position += 6 + pes_payloadlength;
continue loop;
}
if (pes_streamtype == CommonParsing.MPEG1PS_TYPE)
{
pes_offset = 6;
skiploop:
while(true)
{
switch (0xC0 & data[position + pes_offset])
{
case 0x40:
pes_offset += 2;
continue skiploop;
case 0x80:
pes_offset += 3;
continue skiploop;
case 0xC0:
pes_offset++;
continue skiploop;
case 0:
break;
}
switch (0x30 & data[position + pes_offset])
{
case 0x20:
containsPts = true;
break skiploop;
case 0x30:
containsPts = true;
break skiploop;
case 0x10:
containsPts = false;
break skiploop;
case 0:
containsPts = false;
pes_offset++;
break skiploop;
}
}
}
else
{
containsPts = (0x80 & data[position + 7]) != 0;
pes_offset = 9;
}
if (containsPts)
pts = CommonParsing.getPTSfromBytes(data, position + pes_offset);
position += 6 + pes_payloadlength;
if (nullpacket)
break;
}
break;
} // end switch
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
//Common.setMessage("AA " + pts + " / " + ptsoffset + " / " + (pts + ptsoffset) + " / " + lastpts);
//re-use given ptsoffset (chapterpoints of vob)
// pts += ptsoffset;
if (file_number == 0 && startPoint == 0)
{ // need global offset?
pts &= 0xFFFFFFFFL;
String str = collection.getSettings().getProperty(Keys.KEY_PtsShift_Value);
if (str.equals("auto"))
{
long newpts = ((pts / 324000000L) - 1L) * 324000000L;
Common.setMessage(Resource.getString("nextfile.shift.auto", "" + (newpts / 324000000L)));
return newpts;
}
else if (!str.equals("0"))
{
Common.setMessage(Resource.getString("nextfile.shift.manual", collection.getSettings().getProperty(Keys.KEY_PtsShift_Value)));
return ((long)(Double.parseDouble(collection.getSettings().getProperty(Keys.KEY_PtsShift_Value)) * 324000000L));
}
else
return 0L;
}
else
{
pts -= job_processing.getNextFileStartPts();
pts &= 0xFFFFFFFFL;
long ret = 0L;
if (Math.abs(pts - lastpts) < 900000)
ret = -1L;
else if (pts > lastpts)
ret = 0L;
else
ret = ((lastpts + 1728000L) - pts); // offset is multiple of 40,24,32,33.1,8ms
if (ret >= 0)
Common.setMessage(Resource.getString("nextfile.next.file.start", Common.formatTime_1(pts / 90L), Common.formatTime_1(lastpts / 90L)));
if (ret > 0)
Common.setMessage(Resource.getString("nextfile.next.file.start.adaption", Common.formatTime_1(ret / 90L)));
return ret;
}
}
/**
*
*/
public Hashtable getStreamObjects(JobProcessing job_processing)
{
return job_processing.getStreamObjects();
}
/**
*
*/
public String processElementaryStreams(String vptslog, int action, int[] clv, JobCollection collection, JobProcessing job_processing)
{
if (action != CommonParsing.ACTION_DEMUX)
{
streamconverter.close(job_processing, CommonParsing.isInfoScan());
return vptslog;
}
//finish video stream
for (int i = 0, NumberOfVideostreams = 0; i < demuxList.size(); i++)
{
streamdemultiplexer = (StreamDemultiplexer) demuxList.get(i);
if (streamdemultiplexer.getType() == CommonParsing.MPEG_VIDEO)
{
// accept only first video
if (NumberOfVideostreams > 0)
{
Common.setMessage("!> further videostream found (PID 0x" + Integer.toHexString(streamdemultiplexer.getPID()).toUpperCase() + " / ID 0x" + Integer.toHexString(streamdemultiplexer.getID()).toUpperCase() + ") -> ignored");
continue;
}
// d2v project
if (CreateD2vIndex || SplitProjectFile)
job_processing.getProjectFileD2V().write(job_processing.getProjectFileExportLength(), job_processing.getExportedVideoFrameNumber());
Common.setMessage("");
Common.setMessage(formatIDString(Resource.getString("ExportPanel.Streamtype.MpgVideo"), streamdemultiplexer.getPID(), streamdemultiplexer.getID(), streamdemultiplexer.subID()));
Common.setMessage(Resource.getString("video.msg.summary") + " " + job_processing.getExportedVideoFrameNumber() + "-" + clv[0] + "-" + clv[1] + "-" + clv[2] + "-" + clv[3] + "-" + clv[4]);
vptslog = streamdemultiplexer.closeVideo(job_processing, collection.getOutputDirectory() + collection.getFileSeparator());
NumberOfVideostreams++;
}
}
processNonVideoElementaryStreams(vptslog, action, clv, collection, job_processing);
return vptslog;
}
/**
*
*/
public void processNonVideoElementaryStreams(String vptslog, int action, int[] clv, JobCollection collection, JobProcessing job_processing)
{
processNonVideoElementaryStreams(vptslog, action, clv, collection, job_processing, null, null);
}
/**
*
*/
public void processNonVideoElementaryStreams(String vptslog, int action, int[] clv, JobCollection collection, JobProcessing job_processing, List tempfiles, XInputFile aXInputFile)
{
//finish other streams
int[] stream_number = job_processing.getStreamNumbers();
String[] key_values = {
Keys.KEY_Streamtype_Ac3Audio[0],
Keys.KEY_Streamtype_Teletext[0],
Keys.KEY_Streamtype_MpgAudio[0],
"",
Keys.KEY_Streamtype_PcmAudio[0],
Keys.KEY_Streamtype_Subpicture[0],
Keys.KEY_Streamtype_Ac3Audio[0],
Keys.KEY_Streamtype_PcmAudio[0]
};
for (int i = 0, es_streamtype; i < demuxList.size(); i++)
{
streamdemultiplexer = (StreamDemultiplexer) demuxList.get(i);
es_streamtype = streamdemultiplexer.getType();
if (es_streamtype == CommonParsing.MPEG_VIDEO)
continue;
// from pva - test with others
if (streamdemultiplexer.getID() == 0)
continue;
String[] values = streamdemultiplexer.close(job_processing, vptslog);
if (values[0].equals(""))
{
Common.setMessage(formatIDString(Resource.getString("StreamParser.NoExport"), streamdemultiplexer.getPID(), 0xFF & streamdemultiplexer.getID(), streamdemultiplexer.subID()));
continue;
}
if (streamdemultiplexer.getStreamNumber() < 0)
{
streamdemultiplexer.setStreamNumber(stream_number[es_streamtype]);
stream_number[es_streamtype]++;
}
String newfile = values[3] + (streamdemultiplexer.getStreamNumber() > 0 ? ("-" + Common.adaptString(stream_number[es_streamtype], 2)) : "") + "." + values[2];
Common.renameTo(values[0], newfile);
values[0] = newfile;
values[3] = vptslog;
switch (es_streamtype)
{
case CommonParsing.AC3_AUDIO:
case CommonParsing.DTS_AUDIO:
if (streamdemultiplexer.subID() != 0 && (0xF0 & streamdemultiplexer.subID()) != 0x80)
{
Common.setMessage(formatIDString(Resource.getString("StreamParser.NoExport"), streamdemultiplexer.getPID(), 0xFF & streamdemultiplexer.getID(), streamdemultiplexer.subID()));
break;
}
case CommonParsing.TELETEXT:
case CommonParsing.MPEG_AUDIO:
case CommonParsing.LPCM_AUDIO:
case CommonParsing.SUBPICTURE:
createStreamProcess(es_streamtype, collection, values, key_values[es_streamtype]);
break;
}
// save infos for output segmentation
if (tempfiles != null)
{
/**/ tempfiles.add(values[0]);
tempfiles.add(aXInputFile);
tempfiles.add(values[1]);
tempfiles.add(values[2]);
/**/
Common.setMessage("tmpfiles " + tempfiles.size());
if (job_processing.getSplitSize() == 0)
{
new File(newfile).delete();
new File(values[1]).delete();
}
}
else
{
new File(newfile).delete();
new File(values[1]).delete();
}
}
}
/**
*
*/
public String formatIDString(String str1, int pid, int id, int subid)
{
String str = "++> " + str1;
str += ": PID 0x" + Common.adaptString(Integer.toHexString(pid).toUpperCase(), 4);
str += " / PesID 0x" + Common.adaptString(Integer.toHexString(id).toUpperCase(), 2);
str += " / SubID 0x" + Common.adaptString(Integer.toHexString(subid).toUpperCase(), 2);
str += " :";
return str;
}
/**
*
*/
public void createStreamProcess(int es_streamtype, JobCollection collection, String[] values, String key_value)
{
Common.setMessage("");
Common.setMessage(formatIDString(Resource.getString(key_value), streamdemultiplexer.getPID(), streamdemultiplexer.getID(), streamdemultiplexer.subID()));
new StreamProcess(es_streamtype, collection, values);
}
/**
*
*/
public void addCellTimeFromFileSegment(JobProcessing job_processing)
{
//addCellTime(job_processing);
}
/** *
*/
public void addCellTime(JobProcessing job_processing)
{
job_processing.addCellTime(job_processing.getExportedVideoFrameNumber());
}
/**
* init conversions
*/
public void initConversion(JobCollection collection, String parent, int action, int source, int splitpart)
{
if (action <= CommonParsing.ACTION_DEMUX)
return;
String[] ext = { "", ".vdr", ".mpg", ".pva", ".ts" };
switch (action)
{
case CommonParsing.ACTION_TO_VDR:
case CommonParsing.ACTION_TO_M2P:
case CommonParsing.ACTION_TO_PVA:
case CommonParsing.ACTION_TO_TS:
streamconverter.init(collection, parent + "[remux]" + ext[action], MainBufferSize, action, splitpart);
break;
case CommonParsing.ACTION_FILTER:
streamconverter.init(collection, parent + "[filter]" + ext[source], MainBufferSize, action, splitpart);
break;
case CommonParsing.ACTION_COPY:
}
}
/**
* getOverhead to collect more samples
*/
public void setOverheadSize(JobCollection collection)
{
OverheadSize = collection.getSettings().getBooleanProperty(Keys.KEY_Input_useReadOverhead) ? 2048000 : 0;
}
}