1 /*
2  * @(#)StreamParser
3  *
4  * Copyright (c) 2005-2006 by dvb.matt, All rights reserved.
5  *
6  * This file is part of ProjectX, a free Java based demux utility.
7  * By the authors, ProjectX is intended for educational purposes only,
8  * as a non-commercial test project.
9  *
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  *
25  */
26 
27 package net.sourceforge.dvb.projectx.parser;
28 
29 import java.io.File;
30 import java.io.InputStream;
31 import java.io.IOException;
32 import java.io.PushbackInputStream;
33 import java.io.FileOutputStream;
34 
35 import java.util.Arrays;
36 import java.util.List;
37 import java.util.ArrayList;
38 import java.util.Hashtable;
39 
40 import net.sourceforge.dvb.projectx.common.Common;
41 import net.sourceforge.dvb.projectx.common.Resource;
42 import net.sourceforge.dvb.projectx.common.Keys;
43 import net.sourceforge.dvb.projectx.common.JobCollection;
44 import net.sourceforge.dvb.projectx.common.JobProcessing;
45 
46 import net.sourceforge.dvb.projectx.io.StandardBuffer;
47 
48 import net.sourceforge.dvb.projectx.xinput.XInputFile;
49 import net.sourceforge.dvb.projectx.xinput.StreamInfo;
50 
51 import net.sourceforge.dvb.projectx.parser.CommonParsing;
52 import net.sourceforge.dvb.projectx.parser.StreamConverter;
53 import net.sourceforge.dvb.projectx.parser.StreamDemultiplexer;
54 import net.sourceforge.dvb.projectx.parser.StreamParserBase;
55 
56 
57 /**
58  * main thread
59  */
60 public class StreamParserPESPrimary extends StreamParserBase {
61 
62 	private boolean Message_2;
63 	private boolean Debug;
64 	private boolean SimpleMPG;
65 	private boolean GetEnclosedPackets;
66 	private boolean IgnoreScrambledPackets;
67 	private boolean PreviewAllGops;
68 	private boolean DumpDroppedGop;
69 	private boolean Overlap;
70 
71 	/**
72 	 *
73 	 */
StreamParserPESPrimary()74 	public StreamParserPESPrimary()
75 	{
76 		super();
77 	}
78 
79 	/**
80 	 * primary PES Parser
81 	 */
parseStream(JobCollection collection, XInputFile aXInputFile, int _pes_streamtype, int action, String vptslog)82 	public String parseStream(JobCollection collection, XInputFile aXInputFile, int _pes_streamtype, int action, String vptslog)
83 	{
84 		JobProcessing job_processing = collection.getJobProcessing();
85 
86 		setFileName(collection, job_processing, aXInputFile);
87 
88 		Debug = collection.getSettings().getBooleanProperty(Keys.KEY_DebugLog);
89 		Message_2 = collection.getSettings().getBooleanProperty(Keys.KEY_MessagePanel_Msg2);
90 		SimpleMPG = collection.getSettings().getBooleanProperty(Keys.KEY_simpleMPG);
91 		GetEnclosedPackets = collection.getSettings().getBooleanProperty(Keys.KEY_Input_getEnclosedPackets);
92 		IgnoreScrambledPackets = collection.getSettings().getBooleanProperty(Keys.KEY_TS_ignoreScrambled);
93 		PreviewAllGops = collection.getSettings().getBooleanProperty(Keys.KEY_Preview_AllGops);
94 		DumpDroppedGop = collection.getSettings().getBooleanProperty(Keys.KEY_dumpDroppedGop);
95 		CreateD2vIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createD2vIndex);
96 		SplitProjectFile = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_splitProjectFile);
97 		Overlap = collection.getSettings().getBooleanProperty(Keys.KEY_ExportPanel_Export_Overlap);
98 
99 		setOverheadSize(collection);
100 
101 		boolean isTeletext = false;
102 		boolean missing_startcode = false;
103 		boolean scrambling_messaged = false;
104 		boolean pes_isMpeg2;
105 		boolean pes_alignment;
106 		boolean pes_scrambled;
107 		boolean containsPts = false;
108 		boolean usePidfilter = false;
109 		boolean isZeroPacket = false;
110 		boolean isZeroSubPacket = false;
111 		boolean ende = false;
112 		boolean foundObject;
113 
114 		int Infoscan_Value = Integer.parseInt(collection.getSettings().getProperty(Keys.KEY_ExportPanel_Infoscan_Value));
115 		int CutMode = collection.getSettings().getIntProperty(Keys.KEY_CutMode);
116 		int pes_streamtype = _pes_streamtype;
117 		int pes_payloadlength;
118 		int pes_packetlength;
119 		int pes_extensionlength;
120 		int pes_headerlength = 9;
121 		int pes_packetoffset = 6;
122 		int pes_extension2_id;
123 		int pesID;
124 		int subID;
125 		int offset;
126 		int returncode = 0;
127 		int vob_ID = 0;
128 		int cell_ID = 0;
129 		int ZeroPacketPayload = 0x17FA;
130 
131 		int tmp_value1 = 0;
132 
133 		int pesID0 = 0; //first video stream ID
134 		int dumped_packets = 0;
135 
136 		job_processing.clearStatusVariables();
137 		int[] clv = job_processing.getStatusVariables();
138 
139 		int[] newID = { 0x80, 0x90, 0xC0, 0xE0, 0xA0, 0x20 };
140 
141 		byte[] pes_packet = new byte[0x10010];
142 		byte[] buffered_data;
143 
144 		long count = 0;
145 		long base;
146 		long size = count;
147 		long qexit;
148 		long ptsoffset = 0;
149 		long lastpts = 0;
150 		long startPoint = 0;
151 		long starts[] = new long[collection.getPrimaryInputFileSegments()];
152 		long Overlap_Value = 1048576L * (collection.getSettings().getIntProperty(Keys.KEY_ExportPanel_Overlap_Value) + 1);
153 
154 		vptslog = "-1"; //fix
155 
156 		String file_id = aXInputFile.getFileID();
157 
158 		streamconverter = new StreamConverter();
159 
160 		Hashtable substreams = new Hashtable();
161 		StandardBuffer sb;
162 
163 		demuxList = job_processing.getPrimaryPESDemuxList();
164 
165 		/**
166 		 * re-read old streams, for next split part
167 		 */
168 		if (job_processing.getSplitPart() == 0)
169 			demuxList.clear();
170 
171 		else
172 		{
173 			for (int i = 0; i < demuxList.size(); i++)
174 			{
175 				streamdemultiplexer = (StreamDemultiplexer) demuxList.get(i);
176 
177 				if (streamdemultiplexer.getnewID() != 0)
178 					newID[streamdemultiplexer.getType()]++;
179 
180 				if (streamdemultiplexer.getNum() == -1)
181 					continue;
182 
183 				if (streamdemultiplexer.getType() == CommonParsing.MPEG_VIDEO)
184 				{
185 					streamdemultiplexer.initVideo2(collection, fparent);
186 
187 					if (pesID0 == 0) //?
188 						pesID0 = streamdemultiplexer.getID();
189 				}
190 				else
191 					streamdemultiplexer.init2(collection, fparent);
192 			}
193 		}
194 
195 		/**
196 		 * init conversions
197 		 */
198 		String mpeg_type_str = (Keys.ITEMS_FileTypes[pes_streamtype]).toString().toLowerCase();
199 		mpeg_type_str = "[" + mpeg_type_str.substring(0, mpeg_type_str.indexOf(' ')) + "]";
200 
201 		initConversion(collection, fparent + (job_processing.getSplitSize() == 0 ? mpeg_type_str : ""), action, CommonParsing.ACTION_TO_VDR, job_processing.getSplitPart());
202 
203 		/**
204 		 * d2v projectfile
205 	 	 */
206 		if (CreateD2vIndex || SplitProjectFile)
207 			job_processing.getProjectFileD2V().Init(fparent);
208 
209 		job_processing.setMinBitrate(CommonParsing.MAX_BITRATE_VALUE);
210 		job_processing.setMaxBitrate(0);
211 		job_processing.setExportedVideoFrameNumber(0);
212 		job_processing.setEndPtsOfGop(-10000);
213 		job_processing.setSequenceHeader(true);
214 		job_processing.setAllMediaFilesExportLength(0);
215 		job_processing.setProjectFileExportLength(0);
216 		job_processing.setCutByteposition(0);
217 
218 		/**
219 		 * pid inclusion
220 		 */
221 		int[] predefined_Pids = collection.getPIDsAsInteger();
222 		int[] include = new int[predefined_Pids.length];
223 
224 		for (int i = 0; i < include.length; i++)
225 			include[i] = 0xFF & predefined_Pids[i];
226 
227 		if (include.length > 0)
228 		{
229 			Arrays.sort(include);
230 
231 			String str = " ";
232 
233 			for (int i = 0; i < include.length; i++)
234 				str += "0x" + Integer.toHexString(include[i]).toUpperCase() + " ";
235 
236 			Common.setMessage(Resource.getString("parsePrimaryPES.special.pes") + ": {" + str + "}");
237 
238 			usePidfilter = true;
239 		}
240 
241 
242 		try {
243 
244 			/**
245 			 * determine start & end byte pos. of each file segment
246 			 */
247 			for (int i = 0; i < starts.length; i++)
248 			{
249 				aXInputFile = (XInputFile) collection.getInputFile(i);
250 				starts[i] = size;
251 				size += aXInputFile.length();
252 			}
253 
254 			aXInputFile = (XInputFile) collection.getInputFile(job_processing.getFileNumber());
255 
256 			/**
257 			 * set start & end byte pos. of first file segment
258 			 */
259 			count = starts[job_processing.getFileNumber()];
260 			size = count + aXInputFile.length();
261 
262 			/**
263 			 * split skipping first, for next split part
264 			 */
265 			if (job_processing.getSplitSize() > 0)
266 			{
267 				startPoint = job_processing.getLastHeaderBytePosition();
268 				startPoint -= !Overlap ? 0 : Overlap_Value;
269 
270 				job_processing.setLastGopTimecode(0);
271 				job_processing.setLastGopPts(0);
272 				job_processing.setLastSimplifiedPts(0);
273 			}
274 
275 			List CutpointList = collection.getCutpointList();
276 			List ChapterpointList = collection.getChapterpointList();
277 
278 			/**
279 			 * jump near to first cut-in point to collect more audio
280 			 */
281 			if (CutMode == CommonParsing.CUTMODE_BYTE && CutpointList.size() > 0 && CommonParsing.getCutCounter() == 0  && (!PreviewAllGops || action != CommonParsing.ACTION_DEMUX))
282 				startPoint = Long.parseLong(CutpointList.get(CommonParsing.getCutCounter()).toString()) - ((action == CommonParsing.ACTION_DEMUX) ? OverheadSize : 0);
283 
284 			if (startPoint < 0)
285 				startPoint = count;  // =0
286 
287 			else if (startPoint < count)
288 			{
289 				for (int i = starts.length; i > 0; i--)
290 					if (starts[i - 1] > startPoint)
291 						job_processing.countFileNumber(-1);
292 			}
293 
294 			else if (startPoint > count)
295 			{
296 				for (int i = job_processing.getFileNumber() + 1; i < starts.length; i++)
297 				{
298 					if (starts[i] > startPoint)
299 						break;
300 					else
301 						job_processing.countFileNumber(+1);
302 				}
303 			}
304 
305 			aXInputFile = (XInputFile) collection.getInputFile(job_processing.getFileNumber());
306 			count = starts[job_processing.getFileNumber()];
307 
308 			if (job_processing.getFileNumber() > 0)
309 				Common.setMessage(Resource.getString("parsePrimaryPES.continue") + ": " + aXInputFile);
310 
311 			base = count;
312 			size = count + aXInputFile.length();
313 
314 			PushbackInputStream in = new PushbackInputStream(aXInputFile.getInputStream(startPoint - base), pes_packet.length);
315 
316 			count += (startPoint - base);
317 
318 			Common.updateProgressBar((action == CommonParsing.ACTION_DEMUX ? Resource.getString("parsePrimaryPES.demuxing") : Resource.getString("parsePrimaryPES.converting")) + " " + Resource.getString("parsePrimaryPES.avpes.file") + " " + aXInputFile.getName(), (count - base), (size - base));
319 
320 			qexit = count + (0x100000L * Infoscan_Value);
321 
322 			//yield();
323 
324 			bigloop:
325 			while (true)
326 			{
327 				loop:
328 				while (count < size)
329 				{
330 					pes_streamtype = _pes_streamtype;  // reset to original type
331 
332 					Common.updateProgressBar(count, size);
333 
334 					//yield();
335 
336 					while (pause())
337 					{}
338 
339 					if (CommonParsing.isProcessCancelled() || (CommonParsing.isInfoScan() && count > qexit))
340 					{
341 						CommonParsing.setProcessCancelled(false);
342 						job_processing.setSplitSize(0);
343 
344 						break bigloop;
345 					}
346 
347 					/**
348 					 * after last cut out, still read some packets to collect some audio
349 					 */
350 					if (job_processing.getCutComparePoint() + 20 < job_processing.getSourceVideoFrameNumber())
351 					{
352 						ende = true;
353 						break bigloop;
354 					}
355 
356 					/**
357 					 * cut mode bytepos + min 1 cutpoint
358 					 */
359 					if (CutMode == CommonParsing.CUTMODE_BYTE && CutpointList.size() > 0)
360 					{
361 						if (CommonParsing.getCutCounter() == CutpointList.size() && (CommonParsing.getCutCounter() & 1) == 0)
362 							if (count > Long.parseLong(CutpointList.get(CommonParsing.getCutCounter() - 1).toString()) + (action == CommonParsing.ACTION_DEMUX ? OverheadSize : 64000))
363 							{
364 								ende = true;
365 								break bigloop;
366 							}
367 					}
368 
369 					in.read(pes_packet, 0, pes_packetoffset);
370 
371 					pesID = CommonParsing.getPES_IdField(pes_packet, 0);
372 
373 					/**
374 					 *
375 					 */
376 					if ((returncode = CommonParsing.validateStartcode(pes_packet, 0)) < 0 || pesID < CommonParsing.SYSTEM_END_CODE)
377 					{
378 						returncode = returncode < 0 ? -returncode : 4;
379 
380 						if (Message_2 && !missing_startcode)
381 							Common.setMessage(Resource.getString("parsePrimaryPES.missing.startcode") + " " + count);
382 
383 						in.read(pes_packet, pes_packetoffset, pes_packet.length - pes_packetoffset);
384 
385 						int i = returncode;
386 
387 						for (; i < pes_packet.length - 3; )
388 						{
389 							returncode = CommonParsing.validateStartcode(pes_packet, i);
390 
391 							if (returncode < 0)
392 							{
393 								i += -returncode;
394 								continue;
395 							}
396 
397 							else
398 							{
399 								in.unread(pes_packet, i, pes_packet.length - i);
400 
401 								count += i;
402 								missing_startcode = true;
403 
404 								continue loop;
405 							}
406 						}
407 
408 						in.unread(pes_packet, i, pes_packet.length - i);
409 
410 						count += i;
411 						missing_startcode = true;
412 
413 						continue loop;
414 					}
415 
416 					if (Message_2 && missing_startcode)
417 						Common.setMessage(Resource.getString("parsePrimaryPES.found.startcode") + " " + count);
418 
419 					missing_startcode = false;
420 
421 					if (pes_streamtype == CommonParsing.MPEG1PS_TYPE || pes_streamtype == CommonParsing.MPEG2PS_TYPE || SimpleMPG)
422 					{
423 						switch (pesID)
424 						{
425 						case CommonParsing.SYSTEM_END_CODE:
426 							in.unread(pes_packet, 4, 2);
427 							Common.setMessage("-> skip system_end_code @ " + count);
428 							count += 4;
429 							continue loop;
430 
431 						case CommonParsing.PACK_START_CODE:
432 							if ((0xC0 & pes_packet[4]) == 0)  //mpg1
433 							{
434 								in.skip(pes_packetoffset);
435 								count += 12;
436 								continue loop;
437 							}
438 
439 							else if ((0xC0 & pes_packet[4]) == 0x40) //mpg2
440 							{
441 								in.read(pes_packet, pes_packetoffset, 8);
442 
443 								offset = 7 & pes_packet[13];
444 
445 								count += 14;
446 
447 								in.read(pes_packet, 14, offset);
448 
449 								if (offset > 0)
450 								{
451 									for (int i = 0; i < offset; i++)
452 										if (pes_packet[14 + i] != -1)
453 										{
454 											in.unread(pes_packet, 14, offset);
455 
456 											Common.setMessage("!> wrong pack header stuffing @ " + count);
457 											missing_startcode = true;
458 											continue loop;
459 										}
460 								}
461 
462 								count += offset;
463 
464 								continue loop;
465 							}
466 
467 							else
468 							{
469 								in.unread(pes_packet, 4, 2);
470 								count += 4;
471 								continue loop;
472 							}
473 
474 						case CommonParsing.PRIVATE_STREAM_2_CODE: //determine cellids
475 							pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, 0);
476 
477 							in.read(pes_packet, pes_packetoffset, pes_payloadlength);
478 
479 							if (pes_packet[pes_packetoffset] == 1) //substream id 1
480 							{
481 								int cellid = 0xFF & pes_packet[0x22];
482 								int vobid = (0xFF & pes_packet[0x1F])<<8 | (0xFF & pes_packet[0x20]);
483 
484 								if (cell_ID != cellid || vob_ID != vobid)
485 								{
486 									Common.setMessage(Resource.getString("parsePrimaryPES.split.cellids", "" + vobid, "" + cellid, "" + count, "" + clv[6], "" + job_processing.getExportedVideoFrameNumber()));
487 
488 									if (collection.getSettings().getBooleanProperty(Keys.KEY_VOB_resetPts))
489 									{
490 							//			ptsoffset = nextFilePTS(collection, CommonParsing.PRIMARY_PES_PARSER, pes_streamtype, lastpts, job_processing.getFileNumber(), (count - base));
491 										ptsoffset = nextFilePTS(collection, CommonParsing.PRIMARY_PES_PARSER, pes_streamtype, lastpts, ptsoffset, job_processing.getFileNumber(), (count - base));
492 
493 										if (ptsoffset == -1)
494 											ptsoffset = 0;
495 
496 										else
497 										{
498 											for (int i = 0; i < demuxList.size(); i++)
499 											{
500 												streamdemultiplexer = (StreamDemultiplexer)demuxList.get(i);
501 												streamdemultiplexer.PTSOffset(ptsoffset);
502 
503 												if (streamdemultiplexer.getID() == pesID0)
504 													streamdemultiplexer.resetVideo();
505 											}
506 
507 											job_processing.setSequenceHeader(true);
508 											job_processing.setNewVideoStream(true);
509 										//	job_processing.addCellTime(String.valueOf(job_processing.getExportedVideoFrameNumber()));
510 										}
511 									}
512 								}
513 
514 								cell_ID = cellid;
515 								vob_ID = vobid;
516 							}
517 
518 							count += (pes_packetoffset + pes_payloadlength);
519 							continue loop;
520 
521 						case CommonParsing.SYSTEM_START_CODE:
522 						case CommonParsing.PROGRAM_STREAM_MAP_CODE:
523 						case CommonParsing.PADDING_STREAM_CODE:
524 						case CommonParsing.ECM_STREAM_CODE:
525 						case CommonParsing.EMM_STREAM_CODE:
526 						case CommonParsing.DSM_CC_STREAM_CODE:
527 						case 0xF3:
528 						case 0xF4:
529 						case 0xF5:
530 						case 0xF6:
531 						case 0xF7:
532 						case 0xF8:
533 						case 0xF9:
534 						case 0xFA:
535 						case 0xFB:
536 						case 0xFC:
537 						case 0xFD:
538 						case 0xFE:
539 						case 0xFF:
540 							pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, 0);
541 
542 							in.skip(pes_payloadlength);
543 
544 							count += (pes_packetoffset + pes_payloadlength);
545 							continue loop;
546 						}
547 					}
548 
549 					if ( (0xF0 & pesID) != 0xE0 && (0xE0 & pesID) != 0xC0 && pesID != CommonParsing.PRIVATE_STREAM_1_CODE)
550 					{
551 						in.unread(pes_packet, 3, pes_packetoffset - 3);
552 						count += 3;
553 						continue loop;
554 					}
555 
556 					/**
557 					 * mark for split at sequenceheader
558 					 */
559 					job_processing.setLastHeaderBytePosition(count);
560 					job_processing.setCutByteposition(count);
561 
562 					pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, 0);
563 
564 					/**
565 					 * zeropackets of video only as PES from TS allowed
566 					 */
567 					isZeroPacket = pes_payloadlength == 0 && (0xF0 & pesID) == 0xE0;
568 
569 					if (isZeroPacket)
570 						pes_payloadlength = ZeroPacketPayload;
571 
572 					in.read(pes_packet, pes_packetoffset, pes_payloadlength + 4);
573 
574 					pes_packetlength = pes_packetoffset + pes_payloadlength;
575 
576 					pes_isMpeg2 = (0xC0 & pes_packet[6]) == 0x80;
577 
578 					/**
579 					 * determine next startcode in zero packets
580 					 */
581 					if (isZeroPacket)
582 					{
583 						if (Debug)
584 							System.out.println("A " + Resource.getString("parsePrimaryPES.packet.length") + " " + count);
585 
586 						//start after pes header
587 						for (int i = pes_packetoffset + (pes_isMpeg2 ? 3 : 1); isZeroPacket && i <= pes_packetlength; )
588 						{
589 							if ((returncode = CommonParsing.validateStartcode(pes_packet, i)) < 0 || CommonParsing.getPES_IdField(pes_packet, i) < CommonParsing.SYSTEM_END_CODE)
590 							{
591 								i += (returncode < 0 ? -returncode : 4);
592 								continue;
593 							}
594 
595 							/**
596 							 * next header found before max. size of ZeroPacketPayload
597 							 * handle packet as normal, the 4 added bytes will be unread in the next startcode check
598 							 */
599 							in.unread(pes_packet, i + 4, pes_packetlength - i);
600 
601 							pes_packetlength = i;
602 							pes_payloadlength = pes_packetlength - pes_packetoffset;
603 
604 							isZeroPacket = false;
605 						}
606 
607 						CommonParsing.setPES_LengthField(pes_packet, 0, pes_payloadlength);
608 					}
609 
610 					/**
611 					 * check next startcode
612 					 */
613 					if (GetEnclosedPackets && !isZeroPacket && CommonParsing.validateStartcode(pes_packet, pes_packetlength) < 0)
614 					{
615 						if (count + pes_packetlength < size)
616 						{
617 							if (Message_2 && !missing_startcode)
618 								Common.setMessage(Resource.getString("parsePrimaryPES.miss.startcode2", String.valueOf(count + pes_packetlength), String.valueOf(count), Integer.toHexString(pesID).toUpperCase()));
619 
620 							missing_startcode = true;
621 
622 							in.unread(pes_packet, pes_packetoffset, pes_payloadlength + 4);
623 							count += pes_packetoffset;
624 
625 							continue loop;
626 						}
627 					}
628 
629 					else
630 						in.unread(pes_packet, pes_packetlength, 4);
631 
632 
633 					clv[5]++;
634 
635 					if (Debug)
636 						System.out.println(Resource.getString("parsePrimaryPES.packs") + ": " + pesID + "/" + clv[5] + "/" + (pes_packetlength) + "/" + ((count * 100 / size)) + "% " + (count));
637 
638 					pes_extensionlength = CommonParsing.getPES_ExtensionLengthField(pes_packet, 0);
639 
640 					pes_isMpeg2 = (0xC0 & pes_packet[6]) == 0x80;
641 					pes_alignment = pes_isMpeg2 && (4 & pes_packet[6]) != 0;
642 					pes_scrambled = pes_isMpeg2 && (0x30 & pes_packet[6]) != 0;
643 
644 					count += pes_packetlength;
645 
646 					zeropacketloop:
647 					do {
648 						/**
649 						 * exit loop
650 						 */
651 						if (isZeroPacket && count >= size)
652 							break zeropacketloop;
653 
654 						/**
655 						 * read videos zerosubpacket data in a loop, if packet data is greater than max. ZeroPacketPayload
656 						 * the first read header data is re-used, we read only the rest
657 						 */
658 						if (isZeroSubPacket)
659 						{
660 							if (Debug)
661 								System.out.println("B " + Resource.getString("parsePrimaryPES.packet.length") + " " + count + " / " + pes_isMpeg2);
662 
663 							pes_payloadlength = ZeroPacketPayload;
664 
665 							pes_packetlength = pes_packetoffset + pes_payloadlength;
666 
667 							offset = pes_isMpeg2 ? 3 : 1;
668 
669 							in.read(pes_packet, pes_packetoffset + offset, pes_payloadlength - offset + 4);
670 							in.unread(pes_packet, pes_packetlength, 4);
671 
672 							for (int i = pes_packetoffset + offset; isZeroPacket && i <= pes_packetlength; )
673 							{
674 								if ((returncode = CommonParsing.validateStartcode(pes_packet, i)) < 0 || CommonParsing.getPES_IdField(pes_packet, i) < CommonParsing.SYSTEM_END_CODE)
675 								{
676 									i += (returncode < 0 ? -returncode : 4);
677 									continue;
678 								}
679 
680 								/**
681 								 * next header found before max. size of ZeroPacketPayload
682 								 * handle packet as normal, the 4 added bytes will be unread in the next startcode check
683 								 */
684 								in.unread(pes_packet, i, pes_packetlength - i);
685 
686 								pes_packetlength = i;
687 								pes_payloadlength = pes_packetlength - pes_packetoffset;
688 
689 								// set isZeroPacket to false, if next startcode was found
690 								isZeroPacket = false;
691 							}
692 
693 							CommonParsing.setValue(pes_packet, pes_packetoffset, offset, false, pes_isMpeg2 ? (0xF3 & pes_packet[pes_packetoffset])<<16 : 0x0F); // values of pes scrambling and copyright/copy prot. are taken from orig.
694 							CommonParsing.setPES_LengthField(pes_packet, 0, pes_payloadlength);
695 
696 							count += (pes_payloadlength - offset);
697 						}
698 
699 						isZeroSubPacket = isZeroPacket;
700 
701 						/**
702 						 * check scrambling
703 						 */
704 						if (IgnoreScrambledPackets)
705 						{
706 							// cannot work with scrambled data
707 							if (pes_scrambled)
708 							{
709 								if (!scrambling_messaged)
710 								{
711 									scrambling_messaged = true;
712 
713 									Common.setMessage(Resource.getString("parseTS.scrambled", Integer.toHexString(pesID).toUpperCase(), String.valueOf(clv[5]), String.valueOf(count - pes_packetlength)));
714 								}
715 
716 								continue zeropacketloop;
717 							}
718 
719 							else if (scrambling_messaged)
720 							{
721 								Common.setMessage(Resource.getString("parseTS.clear", Integer.toHexString(pesID).toUpperCase(), String.valueOf(clv[5]), String.valueOf(count - pes_packetlength)));
722 								scrambling_messaged = false;
723 							}
724 						}
725 
726 						/**
727 						 * vdr_dvbsub determination
728 						 */
729 						pes_extension2_id = CommonParsing.getExtension2_Id(pes_packet, pes_headerlength, pes_payloadlength, pesID, pes_isMpeg2, job_processing.getLastHeaderBytePosition());
730 
731 						isTeletext = false;
732 						subID = 0;
733 
734 						if (pesID == CommonParsing.PRIVATE_STREAM_1_CODE && pes_payloadlength > 2)
735 						{
736 							offset = pes_headerlength + pes_extensionlength;
737 
738 							if (offset < pes_packetlength)
739 							{
740 								subID = 0xFF & pes_packet[offset];
741 								isTeletext = pes_extensionlength == 0x24 && subID>>>4 == 1;
742 
743 								// vdr 1.5.x dvb-subs container
744 								if (pes_payloadlength >= 4 && subID>>>4 == 2)
745 								{
746 									tmp_value1 = CommonParsing.getIntValue(pes_packet, offset, 4, !CommonParsing.BYTEREORDERING);
747 
748 									//vdr 1.5.x start packet of dvb-sub || subsequent packet
749 									if ((pes_alignment && (0xF0FFFFFF & tmp_value1) == 0x20010000) || (!pes_alignment && (0xF0FFFFFF & tmp_value1) == 0x20010001))
750 									{
751 										for (int i = offset, j = offset + 4; i < j; i++)
752 											pes_packet[i] = (byte) 0xFF;
753 
754 										pes_extensionlength += 4;
755 										pes_packet[8] = (byte)(pes_extensionlength);
756 										pes_payloadlength -= 4;
757 
758 										//pes_extension2_id = 1;
759 										pes_extension2_id = subID = tmp_value1>>>24;
760 										pes_streamtype = CommonParsing.MPEG2PS_TYPE;  //will be resetted before next packet
761 
762 										if (pes_alignment)
763 											pes_packet[offset + 4] = (byte)(subID);
764 									}
765 								}
766 
767 								//subpic in vdr_pes before 1.5.x
768 								if (pes_alignment && !isTeletext && (subID>>>4 == 2 || subID>>>4 == 3))
769 									pes_streamtype = CommonParsing.MPEG2PS_TYPE;  //will be resetted before next packet
770 
771 								if (pes_streamtype != CommonParsing.MPEG1PS_TYPE && pes_streamtype != CommonParsing.MPEG2PS_TYPE && !isTeletext)
772 									subID = 0; //disables LPCM too
773 							}
774 
775 							else if (pes_streamtype != CommonParsing.MPEG1PS_TYPE) //?
776 							{
777 								pes_extensionlength = pes_payloadlength - 3;
778 								pes_packet[8] = (byte)(pes_extensionlength);
779 							}
780 
781 
782 							/**
783 							 * packet buffering esp. of subpics from vdr or other pes
784 							 */
785 							if (pes_extension2_id != -1)
786 							{
787 								String str = String.valueOf(pes_extension2_id);
788 								offset = pes_headerlength + pes_extensionlength;
789 
790 								if ( !substreams.containsKey(str))
791 									substreams.put(str, new StandardBuffer());
792 
793 								sb = (StandardBuffer) substreams.get(str);
794 
795 								// buffer raw packet data
796 								if (!pes_alignment)
797 								{
798 									sb.write(pes_packet, offset, pes_packetlength - offset);
799 									continue zeropacketloop;
800 								}
801 
802 								// start packet, buffer this and get last completed packet
803 								else
804 								{
805 									buffered_data = sb.getData();
806 
807 									sb.reset();
808 									sb.write(pes_packet, 0, pes_packetlength);
809 
810 									if (buffered_data == null || buffered_data.length < 10)
811 										continue zeropacketloop;
812 
813 									pes_packetlength = buffered_data.length;
814 
815 									if (pes_packetlength > 0x10005)
816 									{
817 										Common.setMessage("!> sub packet too long: 0x" + Integer.toHexString(pesID).toUpperCase() + " /ext2_id " + pes_extension2_id);
818 										pes_packetlength = 0x10005;
819 									}
820 
821 									pes_payloadlength = pes_packetlength - pes_packetoffset;
822 
823 									System.arraycopy(buffered_data, 0, pes_packet, 0, pes_packetlength);
824 
825 									CommonParsing.setPES_LengthField(pes_packet, 0, pes_payloadlength);
826 
827 									buffered_data = null;
828 								}
829 							}
830 						}
831 
832 						/**
833 						 * pesID, subID inclusion
834 						 */
835 						if (usePidfilter)
836 						{
837 							if (Arrays.binarySearch(include, pesID) < 0 && Arrays.binarySearch(include, subID) < 0)
838 								continue zeropacketloop;
839 						}
840 
841 						/**
842 						 * raw id filter extraction
843 						 */
844 						if (action == CommonParsing.ACTION_FILTER)
845 						{
846 							if (subID != 0)
847 								pes_packet[6] |= 4; //set alignment
848 
849 							streamconverter.write(job_processing, pes_packet, 0, pes_packetlength, null, job_processing.getCutByteposition(), CommonParsing.isInfoScan(), CutpointList);
850 							continue zeropacketloop;
851 						}
852 
853 						foundObject = false;
854 
855 						/**
856 						 * find ID object
857 						 */
858 						for (int i = 0; i < demuxList.size(); i++)
859 						{
860 							streamdemultiplexer = (StreamDemultiplexer) demuxList.get(i);
861 
862 							foundObject = pesID == streamdemultiplexer.getID() && subID == streamdemultiplexer.subID() && isTeletext == streamdemultiplexer.isTTX();
863 
864 							if (foundObject)
865 								break;
866 						}
867 
868 						/**
869 						 * create new ID object
870 						 */
871 						if (!foundObject)
872 						{
873 							/**
874 							 * dump startpacket
875 							 */
876 							if (DumpDroppedGop)
877 							{
878 								String dumpname = fparent + "(" + Integer.toHexString(pesID) + "-" + Integer.toHexString(subID) + "#" + (dumped_packets++) + "@" + (count - pes_packetlength) + ").bin";
879 
880 								FileOutputStream dump = new FileOutputStream(dumpname);
881 
882 								dump.write(pes_packet, 0, pes_packetlength);
883 								dump.flush();
884 								dump.close();
885 
886 								Common.setMessage(Resource.getString("parsePrimaryPES.dump.1st") + ": " + dumpname);
887 							}
888 
889 							String IDtype = "";
890 
891 							switch (0xF0 & pesID)
892 							{
893 							case 0xE0:
894 								IDtype = Resource.getString("idtype.mpeg.video");
895 
896 								streamdemultiplexer = new StreamDemultiplexer(collection, ptsoffset);
897 								streamdemultiplexer.setID(pesID);
898 								streamdemultiplexer.setType(CommonParsing.MPEG_VIDEO);
899 								streamdemultiplexer.setnewID(newID[CommonParsing.MPEG_VIDEO]++);
900 								streamdemultiplexer.setsubID(0);
901 								streamdemultiplexer.setStreamType(pes_streamtype);
902 
903 								demuxList.add(streamdemultiplexer);
904 
905 								if (pesID0 == 0 || pesID0 == pesID)
906 								{
907 									if (action == CommonParsing.ACTION_DEMUX)
908 										streamdemultiplexer.initVideo(collection, fparent, MainBufferSize, demuxList.size(), CommonParsing.PRIMARY_PES_PARSER);
909 
910 									else
911 										IDtype += " " + Resource.getString("idtype.mapped.to") + Integer.toHexString(streamdemultiplexer.getnewID()).toUpperCase();
912 
913 									pesID0 = pesID;
914 								}
915 
916 								else
917 									IDtype += Resource.getString("idtype.ignored");
918 
919 								break;
920 
921 							case 0xC0:
922 							case 0xD0:
923 								IDtype = Resource.getString("idtype.mpeg.audio");
924 
925 								streamdemultiplexer = new StreamDemultiplexer(collection, ptsoffset);
926 								streamdemultiplexer.setID(pesID);
927 								streamdemultiplexer.setType(CommonParsing.MPEG_AUDIO);
928 								streamdemultiplexer.setnewID(newID[CommonParsing.MPEG_AUDIO]++);
929 								streamdemultiplexer.setsubID(0);
930 								streamdemultiplexer.setStreamType(pes_streamtype);
931 
932 								demuxList.add(streamdemultiplexer);
933 
934 								if (action == CommonParsing.ACTION_DEMUX)
935 									streamdemultiplexer.init(collection, fparent, MainBufferSize / demuxList.size(), demuxList.size(), CommonParsing.PRIMARY_PES_PARSER);
936 
937 								else
938 									IDtype += " " + Resource.getString("idtype.mapped.to") + Integer.toHexString(streamdemultiplexer.getnewID()).toUpperCase();
939 
940 								break;
941 							}
942 
943 							switch (pesID)
944 							{
945 							case CommonParsing.PRIVATE_STREAM_1_CODE:
946 								IDtype = Resource.getString("idtype.private.stream");
947 								IDtype += (isTeletext ? " TTX " : "") + (subID != 0 ? " (SubID 0x" + Integer.toHexString(subID).toUpperCase() + ")" : "");
948 
949 								streamdemultiplexer = new StreamDemultiplexer(collection, ptsoffset);
950 								streamdemultiplexer.setID(pesID);
951 								streamdemultiplexer.setsubID(subID);
952 								streamdemultiplexer.setTTX(isTeletext);
953 
954 								if (isTeletext)
955 								{
956 									streamdemultiplexer.setnewID(newID[CommonParsing.TELETEXT]++);
957 									streamdemultiplexer.setType(CommonParsing.TELETEXT);
958 								}
959 
960 								else
961 								{
962 									switch(subID>>>4)
963 									{
964 									case 0:
965 										if (pes_streamtype != CommonParsing.MPEG1PS_TYPE && pes_streamtype != CommonParsing.MPEG2PS_TYPE)
966 										{
967 											streamdemultiplexer.setnewID(newID[CommonParsing.AC3_AUDIO]++);
968 											streamdemultiplexer.setType(CommonParsing.AC3_AUDIO);
969 										}
970 										break;
971 
972 									case 8:
973 										streamdemultiplexer.setnewID(newID[CommonParsing.AC3_AUDIO]++);
974 										streamdemultiplexer.setType(CommonParsing.AC3_AUDIO);
975 										break;
976 
977 									case 0xA:
978 										streamdemultiplexer.setnewID(newID[CommonParsing.LPCM_AUDIO]++);
979 										streamdemultiplexer.setType(CommonParsing.LPCM_AUDIO);
980 										break;
981 
982 									case 2:
983 									case 3:
984 										streamdemultiplexer.setnewID(newID[CommonParsing.SUBPICTURE]++);
985 										streamdemultiplexer.setType(CommonParsing.SUBPICTURE);
986 										break;
987 
988 									default:
989 										streamdemultiplexer.setType(CommonParsing.UNKNOWN);
990 									}
991 								}
992 
993 								streamdemultiplexer.setStreamType(pes_streamtype);
994 								demuxList.add(streamdemultiplexer);
995 
996 								if (action == CommonParsing.ACTION_DEMUX)
997 								{
998 									switch (streamdemultiplexer.getType())
999 									{
1000 									case CommonParsing.AC3_AUDIO:
1001 									case CommonParsing.DTS_AUDIO:
1002 									case CommonParsing.TELETEXT:
1003 									case CommonParsing.SUBPICTURE:
1004 									case CommonParsing.LPCM_AUDIO:
1005 										streamdemultiplexer.init(collection, fparent, MainBufferSize / demuxList.size(), demuxList.size(), CommonParsing.PRIMARY_PES_PARSER);
1006 										break;
1007 
1008 									default:
1009 										IDtype += Resource.getString("idtype.ignored");
1010 									}
1011 								}
1012 
1013 								else
1014 								{
1015 									if (pes_streamtype != CommonParsing.MPEG1PS_TYPE && pes_streamtype != CommonParsing.MPEG2PS_TYPE)
1016 										IDtype += " " + Resource.getString("idtype.mapped.to") + Integer.toHexString(streamdemultiplexer.getnewID()).toUpperCase();
1017 
1018 									else if (isTeletext || subID>>>4 == 8)
1019 										IDtype += " " + Resource.getString("idtype.mapped.to") + Integer.toHexString(streamdemultiplexer.getnewID()).toUpperCase();
1020 
1021 									else
1022 										IDtype += Resource.getString("idtype.ignored");
1023 								}
1024 
1025 								if (action == CommonParsing.ACTION_TO_VDR)
1026 								{
1027 									if ((pes_streamtype == CommonParsing.MPEG1PS_TYPE || pes_streamtype == CommonParsing.MPEG2PS_TYPE) && subID>>>4 != 8)
1028 										IDtype += Resource.getString("idtype.ignored");
1029 								}
1030 
1031 								break;
1032 							}
1033 
1034 							Common.setMessage(Resource.getString("parsePrimaryPES.found.pesid") + Integer.toHexString(pesID).toUpperCase() + " " + IDtype + " @ " + job_processing.getCutByteposition());
1035 						}
1036 
1037 
1038 						if (!streamdemultiplexer.StreamEnabled())
1039 							continue zeropacketloop;
1040 
1041 						/**
1042 						 * packet to streamdemultiplexer
1043 						 */
1044 						if (action == CommonParsing.ACTION_DEMUX)
1045 						{
1046 							if (streamdemultiplexer.getType() == CommonParsing.MPEG_VIDEO)
1047 							{
1048 								if (pesID0 != streamdemultiplexer.getID())
1049 									continue zeropacketloop;
1050 
1051 								streamdemultiplexer.writeVideo(job_processing, pes_packet, 0, pes_packetlength, true, CutpointList, ChapterpointList);
1052 							}
1053 							else
1054 								streamdemultiplexer.write(job_processing, pes_packet, 0, pes_packetlength, true);
1055 
1056 							if (streamdemultiplexer.getPTS() > lastpts)
1057 								lastpts = streamdemultiplexer.getPTS();
1058 						}
1059 
1060 						/**
1061 						 * packet to streamconverter
1062 						 */
1063 						else
1064 							streamconverter.write(job_processing, pes_packet, streamdemultiplexer, job_processing.getCutByteposition(), CommonParsing.isInfoScan(), CutpointList);
1065 
1066 					} while (isZeroPacket);
1067 
1068 
1069 					if (action != CommonParsing.ACTION_DEMUX)
1070 						job_processing.setLastHeaderBytePosition(count);
1071 
1072 					/**
1073 					 * split size reached
1074 					 */
1075 					if (job_processing.getSplitSize() > 0 && job_processing.getSplitSize() < job_processing.getAllMediaFilesExportLength())
1076 						break loop;
1077 				}
1078 
1079 
1080 				if (job_processing.getSplitSize() > 0 && job_processing.getSplitSize() < job_processing.getAllMediaFilesExportLength())
1081 					break bigloop;
1082 
1083 				/**
1084 				 * next file segment
1085 				 */
1086 				if (job_processing.getFileNumber() < collection.getPrimaryInputFileSegments() - 1)
1087 				{
1088 					in.close();
1089 					//System.gc();
1090 
1091 					if (collection.getSettings().getBooleanProperty(Keys.KEY_Input_concatenateForeignRecords) && action == CommonParsing.ACTION_DEMUX)
1092 					{
1093 						ptsoffset = nextFilePTS(collection, CommonParsing.PRIMARY_PES_PARSER, pes_streamtype, lastpts, job_processing.getFileNumber() + 1);
1094 
1095 						if (ptsoffset == -1)
1096 							ptsoffset = 0;
1097 
1098 						else
1099 						{
1100 							for (int i = 0; i < demuxList.size(); i++)
1101 							{
1102 								streamdemultiplexer = (StreamDemultiplexer) demuxList.get(i);
1103 								streamdemultiplexer.PTSOffset(ptsoffset);
1104 
1105 								if (streamdemultiplexer.getID() == pesID0) // ??
1106 									streamdemultiplexer.resetVideo();
1107 							}
1108 
1109 							job_processing.setSequenceHeader(true);
1110 							job_processing.setNewVideoStream(true);
1111 					//		job_processing.addCellTime(String.valueOf(job_processing.getExportedVideoFrameNumber()));
1112 						}
1113 					}
1114 
1115 					XInputFile nextXInputFile = (XInputFile) collection.getInputFile(job_processing.countFileNumber(+1));
1116 					count = size;
1117 					base = count;
1118 
1119 					size += nextXInputFile.length();
1120 					in = new PushbackInputStream(nextXInputFile.getInputStream(), 0x10010);
1121 
1122 					Common.setMessage(Resource.getString("parsePrimaryPES.actual.written") + " " + job_processing.getExportedVideoFrameNumber());
1123 					Common.setMessage(Resource.getString("parsePrimaryPES.switch") + " " + nextXInputFile + " (" + Common.formatNumber(nextXInputFile.length()) + " bytes) @ " + base);
1124 
1125 					Common.updateProgressBar((action == CommonParsing.ACTION_DEMUX ? Resource.getString("parsePrimaryPES.demuxing") : Resource.getString("parsePrimaryPES.converting")) + " " + Resource.getString("parsePrimaryPES.avpes.file") + " " + nextXInputFile.getName());
1126 				}
1127 
1128 				else
1129 					break bigloop;
1130 
1131 			}
1132 
1133 			/**
1134 			 * file end reached for split
1135 			 */
1136 			if ( (count >= size || ende) && job_processing.getSplitSize() > 0 )
1137 				job_processing.setSplitLoopActive(false);
1138 
1139 			in.close();
1140 
1141 			vptslog = processElementaryStreams(vptslog, action, clv, collection, job_processing);
1142 
1143 		} catch (IOException e2) {
1144 
1145 			Common.setExceptionMessage(e2);
1146 		}
1147 
1148 		return vptslog;
1149 	}
1150 }
1151