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