/*
* @(#)StreamDemultiplexer
*
* Copyright (c) 2005-2011 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.util.ArrayList;
import java.util.List;
import java.io.File;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.BufferedWriter;
import java.io.FileWriter;
import net.sourceforge.dvb.projectx.io.IDDBufferedOutputStream;
import net.sourceforge.dvb.projectx.common.Resource;
import net.sourceforge.dvb.projectx.common.Keys;
import net.sourceforge.dvb.projectx.common.Common;
import net.sourceforge.dvb.projectx.common.JobProcessing;
import net.sourceforge.dvb.projectx.common.JobCollection;
import net.sourceforge.dvb.projectx.parser.CommonParsing;
import net.sourceforge.dvb.projectx.video.Video;
/**
* demuxes all packetized data
*/
public class StreamDemultiplexer extends Object {
private boolean ptsover = false;
private boolean misshead = false;
private boolean first = true;
private boolean overlap = false;
private boolean seqhead = false;
private boolean isPTSwritten = false;
private boolean isEnabled = true;
//
private boolean isH264 = false;
private boolean WriteNonVideo;
private boolean WriteVideo;
private boolean Debug;
private boolean DecodeVBI;
private boolean RebuildPTS;
private boolean RebuildPictPTS;
private boolean RebuildPTStoggle;
private boolean Streamtype_MpgVideo_Enabled;
private boolean Streamtype_MpgAudio_Enabled;
private boolean Streamtype_Ac3Audio_Enabled;
private boolean Streamtype_PcmAudio_Enabled;
private boolean Streamtype_Teletext_Enabled;
private boolean Streamtype_Subpicture_Enabled;
private boolean AddSequenceEndcode;
private boolean RenameVideo;
private boolean CreateD2vIndex;
private boolean CreateM2sIndex;
private boolean SplitProjectFile;
private boolean CreateCellTimes;
private boolean CreateInfoIndex;
private boolean AppendPidToFileName;
private boolean AppendLangToFileName;
private boolean EnableHDDemux;
private String LanguageFilter = "";
private String language = "";
private long AddOffset = 0;
private long target_position = 0;
private long ptsoffset = 0;
private long pts = -1;
private long lastPTS = -1;
private int pack = -1;
private int pes_ID = 0;
private int newID = 0;
private int PID = 0;
private int es_streamtype = 0;
private int subid = 0x1FF;
private int pes_streamtype = 0;
private int lfn = -1;
private int buffersize = 1024;
private int sourcetype = 0;
private int[] MPGVideotype = { -1 }; // 0 =m1v, 1 = m2v, 2 = h264 -- changed at goptest
private int StreamNumber = -1;
private String FileName = "";
private String parentname = "";
private String[] type = { "ac", "tt", "mp", "mv", "pc", "sp", "vp" };
private String[] source = { ".$spes$", ".$ppes$", ".$ts$", ".$pva$" };
// private String[] videoext = { ".mpv", ".mpv", ".m1v", ".m2v" };
private String[] videoext = { ".mpv", ".mpv", ".264", ".m1v", ".m2v", ".264" };
private IDDBufferedOutputStream out;
private DataOutputStream pts_log;
private ByteArrayOutputStream vidbuf;
private ByteArrayOutputStream vptsbytes;
private ByteArrayOutputStream packet;
private DataOutputStream vpts;
private byte[] subpicture_header = { 0x53, 0x50, 0, 0, 0, 0, 0, 0, 0, 0 }; //'SP'+8b(pts)
private byte[] lpcm_header = { 0x50, 0x43, 0x4D, 0, 0, 0, 0, 0, 0, 0 }; //'PCM'+5b(pts)+2b(size)
/**
*
*/
public StreamDemultiplexer(JobCollection collection)
{
getSettings(collection);
}
/**
*
*/
public StreamDemultiplexer(JobCollection collection, long val)
{
getSettings(collection);
ptsoffset = val;
}
/**
* Object yet intialized
*/
public int getNum()
{
return lfn;
}
/**
* get PID for later selection
*/
public int getPID()
{
return PID;
}
/**
* returns PES pes_ID for later selection
*/
public int getID()
{
return pes_ID;
}
/**
* set PID for later selection
*/
public void setPID(int val)
{
PID = val;
}
/**
* set pes_ID for later selection
*/
public void setID(int val)
{
pes_ID = val;
}
/**
* set newID for later selection /or subid
*/
public void setnewID(int val)
{
newID = val;
}
/**
* returns newID for later selection /or subid
*/
public int getnewID()
{
return newID;
}
/**
* returns packet counter
*/
public int getPackCount()
{
return pack;
}
/**
* returns es_streamtype
*/
public int getType()
{
return es_streamtype;
}
/**
* sets stream tpye, vdr/es/mpeg1/2...
*/
public void setStreamType(int val)
{
pes_streamtype = val;
}
/**
* returns stream tpye, vdr/es/mpeg1/2...
*/
public int getStreamType()
{
return pes_streamtype;
}
/**
* sets type
*/
public void setType(int val)
{
es_streamtype = val;
}
/**
* sets subid
*/
public void setsubID(int val)
{
subid = val;
}
/**
* returns subid
*/
public int subID()
{
return subid;
}
/**
* is it TTX?
*/
public boolean isTTX()
{
return es_streamtype == CommonParsing.TELETEXT;
}
/**
* set ttx
*/
public void setTTX(boolean b)
{
if (b)
es_streamtype = CommonParsing.TELETEXT;
}
/**
* last PTS
*/
public long getPTS()
{
return pts;
}
/**
* PTS offset if needed
*/
public void PTSOffset(long val)
{
ptsoffset = val;
}
/**
*
*/
public void setStreamNumber(int val)
{
StreamNumber = val;
}
/**
*
*/
public int getStreamNumber()
{
return StreamNumber;
}
/**
* stream type preselector
*/
public void setStreamEnabled(boolean b)
{
isEnabled = b;
}
/**
* stream type preselector
*/
public boolean StreamEnabled()
{
switch(newID>>>4)
{
case 0xE: //video
return Streamtype_MpgVideo_Enabled;
case 0xC: //mpa
case 0xD:
return StreamEnabled(Streamtype_MpgAudio_Enabled);
case 0x8: //ac3,mpg
return StreamEnabled(Streamtype_Ac3Audio_Enabled);
case 0xA: //lpcm,mpg
return StreamEnabled(Streamtype_PcmAudio_Enabled);
case 0x9: //ttx
return Streamtype_Teletext_Enabled;
case 0x2: //subpic
case 0x3:
return Streamtype_Subpicture_Enabled;
default:
return isEnabled;
}
}
/**
* stream type preselector
*/
private boolean StreamEnabled(boolean b)
{
if (!b || language.length() == 0 || LanguageFilter.length() == 0)
return b;
return (LanguageFilter.indexOf(language) >= 0);
}
/**
*
*/
private void getSettings(JobCollection collection)
{
Streamtype_MpgVideo_Enabled = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_MpgVideo);
Streamtype_MpgAudio_Enabled = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_MpgAudio);
Streamtype_Ac3Audio_Enabled = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_Ac3Audio);
Streamtype_PcmAudio_Enabled = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_PcmAudio);
Streamtype_Teletext_Enabled = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_Teletext);
Streamtype_Subpicture_Enabled = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_Subpicture);
AddOffset = collection.getSettings().getBooleanProperty(Keys.KEY_additionalOffset) ? 90L * collection.getSettings().getIntProperty(Keys.KEY_ExportPanel_additionalOffset_Value) : 0; // time offset for data
WriteNonVideo = collection.getSettings().getBooleanProperty(Keys.KEY_WriteOptions_writeAudio);
WriteVideo = collection.getSettings().getBooleanProperty(Keys.KEY_WriteOptions_writeVideo);
Debug = collection.getSettings().getBooleanProperty(Keys.KEY_DebugLog);
DecodeVBI = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_Vbi);
RebuildPTStoggle = collection.getSettings().getBooleanProperty(Keys.KEY_SubtitlePanel_rebuildPTStoggle);
RebuildPTS = collection.getSettings().getBooleanProperty(Keys.KEY_SubtitlePanel_rebuildPTS);
RebuildPictPTS = collection.getSettings().getBooleanProperty(Keys.KEY_SubtitlePanel_rebuildPictPTS);
AddSequenceEndcode = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_addEndcode);
RenameVideo = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_renameVideo);
CreateD2vIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createD2vIndex);
CreateM2sIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createM2sIndex);
SplitProjectFile = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_splitProjectFile);
CreateCellTimes = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createCellTimes);
CreateInfoIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createInfoIndex);
AppendPidToFileName = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_appendPidToFileName);
AppendLangToFileName = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_appendLangToFileName);
EnableHDDemux = collection.getSettings().getBooleanProperty(Keys.KEY_enableHDDemux);
LanguageFilter = collection.getSettings().getProperty(Keys.KEY_LanguageFilter);
}
/**
*
*/
private void setFileName()
{
FileName = parentname + source[sourcetype] + lfn + "-" + Long.toHexString(0xFFFFFFL & System.currentTimeMillis()).toUpperCase();
}
/**
* init nonVideo streams
*/
private void initNonVideo(JobCollection collection, String _name)
{
parentname = _name;
setFileName();
target_position = 0;
getSettings(collection);
language = collection.getJobProcessing().getAudioStreamLanguage(getPID());
language = language.replace('_', ' '); //simple hack
language = language.trim();
try {
out = new IDDBufferedOutputStream(new FileOutputStream(FileName), buffersize);
pts_log = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(FileName + ".pts"), 65535));
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
/**
* main init nonVideo
*/
public void init(JobCollection collection, String _name, int _buffersize, int _lfn, int _parsertype)
{
lfn = _lfn;
buffersize = _buffersize;
sourcetype = _parsertype;
initNonVideo(collection, _name);
}
/**
* re-init nonVideo
*/
public void init2(JobCollection collection, String _name)
{
initNonVideo(collection, _name);
}
/**
* process nonvideo data = 1 pespacket from demux
*/
public void write(JobProcessing job_processing, byte[] pes_packet, boolean pes_hasHeader)
{
write(job_processing, pes_packet, 0, pes_packet.length, pes_hasHeader);
}
/**
* process nonvideo data = 1 pespacket from demux
*/
public void write(JobProcessing job_processing, byte[] pes_packet, int pes_packetoffset, int pes_payloadlength, boolean pes_hasHeader)
{
boolean pes_isAligned = false;
int pes_extensionlength = 0;
int offset = pes_packetoffset;
int _es_streamtype = CommonParsing.AC3_AUDIO;
pack++;
if (pes_hasHeader)
{
if (CommonParsing.validateStartcode(pes_packet, offset) < 0)
{
Common.setMessage(Resource.getString("demux.error.audio.startcode") + " " + pack + " (" + Integer.toHexString(PID) + "/" + Integer.toHexString(pes_ID) + "/" + Integer.toHexString(newID) + "/" + es_streamtype + ")");
return;
}
pes_ID = CommonParsing.getPES_IdField(pes_packet, offset);
pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, offset);
pes_isAligned = (pes_streamtype == CommonParsing.PES_AV_TYPE || pes_streamtype == CommonParsing.MPEG2PS_TYPE) && (4 & pes_packet[6 + offset]) != 0;
if (pes_ID == CommonParsing.PADDING_STREAM_CODE)
return;
if (pes_streamtype == CommonParsing.MPEG1PS_TYPE)
{
skiploop:
while(true)
{
switch (0xC0 & pes_packet[6 + offset])
{
case 0x40:
offset += 2;
continue skiploop;
case 0x80:
offset += 3;
continue skiploop;
case 0xC0:
offset++;
continue skiploop;
case 0:
break;
}
switch (0x30 & pes_packet[6 + offset])
{
case 0x20: //PTS
pes_extensionlength = 5;
break skiploop;
case 0x30: //PTS+DTS
pes_extensionlength = 10;
break skiploop;
case 0x10: //DTS
offset += 5;
break skiploop;
case 0:
offset++;
break skiploop;
}
}
}
else
{
pes_extensionlength = CommonParsing.getPES_ExtensionLengthField(pes_packet, offset);
if (pes_ID == CommonParsing.PRIVATE_STREAM_1_CODE && pes_extensionlength == 0x24 && (0xFF & pes_packet[9 + pes_extensionlength + offset])>>>4 == 1)
_es_streamtype = CommonParsing.TELETEXT;
// workaround uk freesat teletext
else if (pes_ID == CommonParsing.PRIVATE_STREAM_1_CODE && pes_extensionlength == 0x24 && (0xFF & pes_packet[9 + pes_extensionlength + offset]) == 0x99)
_es_streamtype = CommonParsing.TELETEXT;
/**
* no PTS in PES_extension
*/
if ((0x80 & pes_packet[7 + offset]) == 0)
{
offset += pes_extensionlength;
pes_extensionlength = 0;
}
offset += 3;
}
es_streamtype = pes_ID == CommonParsing.PRIVATE_STREAM_1_CODE ? _es_streamtype : CommonParsing.MPEG_AUDIO;
subid = ((es_streamtype == CommonParsing.AC3_AUDIO || es_streamtype == CommonParsing.DTS_AUDIO || es_streamtype == CommonParsing.TELETEXT) && ((pes_streamtype == CommonParsing.PES_AV_TYPE && (pes_isAligned || es_streamtype == CommonParsing.TELETEXT)) || pes_streamtype == CommonParsing.MPEG2PS_TYPE)) ? (0xFF & pes_packet[9 + (0xFF & pes_packet[8 + pes_packetoffset]) + pes_packetoffset]) : 0;
// workaround uk freesat teletext
if (isTTX())
subid &= 0x1F;
switch (subid>>>4)
{
case 8:
if (pes_streamtype == CommonParsing.PES_AV_TYPE || pes_streamtype == CommonParsing.MPEG1PS_TYPE)
{
subid = 0;
break;
}
case 1:
case 2:
case 3:
case 9:
case 0xA:
break;
case 0:
if (pes_isAligned && subid == 0x09)
break;
default:
if (pes_streamtype != CommonParsing.MPEG2PS_TYPE)
subid = 0;
}
switch (subid>>>4)
{
case 0xA: //LPCM from MPG-PS
es_streamtype = CommonParsing.LPCM_AUDIO;
break;
case 2: //SubPic 0-31 from MPG-PS
case 3: //SubPic 32-63 from MPG-PS
es_streamtype = CommonParsing.SUBPICTURE;
break;
case 8: //AC3-DTS from MPG-PS
case 1: //TTX
case 0: //AC3-DTS from PES/VDR
break;
case 9: //VBI from TS,MPG2
if (pes_isAligned)
{
if (pes_streamtype == CommonParsing.MPEG1PS_TYPE || pes_streamtype == CommonParsing.PES_AV_TYPE)
subid = 0;
if (DecodeVBI)
VBI.parsePES(pes_packet, pes_packetoffset);
return;
}
break;
default:
return;
}
pes_payloadlength -= (offset - pes_packetoffset + pes_extensionlength);
offset += 6;
}
if (!WriteNonVideo)
return;
if (out == null)
return;
try {
// recreate PTS
if (RebuildPTS && es_streamtype == CommonParsing.TELETEXT)
{
if (job_processing.getBorrowedPts() != lastPTS)
{
lastPTS = job_processing.getBorrowedPts();
pts_log.writeLong(lastPTS);
pts_log.writeLong(target_position);
if (Debug)
System.out.println(" stolen ttx PTS: " + lastPTS + " /ao " + AddOffset + " /tp " + target_position);
}
}
//recreate subpicture
else if (RebuildPictPTS && es_streamtype == CommonParsing.SUBPICTURE)
{
if (job_processing.getBorrowedPts() != lastPTS)
{
lastPTS = job_processing.getBorrowedPts();
pts = lastPTS; //to rewrite into sp file
pts_log.writeLong(lastPTS);
pts_log.writeLong(target_position);
if (Debug)
System.out.println(" stolen subpic PTS: " + lastPTS + " /ao " + AddOffset + " /tp " + target_position);
}
}
/**
* read out source PTS
*/
else if (pes_extensionlength > 0 && pes_payloadlength >= 0)
{
//--> �ndern
pts = CommonParsing.getPTSfromBytes(pes_packet, offset); //returns 32bit
pts -= job_processing.getNextFileStartPts();
pts &= 0xFFFFFFFFL; //trim to 32bit
if ( (pts & 0xFF000000L) == 0xFF000000L )
ptsover = true; // bit 33 was set
if (ptsover && pts < 0xF0000000L)
pts |= 0x100000000L;
//<--
pts += ptsoffset;
pts += AddOffset;
if (lastPTS != pts)
{
if ((es_streamtype == CommonParsing.MPEG_AUDIO || es_streamtype == CommonParsing.AC3_AUDIO || es_streamtype == CommonParsing.DTS_AUDIO || es_streamtype == CommonParsing.LPCM_AUDIO)
&& lastPTS != -1 && Math.abs(lastPTS - pts) > 100000)
Common.setMessage("!> ID 0x" + Integer.toHexString(pes_ID).toUpperCase() + " (sub 0x" + Integer.toHexString(subid).toUpperCase() + ") packet# " + pack + ", big PTS difference: this " + pts + ", prev. " + lastPTS);
pts_log.writeLong(pts);
pts_log.writeLong(target_position);
}
if (Debug)
System.out.println(" pda PTS: " + pts + "/ " + AddOffset + "/ " + target_position);
lastPTS = pts;
}
/**
* save re-build PTS, taken from 1st mpa
*/
//if (newID == 0xC0 && job_processing.getBorrowedPts() != lastPTS)
if (!RebuildPTStoggle && newID == 0xC0 && job_processing.getBorrowedPts() != lastPTS)
job_processing.setBorrowedPts(lastPTS);
else if (RebuildPTStoggle && newID == 0x80 && job_processing.getBorrowedPts() != lastPTS)
job_processing.setBorrowedPts(lastPTS);
/**
* skip subid and info fields
*/
switch(subid>>>4)
{
case 0xA: //LPCM, keep info fields 6 bytes
offset += 1; //7
pes_payloadlength -= 1; //7
break;
case 8: //AC3-DTS
offset += 4;
pes_payloadlength -= 4;
break;
case 1: //TTX
offset += 1;
pes_payloadlength -= 1;
break;
case 2: //subpic 0.31
case 3: //subpic 32.63
offset += 1;
pes_payloadlength -= 1;
}
if (subid == 0x09)
{
offset += 4;
pes_payloadlength -= 4;
}
if (pes_payloadlength <= 0)
return;
if (pes_extensionlength > 0)
{
switch(es_streamtype)
{
case CommonParsing.SUBPICTURE:
CommonParsing.setValue(subpicture_header, 2, 8, CommonParsing.BYTEREORDERING, pts);
target_position += writePacket(subpicture_header);
/**
* DVB subs adaption
*/
if (CommonParsing.nextBits(pes_packet, (offset + pes_extensionlength) * 8, 16) == 0xF)
{
out.write(0xFF & (pes_payloadlength + 3)>>>8);
out.write(0xFF & (pes_payloadlength + 3));
out.write(0); //padding
target_position += 3;
}
break;
case CommonParsing.LPCM_AUDIO:
CommonParsing.setValue(lpcm_header, 3, 5, CommonParsing.BYTEREORDERING, pts);
lpcm_header[8] = (byte)(0xFF & pes_payloadlength>>>8);
lpcm_header[9] = (byte)(0xFF & pes_payloadlength);
target_position += writePacket(lpcm_header);
}
}
/**
* DVB subs adaption, prevent lost packets
*/
else if (es_streamtype == CommonParsing.SUBPICTURE && pes_isAligned && CommonParsing.nextBits(pes_packet, (offset + pes_extensionlength) * 8, 16) == 0xF)
// else if (es_streamtype == CommonParsing.SUBPICTURE && CommonParsing.nextBits(pes_packet, (offset + pes_extensionlength) * 8, 16) == 0xF)
{
CommonParsing.setValue(subpicture_header, 2, 8, CommonParsing.BYTEREORDERING, 0);
target_position += writePacket(subpicture_header);
out.write(0xFF & (pes_payloadlength + 3)>>>8);
out.write(0xFF & (pes_payloadlength + 3));
out.write(0); //padding!
target_position += 3;
}
if (subid == 0x09)
for (int i = 0; i < pes_payloadlength; i += 3)
target_position += writePacket(pes_packet, offset + pes_extensionlength + i, 2);
else
target_position += writePacket(pes_packet, offset + pes_extensionlength, pes_payloadlength);
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
/**
*
*/
public String[] close(JobProcessing job_processing, String _vptslog)
{
String pts_log_name = FileName + ".pts";
String parameters[] = { FileName, pts_log_name, type[es_streamtype], parentname };
try {
if (out == null)
{
parameters[0] = "";
return parameters;
}
out.flush();
out.close();
pts_log.flush();
pts_log.close();
if (new File(pts_log_name).length() < 10)
CommonParsing.logAlias(job_processing, _vptslog, pts_log_name);
if (new File(FileName).length() < 10)
{
Common.setMessage("-> temp. Filesize < 10 Bytes");
new File(FileName).delete();
new File(pts_log_name).delete();
parameters[0] = "";
}
else
{
if (AppendPidToFileName)
parameters[3] = parentname + formatIDString(getPID(), getID(), subID());
if (AppendLangToFileName)
parameters[3] += job_processing.getAudioStreamLanguage(getPID());
}
} catch (IOException e) {
Common.setExceptionMessage(e);
}
return parameters;
}
/**
*
*/
private String formatIDString(int pid, int id, int subid)
{
String str = "";
str += "{0x" + Common.adaptString(Integer.toHexString(pid).toUpperCase(), 4);
str += "-0x" + Common.adaptString(Integer.toHexString(id).toUpperCase(), 2);
str += "-0x" + Common.adaptString(Integer.toHexString(subid).toUpperCase(), 2);
str += "}";
return str;
}
/**
*
*/
public void initVideo(JobCollection collection, String _name, int _buffersize, int _lfn, int _parsertype)
{
getSettings(collection);
parentname = _name;
lfn = _lfn;
buffersize = _buffersize;
sourcetype = _parsertype;
setFileName();
es_streamtype = CommonParsing.MPEG_VIDEO;
MPGVideotype[0] = -1;
try {
out = new IDDBufferedOutputStream(new FileOutputStream(FileName), buffersize);
if (CreateM2sIndex)
out.InitIdd(FileName, 1);
if (CreateInfoIndex)
out.InitInfo(FileName);
pts_log = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(FileName + ".pts"), 65535));
pts_log.write(CommonParsing.PTSVideoHeader);
packet = new ByteArrayOutputStream();
vidbuf = new ByteArrayOutputStream();
vptsbytes = new ByteArrayOutputStream();
vpts = new DataOutputStream(vptsbytes);
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
public void initVideo2(JobCollection collection, String _name)
{
getSettings(collection);
parentname = _name;
setFileName();
first = true;
MPGVideotype[0] = -1;
try {
out = new IDDBufferedOutputStream(new FileOutputStream(FileName), buffersize);
if (CreateM2sIndex)
out.InitIdd(FileName, 1);
if (CreateInfoIndex)
out.InitInfo(FileName);
pts_log = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(FileName + ".pts"), 65535));
pts_log.write(CommonParsing.PTSVideoHeader);
packet.reset();
vidbuf.reset();
vptsbytes.reset();
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
/**
* clean Up for next foreign inputfile in case of multiple
*/
public void resetVideo()
{
if (vidbuf != null)
vidbuf.reset();
if (packet != null)
packet.reset();
if (vptsbytes != null)
vptsbytes.reset();
first = true;
}
public String closeVideo(JobProcessing job_processing, String workouts)
{
String logfile = "-1";
List cell = job_processing.getCellTimes();
int[] clv = job_processing.getStatusVariables();
try {
if (AddSequenceEndcode && job_processing.getExportedVideoFrameNumber() > 0)
{
if (MPGVideotype[0] < 2)
{
out.write(Video.getSequenceEndCode());
job_processing.countMediaFilesExportLength(+4);
job_processing.countAllMediaFilesExportLength(+4);
}
else // h264
{
out.write(new byte[]{0, 0, 0, 1, 0xA});
job_processing.countMediaFilesExportLength(+5);
job_processing.countAllMediaFilesExportLength(+5);
}
}
packet.close();
vidbuf.flush();
vidbuf.close();
out.flush();
out.close();
pts_log.flush();
pts_log.close();
vpts.flush();
vpts.close();
vptsbytes.flush();
vptsbytes.close();
String videofile = "";
if (new File(FileName).length() < 10)
{
new File(FileName).delete();
if (!WriteVideo && new File(FileName + ".pts").length() > 16)
logfile = FileName + ".pts";
else
new File(FileName + ".pts").delete();
}
else
{
//int ot = (RenameVideo || CreateD2vIndex || SplitProjectFile) ? 0 : 2;
int ot = (RenameVideo || CreateD2vIndex || SplitProjectFile) ? 0 : 3;
videofile = parentname;
if (AppendPidToFileName)
videofile += formatIDString(getPID(), getID(), subID());
videofile += videoext[MPGVideotype[0] + ot];
File newfile = new File(videofile);
if (newfile.exists())
newfile.delete();
Common.renameTo(new File(FileName), newfile);
logfile = FileName + ".pts";
CommonParsing.setVideoHeader(job_processing, videofile, logfile, clv, MPGVideotype);
/**
* celltimes.txt
*/
if (CreateCellTimes && !cell.isEmpty())
{
BufferedWriter cellout = new BufferedWriter(new FileWriter(workouts + "CellTimes.txt"));
for (int i = 0; i < cell.size(); i++)
{
cellout.write(cell.get(i).toString());
cellout.newLine();
}
cellout.close();
Common.setMessage(Resource.getString("demux.msg.celltimes", workouts));
long fl = new File(workouts + "CellTimes.txt").length();
job_processing.countMediaFilesExportLength(fl);
job_processing.countAllMediaFilesExportLength(fl);
}
cell.clear();
}
if (CreateM2sIndex)
{
if (new File(videofile).exists())
{
String tmpFN = videofile.toString().substring(0, videofile.toString().lastIndexOf("."));
out.renameVideoIddTo(tmpFN);
}
else
out.deleteIdd();
}
if (CreateInfoIndex)
{
if (new File(videofile).exists())
{
String tmpFN = videofile.toString();
out.renameVideoInfoTo(tmpFN);
}
else
out.deleteInfo();
}
} catch (IOException e) {
Common.setExceptionMessage(e);
}
return logfile;
}
/**
* temporary redirected access to goptest from video-es
*/
public void writeVideoES(JobProcessing job_processing, IDDBufferedOutputStream _out, byte[] _vidbuf, byte[] _vptsbytes, DataOutputStream _pts_log, String _parentname, int[] _MPGVideotype, List _CutpointList, List _ChapterpointList, boolean _doWrite)
{
job_processing.getGop().goptest(job_processing, _out, _vidbuf, _vptsbytes, _pts_log, _parentname, _MPGVideotype, _CutpointList, _ChapterpointList, _doWrite);
}
/**
* write video
* data = 1 pespacket from demux
*/
public void writeVideo(JobProcessing job_processing, byte[] pes_packet, boolean pes_hasHeader, List CutpointList, List ChapterpointList)
{
writeVideo(job_processing, pes_packet, 0, pes_packet.length, pes_hasHeader, CutpointList, ChapterpointList);
}
/**
* write video
* data = 1 pespacket from demux
*/
public void writeVideo(JobProcessing job_processing, byte[] pes_packet, int pes_packetoffset, int pes_payloadlength, boolean pes_hasHeader, List CutpointList, List ChapterpointList)
{
int pes_extensionlength = 0;
int offset = pes_packetoffset;
byte[] data = null;
int[] clv = job_processing.getStatusVariables();
pack++;
if (!pes_hasHeader)
{
if (job_processing.getPvaVideoPts() != -1)
{
offset -= 4;
pes_extensionlength += 4;
}
}
else
{
if (CommonParsing.validateStartcode(pes_packet, offset) < 0)
{
Common.setMessage(Resource.getString("demux.error.video.startcode") + " " + pack + " (" + Integer.toHexString(PID) + "/" + Integer.toHexString(pes_ID) + "/" + Integer.toHexString(newID) + "/" + es_streamtype + ")");
return;
}
pes_ID = CommonParsing.getPES_IdField(pes_packet, offset);
pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, offset);
if (pes_streamtype == CommonParsing.MPEG1PS_TYPE)
{
skiploop:
while(true)
{
switch (0xC0 & pes_packet[6 + offset])
{
case 0x40:
offset += 2;
continue skiploop;
case 0x80:
offset += 3;
continue skiploop;
case 0xC0:
offset++;
continue skiploop;
case 0:
break;
}
switch (0x30 & pes_packet[6 + offset])
{
case 0x20: //PTS
pes_extensionlength = 5;
break skiploop;
case 0x30: //PTS + DTS
pes_extensionlength = 10;
break skiploop;
case 0x10: //DTS
offset += 5;
break skiploop;
case 0:
offset++;
break skiploop;
}
}
}
else
{
pes_extensionlength = CommonParsing.getPES_ExtensionLengthField(pes_packet, offset);
if ((0x80 & pes_packet[7 + offset]) == 0)
{
offset += pes_extensionlength;
pes_extensionlength = 0;
}
offset += 3;
}
pes_payloadlength -= (offset - pes_packetoffset + pes_extensionlength);
offset += 6;
}
/**
* read pts
*/
if (pes_extensionlength > 0 && pes_payloadlength >= 0)
{
//--> �ndern
pts = !pes_hasHeader ? job_processing.getPvaVideoPts() : CommonParsing.getPTSfromBytes(pes_packet, offset); //returns 32bit
pts -= job_processing.getNextFileStartPts();
pts &= 0xFFFFFFFFL; // trim to 32bit
if ( (pts & 0xFF000000L) == 0xFF000000L )
ptsover = true; // bit 33 was set
if (ptsover && pts < 0xF0000000L)
pts |= 0x100000000L;
//<--
pts += ptsoffset;
if (Debug)
System.out.println(" pdv PTS: " + pts);
isPTSwritten = false;
}
try {
if (pes_payloadlength <= 0)
Common.setMessage(Resource.getString("demux.error.video.payload") + " (" + pack + "/" + pes_packet.length + "/" + offset + "/" + pes_extensionlength + "/" + pes_payloadlength + ")");
else
packet.write(pes_packet, offset + pes_extensionlength, pes_payloadlength);
packet.flush();
/** simple demux
byte[] ddd = new byte[pes_payloadlength];
System.arraycopy(pes_packet, offset + pes_extensionlength, ddd, 0, pes_payloadlength);
job_processing.getGop().h264test(job_processing, out, ddd, vptsbytes.toByteArray(), pts_log, parentname, MPGVideotype, CutpointList, ChapterpointList);
packet.reset();
if (1 == 1)
return;
**/
data = packet.toByteArray();
packet.reset();
boolean gop = false;
boolean packetfirstframe = true;
int nal_unit = 0;
int nal_ref = 0;
packloop:
for (int i = 0, j = 0, k = 0, id, returncode; i < data.length - 3; i++)
{
if ((returncode = CommonParsing.validateStartcode(data, i)) < 0)
{
i += (-returncode) - 1;
continue packloop;
}
id = CommonParsing.getPES_IdField(data, i);
// mpeg4 part
//optional deactivator
if (!EnableHDDemux)
isH264 = false;
if (isH264)
{
if (i == 0 || data[i - 1] != 0 || (0x80 & id) != 0) // not 00 00 00 01 0XXX-XXXX
continue packloop;
i--; // return to startcode
nal_ref = 3 & id>>5;
nal_unit = 0x1F & id;
if (Debug)
System.out.println("i " + i + " /NAL ref " + nal_ref + " /unit " + nal_unit);
switch (nal_unit)
{
case 1: // non IDR pic data
case 5: // IDR pic data
if (!isPTSwritten && pts != -1)
{
vpts.writeLong(pts);
vpts.writeLong((long) k);
vpts.flush();
isPTSwritten = true;
}
break;
case 7: // sequence param set // 9-7-8-6-1 ; 9-7-6-8-6-1
vidbuf.write(data, 0, j); // save last data until run-in
if (!first)
job_processing.getGop().h264test(job_processing, out, vidbuf.toByteArray(), vptsbytes.toByteArray(), pts_log, parentname, MPGVideotype, CutpointList, ChapterpointList);
vptsbytes.reset();
vidbuf.reset();
if (!isPTSwritten && pts != -1)
{
vpts.writeLong(pts);
vpts.writeLong((long) 0);
vpts.flush();
isPTSwritten = true;
}
first = false;
gop = true;
// save new data from run-in
// expects there's no next seq param set
vidbuf.write(data, j, data.length - j);
break;
case 9: // run-in
j = i;
k = vidbuf.size();
break;
}
i += 2;
continue packloop;
}
//
// 00 00 00 01 + 0XX0-1001 lead-in, toggle with mpeg1-2, shall be set once
else if (MPGVideotype[0] < 0)
{
isH264 = i > 0 && data[i - 1] == 0 && (0x9F & id) == 9;
}
if (isH264) // last return, never been called after that
continue packloop;
/**
* new frame at first
*/
if (!isPTSwritten && packetfirstframe && id == CommonParsing.PICTURE_START_CODE)
{
if (MPGVideotype[0] < 0)
MPGVideotype[0] = 0;
if (misshead && i < 3)
{
misshead = false;
continue packloop;
}
if (pts != -1)
{
vpts.writeLong(pts);
vpts.writeLong((long)vidbuf.size());
vpts.flush();
}
isPTSwritten = true;
packetfirstframe = false;
i += 8;
}
else if (id == CommonParsing.SEQUENCE_HEADER_CODE || id == CommonParsing.SEQUENCE_END_CODE || id == CommonParsing.GROUP_START_CODE)
{
if (MPGVideotype[0] < 0)
MPGVideotype[0] = 0;
if (id == CommonParsing.SEQUENCE_HEADER_CODE)
seqhead = true;
if (id == CommonParsing.GROUP_START_CODE && seqhead && vidbuf.size() < 400)
{
seqhead = false;
continue packloop;
}
vidbuf.write(data, j, i);
if (!first)
job_processing.getGop().goptest(job_processing, out, vidbuf.toByteArray(), vptsbytes.toByteArray(), pts_log, parentname, MPGVideotype, CutpointList, ChapterpointList);
vptsbytes.reset();
vidbuf.reset();
/**
* split size reached
*/
if (job_processing.getSplitSize() > 0 && job_processing.getSplitSize() < job_processing.getAllMediaFilesExportLength())
return;
/**
* d2v split reached
*/
if (SplitProjectFile && job_processing.getProjectFileExportLength() > job_processing.getProjectFileSplitSize())
{
int part = job_processing.getProjectFileD2V().getPart() + 1;
String newpart = parentname + "[" + part + "].mpv";
/**
* sequence end code
*/
if (WriteVideo && AddSequenceEndcode && job_processing.getExportedVideoFrameNumber() > 0 )
{
out.write(Video.getSequenceEndCode());
job_processing.countMediaFilesExportLength(+4);
job_processing.countAllMediaFilesExportLength(+4);
}
out.flush();
out.close();
//System.gc();
out = new IDDBufferedOutputStream( new FileOutputStream(newpart), buffersize);
/**
* M2S idd
*/
if (CreateM2sIndex)
out.InitIdd(newpart, 1);
if (CreateInfoIndex)
out.InitInfo(newpart);
job_processing.getProjectFileD2V().setFile(newpart);
job_processing.setProjectFileExportLength(0);
}
if (!isPTSwritten && packetfirstframe)
{
if (pts != -1)
{
vpts.writeLong(pts);
vpts.writeLong(vidbuf.size());
vpts.flush();
}
isPTSwritten = true;
}
if (id == CommonParsing.SEQUENCE_END_CODE)
{
Common.setMessage(Resource.getString("demux.msg.skip.sec") + " " + clv[6]);
first = true;
job_processing.setSequenceHeader(false);
i += 3;
continue packloop;
}
else
vidbuf.write(data, i, data.length - i);
if (id == CommonParsing.GROUP_START_CODE)
{
job_processing.setSequenceHeader(false);
if (job_processing.getSplitPart() > 0)
first = false;
}
else if (id == CommonParsing.SEQUENCE_HEADER_CODE)
{
job_processing.setSequenceHeader(true);
first = false;
}
gop = true;
misshead = false;
break packloop;
}
} // end packloop
if (!gop)
{
if (data.length > 2)
{
vidbuf.write(data, 0, data.length - 3);
packet.write(data, data.length - 3, 3);
misshead = true;
}
else
vidbuf.write(data);
}
} catch (IOException e) {
Common.setExceptionMessage(e);
}
if (vidbuf.size() > 6144000)
{
vptsbytes.reset();
vidbuf.reset();
packet.reset();
Common.setMessage(Resource.getString("demux.error.gop.toobig"));
misshead = false;
first = true;
}
}
/**
* simply write the packet
*/
private int writePacket(byte[] packet) throws IOException
{
return writePacket(packet, 0, packet.length);
}
/**
* simply write the packet
*/
private int writePacket(byte[] packet, int offset, int length) throws IOException
{
if (offset < 0 || offset >= packet.length)
{
Common.setMessage("!> packet writing: index out of bounds, ignore it.. (" + Integer.toHexString(getPID()) + " / " + Integer.toHexString(getID()) + " / " + Integer.toHexString(getnewID()) + " / " + getPackCount() + " -- " + packet.length + " / " + offset + " / " + length + ") @ PTS " + Common.formatTime_1(lastPTS / 90));
return 0;
}
if (offset + length > packet.length)
{
Common.setMessage("!> packet writing: length index out of bounds, shortened.. (" + Integer.toHexString(getPID()) + " / " + Integer.toHexString(getID()) + " / " + Integer.toHexString(getnewID()) + " / " + getPackCount() + " -- " + packet.length + " / " + offset + " / " + length + ") @ PTS " + Common.formatTime_1(lastPTS / 90));
length = packet.length - offset;
}
out.write(packet, offset, length);
return length;
}
}