/*
* @(#)Gop
*
* Copyright (c) 2005-2010 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
*
*/
/*
* 2009-07 pulldown patch by vq
*/
package net.sourceforge.dvb.projectx.parser;
import java.io.IOException;
import java.io.DataOutputStream;
import java.io.FileOutputStream;
import java.io.ByteArrayOutputStream;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import java.util.Date;
import java.util.Calendar;
import java.util.TimeZone;
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;
import net.sourceforge.dvb.projectx.io.IDDBufferedOutputStream;
import net.sourceforge.dvb.projectx.thirdparty.D2V;
/**
* placeholder for future class
*/
public class Gop extends Object {
private byte[] headerrescue = new byte[1];
private long VbvBuffer_Value = 0;
private boolean progressive_sequence = false; //vq static variable to remember current status
private long total_pulldownfields = 0; //vq Global counter for fields added by repeat_first_field.
//vq For esthetic reasons this may need to move into the
//vq job_processing object
private boolean Debug;
private boolean CreateD2vIndex;
private boolean SplitProjectFile;
private boolean AddSequenceHeader;
private boolean Message_3;
private boolean AddSequenceDisplayExension;
private boolean PatchToProgressive;
private boolean PatchToInterlaced;
private boolean ToggleFieldorder;
private boolean ClearCDF;
private boolean Save1stFrameOfGop;
private boolean Preview_AllGops;
private boolean Preview_fastDecode;
private boolean TrimPts;
private boolean IgnoreErrors;
private boolean OptionDAR;
private boolean OptionHorizontalResolution;
private boolean WriteVideo;
private boolean InsertEndcode;
private boolean DumpDroppedGop;
private boolean UseGOPEditor;
private int ChangeBitrateInAllSequences;
private int ChangeVbvDelay;
private int CutMode;
private int ExportDAR;
private int ChangeAspectRatio;
private int ChangeVbvBuffer;
private int Preview_YGain;
private String ExportHorizontalResolution;
private String SDE_Value;
private JobProcessing job_processing;
public Gop(JobCollection collection)
{
getSettings(collection);
}
private void getSettings(JobCollection collection)
{
Debug = collection.getSettings().getBooleanProperty(Keys.KEY_DebugLog);
CreateD2vIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createD2vIndex);
SplitProjectFile = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_splitProjectFile);
AddSequenceHeader = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_addSequenceHeader);
Message_3 = collection.getSettings().getBooleanProperty(Keys.KEY_MessagePanel_Msg3);
AddSequenceDisplayExension = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_addSde);
PatchToProgressive = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_patchToProgressive);
PatchToInterlaced = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_patchToInterlaced);
ToggleFieldorder = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_toggleFieldorder);
ClearCDF = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_clearCDF);
Save1stFrameOfGop = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_save1stFrameOfGop);
Preview_AllGops = collection.getSettings().getBooleanProperty(Keys.KEY_Preview_AllGops);
Preview_fastDecode = collection.getSettings().getBooleanProperty(Keys.KEY_Preview_fastDecode);
TrimPts = collection.getSettings().getBooleanProperty(Keys.KEY_Video_trimPts);
IgnoreErrors = collection.getSettings().getBooleanProperty(Keys.KEY_Video_ignoreErrors);
OptionDAR = collection.getSettings().getBooleanProperty(Keys.KEY_OptionDAR);
OptionHorizontalResolution = collection.getSettings().getBooleanProperty(Keys.KEY_OptionHorizontalResolution);
WriteVideo = collection.getSettings().getBooleanProperty(Keys.KEY_WriteOptions_writeVideo);
InsertEndcode = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_insertEndcode);
DumpDroppedGop = collection.getSettings().getBooleanProperty(Keys.KEY_dumpDroppedGop);
ChangeBitrateInAllSequences = collection.getSettings().getIntProperty(Keys.KEY_ChangeBitrateInAllSequences);
ChangeVbvDelay = collection.getSettings().getIntProperty(Keys.KEY_ChangeVbvDelay);
CutMode = collection.getSettings().getIntProperty(Keys.KEY_CutMode);
ExportDAR = collection.getSettings().getIntProperty(Keys.KEY_ExportDAR);
ChangeAspectRatio = collection.getSettings().getIntProperty(Keys.KEY_ChangeAspectRatio);
ChangeVbvBuffer = collection.getSettings().getIntProperty(Keys.KEY_ChangeVbvBuffer);
Preview_YGain = collection.getSettings().getIntProperty(Keys.KEY_Preview_YGain);
ExportHorizontalResolution = collection.getSettings().getProperty(Keys.KEY_ExportHorizontalResolution);
SDE_Value = collection.getSettings().getProperty(Keys.KEY_VideoPanel_SdeValue);
UseGOPEditor = collection.getSettings().getBooleanProperty(Keys.KEY_useGOPEditor);
}
/**
* gop dropp
*/
private void messageDropError(byte[] gop, byte[] pts, int maxtref, int frame, int gop_number, int frame_number, long startpts, long lastpts, String dumpname, int errorcode)
{
Common.setMessage(Resource.getString("video.msg.error.gop.drop", String.valueOf(gop_number - 1), Common.formatTime_1(startpts / 90L), String.valueOf(startpts)) + ", errorcode: " + Integer.toHexString(errorcode).toUpperCase());
Common.setMessage(Resource.getString("video.msg.error.gop.diff", String.valueOf(maxtref + 1) + "/" + (frame + 1), String.valueOf((lastpts - startpts) / 90)) + " " + Common.formatTime_1((long)(frame_number * (double)(CommonParsing.getVideoFramerate() / 90.0f) )));
/**
* dump dropped gop to file
*/
if (DumpDroppedGop)
{
String gopdumpname = dumpname + "-GOP#" + (gop_number - 1) + ".bin";
byte[] dumpfill = new byte[16];
Arrays.fill(dumpfill, (byte)0xFF);
try {
DataOutputStream dump = new DataOutputStream(new FileOutputStream(gopdumpname));
dump.writeInt(gop_number - 1);
dump.writeInt(maxtref);
dump.writeInt(frame);
dump.write(dumpfill, 0, 4);
dump.write(pts);
dump.write(dumpfill);
dump.write(gop);
dump.write(Video.getSequenceEndCode());
dump.flush();
dump.close();
Common.setMessage(Resource.getString("video.msg.error.gop.dump") + " " + dumpname);
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
}
/**
* gop changing/testing
*/
public void goptest(JobProcessing job_processing, IDDBufferedOutputStream video_sequence, byte[] gop, byte[] pts, DataOutputStream log, String dumpname, int[] MPGVideotype, List CutpointList, List ChapterpointList)
{
goptest(job_processing, video_sequence, gop, pts, log, dumpname, MPGVideotype, CutpointList, ChapterpointList, true);
}
/**
* gop changing/testing
*/
public void goptest(JobProcessing job_processing, IDDBufferedOutputStream video_sequence, byte[] gop, byte[] pts, DataOutputStream log, String dumpname, int[] MPGVideotype, List CutpointList, List ChapterpointList, boolean doWrite)
{
int[] clv = job_processing.getStatusVariables();
int ErrorCode = 0;
String[] VBASIC = job_processing.getStatusStrings();
Common.setFps(job_processing.getSourceVideoFrameNumber());
if (gop.length < 12)
{
Common.setMessage(Resource.getString("video.msg.error.lackofdata", String.valueOf(clv[6])));
return;
}
if (pts.length == 0)
{
double npts = 0;
double thisTC = 0;
double diff = 0;
double ref = 0;
int p = 0;
for (int i = 0, returncode, pes_ID; i < gop.length - 7; )
{
if ((returncode = CommonParsing.validateStartcode(gop, i)) < 0)
{
i += (-returncode);
continue;
}
pes_ID = CommonParsing.getPES_IdField(gop, i);
if (pes_ID == CommonParsing.GROUP_START_CODE)
{
/** options[8] ist ende PTS vom letzten GOP = beginn dieses Gop
wie bei ttx die diff merken zw. PTS und TC zu beginn,
dann vergleichen und startpts neu setzen,
NUR wenn TC genau past zur erwarteten PTS, dann nehm wa den (TC 0 was dann)
**/
thisTC = 90.0
* (3600000.0 * ((0x7C & gop[i + 4])>>>2)
+ 60000.0 * ((3 & gop[i + 4])<<4 | (0xF0 & gop[i + 5])>>>4)
+ 1000.0 * ((7 & gop[i + 5])<<3 | (0xE0 & gop[i + 6])>>>5)
+ (((0x1F & gop[i + 6])<<1 | (0x80 & gop[i + 7])>>>7)
* (CommonParsing.getVideoFramerate() / 90.0f)) );
i += 7;
}
else if (pes_ID == CommonParsing.PICTURE_START_CODE)
{
p = i;
ref = CommonParsing.getVideoFramerate() * ((0xFF & gop[i + 4])<<2 | (0xC0 & gop[i + 5])>>>6);
npts = ref + job_processing.getEndPtsOfGop();
break;
}
else
i += 4;
}
diff = thisTC - job_processing.getLastGopTimecode();
// TC diff >=0ms <5min
if (diff >= 0 && diff < 27000000)
{
npts = job_processing.getLastGopPts() + diff + ref;
Common.setMessage(Resource.getString("video.msg.error.nopts.use_goptc", "" + clv[6]));
}
else
Common.setMessage(Resource.getString("video.msg.error.nopts.use_lastpts", "" + clv[6]));
pts = new byte[16];
for (int i = 0; i < 8; i++)
{
pts[7 - i] = (byte)(0xFF & (long)npts>>>(i * 8));
pts[15 - i] = (byte)(0xFF & (long)p>>>(i * 8));
}
job_processing.setLastGopTimecode((long)thisTC);
}
//old /* vpts[0] = pts, vpts[1] = for next frame following this byteposition in sequ_array */
//pd /*vq vpts[0] = pts, vpts[1] = frame starts at this byteposition in sequ_array */
long[][] vpts = new long[2][pts.length / 16];
/*vq The frepeat array stores the number of fields to be repeated for
*vq the frame, indexed by temporal reference, when repeat_top_field is set.
*vq If I would speak decent Java I would make this a List object,
*vq but as it is I reserve room for the maximum tref number possible.
*/
int[] frepeat = new int[0x400];
//Arrays.fill(frepeat, 0); //always 0
for (int i = 0; i < (pts.length / 16); i++)
{
for (int j = 0; j < 8; j++)
{
vpts[0][i] |= (0xFFL & pts[(i * 16) + j])<<((7 - j) * 8);
vpts[1][i] |= (0xFFL & pts[(i * 16) + 8 + j])<<((7 - j) * 8);
}
}
//horrible nebula PTS stuff
//vq If more than one PTS is available (means probably there is one for each frame
//vq but the first and the last have the same PTS, than keep only the first vpts entry
if (vpts[0].length > 1 && Math.abs(vpts[0][vpts[0].length-1] - vpts[0][0]) < 100)
{
long a1 = vpts[0][0];
long a2 = vpts[1][0];
vpts = new long[2][1];
vpts[0][0] = a1;
vpts[1][0] = a2;
clv[8]++;
}
if (Debug)
{
System.out.println("\ngop" + clv[6] + "/tc_o53 " + job_processing.getLastGopTimecode() + "/lp_o54 " + job_processing.getLastGopPts() + "/lp_o8 " + job_processing.getEndPtsOfGop() + "/lp_o40 " + job_processing.getLastSimplifiedPts());
for (int i = 0; i < vpts[0].length; i++)
System.out.println("p" + i + " " + vpts[0][i] + "/ " + vpts[1][i]);
}
Calendar cal = Calendar.getInstance();
String ct = Common.formatTime_1((long)(job_processing.getExportedVideoFrameNumber() * (double)(CommonParsing.getVideoFramerate() / 90.0f)));
String nv = "";
String[] MPGVideoType_Str = { "MPEG-1", "MPEG-2", "MPEG-4/H.264" };
String[] aspratio = {"res.","1.000 (1:1)","0.6735 (4:3)","0.7031 (16:9)","0.7615 (2.21:1)","0.8055","0.8437","0.9375","0.9815","1.0255","1.0695","1.1250","1.1575","1.2015","res." };
String[] fps_tabl1 = {"forbidden fps","23.976fps","24fps","25fps","29.97fps","30fps","50fps","59.94fps","60fps","n.def.","n.def.","n.def.","n.def.","n.def.","n.def.","n.def."};
double[] fps_tabl2 = { 0, 3753.7537, 3750, 3600, 3003.003, 3000, 1800, 1501.5015, 1500, 0,0,0,0,0,0,0};
//vq
int starttref = -1;
int lasttref = -1;
int repeat_first_field = 0;
int top_field_first = 0;
int pulldownfields = 0;
int pulldownmult = 1;
int startpd = 0;
int lastpd = 0;
int tref = 0;
int maxtref = 0;
int frame = -1;
int newframes = 0;
int progressive = 0;
int closedgop = 0;
int smark = 0;
int trefcheck = 0;
int vbvdelay = 65535;
int lastframes = job_processing.getExportedVideoFrameNumber();
int frametype = 0;
int SDE_marker = -1;
int pD_marker = -1;
int prog_seq = -1;
int s = 0;
int[] frt = { 5,50,20,5,5,5,5,5 };
boolean start = false;
boolean last = false;
boolean changegop = false;
boolean writeframe = true;
boolean is_I_Frame = false;
boolean d2vinsert = false;
boolean mpeg2type = false;
boolean format_changed = false;
boolean error = false;
boolean broken_link = false;
boolean gop_closed = false;
boolean sequenceheader_complete = false;
boolean SDE_found = false;
boolean doExport = false;
long videotimes = job_processing.getVideoExportTime();
long lTC = job_processing.getLastGopTimecode();
long TC = 0;
long lastpts = job_processing.getEndPtsOfGop() == -10000 ? -20000 : job_processing.getEndPtsOfGop();
long startpts = lastpts;
long cutposition = 0;
String frT[] = { "0","I","P","B","D","5","6","7" };
List newPics = new ArrayList();
List dropList = new ArrayList();
List infos = new ArrayList();
List newcut = new ArrayList();
ByteArrayOutputStream gopbuffer = new ByteArrayOutputStream();
ByteArrayOutputStream frametypebuffer = new ByteArrayOutputStream();
byte d2vframerate = 0;
try {
if (job_processing.hasSequenceHeader())
frametypebuffer.write((byte)0x88);
else
frametypebuffer.write((byte)0x80);
if (job_processing.getSplitSize() > 0 && !job_processing.hasSequenceHeader() && job_processing.getExportedVideoFrameNumber() == 0)
{
byte[] newgop = new byte[headerrescue.length + gop.length];
System.arraycopy(headerrescue, 0, newgop, 0, headerrescue.length);
System.arraycopy(gop, 0, newgop, headerrescue.length, gop.length);
gop = newgop;
job_processing.setSequenceHeader(true);
for (int a = 0; a < vpts[1].length; a++)
vpts[1][a] += headerrescue.length;
}
else if (!CutpointList.isEmpty() && !job_processing.hasSequenceHeader() && !CommonParsing.getCutStatus())
{
byte[] newgop = new byte[headerrescue.length + gop.length];
System.arraycopy(headerrescue, 0, newgop, 0, headerrescue.length);
System.arraycopy(gop, 0, newgop, headerrescue.length, gop.length);
gop = newgop;
job_processing.setSequenceHeader(true);
for (int a = 0; a < vpts[1].length; a++)
vpts[1][a] += headerrescue.length;
}
else if (!job_processing.hasSequenceHeader() && AddSequenceHeader)
{
byte[] newgop = new byte[headerrescue.length + gop.length];
System.arraycopy(headerrescue, 0, newgop, 0, headerrescue.length);
System.arraycopy(gop, 0, newgop, headerrescue.length, gop.length);
gop = newgop;
job_processing.setSequenceHeader(true);
for (int a = 0; a < vpts[1].length; a++)
vpts[1][a] += headerrescue.length;
}
/* header check */
if (job_processing.hasSequenceHeader())
{
/**
* get new header, check if valid and not 0!!
*/
int _h_reso = ((0xFF & gop[s + 4])<<4 | (0xF0 & gop[s + 5])>>>4);
int _v_reso = ((0xF & gop[s + 5])<<8 | (0xFF & gop[s + 6]));
int _framerate = 0xF & gop[s + 7];
int _dar = 0xF & gop[s + 7]>>>4;
double d = fps_tabl2[_framerate];
if (_h_reso == 0 || _v_reso == 0 || d == 0 || _dar == 0 || _dar > 13)
{
clv[4]++;
ErrorCode |= 0x80;
messageDropError(gop, pts, maxtref, frame, clv[6], job_processing.getExportedVideoFrameNumber(), startpts, lastpts, dumpname, ErrorCode);
job_processing.setSequenceHeader(false);
gopbuffer.close();
frametypebuffer.close();
gop = null;
return;
}
CommonParsing.setVideoFramerate(d); // framerateconstant
VbvBuffer_Value = 16 * 1024 * ( (0x1F & gop[s + 10])<<5 | (0xF8 & gop[s + 11])>>>3 );
String[] vbasics = {
String.valueOf(_h_reso),
String.valueOf(_v_reso),
fps_tabl1[_framerate],
aspratio[_dar],
""
};
clv[7] = _dar - 1;
if (job_processing.isNewVideoStream() || job_processing.getExportedVideoFrameNumber() == 0)
{
nv = Resource.getString("video.msg.basics") + " " + vbasics[0] + "*" + vbasics[1] + " @ " + vbasics[2] + " @ " + vbasics[3] + " @ " + ( ((255&gop[s+8])<<10 | (255&gop[s+9])<<2 | (192 & gop[s+10])>>>6)*400 ) + " bps - vbv " + ( (31&gop[s+10])<<5 | (248&gop[s+11])>>>3 );
infos.add(nv);
/**
* no frames written 'til now
*/
if (job_processing.getExportedVideoFrameNumber() == 0)
{
d2vinsert = true;
d2vframerate = gop[s + 7];
}
else
format_changed = true;
System.arraycopy(vbasics, 0, VBASIC, 0, VBASIC.length);
}
if (!Arrays.equals(VBASIC, vbasics))
{
String str = Resource.getString("video.msg.basics") + " " + vbasics[0] + "*" + vbasics[1] + " @ " + vbasics[2] + " @ " + vbasics[3] + " @ " + ( ((255&gop[s+8])<<10 | (255&gop[s+9])<<2 | (192 & gop[s+10])>>>6)*400 ) + " bps - vbv " + ( (31&gop[s+10])<<5 | (248&gop[s+11])>>>3 );
Common.setMessage("-> " + Resource.getString("video.msg.newformat", "" + clv[6]) + " (" + ct + ")");
Common.setMessage(str);
job_processing.getChapters().addChapter(ct, str);
format_changed = true;
System.arraycopy(vbasics, 0, VBASIC, 0, VBASIC.length);
}
s = 12;
}
clv[6]++;
/* gop check */
goploop:
for (int returncode, pes_ID; s < gop.length - 10; s++ )
{
if ((returncode = CommonParsing.validateStartcode(gop, s)) < 0)
{
s += (-returncode) - 1;
continue goploop;
}
/**
* action on next found startcode
*/
if (pD_marker > -1 && ClearCDF)
{
Arrays.fill( gop, pD_marker, s, (byte)0); //clear privare data on behalf of cdf flag setting
pD_marker = -1;
}
//
if ((dropList.size() & 1) != 0 && !dropList.get(dropList.size() - 1).toString().startsWith("_"))
{
dropList.add(String.valueOf(s));
}
pes_ID = CommonParsing.getPES_IdField(gop, s);
/**
* slices
*/
if (pes_ID >= CommonParsing.SLICE_START_CODE_MIN && pes_ID <= CommonParsing.SLICE_START_CODE_MAX)
{
if (Debug)
System.out.println("A " + s + " /slice " + pes_ID);
s += 7;
continue goploop;
}
/**
* user data
*/
if (pes_ID == CommonParsing.USER_DATA_START_CODE)
{
if ((dropList.size() & 1) == 0)
dropList.add(String.valueOf(s));
pD_marker = s;
s += 3;
continue goploop;
}
//
/**
* shit progdvb_data check
*/
else if ((0xF0 & gop[s+3]) == 0xE0)
{
int drop_length = 4;
if (CommonParsing.getPES_LengthField(gop, s) == 0 )
{
drop_length += 5 + (0xFF & gop[s + 8]);
if (s + drop_length >= gop.length)
drop_length = gop.length - s;
Arrays.fill( gop, s, s + drop_length, (byte)0);
if (Message_3)
Common.setMessage(Resource.getString("video.msg.error.pesext_in_es", String.valueOf(clv[6] - 1), String.valueOf(s)));
}
else
{
Arrays.fill( gop, s, s + drop_length, (byte)0);
if (Message_3)
Common.setMessage(Resource.getString("video.msg.error.pes_in_es", String.valueOf(clv[6] - 1), String.valueOf(s)));
}
s += drop_length - 1;
continue goploop;
}
else if (!mpeg2type && pes_ID == CommonParsing.EXTENSION_START_CODE && gop[s + 4]>>>4 == 1) /*** 0xb5 sequence extension ***/
{
MPGVideotype[0] = 1;
mpeg2type = true;
prog_seq = s + 5;
progressive_sequence = (8 & gop[prog_seq]) != 0; //vq
SDE_marker = s + 10;
s += 9;
if (job_processing.hasSequenceHeader() && frametypebuffer.size() == 1)
{
frametypebuffer.reset();
frametypebuffer.write((byte)(8 | (8 & gop[prog_seq])<<4));
}
}
else if (!sequenceheader_complete && pes_ID == CommonParsing.EXTENSION_START_CODE && gop[s + 4]>>>4 == 2) /*** 0xb5 MPEG-2 seq dis extension ***/
{
if (AddSequenceDisplayExension && job_processing.hasSequenceHeader())
Video.setSequenceDisplayExtension( gop, s, SDE_Value, VBASIC);
SDE_found = true;
s += 8;
}
/**
* 0xb8 gop header + timecode
*/
else if (pes_ID == CommonParsing.GROUP_START_CODE)
{
sequenceheader_complete = true;
closedgop = s + 7;
writeframe = true;
gop_closed = (0x40 & gop[s + 7]) != 0;
broken_link = (0x20 & gop[s + 7]) != 0;
//gop TC
TC = 90L * (3600000L * ((0x7C & gop[s + 4])>>>2) +
60000L * ((3 & gop[s + 4])<<4 | (0xF0 & gop[s + 5])>>>4) +
1000L * ((7 & gop[s + 5])<<3 | (0xE0 & gop[s + 6])>>>5) +
(long)(((0x1F & gop[s + 6])<<1 | (0x80 & gop[s + 7])>>>7) * (double)(CommonParsing.getVideoFramerate() / 90.0f)) );
if (Math.abs(TC - job_processing.getLastGopTimecode()) < CommonParsing.getVideoFramerate() || job_processing.getSourceVideoFrameNumber() == 0)
job_processing.setLastGopTimecode(TC);
if (Debug)
System.out.println("\n//b8 " + TC + "/ " + Integer.toHexString((0x80&gop[s+4]) | (0x7F&gop[s+7])) + "/ " + s + "/ " + job_processing.hasSequenceHeader());
if (job_processing.hasSequenceHeader())
{
headerrescue = new byte[s];
System.arraycopy(gop, 0, headerrescue, 0, s);
}
/**
* set Timezone (videoframerate may be 0!!, check this)
*/
Date videotime = new Date((long)(job_processing.getExportedVideoFrameNumber() * (double)(CommonParsing.getVideoFramerate() / 90.0f)));
cal.setTimeZone(TimeZone.getTimeZone("GMT+0:00"));
cal.setTime(videotime);
/**
* get SMPTE (videoframerate may be 0!!, check this)
*/
int vh = cal.get(11);
int vm = cal.get(12);
int vs = cal.get(13);
int vf = (cal.get(14) / ((int)CommonParsing.getVideoFramerate() / 90)) ; // earlier versions +1
gop[4 + s] = (byte)( (128 & gop[s+4]) | vh<<2 | vm>>>4 );
gop[5 + s] = (byte)( (15 & vm)<<4 | 8 | vs>>>3 );
gop[6 + s] = (byte)( (7 & vs)<<5 | vf>>>1 );
gop[7 + s] = (byte)( 127 & gop[s+7] | vf<<7 );
s += 6;
}
/**
* 0x0 new frame
*/
else if (pes_ID == CommonParsing.PICTURE_START_CODE)
{
sequenceheader_complete = true;
tref = ((0xFF & gop[s + 4]) << 2) | (0xC0 & gop[s + 5])>>>6; // temporalrefence of picture
frametype = (0x38 & gop[s + 5])>>>3;
if (frametype < CommonParsing.FRAME_I_TYPE || frametype > CommonParsing.FRAME_D_TYPE)
{
Common.setMessage(Resource.getString("video.msg.error.frame.wrong", "" + frametype) + " " + tref);
error = true;
ErrorCode |= 1;
}
newPics.add(String.valueOf(tref<<4 | frametype));
/**
* vbv delay to 0xffff
*/
if (ChangeBitrateInAllSequences != 2 && ChangeBitrateInAllSequences > 0 && ChangeVbvDelay > 0)
{
gop[s + 5] |= 0x07;
gop[s + 6] |= 0xFF;
gop[s + 7] |= 0xF8;
}
if (tref > maxtref)
maxtref = tref;
if (Debug)
System.out.println(frame + "/ " + maxtref + "/ " + tref + "/ " + s + " * " + frT[frametype] + "/ " + gop.length);
if (!start && s >= vpts[1][0])
{
startpts = vpts[0][0] - (long)(CommonParsing.getVideoFramerate() * tref);
starttref = tref; //vq
start = true;
}
else if (!last && s >= vpts[1][vpts[1].length - 1])
{
lastpts = vpts[0][vpts[0].length-1] - (long)(CommonParsing.getVideoFramerate() * tref);
lasttref = tref; //vq
last = true;
}
/**
* determine cuts
*/
if (frame == -1 && frametype == CommonParsing.FRAME_I_TYPE)
{
/**
* determine vbv-delay of I-frame
*/
vbvdelay = (7 & gop[s + 5])<<13 | (0xFF & gop[s + 6])<<5 | (0xF8 & gop[s + 7])>>>3;
is_I_Frame = true;
if (job_processing.getExportedVideoFrameNumber() == 0)
infos.add(Resource.getString("video.msg.export.start") + " " + (clv[6] - 1));
/**
* drop B-Frames, also if broken_link flag is set
*/
if (tref > 0 && (broken_link || Math.abs(startpts - job_processing.getEndPtsOfGop()) > CommonParsing.getVideoFramerate()))
{
if (!gop_closed || (gop_closed && broken_link))
{
gop[s + 4] = 0;
gop[s + 5] &= 0x3F; /* set first I-Frame's tref to 0 */
gop[closedgop] |= 0x40;
if (broken_link)
gop[closedgop] &= ~0x20;
gopbuffer.write(gop, 0, s);
smark = s;
if (job_processing.getExportedVideoFrameNumber() > 0)
infos.add(Resource.getString("video.msg.pts.diff", String.valueOf(startpts - job_processing.getEndPtsOfGop()), Common.formatTime_1((startpts - job_processing.getEndPtsOfGop()) / 90)) + " " + (broken_link ? Resource.getString("video.msg.error.brokenlink") : ""));
infos.add(Resource.getString("video.msg.frame.drop", "" + (clv[6] - 1)) + " " + Common.formatTime_1((long)(job_processing.getExportedVideoFrameNumber() * (double)(CommonParsing.getVideoFramerate() / 90.0f))));
//vq This should use writeframe instead
job_processing.countExportedVideoFrameNumber(-tref);
newframes -= tref;
trefcheck = tref;
changegop = true;
clv[0]++; //cut
}
}
}
/**
* recalculate tref, timecode, frames + delete b-frames
*/
if (frame > -1 && changegop)
{
if (writeframe)
gopbuffer.write(gop, smark, s - smark);
smark = s;
/**
* drop b-frame from temporal sequence
*/
if ( trefcheck > tref )
{
writeframe = false;
if ((dropList.size() & 1) == 0)
dropList.add("_" + String.valueOf(s));
}
else
{
writeframe = true;
gop[s + 4] = (byte)( (tref - trefcheck)>>>2);
gop[s + 5] = (byte)( (63 & gop[s + 5]) | ((tref - trefcheck)<<6));
if ((dropList.size() & 1) == 1)
dropList.add("_" + String.valueOf(s));
}
}
/**
* P-frames for cut in gop
*/
if (frametype == CommonParsing.FRAME_P_TYPE)
{
if (!changegop)
{
long[] cutdata = { job_processing.getSourceVideoFrameNumber(), s };
newcut.add(cutdata);
}
else
{
long[] cutdata = { job_processing.getSourceVideoFrameNumber(), gopbuffer.size() };
newcut.add(cutdata);
}
}
job_processing.countSourceVideoFrameNumber(+1);
job_processing.countExportedVideoFrameNumber(+1);
frame++;
newframes++;
progressive = 0x80; /* 0xb5 pic coding extension */
repeat_first_field = 0; //vq
top_field_first = 0; //vq
for (int i = s + 6; i < s + 15 && i + 10 < gop.length; i++ ) //i + 8 doesn't reach
{
if ((returncode = CommonParsing.validateStartcode(gop, i)) < 0)
{
i += (-returncode) - 1;
continue;
}
if (gop[i + 3] != (byte)0xb5 || (0xF0 & gop[i + 4]) != 0x80)
continue;
repeat_first_field = (0x02 & gop[i + 7]); //vq
progressive = (0x80 & gop[i + 8]);
if (PatchToProgressive)
gop[i + 8] |= (byte)0x80; // mark as progressiv
else if (PatchToInterlaced)
gop[i + 8] &= (byte)~0x80; // mark as interlaced
if (ToggleFieldorder)
gop[i + 7] ^= (byte)0x80; // toggle top field first
top_field_first = gop[i + 7] | 0x80; //vq
/**
* zero'es the comp.disp.flag infos
*/
if (ClearCDF && (0x40 & gop[i + 8]) != 0)
{
gop[i + 8] &= (byte)0x80;
gop[i + 9] = 0;
gop[i + 10] = 0;
if ((dropList.size() & 1) == 0)
dropList.add(String.valueOf(i + 9));
s = i;
}
break;
}
//vq Calculate extra number of frames
if (repeat_first_field != 0 && writeframe)
{
pulldownmult = (progressive_sequence ? 2 : 1);
int extrafields = pulldownmult;
// top_field_first set when progressive implies another repeat
if (progressive_sequence && top_field_first != 0 )
extrafields += pulldownmult;
// tref is a 10bit positive integer
frepeat[tref] = extrafields;
pulldownfields += extrafields;
total_pulldownfields += extrafields;
if (total_pulldownfields % 2 == 0 || progressive_sequence)
{
extrafields = (extrafields < 2 ? 1 : extrafields / 2);
job_processing.countSourceVideoFrameNumber(extrafields);
job_processing.countExportedVideoFrameNumber(extrafields);
}
}
if (is_I_Frame || !changegop || (changegop && writeframe))
frametypebuffer.write((byte)(frametype | progressive));
s += 7; // slices B min 5, I min 50, p min 25
is_I_Frame = false;
}
} // end of gop search
//vq For pulldown: correct startpts and lastpts accordingly
if (pulldownfields > 0)
{
for (int i = 0; i < starttref; i++ )
startpd += frepeat[i];
lastpd = startpd;
for (int i = starttref; i < lasttref; i++ )
lastpd += frepeat[i];
startpts -= (long)(CommonParsing.getVideoFramerate() * startpd / 2 );
if (vpts[0].length > 1)
lastpts -= (long)(CommonParsing.getVideoFramerate() * lastpd / 2 );
else
lastpts = startpts;
}
/**
for (int dl = 0; dl < dropList.size(); dl++)
{
String str = dropList.get(dl).toString();
if (str.startsWith("_"))
str = "_" + Integer.toHexString(Integer.parseInt(str.substring(1)));
else
str = Integer.toHexString(Integer.parseInt(str));
Common.setMessage("dl " + dl + " : '0x" + str.toUpperCase() + "'");
}
**/
/**
* put the rest of data into buffer, if gop was changed
*/
if (changegop)
{
if (writeframe)
gopbuffer.write(gop, smark, gop.length - smark);
gop = gopbuffer.toByteArray();
gopbuffer.reset();
changegop = false;
}
if (prog_seq != -1 && prog_seq < gop.length)
{
if (PatchToProgressive)
gop[prog_seq] |= (byte)8; // mark as progressiv_seq
else if (PatchToInterlaced)
gop[prog_seq] &= (byte)~8; // unmark ^
}
if (vpts[0].length < 2)
{
lastpts = startpts;
clv[1]++;
}
if (job_processing.get1stVideoPTS() == -1)
job_processing.set1stVideoPTS(startpts);
int Pics[] = new int[newPics.size()];
for (int a = 0; a < Pics.length; a++)
Pics[a] = Integer.parseInt(newPics.get(a).toString());
int newTref[] = new int[Pics.length];
Arrays.fill(newTref, -1);
// error, no Frames found
if (Pics.length == 0)
{
Common.setMessage(Resource.getString("video.msg.error.frame.not", String.valueOf(clv[6] - 1)));
error = true;
ErrorCode |= 2;
}
// error, no leading I-Frame
if (Pics.length > 0 && (Pics[0] & 0xF) != 1)
Common.setMessage(Resource.getString("video.msg.error.frame.not.i", String.valueOf(clv[6] - 1)));
for (int i = 0; !error && i < Pics.length; i++)
{
int Tref = Pics[i]>>>4;
if (Tref < 0 || Tref > Pics.length - 1 || newTref[Tref] != -1)
//test if (Tref < 0 || newTref[Tref] != -1)
{
error = true;
ErrorCode |= 4;
break;
}
newTref[Tref] = Pics[i];
}
for (int i = 0; !error && i < newTref.length; i++)
if (newTref[i] == -1)
{
error = true;
ErrorCode |= 8;
}
/**
* show and save I-frame when demuxing
* disabled, will be changed later
*/
if (Save1stFrameOfGop)
{
Common.getMpvDecoderClass().decodeArray(gop, false, Preview_AllGops, Preview_fastDecode, Preview_YGain);
//CommonGui.getPicturePanel().saveBMP(true, job_processing.isRunningFromCLI());
}
/**
* error, start pts is too early as last
*/
if (startpts < job_processing.getLastGopPts() - CommonParsing.getVideoFramerate() / 2)
{
Common.setMessage(Resource.getString("video.msg.error.pts.early", String.valueOf(clv[6] - 1), String.valueOf(job_processing.getLastGopPts())));
error = true;
ErrorCode |= 0x10;
}
/**
* falls startpts kleiner als options[8] (lende letzte GOP) aber innerhalb toleranz,
* dann options[8] schreiben, setzen des neuen Endes aber mit ermittelter lastpts dieser gop
*/
if (TrimPts && startpts < job_processing.getEndPtsOfGop() && (double)(job_processing.getEndPtsOfGop() - startpts) < (CommonParsing.getVideoFramerate() / 2.0))
{
if (Debug)
System.out.println("videostart trimmed to o8 " + job_processing.getEndPtsOfGop() + " /sp " + startpts);
startpts = job_processing.getEndPtsOfGop();
}
/**
* error, if pts diff. between frames > half of a frame
*/
if (maxtref != frame || Math.abs(lastpts - startpts) > 2000)
{
error = true;
ErrorCode |= 0x20;
}
/**
* ignore v-errors
*/
if (job_processing.getSourceVideoFrameNumber() > 0 && IgnoreErrors)
{
lastpts = startpts;
maxtref = frame;
error = false;
}
/**
* error, if gop on mp@ml is too big
*/
if (Integer.parseInt(VBASIC[0]) <= 720 && gop.length > 2750000)
{
error = true;
ErrorCode |= 0x40;
}
/**
* return last orig pts for plain mpv
*/
//old
//job_processing.setLastSimplifiedPts(startpts + (long)(trefcheck * CommonParsing.getVideoFramerate()) + (long)((maxtref - trefcheck + 1) * CommonParsing.getVideoFramerate()));
//vq Force conversion to float by the 2.0 below, because the duration can be half a frame
//vq longer when odd numbers for pulldownfields occur. Same comment applies at various
//vq other places where pulldownfields occurs.
job_processing.setLastSimplifiedPts(startpts + (long)(trefcheck * CommonParsing.getVideoFramerate()) + (long)((maxtref + pulldownfields / 2.0 - trefcheck + 1) * CommonParsing.getVideoFramerate()));
// test - edit fehlerhafte gop
if (error)
{
if (UseGOPEditor)
gop = Common.getGuiInterface().editGOP(gop, vpts);
}
//
/**
* message error
*/
if (error)
{
job_processing.setExportedVideoFrameNumber(lastframes);
clv[4]++;
messageDropError(gop, pts, maxtref, frame, clv[6], job_processing.getExportedVideoFrameNumber(), startpts, lastpts, dumpname, ErrorCode);
}
else
{
/**
* unused!
* temp delete, disable for P-frame cutout
*/
newcut.clear();
/**
* read out gop timecode as long ,TC 081.5++
*/
//old
//job_processing.countLastGopTimecode((long)(CommonParsing.getVideoFramerate() * (maxtref + 1)));
//job_processing.setLastGopPts(startpts + (long)(CommonParsing.getVideoFramerate() * (maxtref + 1)));
//vq
job_processing.countLastGopTimecode((long)(CommonParsing.getVideoFramerate() * (maxtref + pulldownfields / 2.0 + 1)));
job_processing.setLastGopPts(startpts + (long)(CommonParsing.getVideoFramerate() * (maxtref + pulldownfields / 2.0 + 1)));
/**
* how to cut
*/
switch (CutMode)
{
case CommonParsing.CUTMODE_BYTE:
cutposition = job_processing.getCutByteposition();
break;
case CommonParsing.CUTMODE_GOP:
cutposition = clv[6];
break;
case CommonParsing.CUTMODE_FRAME:
cutposition = job_processing.getSourceVideoFrameNumber();
break;
case CommonParsing.CUTMODE_PTS:
cutposition = startpts + 1000; //exclude jitter
break;
case CommonParsing.CUTMODE_TIME:
cutposition = startpts - job_processing.get1stVideoPTS();
}
/**
* cut using bytepos, frame#, gop#, timecode or pts
*/
if (!CommonParsing.makecut(job_processing, dumpname, startpts, cutposition, newcut, lastframes, CutpointList, clv[6] - 1, job_processing.getCellTimes()))
job_processing.setExportedVideoFrameNumber(lastframes);
/**
* DAR request for auto cut
*/
else if (OptionDAR && ExportDAR != clv[7])
job_processing.setExportedVideoFrameNumber(lastframes);
/**
* H Resolution request for auto cut
*/
else if (OptionHorizontalResolution && !ExportHorizontalResolution.equals(VBASIC[0]))
job_processing.setExportedVideoFrameNumber(lastframes);
else if (!doWrite)
{}
else
{
startpts += (long)(trefcheck * CommonParsing.getVideoFramerate());
/**
* videoframe-pts-counter
*/
//old
//job_processing.setEndPtsOfGop(startpts + (long)((maxtref - trefcheck + 1) * CommonParsing.getVideoFramerate()));
//vq
job_processing.setEndPtsOfGop(startpts + (long)((maxtref + pulldownfields / 2.0 - trefcheck + 1) * CommonParsing.getVideoFramerate()));
/**
* write V-PTS Log for audio/data sync
*/
log.writeLong(startpts);
log.writeLong(job_processing.getEndPtsOfGop());
/**
* write V-Time Log for audio/data sync
*/
log.writeLong(videotimes);
//old
//log.writeLong(job_processing.countVideoExportTime((long)((maxtref - trefcheck + 1) * CommonParsing.getVideoFramerate())));
//vq
log.writeLong(job_processing.countVideoExportTime((long)((maxtref + pulldownfields/2.0 - trefcheck + 1) * CommonParsing.getVideoFramerate())));
/**
* value for gop bitrate per second
*/
double svbr = CommonParsing.getVideoFramerate() * (maxtref - trefcheck + 1);
if (svbr <= 0)
svbr = CommonParsing.getVideoFramerate() * 10;
/**
* calculate the bitrate from gop length
* !!renew determination
*/
int vbr = (int)( ( (90000L * (gop.length * 8)) / svbr ) / 400);
/**
* set value for gop bitrate per second
*/
if (job_processing.hasSequenceHeader())
{
/**
* value for bitrate per vbv
*/
if (ChangeBitrateInAllSequences == 2 && vbvdelay < 65535 )
vbr = (int)( (90000L * VbvBuffer_Value) / vbvdelay / 400);
/**
* set new aspectratio
*/
if (ChangeAspectRatio > 0)
gop[7] = (byte)((0xF & gop[7]) | ChangeAspectRatio<<4);
/**
* set new vbvsize
*/
if (ChangeVbvBuffer > 0)
{
gop[10] = (byte)((0xE0 & gop[10]) | 112>>>5);
gop[11] = (byte)((7 & gop[11]) | (0x1F & 112)<<3);
}
}
/**
* determ. avg bitrates
*/
if (vbr < job_processing.getMinBitrate())
job_processing.setMinBitrate(vbr);
if (vbr > job_processing.getMaxBitrate())
job_processing.setMaxBitrate(vbr);
if (job_processing.hasSequenceHeader())
{
/**
* set new bitrate in SH
*/
if (ChangeBitrateInAllSequences > 0)
{
int val = (ChangeBitrateInAllSequences - 3) * 2500 * 3;
if (val > 0)
vbr = val;
int vbr1 = vbr;
if (ChangeBitrateInAllSequences == 3)
vbr1 = 262143;
/**
* set sequence bitrate
*/
gop[8] = (byte)(vbr1>>>10);
gop[9] = (byte)(0xFF & (vbr1>>2));
gop[10] = (byte)( (0x3F & gop[10]) | (3 & vbr1)<<6 );
clv[2]++;
}
else
clv[3]++;
}
/**
* update data for the gop info picture
*/
frametypebuffer.flush();
byte ftb[] = frametypebuffer.toByteArray();
int fields_in_frame = 0, min = 0x7FFF & clv[9]>>>15, max = 0x7FFF & clv[9], prog_flag = clv[9]>>>30;
for (int i = 1; i < ftb.length; i++)
{
fields_in_frame += 2;
prog_flag |= (0x80 & ftb[i]) != 0 ? 2 : 1;
}
if (fields_in_frame < min || min == 0)
min = fields_in_frame;
if (fields_in_frame > max || max == 0)
max = fields_in_frame;
clv[9] = prog_flag<<30 | (0x7FFF & min)<<15 | (0x7FFF & max);
/**
* refresh the gop info picture
*/
Common.getGuiInterface().updateBitrateMonitor(vbr, ftb, Common.formatTime_1((job_processing.getVideoExportTimeSummary() + job_processing.getVideoExportTime()) / 90).substring(0, 8));
/**
* add chapter index of a change in basic data
*/
if (job_processing.isNewVideoStream() && infos.size() > 0)
job_processing.getChapters().addChapter(ct, nv);
/**
* print cached messages
*/
for (int i = 0; i < infos.size(); i++)
{
Common.setMessage(infos.get(i).toString());
job_processing.setNewVideoStream(false);
}
/**
* d2v project, update framerate
*/
if (d2vinsert && (CreateD2vIndex || SplitProjectFile))
{
job_processing.getProjectFileD2V().FrameRate(d2vframerate);
d2vinsert = false;
}
/**
* d2v project, update gop line, even if video export is disabled
*/
job_processing.getProjectFileD2V().addGOP(job_processing.getProjectFileExportLength(), newframes);
/**
* add SEC on a change of basic data
*/
if (format_changed && InsertEndcode)
{
if (WriteVideo)
{
video_sequence.write(Video.getSequenceEndCode());
job_processing.countMediaFilesExportLength(+4);
job_processing.countAllMediaFilesExportLength(+4);
job_processing.countProjectFileExportLength(+4);
}
job_processing.addCellTime(String.valueOf(job_processing.getExportedVideoFrameNumber()));
Common.setMessage("-> save ChapterFrameIndex: " + job_processing.getExportedVideoFrameNumber());
}
/**
* if write is enabled, write gop
*/
if (WriteVideo)
{
/**
* add SDE, mpeg2 only
*/
if (mpeg2type && AddSequenceDisplayExension && !SDE_found && job_processing.hasSequenceHeader())
{
int offs = SDE_marker != -1 ? SDE_marker : headerrescue.length;
video_sequence.write(gop, 0, offs);
video_sequence.write(Video.setSequenceDisplayExtension(SDE_Value, VBASIC));
video_sequence.write(gop, offs, gop.length - offs);
job_processing.countMediaFilesExportLength(+12);
job_processing.countAllMediaFilesExportLength(+12);
job_processing.countProjectFileExportLength(+12);
}
else
{
video_sequence.write(gop);
}
job_processing.countMediaFilesExportLength(gop.length);
doExport = true;
}
if (ChapterpointList.indexOf(String.valueOf(cutposition)) >= 0)
{
job_processing.addCellTime(String.valueOf(job_processing.getExportedVideoFrameNumber()));
Common.setMessage("-> save ChapterFrameIndex: " + job_processing.getExportedVideoFrameNumber());
}
job_processing.countProjectFileExportLength(gop.length);
job_processing.countAllMediaFilesExportLength(gop.length);
} // end if makecut
Common.getGuiInterface().showExportStatus(doExport ? Resource.getString("audio.status.write") : Resource.getString("audio.status.pause"));
}
infos.clear();
job_processing.setSequenceHeader(false);
gopbuffer.close();
frametypebuffer.close();
gop = null;
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
/**
* gop h264 testing
*/
public void h264test(JobProcessing job_processing, IDDBufferedOutputStream video_sequence, byte[] gop, byte[] pts, DataOutputStream log, String dumpname, int[] MPGVideotype, List CutpointList, List ChapterpointList)
{
MPGVideotype[0] = 2; //h264
String[] MPGVideoType_Str = { "MPEG-1", "MPEG-2", "MPEG-4/H.264" };
ByteArrayOutputStream frametypebuffer = new ByteArrayOutputStream();
ByteArrayOutputStream gopbuffer = new ByteArrayOutputStream();
boolean doExport = false;
int[] clv = job_processing.getStatusVariables();
int ErrorCode = 0;
long lastpts = job_processing.getEndPtsOfGop() == -10000 ? -20000 : job_processing.getEndPtsOfGop();
long startpts = lastpts;
long videotimes = job_processing.getVideoExportTime();
int lastframes = job_processing.getExportedVideoFrameNumber();
long cutposition = 0;
boolean format_changed = false;
boolean error = false;
List newcut = new ArrayList(); // unused
List infos = new ArrayList();
long[][] vpts = new long[2][pts.length / 16];
long pts_value = -1;
for (int i = 0; i < (pts.length / 16); i++)
{
for (int j = 0; j < 8; j++)
{
vpts[0][i] |= (0xFFL & pts[(i * 16) + j])<<((7 - j) * 8); //pts
vpts[1][i] |= (0xFFL & pts[(i * 16) + 8 + j])<<((7 - j) * 8); //valid for next pic. from pos.
}
}
if (Debug)
{
System.out.println("\ngop" + clv[6] + "/tc_o53 " + job_processing.getLastGopTimecode() + "/lp_o54 " + job_processing.getLastGopPts() + "/lp_o8 " + job_processing.getEndPtsOfGop() + "/lp_o40 " + job_processing.getLastSimplifiedPts());
for (int i = 0; i < vpts[0].length; i++)
System.out.println("p" + i + " " + vpts[0][i] + "/ " + vpts[1][i]);
}
int au_cnt = 0;
int frame = -1;
int field = 0;
int primary_pic_type = 0;
int[] preceding_fields = new int[2];
boolean accessunit = false;
boolean sequ_head = false;
int sequ_head_end = -1;
int accessunit_pos = 0;
int vpts_pos = 0;
String[] VBASIC = job_processing.getStatusStrings();
String[] vbasics = new String[5];
int[] temp_values = new int[12];
double[] base_pts = new double[3];
byte[] sequ_end = { 0, 0, 0, 1, 10 };
AccessUnit au = null;
ArrayList accessunit_list = new ArrayList();
ArrayList reorder_list = new ArrayList();
try {
// gop read
goploop:
for (int s = 0, returncode, unit_type, unit_ref; s < gop.length - 10; s++ )
{
if ((returncode = CommonParsing.validateMp4Startcode(gop, s)) < 0)
{
s += (-returncode) - 1;
continue goploop;
}
if ((unit_type = gop[s + 4]) < 0) // is forb_zero set ?
continue goploop;
else
unit_type &= 0x1F;
unit_ref = 3 & gop[s + 4]>>5;
if (Debug)
System.out.println("Au " + accessunit + " /Ap " + accessunit_pos + " /U " + s + " /ppt " + (7 & gop[s+5]>>5) + " /ref " + unit_ref + " /typ " + unit_type);
//save pos for SEI inserting
if (sequ_head && sequ_head_end < 0)
sequ_head_end = s;
switch (unit_type)
{
case 1: //nonIDR
case 5: //IDR
if (accessunit)
{
pts_value = -1; //reset to pts-less state
for (int i = vpts_pos; i < vpts[1].length; i++)
{
if (vpts[1][i] <= accessunit_pos)
{
pts_value = vpts[0][i];
vpts_pos = i+1;
break;
}
}
if (au != null)
au.setEnd(accessunit_pos);
//slice header, get frame_num etc
slice_wo_partitioning(gop, s, unit_type, temp_values);
au = new AccessUnit(au_cnt, primary_pic_type, (7 & gop[s + 5]>>5), temp_values, pts_value, unit_type, unit_ref, accessunit_pos);
accessunit_list.add(au);
au_cnt++;
frametypebuffer.write((byte)(((1 - temp_values[6])<<7 | (primary_pic_type + 1))));
accessunit = false;
}
break;
case 2: //slice p A
case 3: //slice p B
case 4: //slice p C
break;
case 6: //SEI
readSEIMessage(gop, s);
break;
case 7: //sequ set
sequ_head = true;
readH264SequenceParameterSet(gop, s, vbasics, temp_values);
frametypebuffer.write((byte)0x88);
CommonParsing.setVideoFramerate(90000.0 / Double.parseDouble((vbasics[2].substring(0, vbasics[2].indexOf(" ", 2))).trim())); // framerateconstant
if (job_processing.isNewVideoStream() || job_processing.getExportedVideoFrameNumber() == 0)
{
infos.add(Resource.getString("video.msg.basics") + " " + MPGVideoType_Str[MPGVideotype[0]] + " " + vbasics[4] + ", " + vbasics[0] + "*" + vbasics[1] + ", " + vbasics[2] + " @ " + vbasics[3]);
// no frames written 'til now
if (job_processing.getExportedVideoFrameNumber() > 0)
format_changed = true;
System.arraycopy(vbasics, 0, VBASIC, 0, VBASIC.length);
}
if (!Arrays.equals(VBASIC, vbasics))
{
String str = Resource.getString("video.msg.basics") + " MPEG-4/H.264, " + vbasics[4] + ", " + vbasics[0] + "*" + vbasics[1] + ", " + vbasics[2] + " @ " + vbasics[3];
Common.setMessage("-> " + Resource.getString("video.msg.newformat", "" + clv[6]));
Common.setMessage(str);
format_changed = true;
System.arraycopy(vbasics, 0, VBASIC, 0, VBASIC.length);
}
break;
case 8: //pict set
break;
case 9: //access
accessunit = true;
accessunit_pos = s;
primary_pic_type = (7 & gop[s + 5]>>5); //XXX0-0000 0 to 7 prim pic type p.73
break;
case 10: //seq end
Common.setMessage("-> SequenceEndMarker detected at rel. pos " + s);
break;
case 11: //stream end
Common.setMessage("-> StreamEndMarker detected at rel. pos " + s);
break;
case 12: //fill
break;
}
}
if (au != null)
au.setEnd(gop.length);
//error checks
if (accessunit_list.size() > 0)
{
au = (AccessUnit) accessunit_list.get(0);
if (au.getPPTAU() != 0)
{
// error, no leading I-Frame
Common.setMessage(Resource.getString("video.msg.error.frame.not.i", String.valueOf(clv[6] - 1)));
ErrorCode |= 1;
}
else
startpts = au.getPTS();
}
else
{
// error, no Frames found
Common.setMessage(Resource.getString("video.msg.error.frame.not", String.valueOf(clv[6] - 1)));
ErrorCode |= 2;
}
if (ErrorCode != 0)
error = true;
//info
if (!error)
{
for (int i = 0, fr = 0, j = 0, k = 0; i < accessunit_list.size(); i++)
{
au = (AccessUnit) accessunit_list.get(i);
// Common.setMessage(au.toString());
if (i == 0) // first frame, can have Picorder from last seq
{
base_pts[0] = au.getPTS() - au.getPicOrder() * (CommonParsing.getVideoFramerate() / 2.0);
fr = au.getFrameNum();
reorder_list.add(au);
}
else
{
base_pts[1] = au.getPTS() - au.getPicOrder() * (CommonParsing.getVideoFramerate() / 2.0);
//base pts, cannot detect missing frames
if (au.getPTS() != -1 && Math.abs(base_pts[0] - base_pts[1]) > 100)
{
//Common.setMessage("!> base pts validation failed: " + Math.abs(base_pts[0] - base_pts[1]));
ErrorCode |= 4;
}
}
// field cnt
field += au.isFieldPic() ? 1 : 2;
//determine 1st pts of Sequ
if (au.getPTS() < startpts && au.getPTS() != -1)
startpts = au.getPTS();
//determine framenum discontinuity, may not detect missing frames
if (au.getFrameNum() != fr && au.getFrameNum() != ((1 + fr) & (-1>>>(32 - temp_values[0]))))
{
ErrorCode |= 8;
Common.setMessage("!> discontinuity in framenum: exp/rec " + (fr + 1) + "/" + au.getFrameNum());
}
fr = au.getFrameNum();
//reorder to pts indicated order
for (k = j, j = 0; i > 0 && j < reorder_list.size(); j++)
{
if (au.getPTS() == -1)
{
reorder_list.add(k + 1, au);
break;
}
if (au.getPTS() < ((AccessUnit) reorder_list.get(j)).getPTS())
{
reorder_list.add(j, au);
break;
}
}
if (j == reorder_list.size())
reorder_list.add(au);
}
//detect missing frames in reordered list
for (int i = 0, j = 0, k = 0; i < reorder_list.size(); i++)
{
au = (AccessUnit) reorder_list.get(i);
if (i == 0)
base_pts[2] = au.getPTS();
j = au.isFieldPic() ? 2 : 1; //offs-length of last field/frame
if (i > 0 && au.getPTS() != -1 && Math.abs(au.getPTS() - base_pts[2]) > 100)
ErrorCode |= 0x20;
base_pts[2] += CommonParsing.getVideoFramerate() / j;
//count preceding fields ref'd from last gop
if (au.getPPTAU() == 0) // || au.getUnitRef() != 0)
k = 1;
else if (k == 0)
{
preceding_fields[0]++; //au cnt
preceding_fields[1] += au.isFieldPic() ? 1 : 2; //field cnt
}
}
//no missing frame, reset pts base validation error
if ((0x24 & ErrorCode) == 4)
ErrorCode &= ~4;
//frame count, maybe not suitable for pulldown
frame = field / 2;
//cleaned pts for gop end
lastpts = startpts + Math.round(field * CommonParsing.getVideoFramerate() / 2.0);
}
clv[6]++; //GOP count
// error, start pts is too early against last
if (startpts < job_processing.getLastGopPts() - CommonParsing.getVideoFramerate() / 2)
{
Common.setMessage(Resource.getString("video.msg.error.pts.early", String.valueOf(clv[6] - 1), String.valueOf(job_processing.getLastGopPts())));
ErrorCode |= 0x10;
}
if (ErrorCode != 0)
error = true;
if (Debug)
{
for (int i = 0; i < accessunit_list.size(); i++)
System.out.println("E " + accessunit_list.get(i).toString());
for (int i = 0; i < reorder_list.size(); i++)
System.out.println("R " + reorder_list.get(i).toString());
}
// message error
if (error)
{
//Common.setMessage("<< error >>");
//Common.setMessage("fld " + field + " /sp " + startpts + " /lp " + lastpts + " /E " + error);
job_processing.setExportedVideoFrameNumber(lastframes);
clv[4]++;
messageDropError(gop, pts, (int)((base_pts[2] - startpts) / CommonParsing.getVideoFramerate()), frame, clv[6], job_processing.getExportedVideoFrameNumber(), startpts, lastpts, dumpname, ErrorCode);
// messageDropError(gop, pts, au_cnt, frame, clv[6], job_processing.getExportedVideoFrameNumber(), startpts, lastpts, dumpname, ErrorCode);
}
else
{
// how to cut
switch (CutMode)
{
case CommonParsing.CUTMODE_BYTE:
cutposition = job_processing.getCutByteposition();
break;
case CommonParsing.CUTMODE_GOP:
cutposition = clv[6];
break;
case CommonParsing.CUTMODE_FRAME:
cutposition = job_processing.getSourceVideoFrameNumber();
break;
case CommonParsing.CUTMODE_PTS:
cutposition = startpts + 1000; //exclude jitter
break;
case CommonParsing.CUTMODE_TIME:
cutposition = startpts - job_processing.get1stVideoPTS();
}
// cut using bytepos, frame#, gop#, timecode or pts
if (!CommonParsing.makecut(job_processing, dumpname, startpts, cutposition, newcut, lastframes, CutpointList, clv[6] - 1, job_processing.getCellTimes()))
job_processing.setExportedVideoFrameNumber(lastframes);
// DAR request for auto cut
else if (OptionDAR && ExportDAR != clv[7])
job_processing.setExportedVideoFrameNumber(lastframes);
// H Resolution request for auto cut
else if (OptionHorizontalResolution && !ExportHorizontalResolution.equals(VBASIC[0]))
job_processing.setExportedVideoFrameNumber(lastframes);
//else if (!doWrite)
//{}
else
{
if (job_processing.getExportedVideoFrameNumber() == 0)
infos.add(Resource.getString("video.msg.export.start") + " " + (clv[6] - 1));
// gap > 1/2 frame time to last gop, with possibly unref'd frames in gop
if (preceding_fields[1] > 0 && job_processing.getLastGopPts() < startpts - CommonParsing.getVideoFramerate() / 2)
{
for (int i = 0, j = 0; i < preceding_fields[0]; i++)
{
j = ((AccessUnit) reorder_list.get(i)).getCount();
//remove first unref'd frames
for (int k = 0; k < accessunit_list.size(); k++)
{
au = (AccessUnit) accessunit_list.get(k);
if (j == au.getCount())
{
field -= au.isFieldPic() ? 1 : 2;
startpts += CommonParsing.getVideoFramerate() / (au.isFieldPic() ? 2 : 1);
accessunit_list.remove(k);
break;
}
}
}
//rewrite new gop, adapt picorder ??
for (int i = 0; i < accessunit_list.size(); i++)
{
au = (AccessUnit) accessunit_list.get(i);
/**
if (i == 0 && job_processing.getExportedVideoFrameNumber() > 0)
{
gopbuffer.write(gop, au.getStart(), sequ_head_end);
gopbuffer.write(setSEIMsg6(au.getFrameNum()));
gopbuffer.write(gop, sequ_head_end, au.getLength());
}
else
**/
gopbuffer.write(gop, au.getStart(), au.getLength());
if (Debug)
System.out.println("bu " + i + " /au " + au.getCount() + " /fn " + au.getFrameNum() + " /pptau " + au.getPPTAU() + " /ppt " + au.getPPT() + " /st " + au.getStart() + " /en " + au.getEnd());
}
gopbuffer.flush();
gop = gopbuffer.toByteArray();
frame = field / 2;
infos.add("!> removing " + preceding_fields[1] + " preceding fields from GOP #" + (clv[6] - 1));
}
// print cached messages
for (int i = 0; i < infos.size(); i++)
{
Common.setMessage(infos.get(i).toString());
job_processing.setNewVideoStream(false);
}
// if write is enabled, write gop
if (WriteVideo)
{
// value for gop bitrate per second
double svbr = CommonParsing.getVideoFramerate() * frame;
if (svbr <= 0) //20 frames sch�tzung
svbr = CommonParsing.getVideoFramerate() * 20;
int vbr = (int)( ( (90000L * (gop.length * 8)) / svbr ) / 400);
// determ. avg bitrates
if (vbr < job_processing.getMinBitrate())
job_processing.setMinBitrate(vbr);
if (vbr > job_processing.getMaxBitrate())
job_processing.setMaxBitrate(vbr);
// update data for the gop info picture
frametypebuffer.flush();
byte[] ftb = frametypebuffer.toByteArray();
int min = 0x7FFF & clv[9]>>>15;
int max = 0x7FFF & clv[9];
int prog_flag = clv[9]>>>30;
for (int i = 1; i < ftb.length; i++)
prog_flag |= (0x80 & ftb[i]) != 0 ? 2 : 1;
if (field < min || min == 0)
min = field;
if (field > max || max == 0)
max = field;
clv[9] = prog_flag<<30 | (0x7FFF & min)<<15 | (0x7FFF & max);
//<--
Common.getGuiInterface().updateBitrateMonitor(vbr, ftb, Common.formatTime_1((job_processing.getVideoExportTimeSummary() + job_processing.getVideoExportTime()) / 90).substring(0, 8));
/**gap what breaks sequence
if (job_processing.getExportedVideoFrameNumber() > 0 && job_processing.getLastGopPts() < startpts - CommonParsing.getVideoFramerate() / 2)
{
video_sequence.write(sequ_end);
Common.setMessage("-> sequence closed after GOP# " + (clv[6] - 1));
job_processing.countMediaFilesExportLength(sequ_end.length);
job_processing.countAllMediaFilesExportLength(sequ_end.length);
}
**/
video_sequence.write(gop);
//clv[6]++; //GOP count
clv[3]++; //unchanged GOP count
job_processing.countExportedVideoFrameNumber(frame);
job_processing.setEndPtsOfGop(lastpts);
job_processing.setLastGopPts(lastpts);
// write V-PTS Log for audio/data sync
log.writeLong(startpts);
log.writeLong(job_processing.getEndPtsOfGop());
// write V-Time Log for audio/data sync
log.writeLong(videotimes);
job_processing.countVideoExportTime(lastpts - startpts);
log.writeLong(job_processing.getVideoExportTime());
job_processing.countMediaFilesExportLength(gop.length);
job_processing.countAllMediaFilesExportLength(gop.length);
doExport = true; // no cut ATM
Common.getGuiInterface().showExportStatus(doExport ? Resource.getString("audio.status.write") : Resource.getString("audio.status.pause"));
}
}
}
infos.clear();
} catch (IOException e) {
Common.setExceptionMessage(e);
}
}
/**
* H264 SequenceParameterSet
*/
private boolean readH264SequenceParameterSet(byte[] array, int offset, String[] vbasics, int[] temp_values)
{
byte[] check = new byte[100];
int[] BitPosition = { 0 };
BitPosition[0] = (4 + offset)<<3;
int zero = getBits(array, BitPosition, 1); //forb_zero = 0x80 & check[4 + i];
int nal_ref = getBits(array, BitPosition, 2); //nal_ref = (0xE0 & check[4 + i])>>>5;
int nal_unit = getBits(array, BitPosition, 5); //nal_unit = 0x1F & check[4 + i];
if (zero != 0 || nal_unit != 7)
return false;
//emulation prevention
for (int m = 5 + offset, rbsp = 0; rbsp < 100 - 3; m++)
{
if (array[m] == 0 && array[m + 1] == 0 && array[m + 2] == 3)
{
rbsp += 2; //2 bytes value 0
m += 2; //emulation_prevention_three_byte /* equal to 0x03 */
}
else
check[rbsp++] = array[m];
}
//reset for check
BitPosition[0] = 0;
//seq_param
int profile_idc = getBits(check, BitPosition, 8); //profile = 0xFF & check[5 + i];
getBits(check, BitPosition, 4); //constraint 0,1,2,3
zero = getBits(check, BitPosition, 4); //4 res_zero_bits
if (zero != 0)
return false;
int level_idc = getBits(check, BitPosition, 8); //0xFF & check[7 + i];
int flag = getCodeNum(check, BitPosition); // seq_parameter_set_id 0 ue(v)
int chroma_format_idc = 1; //dflt
if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 44 || profile_idc == 244)
{
chroma_format_idc = getCodeNum(check, BitPosition); //chroma_format_idc 0 ue(v)
if (chroma_format_idc == 3)
getBits(check, BitPosition, 1); //separate_colour_plane_flag 0 u(1)
getCodeNum(check, BitPosition); //bit_depth_luma_minus8 0 ue(v)
getCodeNum(check, BitPosition); //bit_depth_chroma_minus8 0 ue(v)
getBits(check, BitPosition, 1); //qpprime_y_zero_transform_bypass_flag 0 u(1)
flag = getBits(check, BitPosition, 1); //seq_scaling_matrix_present_flag 0 u(1)
if (flag == 1)
{
for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); i++)
{
//seq_scaling_list_present_flag[ i ] 0 u(1)
if (getBits(check, BitPosition, 1) == 1)
if (i < 6)
scaling_list(check, BitPosition, null, 16, null); //scaling_list( ScalingList4x4[ i ], 16,
//UseDefaultScalingMatrix4x4Flag[ i ])
else
scaling_list(check, BitPosition, null, 64, null); //scaling_list( ScalingList8x8[ i � 6 ], 64,
//UseDefaultScalingMatrix8x8Flag[ i � 6 ] )
}
}
}
temp_values[0] = 4 + getCodeNum(check, BitPosition); // log2_max_frame_num_minus4 0 ue(v)
temp_values[2] = getCodeNum(check, BitPosition); // pic_order_cnt_type 0 ue(v)
if (temp_values[2] == 0)
temp_values[3] = 4 + getCodeNum(check, BitPosition); // log2_max_pic_order_cnt_lsb_minus4 0 ue(v)
else if (temp_values[2] == 1)
{
getBits(check, BitPosition, 1); //delta_pic_order_always_zero_flag 0 u(1)
getSignedCodeNum(check, BitPosition); //offset_for_non_ref_pic 0 se(v)
getSignedCodeNum(check, BitPosition); //offset_for_top_to_bottom_field 0 se(v)
flag = getCodeNum(check, BitPosition); // num_ref_frames_in_pic_order_cnt_cycle 0 ue(v)
for (int k = 0; k < flag; k++)
getSignedCodeNum(check, BitPosition); //offset_for_ref_frame[ i ] 0 se(v)
}
temp_values[8] = getCodeNum(check, BitPosition); //num_ref_frames 0 ue(v)
temp_values[9] = getBits(check, BitPosition, 1); //gaps_in_frame_num_value_allowed_flag 0 u(1)
if (Debug)
System.out.println("num_ref_fr " + temp_values[8] + " /gapsallow " + temp_values[9]);
int hori = 16 * (1 + getCodeNum(check, BitPosition)); //pic_width_in_mbs_minus1 0 ue(v)
int vert = 16 * (1 + getCodeNum(check, BitPosition)); //pic_height_in_map_units_minus1 0 ue(v)
flag = getBits(check, BitPosition, 1); //frame_mbs_only_flag 0 u(1)
temp_values[5] = flag;
vbasics[0] = String.valueOf(hori);
vbasics[1] = String.valueOf(flag == 0 ? vert<<1 : vert);
if (profile_idc == 66)
vbasics[4] = "Base@";
else if (profile_idc == 77)
vbasics[4] = "Main@";
else if (profile_idc == 88)
vbasics[4] = "Ext@";
else if (profile_idc == 100)
vbasics[4] = "High@";
else if (profile_idc == 110)
vbasics[4] = "High10@";
else if (profile_idc == 122)
vbasics[4] = "High422";
else if (profile_idc == 144)
vbasics[4] = "High444@";
else if (profile_idc == 44)
vbasics[4] = "High444a@";
else if (profile_idc == 244)
vbasics[4] = "High444b@";
//else if (profile_idc == 166)
// vbasics[2] = "High@";
//else if (profile_idc == 188)
// vbasics[2] = "High@";
else
vbasics[4] = String.valueOf(profile_idc) + "@";
vbasics[4] += String.valueOf(level_idc / 10.0);
if (flag == 0) //if( !frame_mbs_only_flag )
getBits(check, BitPosition, 1); //mb_adaptive_frame_field_flag 0 u(1)
getBits(check, BitPosition, 1); //direct_8x8_inference_flag 0 u(1)
flag = getBits(check, BitPosition, 1); //frame_cropping_flag 0 u(1)
if (flag == 1) //if( frame_cropping_flag ) {
{
getCodeNum(check, BitPosition); //frame_crop_left_offset 0 ue(v)
getCodeNum(check, BitPosition); //frame_crop_right_offset 0 ue(v)
getCodeNum(check, BitPosition); //frame_crop_top_offset 0 ue(v)
getCodeNum(check, BitPosition); //frame_crop_bottom_offset 0 ue(v)
}
flag = getBits(check, BitPosition, 1); //vui_parameters_present_flag 0 u(1)
if (flag == 1) //if( vui_parameters_present_flag )
vui_parameters(check, BitPosition, vbasics); //vui_parameters( ) 0
//rbsp_trailing_bits( ) 0
return true;
}
// h264 parse signed code num
private int getSignedCodeNum(byte[] array, int[] BitPosition)
{
int codeNum = getCodeNum(array, BitPosition);
codeNum = (codeNum & 1) == 0 ? codeNum>>>1 : -(codeNum>>>1);
return codeNum;
}
// h264 parse unsigned code num
private int getCodeNum(byte[] array, int[] BitPosition)
{
int leadingZeroBits = -1;
int codeNum;
for (int b = 0; b == 0; leadingZeroBits++)
b = getBits(array, BitPosition, 1);
codeNum = (1<<leadingZeroBits) - 1 + getBits(array, BitPosition, leadingZeroBits);
return codeNum;
}
// 7.3.2.1.1 Scaling list syntax
private void scaling_list(byte[] check, int[] BitPosition, int[] scalingList, int sizeOfScalingList, boolean[] useDefaultScalingMatrixFlag)
{
int lastScale = 8;
int nextScale = 8;
for (int j = 0; j < sizeOfScalingList; j++)
{
if (nextScale != 0)
{
int delta_scale = getSignedCodeNum(check, BitPosition); //delta_scale 0 | 1 se(v)
nextScale = (lastScale + delta_scale + 256) % 256;
//useDefaultScalingMatrixFlag = ( j == 0 && nextScale == 0 );
}
//scalingList[ j ] = ( nextScale = = 0 ) ? lastScale : nextScale
lastScale = (nextScale == 0) ? lastScale : nextScale; //lastScale = scalingList[ j ]
}
}
// E1.1 VUI parameters syntax
private void vui_parameters(byte[] check, int[] BitPosition, String[] vbasics)
{
String[] aspect_ratio_string_h264 = {
"Unspec.", "1:1", "12:11", "10:11", "16:11", "40:33", "24:11", "20:11",
"32:11", "80:33", "18:11", "15:11", "64:33", "160:99", "4:3", "3:2", "2:1"
};
double[] aspect_ratio_double_h264 = {
1.0, 1.0, 1.0909, 0.90909, 1.45454, 1.21212, 2.18181, 1.81818,
2.90909, 2.42424, 1.63636, 1.36364, 1.93939, 1.61616, 1.33333, 1.5, 2
};
int flag = getBits(check, BitPosition, 1); //aspect_ratio_info_present_flag 0 u(1)
if (flag == 1) //if( aspect_ratio_info_present_flag ) {
{
int aspect_ratio_idc = getBits(check, BitPosition, 8); //aspect_ratio_idc 0 u(8)
if (aspect_ratio_idc == 255) //if( aspect_ratio_idc = = Extended_SAR ) {
{
int sar_w = getBits(check, BitPosition, 16); //sar_width 0 u(16)
int sar_h = getBits(check, BitPosition, 16); //sar_height 0 u(16)
vbasics[3] = "SAR: " + sar_w + ":" + sar_h;
vbasics[3] += " >> " + String.valueOf((int)(Double.parseDouble(vbasics[0]) * sar_w / sar_h)) + "*" + vbasics[1];
}
else
{
vbasics[3] = "SAR: " + (aspect_ratio_idc < 17 ? aspect_ratio_string_h264[aspect_ratio_idc] : "res.");
vbasics[3] += " >> " + (aspect_ratio_idc < 17 ? String.valueOf(Math.round(Double.parseDouble(vbasics[0]) * aspect_ratio_double_h264[aspect_ratio_idc])) : "res.") + "*" + vbasics[1];
}
}
flag = getBits(check, BitPosition, 1); //overscan_info_present_flag 0 u(1)
if (flag == 1) //if( overscan_info_present_flag )
getBits(check, BitPosition, 1); //overscan_appropriate_flag 0 u(1)
flag = getBits(check, BitPosition, 1); //video_signal_type_present_flag 0 u(1)
if (flag == 1) //if( video_signal_type_present_flag ) {
{
getBits(check, BitPosition, 3); //video_format 0 u(3)
getBits(check, BitPosition, 1); //video_full_range_flag 0 u(1)
flag = getBits(check, BitPosition, 1); //colour_description_present_flag 0 u(1)
if (flag == 1) //if( colour_description_present_flag ) {
{
getBits(check, BitPosition, 8); //colour_primaries 0 u(8)
getBits(check, BitPosition, 8); //transfer_characteristics 0 u(8)
getBits(check, BitPosition, 8); //matrix_coefficients 0 u(8)
}
}
flag = getBits(check, BitPosition, 1); //chroma_loc_info_present_flag 0 u(1)
if (flag == 1) //if( chroma_loc_info_present_flag ) {
{
getCodeNum(check, BitPosition); //chroma_sample_loc_type_top_field 0 ue(v)
getCodeNum(check, BitPosition); //chroma_sample_loc_type_bottom_field 0 ue(v)
}
flag = getBits(check, BitPosition, 1); //timing_info_present_flag 0 u(1)
if (flag == 1) //if( timing_info_present_flag ) {
{
int num_units_ticks = getBits(check, BitPosition, 24)<<8 | getBits(check, BitPosition, 8); //num_units_in_tick 0 u(32)
int time_scale = getBits(check, BitPosition, 24)<<8 | getBits(check, BitPosition, 8); //time_scale 0 u(32)
int fixed_framerate = getBits(check, BitPosition, 1); //fixed_frame_rate_flag 0 u(1)
vbasics[2] = ((1000 * time_scale / (2 * num_units_ticks)) / 1000.0) + " fps (" + (fixed_framerate == 1 ? "f)" : "v)");
}
//getBits(check, BitPosition, 1); //nal_hrd_parameters_present_flag 0 u(1)
//if( nal_hrd_parameters_present_flag )
//hrd_parameters( )
//vcl_hrd_parameters_present_flag 0 u(1)
//if( vcl_hrd_parameters_present_flag )
//hrd_parameters( )
//if( nal_hrd_parameters_present_flag | | vcl_hrd_parameters_present_flag )
//low_delay_hrd_flag 0 u(1)
//pic_struct_present_flag 0 u(1)
//bitstream_restriction_flag 0 u(1)
//if( bitstream_restriction_flag ) {
//motion_vectors_over_pic_boundaries_flag 0 u(1)
//max_bytes_per_pic_denom 0 ue(v)
//max_bits_per_mb_denom 0 ue(v)
//log2_max_mv_length_horizontal 0 ue(v)
//log2_max_mv_length_vertical 0 ue(v)
//num_reorder_frames 0 ue(v)
//max_dec_frame_buffering 0 ue(v)
//}
}
// 7.3.2.8 + 7.3.3 slice w/o partitioning
private void slice_wo_partitioning(byte[] check, int offset, int unittype, int[] temp_values)
{
int flag;
int[] BitPosition = { 0 };
BitPosition[0] = (5 + offset)<<3;
flag = getCodeNum(check, BitPosition); //first_mb_in_slice 2 ue(v)
temp_values[11] = getCodeNum(check, BitPosition); //slice_type 2 ue(v)
flag = getCodeNum(check, BitPosition); //pic_parameter_set_id 2 ue(v)
temp_values[1] = getBits(check, BitPosition, temp_values[0]);//frame_num 2 u(v)
if (temp_values[5] == 0) //if( !frame_mbs_only_flag )
{
temp_values[6] = getBits(check, BitPosition, 1); //field_pic_flag 2 u(1)
if (temp_values[6] == 1) //if( field_pic_flag )
temp_values[7] = getBits(check, BitPosition, 1); //bottom_field_flag 2 u(1)
}
if (unittype == 5) //if( nal_unit_type = = 5 )
getCodeNum(check, BitPosition); //idr_pic_id 2 ue(v)
if (temp_values[2] == 0) //if( pic_order_cnt_type = = 0 )
{
temp_values[10] = BitPosition[0]; //mark position
temp_values[4] = getBits(check, BitPosition, temp_values[3]); //pic_order_cnt_lsb 2 u(v)
//if( pic_order_present_flag && !field_pic_flag )
//delta_pic_order_cnt_bottom 2 se(v)
}
//if( pic_order_cnt_type = = 1 && !delta_pic_order_always_zero_flag ) {
//delta_pic_order_cnt[ 0 ] 2 se(v)
//if( pic_order_present_flag && !field_pic_flag )
//delta_pic_order_cnt[ 1 ] 2 se(v)
//}
}
// 7.3.2.3 SEI
private void readSEIMessage(byte[] check, int offset)
{
int val = 0;
int[] BitPosition = { 0 };
BitPosition[0] = (5 + offset)<<3;
int payloadType = 0; //payloadType = 0
while ((val = getBits(check, BitPosition, 8)) == 0xFF) //while( next_bits( 8 ) = = 0xFF )
{
//ff_byte /* equal to 0xFF */ 5 f(8)
payloadType += 255; //payloadType += 255
}
//last_payload_type_byte 5 u(8)
payloadType += val; //payloadType += last_payload_type_byte
int payloadSize = 0; //payloadSize = 0
while ((val = getBits(check, BitPosition, 8)) == 0xFF)//while( next_bits( 8 ) = = 0xFF ) {
{
//ff_byte /* equal to 0xFF */ 5 f(8)
payloadSize += 255; //payloadSize += 255
}
//last_payload_size_byte 5 u(8)
payloadSize += val; //payloadSize += last_payload_size_byte
if (Debug)
System.out.println("SEI pos " + offset + " /pT " + payloadType + " /pS " + payloadSize);
//sei_payload( payloadType, payloadSize ) 5
}
// SEI recovery point D2.7
private byte[] setSEIMsg6(int framenum)
{
//00 00 00 01 06 06 01 A4 80
//rec_frmcnt ue(v) = 1 = 0 = framenum-start of gop
//exactmatch 1 = 0
//brokenlink 1 = 1
//chang_slic 2 = 00
//endmark .. = 100
int val = 0x00000000; // 01 00 00 00 - 01 length-v
//exp golomb set ue(v)
for (int i = 31, nval = framenum + 1, j = 0; i >= 0; i--)
{
if ((nval & 1<<i) != 0)
{
j = 24 - 1 - (i<<1);
val |= nval<<j; //framenum
j -= 4;
val |= 4<<j; //exm, bl, chng
if ((j & 7) != 0)
{
j -= 1;
val |= 1<<j;
}
j >>>= 3;
val |= (3 - j)<<24; //len
j -= 1;
val |= 0x80<<(j<<3);
// 00 00 00 01 06 06 0x aa bb
byte[] sei = new byte[10 - j];
sei[3] = 1;
sei[4] = 6;
sei[5] = 6;
sei[6] = (byte)(val>>24);
sei[7] = (byte)(val>>16);
sei[8] = (byte)(val>>8);
if (sei.length > 9)
sei[9] = (byte)val;
if (Debug)
{
for (int m = 0; m < sei.length; m++)
System.out.println("m " + m + " / " + Integer.toHexString(0xFF & sei[m]));
}
return sei;
}
}
return (new byte[0]); // failure
}
// parse bits
private int getBits(byte[] array, int[] BitPosition, int N)
{
int Pos, Val;
Pos = BitPosition[0]>>>3;
if (N == 0)
return 0;
if (Pos >= array.length - 4)
{
BitPosition[0] += N;
return -1;
}
Val = (0xFF & array[Pos])<<24 |
(0xFF & array[Pos + 1])<<16 |
(0xFF & array[Pos + 2])<<8 |
(0xFF & array[Pos + 3]);
Val <<= BitPosition[0] & 7;
Val >>>= 32 - N;
BitPosition[0] += N;
return Val;
}
class AccessUnit {
int cnt = -1;
int pptau = -1;
int ppt = -1;
int framenum = -1;
int picorder = -1;
long pts = -1;
int unittype = -1;
int unitref = -1;
int start = -1;
int end = -1;
int fieldpic = 0;
int btmfield = 0;
int marker = 0;
int slicetype = 0;
public AccessUnit(int _cnt, int _pptau, int _ppt, int[] _tempval, long _pts, int _unittype, int _unitref, int _start)
{
cnt = _cnt;
pptau = _pptau;
ppt = _ppt;
framenum = _tempval[1];
picorder = _tempval[4];
fieldpic = _tempval[6];
btmfield = _tempval[7];
marker = _tempval[10];
slicetype = _tempval[11];
pts = _pts;
start = _start;
unittype = _unittype;
unitref = _unitref;
}
private void setEnd(int _end)
{
end = _end;
}
private int getCount()
{
return cnt;
}
private int getPPTAU()
{
return pptau;
}
private int getPPT()
{
return ppt;
}
private int getPicOrder()
{
return picorder;
}
private boolean isFieldPic()
{
return (fieldpic != 0);
}
private boolean isBtmPic()
{
return (btmfield != 0);
}
private int getFrameNum()
{
return framenum;
}
private int getUnitType()
{
return unittype;
}
private int getUnitRef()
{
return unitref;
}
private int getStart()
{
return start;
}
private int getEnd()
{
return end;
}
private int getLength()
{
return end - start;
}
private int getMarker()
{
return marker;
}
private long getPTS()
{
return pts;
}
public String toString()
{
return ("AU " + cnt + " /frnm " + framenum + " /picord " + picorder + " /pptau " + pptau + " /ppt " + ppt + " /slicetyp " + slicetype + " /fldpic " + fieldpic + " /btmfld " + btmfield + " /ut " + unittype + " /ur " + unitref + " /pts " + pts + " /st " + start + " /en " + end);
}
}
}