1 /*
2  * @(#)Preview.java - prepare files for previewing
3  *
4  * Copyright (c) 2004-2009 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 
28 package net.sourceforge.dvb.projectx.video;
29 
30 import java.io.ByteArrayOutputStream;
31 import java.io.IOException;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.List;
35 
36 import net.sourceforge.dvb.projectx.xinput.XInputFile;
37 import net.sourceforge.dvb.projectx.common.Common;
38 import net.sourceforge.dvb.projectx.parser.CommonParsing;
39 
40 public class Preview extends Object {
41 
42 	private byte preview_data[];
43 
44 	private int loadSizeForward;
45 	private int active_collection;
46 	private int processed_PID;
47 	private int position[];
48 
49 	private String processed_file;
50 
51 	private boolean silent = true;
52 
53 	private List positionList;
54 	private PreviewObject preview_object;
55 	private Object[] predefined_Pids;
56 
Preview(int _loadSizeForward)57 	public Preview(int _loadSizeForward)
58 	{
59 		loadSizeForward = _loadSizeForward;
60 
61 		position = new int[2];
62 		positionList = new ArrayList();
63 		processed_PID = -1;
64 		processed_file = "";
65 	}
66 
67 	/**
68 	 *
69 	 */
getProcessedFile()70 	public String getProcessedFile()
71 	{
72 		return processed_file;
73 	}
74 
75 	/**
76 	 *
77 	 */
getProcessedPID()78 	public String getProcessedPID()
79 	{
80 		if (processed_PID < 0)
81 			return "no (P)ID";
82 
83 		return ("(P)ID 0x" + Common.adaptString(Integer.toHexString(processed_PID).toUpperCase(), 4));
84 	}
85 
86 	/**
87 	 *
88 	 */
silentload(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection)89 	public long silentload(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection)
90 	{
91 		return load(startposition, size, previewList, direction, all_gops, fast_decode, y_gain, _predefined_Pids, _active_collection, true);
92 	}
93 
94 	/**
95 	 *
96 	 */
load(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection)97 	public long load(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection)
98 	{
99 		return load(startposition, size, previewList, direction, all_gops, fast_decode, y_gain, _predefined_Pids, _active_collection, false);
100 	}
101 
102 	/**
103 	 *
104 	 */
load(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection, boolean silent)105 	public long load(long startposition, int size, List previewList, boolean direction, boolean all_gops, boolean fast_decode, int y_gain, Object[] _predefined_Pids, int _active_collection, boolean silent)
106 	{
107 		predefined_Pids = _predefined_Pids;
108 		active_collection = _active_collection;
109 
110 		preview_data = new byte[size];
111 
112 		int filetype = 0;
113 		int subfiletype = 0;
114 		int read_offset = 0;
115 
116 		try {
117 
118 			for (int i = 0; i < previewList.size(); i++)
119 			{
120 				preview_object = (PreviewObject) previewList.get(i);
121 				filetype = preview_object.getType();
122 
123 				if (startposition < preview_object.getEnd())
124 				{
125 					XInputFile lXInputFile = (XInputFile) preview_object.getFile();
126 					lXInputFile.randomAccessOpen("r");
127 					lXInputFile.randomAccessSeek(startposition - preview_object.getStart());
128 					lXInputFile.randomAccessRead(preview_data, read_offset, size);
129 					lXInputFile.randomAccessClose();
130 
131 					if (preview_object.getEnd() - startposition < size && i < previewList.size() - 1)
132 					{
133 						i++;
134 
135 						int diff = (int)(preview_object.getEnd() - startposition);
136 						byte data2[] = new byte[size];
137 
138 						preview_object = (PreviewObject) previewList.get(i);
139 
140 						lXInputFile = (XInputFile) preview_object.getFile();
141 						lXInputFile.randomAccessSingleRead(data2, 0);
142 
143 						System.arraycopy(data2, 0, preview_data, diff, size - diff);
144 						data2 = null;
145 					}
146 
147 					subfiletype = lXInputFile.getStreamInfo().getStreamSubType();
148 
149 					break;
150 				}
151 			}
152 
153 			preview_data = search(preview_data, startposition, filetype, subfiletype);
154 
155 			long newposition = Common.getMpvDecoderClass().decodeArray(preview_data, direction, all_gops, fast_decode, y_gain, silent);
156 
157 			for (int i = positionList.size() - 1; i >= 0; i--)
158 			{
159 				position = (int[]) positionList.get(i);
160 				if (position[1] <= newposition)
161 				{
162 					startposition += position[0];
163 					i = 0;
164 				}
165 			}
166 
167 			if (positionList.size() == 0)
168 				startposition += newposition;
169 
170 			for (int i = 0; !silent && i < previewList.size(); i++)
171 			{
172 				preview_object = (PreviewObject)previewList.get(i);
173 
174 				if (startposition < preview_object.getEnd())
175 				{
176 					processed_file = "" + i + "/" + (previewList.size() - 1) + " - " + preview_object.getFile().getName();
177 					break;
178 				}
179 			}
180 
181 			preview_data = null;
182 
183 			if (!silent)
184 			{
185 				Common.getMpvDecoderClass().setProcessedPosition(startposition, previewList);
186 				Common.getMpvDecoderClass().setPidAndFileInfo(getProcessedPID() + "   Part: " + getProcessedFile());
187 				Common.getGuiInterface().repaintPicturePanel();
188 			}
189 
190 		} catch (Exception e) {
191 			Common.setExceptionMessage(e);
192 		}
193 
194 		return startposition;
195 	}
196 
197 	/**
198 	 *
199 	 */
previewFile(XInputFile lXInputFile, long startposition, int size, boolean all_gops, boolean fast_decode, int y_gain)200 	public long previewFile(XInputFile lXInputFile, long startposition, int size, boolean all_gops, boolean fast_decode, int y_gain)
201 	{
202 		preview_data = new byte[size];
203 		predefined_Pids = new Object[0];
204 
205 		int filetype = lXInputFile.getStreamInfo().getStreamType();
206 		int subfiletype = lXInputFile.getStreamInfo().getStreamSubType();
207 
208 		try {
209 
210 			lXInputFile.randomAccessOpen("r");
211 			lXInputFile.randomAccessSeek(startposition);
212 			lXInputFile.randomAccessRead(preview_data, 0, size);
213 			lXInputFile.randomAccessClose();
214 
215 		} catch (IOException e) {
216 			Common.setExceptionMessage(e);
217 		}
218 
219 		preview_data = search(preview_data, startposition, filetype, subfiletype);
220 
221 		long newposition = Common.getMpvDecoderClass().decodeArray(preview_data, false, all_gops, fast_decode, y_gain, true);
222 
223 		startposition += newposition;
224 
225 		preview_data = null;
226 		//System.gc();
227 
228 		return startposition;
229 	}
230 
231 	/**
232 	 *
233 	 */
search(byte data[], long startposition, int filetype, int subfiletype)234 	private byte[] search(byte data[], long startposition, int filetype, int subfiletype)
235 	{
236 		positionList.clear();
237 
238 		int[] include = new int[predefined_Pids.length];
239 
240 		for (int i = 0; i < include.length; i++)
241 			include[i] = Integer.parseInt(predefined_Pids[i].toString().substring(2), 16);
242 
243 		Arrays.sort(include);
244 
245 		//do the parse
246 		switch (filetype)
247 		{
248 		case CommonParsing.ES_MPV_TYPE:
249 			return data;
250 
251 		case CommonParsing.MPEG2PS_TYPE:
252 		case CommonParsing.PES_AV_TYPE:
253 			return parseMPG2(data, include);
254 
255 		case CommonParsing.MPEG1PS_TYPE:
256 			return parseMPG1(data, include);
257 
258 		case CommonParsing.PVA_TYPE:
259 			return parsePVA(data, include);
260 
261 		case CommonParsing.TS_TYPE:
262 			if (subfiletype == CommonParsing.TS_TYPE_192BYTE>>>8)
263 				return parseTS192(data, include);
264 			else
265 				return parseTS(data, include);
266 		}
267 
268 		return data;
269 	}
270 
271 	/**
272 	 * TS 188
273 	 */
parseTS(byte[] data, int[] include)274 	private byte[] parseTS(byte[] data, int[] include)
275 	{
276 		ByteArrayOutputStream array = new ByteArrayOutputStream();
277 
278 		byte[] hav_chunk = { 0x5B, 0x48, 0x4F, 0x4A, 0x49, 0x4E, 0x20, 0x41 }; //'[HOJIN A'
279 
280 		boolean save = false;
281 
282 		int mark = 0;
283 		int offset = 0;
284 		int ID = -1;
285 
286 		int a = 0;
287 
288 		for (int PID, dl = data.length - 9; a < dl; a++)
289 		{
290 			//humax chunk
291 			if (data[a] == 0x7F && data[a + 1] == 0x41 && data[a + 2] == 4 && data[a + 3] == (byte)0xFD)
292 			{
293 				if (save && mark <= a)
294 					array.write(data, mark, a - mark);
295 
296 				save = false;
297 				a += 1183;
298 				mark = a + 1;
299 
300 				continue;
301 			}
302 
303 			//koscom chunk
304 			if (a < data.length - 5 && data[a + 2] == 0 && data[a + 3] == 0 && data[a + 4] == 0x47)
305 			{
306 				if (save && mark <= a)
307 					array.write(data, mark, a - mark);
308 
309 				save = false;
310 				a += 3;
311 				mark = a + 1;
312 
313 				continue;
314 			}
315 
316 			//jepssen chunk
317 			if (a < data.length - 36 && data[a + 2] == 0 && data[a + 3] == 0 && data[a + 36] == 0x47)
318 			{
319 				if (save && mark <= a)
320 					array.write(data, mark, a - mark);
321 
322 				save = false;
323 				a += 35;
324 				mark = a + 1;
325 
326 				continue;
327 			}
328 
329 			//ts:
330 		//	if (a < data.length - 188 && data[a] == 0x47)
331 			if (a < data.length && data[a] == 0x47)
332 			{
333 				int chunk_offset = 0;
334 
335 				//handan chunk
336 			//	if (data[a + 188] != 0x47 && data[a + 188] != 0x7F)
337 				if (a < data.length - 188 && data[a + 188] != 0x47 && data[a + 188] != 0x7F)
338 				{
339 					int i = a + 188;
340 					int j;
341 					int k = a + 189;
342 					int l = hav_chunk.length;
343 
344 					while (i > a)
345 					{
346 						j = 0;
347 
348 						while (i > a && data[i] != hav_chunk[j])
349 							i--;
350 
351 						for ( ; i > a && j < l && i + j < k; j++)
352 							if (data[i + j] != hav_chunk[j])
353 								break;
354 
355 						/**
356 						 * found at least one byte of chunk
357 						 */
358 						if (j > 0)
359 						{
360 							/** ident of chunk doesnt match completely */
361 							if (j < l && i + j < k)
362 							{
363 								i--;
364 								continue;
365 							}
366 
367 							/**
368 							 * re-sorts packet in array
369 							 */
370 							if (i + 0x200 + (k - i) < data.length)
371 							{
372 								chunk_offset = 0x200;
373 								System.arraycopy(data, i + chunk_offset, data, i, k - i - 1);
374 								System.arraycopy(data, a, data, a + chunk_offset, i - a);
375 							}
376 
377 							break;
378 						}
379 					}
380 
381 					//jepssen chunk
382 					if (chunk_offset == 0 && a < data.length - 224 && data[a + 190] == 0 && data[a + 191] == 0 && data[a + 224] == 0x47)
383 					{}
384 
385 					//koscom chunk
386 					else if (chunk_offset == 0 && a < data.length - 224 && data[a + 190] == 0 && data[a + 191] == 0 && data[a + 192] == 0x47)
387 					{}
388 
389 					else if (chunk_offset == 0)
390 						continue;
391 				}
392 
393 				if (save && mark <= a)
394 					array.write(data, mark, a - mark);
395 
396 				mark = a;
397 
398 				PID = (0x1F & data[a + 1])<<8 | (0xFF & data[a + 2]);
399 
400 				if (include.length > 0 && Arrays.binarySearch(include, PID) < 0)
401 					save = false;
402 
403 				else if ((ID == PID || ID == -1) && (0xD & data[a + 3]>>>4) == 1)
404 				{
405 					if ((0x20 & data[a + 3]) != 0)       //payload start position, adaption field
406 						mark = a + 5 + (0xFF & data[a + 4]);
407 					else
408 						mark = a + 4;
409 
410 					if ((0x40 & data[a + 1]) != 0)    //start indicator
411 					{
412 						if (data[mark] == 0 && data[mark + 1] == 0 && data[mark + 2] == 1 && (0xF0 & data[mark + 3]) == 0xE0) //DM06032004 081.6 int18 fix
413 						{
414 							ID = PID;
415 							mark = mark + 9 + (0xFF & data[mark + 8]);
416 							int[] currentposition = { a, array.size() };
417 							positionList.add(currentposition);
418 						}
419 						else
420 							save = false;
421 					}
422 
423 					if (ID == PID)
424 						save = true;
425 				}
426 				else
427 					save = false;
428 
429 				a += 187;
430 				a += chunk_offset;
431 				mark += chunk_offset;
432 			}
433 
434 		}
435 
436 		//save last marked packet
437 		if (save && mark < data.length && a <= data.length)
438 			array.write(data, mark, a - mark);
439 
440 
441 		processed_PID = ID;
442 
443 		return array.toByteArray();
444 	}
445 
446 	/**
447 	 * TS 192
448 	 */
parseTS192(byte[] data, int[] include)449 	private byte[] parseTS192(byte[] data, int[] include)
450 	{
451 		ByteArrayOutputStream array = new ByteArrayOutputStream();
452 
453 		boolean save = false;
454 
455 		int mark = 0;
456 		int offset = 0;
457 		int ID = -1;
458 
459 		for (int a = 0, PID, dl = data.length - 9; a < dl; a++)
460 		{
461 			//ts:
462 			if (a < data.length - 192 && data[a] == 0x47)
463 			{
464 				if (save && mark <= a)
465 					array.write(data, mark, a - mark - 4);
466 
467 				mark = a;
468 
469 				PID = (0x1F & data[a + 1])<<8 | (0xFF & data[a + 2]);
470 
471 				if (include.length > 0 && Arrays.binarySearch(include, PID) < 0)
472 					save = false;
473 
474 				else if ((ID == PID || ID == -1) && (0xD & data[a + 3]>>>4) == 1)
475 				{
476 					if ((0x20 & data[a + 3]) != 0)       //payload start position, adaption field
477 						mark = a + 5 + (0xFF & data[a + 4]);
478 					else
479 						mark = a + 4;
480 
481 					if ((0x40 & data[a + 1]) != 0)    //start indicator
482 					{
483 						if (data[mark] == 0 && data[mark + 1] == 0 && data[mark + 2] == 1 && (0xF0 & data[mark + 3]) == 0xE0) //DM06032004 081.6 int18 fix
484 						{
485 							ID = PID;
486 							mark = mark + 9 + (0xFF & data[mark + 8]);
487 							int[] currentposition = { a, array.size() };
488 							positionList.add(currentposition);
489 						}
490 						else
491 							save = false;
492 					}
493 
494 					if (ID == PID)
495 						save = true;
496 				}
497 				else
498 					save = false;
499 
500 				a += 191;
501 			}
502 
503 		}
504 
505 		processed_PID = ID;
506 
507 		return array.toByteArray();
508 	}
509 
510 	/**
511 	 * PES_AV + MPG2
512 	 */
parseMPG2(byte[] pes_data, int[] include)513 	private byte[] parseMPG2(byte[] pes_data, int[] include)
514 	{
515 		ByteArrayOutputStream array = new ByteArrayOutputStream();
516 
517 		boolean savePacket = false;
518 
519 		int mark = 0;
520 		int pes_headerlength = 9;
521 		int pes_offset = 6;
522 		int pes_payloadlength = 0;
523 		int pes_ID = -1;
524 
525 		for (int offset = 0, offset2, pes_ID_tmp, returncode, j = pes_data.length - pes_headerlength; offset < j; )
526 		{
527 			if ((returncode = CommonParsing.validateStartcode(pes_data, offset)) < 0)
528 			{
529 				offset += -returncode;
530 				continue;
531 			}
532 
533 			pes_ID_tmp = CommonParsing.getPES_IdField(pes_data, offset);
534 
535 			if (pes_ID_tmp < CommonParsing.SYSTEM_END_CODE)
536 			{
537 				offset += 4;
538 				continue;
539 			}
540 
541 			if (savePacket)
542 				array.write(pes_data, mark, offset - mark);
543 
544 			mark = offset;
545 
546 			if (include.length > 0 && Arrays.binarySearch(include, pes_ID_tmp) < 0)
547 				savePacket = false;
548 
549 			else if ((pes_ID == pes_ID_tmp || pes_ID == -1) && (0xF0 & pes_ID_tmp) == 0xE0)
550 			{
551 				pes_ID = pes_ID_tmp;
552 				mark = offset + pes_headerlength + CommonParsing.getPES_ExtensionLengthField(pes_data, offset);
553 
554 				int[] currentposition = { offset, array.size() };
555 
556 				positionList.add(currentposition);
557 				savePacket = true;
558 			}
559 
560 			else
561 				savePacket = false;
562 
563 			offset2 = pes_ID_tmp < CommonParsing.SYSTEM_START_CODE ? 12 : 0; //BA
564 
565 			pes_payloadlength = CommonParsing.getPES_LengthField(pes_data, offset);
566 
567 			if (pes_payloadlength == 0) //zero length
568 				offset2 = pes_headerlength;
569 
570 			offset += (offset2 == 0 ? (pes_offset + pes_payloadlength) : offset2);
571 		}
572 
573 		processed_PID = pes_ID;
574 
575 		return array.toByteArray();
576 	}
577 
578 	/**
579 	 * MPG1
580 	 */
581 	private byte[] parseMPG1(byte[] pes_data, int[] include)
582 	{
583 		ByteArrayOutputStream array = new ByteArrayOutputStream();
584 
585 		boolean savePacket = false;
586 
587 		int mark = 0;
588 		int pes_headerlength = 7;
589 		int pes_offset = 6;
590 		int pes_payloadlength = 0;
591 		int pes_ID = -1;
592 
593 		for (int offset = 0, offset2, pes_ID_tmp, returncode, shift, j = pes_data.length - pes_headerlength; offset < j; )
594 		{
595 			if ((returncode = CommonParsing.validateStartcode(pes_data, offset)) < 0)
596 			{
597 				offset += -returncode;
598 				continue;
599 			}
600 
601 			pes_ID_tmp = CommonParsing.getPES_IdField(pes_data, offset);
602 
603 			if (pes_ID_tmp < CommonParsing.SYSTEM_END_CODE)
604 			{
605 				offset += 4;
606 				continue;
607 			}
608 
609 			if (savePacket)
610 				array.write(pes_data, mark, offset - mark);
611 
612 			mark = offset;
613 
614 			if (include.length > 0 && Arrays.binarySearch(include, pes_ID_tmp) < 0)
615 				savePacket = false;
616 
617 			else if ((pes_ID == pes_ID_tmp || pes_ID == -1) && (0xF0 & pes_ID_tmp) == 0xE0)
618 			{
619 				pes_ID = pes_ID_tmp;
620 
621 				shift = offset + pes_offset;
622 
623 				skiploop:
624 				while(true)
625 				{
626 					switch (0xC0 & pes_data[shift])
627 					{
628 					case 0x40:
629 						shift += 2;
630 						continue skiploop;
631 
632 					case 0x80:
633 						shift += 3;
634 						continue skiploop;
635 
636 					case 0xC0:
637 						shift++;
638 						continue skiploop;
639 
640 					case 0:
641 						break;
642 					}
643 
644 					switch (0x30 & pes_data[shift])
645 					{
646 					case 0x20:
647 						shift += 5;
648 						break skiploop;
649 
650 					case 0x30:
651 						shift += 10;
652 						break skiploop;
653 
654 					case 0x10:
655 						shift += 5;
656 						break skiploop;
657 
658 					case 0:
659 						shift++;
660 						break skiploop;
661 					}
662 				}
663 
664 				mark = shift;
665 
666 				int[] currentposition = { offset, array.size() };
667 
668 				positionList.add(currentposition);
669 				savePacket = true;
670 			}
671 
672 			else
673 				savePacket = false;
674 
675 
676 			offset2 = pes_ID_tmp < CommonParsing.SYSTEM_START_CODE ? 12 : 0; //BA
677 
678 			pes_payloadlength = CommonParsing.getPES_LengthField(pes_data, offset);
679 
680 			if (pes_payloadlength == 0) //zero length
681 				offset2 = pes_headerlength;
682 
683 			offset += (offset2 == 0 ? (pes_offset + pes_payloadlength) : offset2);
684 		}
685 
686 		processed_PID = pes_ID;
687 
688 		return array.toByteArray();
689 	}
690 
691 	/**
692 	 * PVA
693 	 */
694 	private byte[] parsePVA(byte[] pes_data, int[] include)
695 	{
696 		ByteArrayOutputStream array = new ByteArrayOutputStream();
697 
698 		boolean savePacket = false;
699 
700 		int mark = 0;
701 		int pes_headerlength = 8;
702 		int pes_offset = 8;
703 		int pes_payloadlength = 0;
704 		int pes_ID = -1;
705 
706 		for (int offset = 0, pes_ID_tmp, j = pes_data.length - pes_headerlength; offset < j; )
707 		{
708 			if ((0xFF & pes_data[offset]) != 0x41 || (0xFF & pes_data[offset + 1]) != 0x56 || (0xFF & pes_data[offset + 4]) != 0x55)
709 			{
710 				offset++;
711 				continue;
712 			}
713 
714 			if (savePacket)
715 				array.write(pes_data, mark, offset - mark);
716 
717 			mark = offset;
718 
719 			pes_ID_tmp = 0xFF & pes_data[offset + 2];
720 
721 			if (pes_ID_tmp == 1)
722 			{
723 				pes_ID = 1;
724 				mark = ((0x10 & pes_data[offset + 5]) != 0) ? offset + pes_offset + 4 : offset + pes_offset;
725 
726 				int[] currentposition = { offset, array.size() };
727 
728 				positionList.add(currentposition);
729 				savePacket = true;
730 			}
731 
732 			else
733 				savePacket = false;
734 
735 			pes_payloadlength = (0xFF & pes_data[offset + 6])<<8 | (0xFF & pes_data[offset + 7]);
736 
737 			offset += (pes_offset + pes_payloadlength);
738 		}
739 
740 		processed_PID = pes_ID;
741 
742 		return array.toByteArray();
743 	}
744 }
745 
746