/* * @(#)StreamParser * * Copyright (c) 2005-2009 by dvb.matt, All rights reserved. * * This file is part of ProjectX, a free Java based demux utility. * By the authors, ProjectX is intended for educational purposes only, * as a non-commercial test project. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ package net.sourceforge.dvb.projectx.parser; import java.io.File; import java.io.InputStream; import java.io.IOException; import java.io.PushbackInputStream; import java.io.FileOutputStream; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.BufferedWriter; import java.io.FileWriter; import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.util.Hashtable; import net.sourceforge.dvb.projectx.common.Common; import net.sourceforge.dvb.projectx.common.Resource; import net.sourceforge.dvb.projectx.common.Keys; import net.sourceforge.dvb.projectx.common.JobCollection; import net.sourceforge.dvb.projectx.common.JobProcessing; import net.sourceforge.dvb.projectx.io.StandardBuffer; import net.sourceforge.dvb.projectx.io.IDDBufferedOutputStream; import net.sourceforge.dvb.projectx.xinput.XInputFile; import net.sourceforge.dvb.projectx.xinput.StreamInfo; import net.sourceforge.dvb.projectx.parser.CommonParsing; import net.sourceforge.dvb.projectx.parser.StreamConverter; import net.sourceforge.dvb.projectx.parser.StreamDemultiplexer; import net.sourceforge.dvb.projectx.parser.StreamParserBase; import net.sourceforge.dvb.projectx.video.Video; /** * main thread */ public class StreamParserESVideo extends StreamParserBase { private double videotimecount = 0.0; /** * */ public StreamParserESVideo() { super(); } /** * check video ES */ public String parseStream(JobCollection collection, XInputFile aXInputFile, int pes_streamtype, int action, String vptslog) { JobProcessing job_processing = collection.getJobProcessing(); setFileName(collection, job_processing, aXInputFile, ".new"); boolean CreateInfoIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createInfoIndex); boolean CreateM2sIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createM2sIndex); CreateD2vIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createD2vIndex); SplitProjectFile = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_splitProjectFile); boolean WriteVideo = collection.getSettings().getBooleanProperty(Keys.KEY_WriteOptions_writeVideo); boolean AddSequenceEndcode = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_addEndcode); boolean RenameVideo = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_renameVideo); boolean Debug = collection.getSettings().getBooleanProperty(Keys.KEY_DebugLog); boolean Overlap = collection.getSettings().getBooleanProperty(Keys.KEY_ExportPanel_Export_Overlap); boolean CreateCellTimes = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createCellTimes); boolean first = true; boolean doWrite = true; boolean lead_sequenceheader = false; boolean isHeaderless = false; byte[] vgl = new byte[4]; byte[] vptsbytes = new byte[16]; byte[] vload = new byte[0]; byte[] es_packet; long filelength = aXInputFile.length(); long pos = 0; long pts = 0; long startPoint = 0; long Overlap_Value = 1048576L * (collection.getSettings().getIntProperty(Keys.KEY_ExportPanel_Overlap_Value) + 1); int CutMode = collection.getSettings().getIntProperty(Keys.KEY_CutMode); int[] MPGVideotype = { 0 }; int load = MainBufferSize / 2; int mark; int diff; int extension_index; int part; job_processing.clearStatusVariables(); int[] clv = job_processing.getStatusVariables(); double[] fps_tabl2 = { 0, 3753.7537, 3750, 3600, 3003.003, 3000, 1800, 1501.5015, 1500, 0,0,0,0,0,0,0 }; String[] videoext = { ".mpv", ".mpv", ".m1v", ".m2v" }; vptslog = "-1"; //fix Common.updateProgressBar(Resource.getString("video.progress") + " " + fchild, 0, 0); streamdemultiplexer = new StreamDemultiplexer(collection); try { PushbackInputStream in = new PushbackInputStream(aXInputFile.getInputStream(), 4); IDDBufferedOutputStream vstream = new IDDBufferedOutputStream( new FileOutputStream(fparent + ".s1"), MainBufferSize); /** * M2s project */ if (CreateM2sIndex) vstream.InitIdd(fparent, 1); /** * CM project */ if (CreateInfoIndex) vstream.InitInfo(fparent); DataOutputStream vlog = new DataOutputStream( new FileOutputStream(fparent + ".s1.pts") ); vlog.write(CommonParsing.PTSVideoHeader); ByteArrayOutputStream es_packetbuffer = new ByteArrayOutputStream(); job_processing.setElementaryVideoStream(true); job_processing.setMinBitrate(CommonParsing.MAX_BITRATE_VALUE); job_processing.setMaxBitrate(0); job_processing.setExportedVideoFrameNumber(0); job_processing.setEndPtsOfGop(-10000); job_processing.setAllMediaFilesExportLength(0); job_processing.setProjectFileExportLength(0); job_processing.setCutByteposition(0); /** * d2v project */ if (CreateD2vIndex || SplitProjectFile) job_processing.getProjectFileD2V().Init(fparent); /** * split skipping first */ if (job_processing.getSplitSize() > 0) { startPoint = job_processing.getLastHeaderBytePosition(); startPoint -= !Overlap ? 0 : Overlap_Value; doWrite = false; /** * set to 0, because we do not jump directly to the position due to possible audio sync-lost of elementary streams */ job_processing.setLastGopTimecode(0); job_processing.setLastGopPts(0); job_processing.setLastSimplifiedPts(0); /** if (pos < startPoint) startPoint = pos; while (pos < startPoint) pos += in.skip(startPoint-pos); **/ } List CutpointList = collection.getCutpointList(); List ChapterpointList = collection.getChapterpointList(); /** * if you do so, there's no common entry point with audio anymore, * therefore only one inputfile is allowed * jump to first cut-in point * if (CutMode == CommonParsing.CUTMODE_BYTE && CommonParsing.getCutCounter() == 0 && CutpointList.size() > 0) { long startPoint = Long.parseLong(CutpointList.get(CommonParsing.getCutCounter()).toString()); while (pos < startPoint) pos += in.skip(startPoint-pos); } **/ bigloop: while (pos < filelength) { while (pause()) {} if (CommonParsing.isProcessCancelled()) { CommonParsing.setProcessCancelled(false); job_processing.setSplitSize(0); break bigloop; } /** * cut end reached */ if (job_processing.getCutComparePoint() + 20 < job_processing.getSourceVideoFrameNumber()) break bigloop; load = (filelength - pos < (long)load) ? (int)(filelength - pos) : load; vload = new byte[load]; in.read(vload); pos += load; Common.updateProgressBar(pos, filelength); //yield(); mark = 0; loop: for (int i = 0, returncode, pes_ID; i < vload.length - 3; i++) { if (CutMode == CommonParsing.CUTMODE_BYTE && CutpointList.size() > 0) { if (CommonParsing.getCutCounter() == CutpointList.size() && (CommonParsing.getCutCounter() & 1) == 0) if (pos + i > Long.parseLong(CutpointList.get(CommonParsing.getCutCounter() - 1).toString())) break bigloop; } if ((returncode = CommonParsing.validateStartcode(vload, i)) < 0) { i += (-returncode) - 1; //note i++ continue loop; } pes_ID = CommonParsing.getPES_IdField(vload, i); switch (pes_ID) { case CommonParsing.PICTURE_START_CODE: lead_sequenceheader = false; i += 3; continue loop; case CommonParsing.GROUP_START_CODE: if (lead_sequenceheader) { lead_sequenceheader = false; i += 8; continue loop; } // do not break case CommonParsing.SEQUENCE_HEADER_CODE: case CommonParsing.SEQUENCE_END_CODE: if (pes_ID == CommonParsing.SEQUENCE_HEADER_CODE) lead_sequenceheader = true; es_packetbuffer.write(vload, mark, i - mark); mark = i; job_processing.setCutByteposition(pos - load + mark); if (job_processing.getCutByteposition() >= startPoint) doWrite = true; //if (!first) if (!first && !isHeaderless) //changed { es_packet = es_packetbuffer.toByteArray(); es_packetbuffer.reset(); firstframeloop: for (int j = 0, pes_ID_temp; j < 6000 && j < es_packet.length - 5; j++) { if ((returncode = CommonParsing.validateStartcode(es_packet, j)) < 0) { j += (-returncode) - 1; //note j++ continue firstframeloop; } pes_ID_temp = CommonParsing.getPES_IdField(es_packet, j); if (pes_ID_temp == CommonParsing.SEQUENCE_HEADER_CODE) videotimecount = fps_tabl2[15 & es_packet[j + 7]]; else if (pes_ID_temp == CommonParsing.PICTURE_START_CODE) { pts = (long)(job_processing.getSourceVideoFrameNumber() == 0 ? videotimecount * ((0xFF & es_packet[j + 4])<<2 | (0xC0 & es_packet[j + 5])>>>6) : (videotimecount * ( (0xFF & es_packet[j + 4])<<2 | (0xC0 & es_packet[j + 5])>>>6 )) + job_processing.getLastSimplifiedPts()); CommonParsing.setValue(vptsbytes, 0, 8, !CommonParsing.BYTEREORDERING, pts); break firstframeloop; } } // route through demuxer streamdemultiplexer.writeVideoES(job_processing, vstream, es_packet, vptsbytes, vlog, fparent, MPGVideotype, CutpointList, ChapterpointList, doWrite); } es_packetbuffer.reset(); if (pes_ID == CommonParsing.SEQUENCE_END_CODE) { Common.setMessage(Resource.getString("video.msg.skip.sec", "" + clv[6]) + " " + (pos - load + i)); i += 4; mark = i; } job_processing.setLastHeaderBytePosition(pos - load + i); // split marker sequence_gop start /** * split size reached */ if (job_processing.getSplitSize() > 0 && job_processing.getSplitSize() < job_processing.getAllMediaFilesExportLength()) break bigloop; /** * d2v split reached */ if (SplitProjectFile && job_processing.getProjectFileExportLength() > job_processing.getProjectFileSplitSize()) { part = job_processing.getProjectFileD2V().getPart() + 1; String newpart = fparent + "[" + part + "].mpv"; /** * sequence end code */ if (WriteVideo && AddSequenceEndcode && job_processing.getExportedVideoFrameNumber() > 0 ) { vstream.write(Video.getSequenceEndCode()); job_processing.countMediaFilesExportLength(+4); } job_processing.countAllMediaFilesExportLength(+4); vstream.flush(); vstream.close(); //System.gc(); vstream = new IDDBufferedOutputStream( new FileOutputStream(newpart), MainBufferSize - 1000000); if (CreateM2sIndex) vstream.InitIdd(newpart, 1); if (CreateInfoIndex) vstream.InitInfo(newpart); job_processing.getProjectFileD2V().setFile(newpart); job_processing.setProjectFileExportLength(0); } if (pes_ID == CommonParsing.GROUP_START_CODE) { job_processing.setSequenceHeader(false); if (job_processing.getSplitPart() > 0) first = false; } else { job_processing.setSequenceHeader(true); first = false; } //new isHeaderless = false; diff = (vload.length - mark - 4 < 2500) ? (vload.length - mark - 4) : 2500; //new if (pes_ID == CommonParsing.SEQUENCE_END_CODE) { diff = 0; isHeaderless = true; //skip data between sequnece end + sequence start } // if (diff > 0) { es_packetbuffer.write(vload, mark, diff); i += diff; mark = i; } break; default: i += 3; // do nothing } //overload if (es_packetbuffer.size() > 6144000) { Arrays.fill(vptsbytes, (byte)0); es_packetbuffer.reset(); first = true; Common.setMessage(Resource.getString("demux.error.gop.toobig")); } } /** * file end reached */ if (pos >= filelength - 1) { if (job_processing.getSplitSize() > 0) job_processing.setSplitLoopActive(false); break bigloop; } diff = vload.length < 3 ? vload.length : 3; es_packetbuffer.write(vload, mark, vload.length - mark - diff); in.unread(vload, vload.length - diff, diff); pos -= diff; } /** * d2v project */ if (CreateD2vIndex || SplitProjectFile) job_processing.getProjectFileD2V().write(job_processing.getProjectFileExportLength(), job_processing.getExportedVideoFrameNumber()); /** * sequence end code */ if (WriteVideo && AddSequenceEndcode && job_processing.getExportedVideoFrameNumber() > 0 ) { vstream.write(Video.getSequenceEndCode()); job_processing.countMediaFilesExportLength(+4); } in.close(); vstream.flush(); vstream.close(); vlog.flush(); vlog.close(); Common.setMessage(""); Common.setMessage(Resource.getString("video.msg.summary") + " " + job_processing.getExportedVideoFrameNumber() + "-" + clv[0] + "-" + clv[1] + "-" + clv[2] + "-" + clv[3] + "-" + clv[4]); File newfile = new File(fparent + ".s1"); String videofile = ""; if (newfile.length() < 20) newfile.delete(); else if (!WriteVideo) newfile.delete(); else { extension_index = (RenameVideo || CreateD2vIndex || SplitProjectFile) ? 0 : 2; videofile = fparent + videoext[MPGVideotype[0] + extension_index]; newfile = new File(videofile); if (newfile.exists()) newfile.delete(); Common.renameTo(new File(fparent + ".s1"), newfile); vptslog = fparent + ".s1.pts"; CommonParsing.setVideoHeader(job_processing, videofile, vptslog, clv, MPGVideotype); } if (CreateM2sIndex) { if (new File(videofile).exists()) { String tmpFN = videofile.toString(); vstream.renameVideoIddTo(tmpFN); } else vstream.deleteIdd(); } if (CreateInfoIndex) { if (new File(videofile).exists()) { String tmpFN = videofile.toString(); vstream.renameVideoInfoTo(tmpFN); } else vstream.deleteInfo(); } List cell = job_processing.getCellTimes(); String workouts = collection.getOutputDirectory() + collection.getFileSeparator(); /** * 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); } } catch (IOException e) { Common.setExceptionMessage(e); } return vptslog; } }