1 /* 2 * @(#)StreamDemultiplexer 3 * 4 * Copyright (c) 2005-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 package net.sourceforge.dvb.projectx.parser; 28 29 import java.util.ArrayList; 30 import java.util.List; 31 32 import java.io.File; 33 import java.io.ByteArrayOutputStream; 34 import java.io.DataOutputStream; 35 import java.io.BufferedOutputStream; 36 import java.io.FileOutputStream; 37 import java.io.IOException; 38 import java.io.BufferedWriter; 39 import java.io.FileWriter; 40 41 import net.sourceforge.dvb.projectx.io.IDDBufferedOutputStream; 42 43 import net.sourceforge.dvb.projectx.common.Resource; 44 import net.sourceforge.dvb.projectx.common.Keys; 45 import net.sourceforge.dvb.projectx.common.Common; 46 import net.sourceforge.dvb.projectx.common.JobProcessing; 47 import net.sourceforge.dvb.projectx.common.JobCollection; 48 49 import net.sourceforge.dvb.projectx.parser.CommonParsing; 50 import net.sourceforge.dvb.projectx.video.Video; 51 52 /** 53 * demuxes all packetized data 54 */ 55 public class StreamDemultiplexer extends Object { 56 57 private boolean ptsover = false; 58 private boolean misshead = false; 59 private boolean first = true; 60 private boolean overlap = false; 61 private boolean seqhead = false; 62 private boolean isPTSwritten = false; 63 private boolean isEnabled = true; 64 // 65 private boolean isH264 = false; 66 67 private boolean WriteNonVideo; 68 private boolean WriteVideo; 69 private boolean Debug; 70 private boolean DecodeVBI; 71 private boolean RebuildPTS; 72 private boolean RebuildPictPTS; 73 private boolean Streamtype_MpgVideo; 74 private boolean Streamtype_MpgAudio; 75 private boolean Streamtype_Ac3Audio; 76 private boolean Streamtype_PcmAudio; 77 private boolean Streamtype_Teletext; 78 private boolean Streamtype_Subpicture; 79 private boolean AddSequenceEndcode; 80 private boolean RenameVideo; 81 private boolean CreateD2vIndex; 82 private boolean CreateM2sIndex; 83 private boolean SplitProjectFile; 84 private boolean CreateCellTimes; 85 private boolean CreateInfoIndex; 86 private boolean AppendPidToFileName; 87 private boolean AppendLangToFileName; 88 private boolean EnableHDDemux; 89 90 private long AddOffset = 0; 91 private long target_position = 0; 92 private long ptsoffset = 0; 93 private long pts = -1; 94 private long lastPTS = -1; 95 96 private int pack = -1; 97 private int pes_ID = 0; 98 private int newID = 0; 99 private int PID = 0; 100 private int es_streamtype = 0; 101 private int subid = 0x1FF; 102 private int pes_streamtype = 0; 103 private int lfn = -1; 104 private int buffersize = 1024; 105 private int sourcetype = 0; 106 private int[] MPGVideotype = { -1 }; // 0 =m1v, 1 = m2v, 2 = h264 -- changed at goptest 107 108 private int StreamNumber = -1; 109 110 private String FileName = ""; 111 private String parentname = ""; 112 private String[] type = { "ac", "tt", "mp", "mv", "pc", "sp", "vp" }; 113 private String[] source = { ".$spes$", ".$ppes$", ".$ts$", ".$pva$" }; 114 // private String[] videoext = { ".mpv", ".mpv", ".m1v", ".m2v" }; 115 private String[] videoext = { ".mpv", ".mpv", ".264", ".m1v", ".m2v", ".264" }; 116 117 private IDDBufferedOutputStream out; 118 private DataOutputStream pts_log; 119 private ByteArrayOutputStream vidbuf; 120 private ByteArrayOutputStream vptsbytes; 121 private ByteArrayOutputStream packet; 122 private DataOutputStream vpts; 123 124 private byte[] subpicture_header = { 0x53, 0x50, 0, 0, 0, 0, 0, 0, 0, 0 }; 125 private byte[] lpcm_header = { 0x50, 0x43, 0x4D, 0, 0, 0, 0, 0, 0, 0 }; //'PCM'+5b(pts)+2b(size) 126 127 /** 128 * 129 */ StreamDemultiplexer(JobCollection collection)130 public StreamDemultiplexer(JobCollection collection) 131 { 132 getSettings(collection); 133 } 134 135 /** 136 * 137 */ StreamDemultiplexer(JobCollection collection, long val)138 public StreamDemultiplexer(JobCollection collection, long val) 139 { 140 getSettings(collection); 141 ptsoffset = val; 142 } 143 144 /** 145 * Object yet intialized 146 */ getNum()147 public int getNum() 148 { 149 return lfn; 150 } 151 152 /** 153 * get PID for later selection 154 */ getPID()155 public int getPID() 156 { 157 return PID; 158 } 159 160 /** 161 * returns PES pes_ID for later selection 162 */ getID()163 public int getID() 164 { 165 return pes_ID; 166 } 167 168 /** 169 * set PID for later selection 170 */ setPID(int val)171 public void setPID(int val) 172 { 173 PID = val; 174 } 175 176 /** 177 * set pes_ID for later selection 178 */ setID(int val)179 public void setID(int val) 180 { 181 pes_ID = val; 182 } 183 184 /** 185 * set newID for later selection /or subid 186 */ setnewID(int val)187 public void setnewID(int val) 188 { 189 newID = val; 190 } 191 192 /** 193 * returns newID for later selection /or subid 194 */ getnewID()195 public int getnewID() 196 { 197 return newID; 198 } 199 200 /** 201 * returns packet counter 202 */ getPackCount()203 public int getPackCount() 204 { 205 return pack; 206 } 207 208 /** 209 * returns es_streamtype 210 */ getType()211 public int getType() 212 { 213 return es_streamtype; 214 } 215 216 /** 217 * sets stream tpye, vdr/es/mpeg1/2... 218 */ setStreamType(int val)219 public void setStreamType(int val) 220 { 221 pes_streamtype = val; 222 } 223 224 /** 225 * returns stream tpye, vdr/es/mpeg1/2... 226 */ getStreamType()227 public int getStreamType() 228 { 229 return pes_streamtype; 230 } 231 232 /** 233 * sets type 234 */ setType(int val)235 public void setType(int val) 236 { 237 es_streamtype = val; 238 } 239 240 /** 241 * sets subid 242 */ setsubID(int val)243 public void setsubID(int val) 244 { 245 subid = val; 246 } 247 248 /** 249 * returns subid 250 */ subID()251 public int subID() 252 { 253 return subid; 254 } 255 256 /** 257 * is it TTX? 258 */ isTTX()259 public boolean isTTX() 260 { 261 return es_streamtype == CommonParsing.TELETEXT; 262 } 263 264 /** 265 * set ttx 266 */ setTTX(boolean b)267 public void setTTX(boolean b) 268 { 269 if (b) 270 es_streamtype = CommonParsing.TELETEXT; 271 } 272 273 /** 274 * last PTS 275 */ getPTS()276 public long getPTS() 277 { 278 return pts; 279 } 280 281 /** 282 * PTS offset if needed 283 */ PTSOffset(long val)284 public void PTSOffset(long val) 285 { 286 ptsoffset = val; 287 } 288 289 /** 290 * 291 */ setStreamNumber(int val)292 public void setStreamNumber(int val) 293 { 294 StreamNumber = val; 295 } 296 297 /** 298 * 299 */ getStreamNumber()300 public int getStreamNumber() 301 { 302 return StreamNumber; 303 } 304 305 /** 306 * stream type preselector 307 */ setStreamEnabled(boolean b)308 public void setStreamEnabled(boolean b) 309 { 310 isEnabled = b; 311 } 312 313 /** 314 * stream type preselector 315 */ StreamEnabled()316 public boolean StreamEnabled() 317 { 318 switch(newID>>>4) 319 { 320 case 0xE: //video 321 return Streamtype_MpgVideo; 322 323 case 0xC: //mpa 324 case 0xD: 325 return Streamtype_MpgAudio; 326 327 case 0x8: //ac3,mpg 328 return Streamtype_Ac3Audio; 329 330 case 0xA: //lpcm,mpg 331 return Streamtype_PcmAudio; 332 333 case 0x9: //ttx 334 return Streamtype_Teletext; 335 336 case 0x2: //subpic 337 case 0x3: 338 return Streamtype_Subpicture; 339 340 default: 341 return isEnabled; 342 } 343 } 344 345 346 /** 347 * 348 */ getSettings(JobCollection collection)349 private void getSettings(JobCollection collection) 350 { 351 Streamtype_MpgVideo = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_MpgVideo); 352 Streamtype_MpgAudio = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_MpgAudio); 353 Streamtype_Ac3Audio = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_Ac3Audio); 354 Streamtype_PcmAudio = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_PcmAudio); 355 Streamtype_Teletext = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_Teletext); 356 Streamtype_Subpicture = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_Subpicture); 357 AddOffset = collection.getSettings().getBooleanProperty(Keys.KEY_additionalOffset) ? 90L * collection.getSettings().getIntProperty(Keys.KEY_ExportPanel_additionalOffset_Value) : 0; // time offset for data 358 WriteNonVideo = collection.getSettings().getBooleanProperty(Keys.KEY_WriteOptions_writeAudio); 359 WriteVideo = collection.getSettings().getBooleanProperty(Keys.KEY_WriteOptions_writeVideo); 360 Debug = collection.getSettings().getBooleanProperty(Keys.KEY_DebugLog); 361 DecodeVBI = collection.getSettings().getBooleanProperty(Keys.KEY_Streamtype_Vbi); 362 RebuildPTS = collection.getSettings().getBooleanProperty(Keys.KEY_SubtitlePanel_rebuildPTS); 363 RebuildPictPTS = collection.getSettings().getBooleanProperty(Keys.KEY_SubtitlePanel_rebuildPictPTS); 364 AddSequenceEndcode = collection.getSettings().getBooleanProperty(Keys.KEY_VideoPanel_addEndcode); 365 RenameVideo = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_renameVideo); 366 CreateD2vIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createD2vIndex); 367 CreateM2sIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createM2sIndex); 368 SplitProjectFile = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_splitProjectFile); 369 CreateCellTimes = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createCellTimes); 370 CreateInfoIndex = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_createInfoIndex); 371 AppendPidToFileName = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_appendPidToFileName); 372 AppendLangToFileName = collection.getSettings().getBooleanProperty(Keys.KEY_ExternPanel_appendLangToFileName); 373 EnableHDDemux = collection.getSettings().getBooleanProperty(Keys.KEY_enableHDDemux); 374 } 375 376 /** 377 * 378 */ setFileName()379 private void setFileName() 380 { 381 FileName = parentname + source[sourcetype] + lfn + "-" + Long.toHexString(0xFFFFFFL & System.currentTimeMillis()).toUpperCase(); 382 } 383 384 /** 385 * init nonVideo streams 386 */ initNonVideo(JobCollection collection, String _name)387 private void initNonVideo(JobCollection collection, String _name) 388 { 389 parentname = _name; 390 391 setFileName(); 392 393 target_position = 0; 394 395 getSettings(collection); 396 397 try { 398 out = new IDDBufferedOutputStream(new FileOutputStream(FileName), buffersize); 399 pts_log = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(FileName + ".pts"), 65535)); 400 401 } catch (IOException e) { 402 403 Common.setExceptionMessage(e); 404 } 405 } 406 407 /** 408 * main init nonVideo 409 */ init(JobCollection collection, String _name, int _buffersize, int _lfn, int _parsertype)410 public void init(JobCollection collection, String _name, int _buffersize, int _lfn, int _parsertype) 411 { 412 lfn = _lfn; 413 buffersize = _buffersize; 414 sourcetype = _parsertype; 415 416 initNonVideo(collection, _name); 417 } 418 419 /** 420 * re-init nonVideo 421 */ init2(JobCollection collection, String _name)422 public void init2(JobCollection collection, String _name) 423 { 424 initNonVideo(collection, _name); 425 } 426 427 /** 428 * process nonvideo data = 1 pespacket from demux 429 */ write(JobProcessing job_processing, byte[] pes_packet, boolean pes_hasHeader)430 public void write(JobProcessing job_processing, byte[] pes_packet, boolean pes_hasHeader) 431 { 432 write(job_processing, pes_packet, 0, pes_packet.length, pes_hasHeader); 433 } 434 435 /** 436 * process nonvideo data = 1 pespacket from demux 437 */ write(JobProcessing job_processing, byte[] pes_packet, int pes_packetoffset, int pes_payloadlength, boolean pes_hasHeader)438 public void write(JobProcessing job_processing, byte[] pes_packet, int pes_packetoffset, int pes_payloadlength, boolean pes_hasHeader) 439 { 440 boolean pes_isAligned = false; 441 442 int pes_extensionlength = 0; 443 int offset = pes_packetoffset; 444 int _es_streamtype = CommonParsing.AC3_AUDIO; 445 446 pack++; 447 448 if (pes_hasHeader) 449 { 450 if (CommonParsing.validateStartcode(pes_packet, offset) < 0) 451 { 452 Common.setMessage(Resource.getString("demux.error.audio.startcode") + " " + pack + " (" + Integer.toHexString(PID) + "/" + Integer.toHexString(pes_ID) + "/" + Integer.toHexString(newID) + "/" + es_streamtype + ")"); 453 return; 454 } 455 456 pes_ID = CommonParsing.getPES_IdField(pes_packet, offset); 457 pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, offset); 458 pes_isAligned = (pes_streamtype == CommonParsing.PES_AV_TYPE || pes_streamtype == CommonParsing.MPEG2PS_TYPE) && (4 & pes_packet[6 + offset]) != 0; 459 460 if (pes_ID == CommonParsing.PADDING_STREAM_CODE) 461 return; 462 463 if (pes_streamtype == CommonParsing.MPEG1PS_TYPE) 464 { 465 skiploop: 466 while(true) 467 { 468 switch (0xC0 & pes_packet[6 + offset]) 469 { 470 case 0x40: 471 offset += 2; 472 continue skiploop; 473 474 case 0x80: 475 offset += 3; 476 continue skiploop; 477 478 case 0xC0: 479 offset++; 480 continue skiploop; 481 482 case 0: 483 break; 484 } 485 486 switch (0x30 & pes_packet[6 + offset]) 487 { 488 case 0x20: //PTS 489 pes_extensionlength = 5; 490 break skiploop; 491 492 case 0x30: //PTS+DTS 493 pes_extensionlength = 10; 494 break skiploop; 495 496 case 0x10: //DTS 497 offset += 5; 498 break skiploop; 499 500 case 0: 501 offset++; 502 break skiploop; 503 } 504 } 505 } 506 507 else 508 { 509 pes_extensionlength = CommonParsing.getPES_ExtensionLengthField(pes_packet, offset); 510 511 if (pes_ID == CommonParsing.PRIVATE_STREAM_1_CODE && pes_extensionlength == 0x24 && (0xFF & pes_packet[9 + pes_extensionlength + offset])>>>4 == 1) 512 _es_streamtype = CommonParsing.TELETEXT; 513 514 // workaround uk freesat teletext 515 else if (pes_ID == CommonParsing.PRIVATE_STREAM_1_CODE && pes_extensionlength == 0x24 && (0xFF & pes_packet[9 + pes_extensionlength + offset]) == 0x99) 516 _es_streamtype = CommonParsing.TELETEXT; 517 518 /** 519 * no PTS in PES_extension 520 */ 521 if ((0x80 & pes_packet[7 + offset]) == 0) 522 { 523 offset += pes_extensionlength; 524 pes_extensionlength = 0; 525 } 526 527 offset += 3; 528 } 529 530 es_streamtype = pes_ID == CommonParsing.PRIVATE_STREAM_1_CODE ? _es_streamtype : CommonParsing.MPEG_AUDIO; 531 subid = ((es_streamtype == CommonParsing.AC3_AUDIO || es_streamtype == CommonParsing.DTS_AUDIO || es_streamtype == CommonParsing.TELETEXT) && ((pes_streamtype == CommonParsing.PES_AV_TYPE && (pes_isAligned || es_streamtype == CommonParsing.TELETEXT)) || pes_streamtype == CommonParsing.MPEG2PS_TYPE)) ? (0xFF & pes_packet[9 + (0xFF & pes_packet[8 + pes_packetoffset]) + pes_packetoffset]) : 0; 532 533 // workaround uk freesat teletext 534 if (isTTX()) 535 subid &= 0x1F; 536 537 switch (subid>>>4) 538 { 539 case 8: 540 if (pes_streamtype == CommonParsing.PES_AV_TYPE || pes_streamtype == CommonParsing.MPEG1PS_TYPE) 541 { 542 subid = 0; 543 break; 544 } 545 546 case 1: 547 case 2: 548 case 3: 549 case 9: 550 case 0xA: 551 break; 552 553 case 0: 554 if (pes_isAligned && subid == 0x09) 555 break; 556 557 default: 558 if (pes_streamtype != CommonParsing.MPEG2PS_TYPE) 559 subid = 0; 560 } 561 562 563 switch (subid>>>4) 564 { 565 case 0xA: //LPCM from MPG-PS 566 es_streamtype = CommonParsing.LPCM_AUDIO; 567 break; 568 569 case 2: //SubPic 0-31 from MPG-PS 570 case 3: //SubPic 32-63 from MPG-PS 571 es_streamtype = CommonParsing.SUBPICTURE; 572 break; 573 574 case 8: //AC3-DTS from MPG-PS 575 case 1: //TTX 576 case 0: //AC3-DTS from PES/VDR 577 break; 578 579 case 9: //VBI from TS,MPG2 580 if (pes_isAligned) 581 { 582 if (pes_streamtype == CommonParsing.MPEG1PS_TYPE || pes_streamtype == CommonParsing.PES_AV_TYPE) 583 subid = 0; 584 585 if (DecodeVBI) 586 VBI.parsePES(pes_packet, pes_packetoffset); 587 588 return; 589 } 590 break; 591 592 default: 593 return; 594 } 595 596 pes_payloadlength -= (offset - pes_packetoffset + pes_extensionlength); 597 offset += 6; 598 } 599 600 if (!WriteNonVideo) 601 return; 602 603 if (out == null) 604 return; 605 606 try { 607 608 // recreate PTS 609 if (RebuildPTS && es_streamtype == CommonParsing.TELETEXT) 610 { 611 if (job_processing.getBorrowedPts() != lastPTS) 612 { 613 lastPTS = job_processing.getBorrowedPts(); 614 615 pts_log.writeLong(lastPTS); 616 pts_log.writeLong(target_position); 617 618 if (Debug) 619 System.out.println(" stolen ttx PTS: " + lastPTS + " /ao " + AddOffset + " /tp " + target_position); 620 } 621 } 622 623 //recreate subpicture 624 else if (RebuildPictPTS && es_streamtype == CommonParsing.SUBPICTURE) 625 { 626 if (job_processing.getBorrowedPts() != lastPTS) 627 { 628 lastPTS = job_processing.getBorrowedPts(); 629 pts = lastPTS; //to rewrite into sp file 630 631 pts_log.writeLong(lastPTS); 632 pts_log.writeLong(target_position); 633 634 if (Debug) 635 System.out.println(" stolen subpic PTS: " + lastPTS + " /ao " + AddOffset + " /tp " + target_position); 636 } 637 } 638 639 /** 640 * read out source PTS 641 */ 642 else if (pes_extensionlength > 0 && pes_payloadlength >= 0) 643 { 644 //--> �ndern 645 pts = CommonParsing.getPTSfromBytes(pes_packet, offset); //returns 32bit 646 647 pts -= job_processing.getNextFileStartPts(); 648 pts &= 0xFFFFFFFFL; //trim to 32bit 649 650 if ( (pts & 0xFF000000L) == 0xFF000000L ) 651 ptsover = true; // bit 33 was set 652 653 if (ptsover && pts < 0xF0000000L) 654 pts |= 0x100000000L; 655 //<-- 656 pts += ptsoffset; 657 pts += AddOffset; 658 659 if (lastPTS != pts) 660 { 661 if ((es_streamtype == CommonParsing.MPEG_AUDIO || es_streamtype == CommonParsing.AC3_AUDIO || es_streamtype == CommonParsing.DTS_AUDIO || es_streamtype == CommonParsing.LPCM_AUDIO) 662 && lastPTS != -1 && Math.abs(lastPTS - pts) > 100000) 663 Common.setMessage("!> ID 0x" + Integer.toHexString(pes_ID).toUpperCase() + " (sub 0x" + Integer.toHexString(subid).toUpperCase() + ") packet# " + pack + ", big PTS difference: this " + pts + ", prev. " + lastPTS); 664 665 pts_log.writeLong(pts); 666 pts_log.writeLong(target_position); 667 } 668 669 if (Debug) 670 System.out.println(" pda PTS: " + pts + "/ " + AddOffset + "/ " + target_position); 671 672 lastPTS = pts; 673 } 674 675 /** 676 * save re-build PTS, taken from 1st mpa 677 */ 678 if (newID == 0xC0 && job_processing.getBorrowedPts() != lastPTS) 679 job_processing.setBorrowedPts(lastPTS); 680 681 /** 682 * skip subid and info fields 683 */ 684 switch(subid>>>4) 685 { 686 case 0xA: //LPCM, keep info fields 6 bytes 687 offset += 1; //7 688 pes_payloadlength -= 1; //7 689 break; 690 691 case 8: //AC3-DTS 692 offset += 4; 693 pes_payloadlength -= 4; 694 break; 695 696 case 1: //TTX 697 offset += 1; 698 pes_payloadlength -= 1; 699 break; 700 701 case 2: //subpic 0.31 702 case 3: //subpic 32.63 703 offset += 1; 704 pes_payloadlength -= 1; 705 } 706 707 if (subid == 0x09) 708 { 709 offset += 4; 710 pes_payloadlength -= 4; 711 } 712 713 714 if (pes_payloadlength <= 0) 715 return; 716 717 if (pes_extensionlength > 0) 718 { 719 switch(es_streamtype) 720 { 721 case CommonParsing.SUBPICTURE: 722 CommonParsing.setValue(subpicture_header, 2, 8, CommonParsing.BYTEREORDERING, pts); 723 724 target_position += writePacket(subpicture_header); 725 726 /** 727 * DVB subs adaption 728 */ 729 if (CommonParsing.nextBits(pes_packet, (offset + pes_extensionlength) * 8, 16) == 0xF) 730 { 731 out.write(0xFF & (pes_payloadlength + 3)>>>8); 732 out.write(0xFF & (pes_payloadlength + 3)); 733 out.write(0); //padding 734 735 target_position += 3; 736 } 737 738 break; 739 740 case CommonParsing.LPCM_AUDIO: 741 CommonParsing.setValue(lpcm_header, 3, 5, CommonParsing.BYTEREORDERING, pts); 742 743 lpcm_header[8] = (byte)(0xFF & pes_payloadlength>>>8); 744 lpcm_header[9] = (byte)(0xFF & pes_payloadlength); 745 746 target_position += writePacket(lpcm_header); 747 } 748 } 749 750 /** 751 * DVB subs adaption, prevent lost packets 752 */ 753 else if (es_streamtype == CommonParsing.SUBPICTURE && pes_isAligned && CommonParsing.nextBits(pes_packet, (offset + pes_extensionlength) * 8, 16) == 0xF) 754 // else if (es_streamtype == CommonParsing.SUBPICTURE && CommonParsing.nextBits(pes_packet, (offset + pes_extensionlength) * 8, 16) == 0xF) 755 { 756 CommonParsing.setValue(subpicture_header, 2, 8, CommonParsing.BYTEREORDERING, 0); 757 758 target_position += writePacket(subpicture_header); 759 760 out.write(0xFF & (pes_payloadlength + 3)>>>8); 761 out.write(0xFF & (pes_payloadlength + 3)); 762 out.write(0); //padding! 763 764 target_position += 3; 765 } 766 767 if (subid == 0x09) 768 for (int i = 0; i < pes_payloadlength; i += 3) 769 target_position += writePacket(pes_packet, offset + pes_extensionlength + i, 2); 770 771 else 772 target_position += writePacket(pes_packet, offset + pes_extensionlength, pes_payloadlength); 773 774 } catch (IOException e) { 775 776 Common.setExceptionMessage(e); 777 } 778 } 779 780 /** 781 * 782 */ close(JobProcessing job_processing, String _vptslog)783 public String[] close(JobProcessing job_processing, String _vptslog) 784 { 785 String pts_log_name = FileName + ".pts"; 786 String parameters[] = { FileName, pts_log_name, type[es_streamtype], parentname }; 787 788 try { 789 790 if (out == null) 791 { 792 parameters[0] = ""; 793 return parameters; 794 } 795 796 out.flush(); 797 out.close(); 798 799 pts_log.flush(); 800 pts_log.close(); 801 802 if (new File(pts_log_name).length() < 10) 803 CommonParsing.logAlias(job_processing, _vptslog, pts_log_name); 804 805 if (new File(FileName).length() < 10) 806 { 807 Common.setMessage("-> temp. Filesize < 10 Bytes"); 808 809 new File(FileName).delete(); 810 new File(pts_log_name).delete(); 811 812 parameters[0] = ""; 813 } 814 815 else 816 { 817 if (AppendPidToFileName) 818 parameters[3] = parentname + formatIDString(getPID(), getID(), subID()); 819 820 if (AppendLangToFileName) 821 parameters[3] += job_processing.getAudioStreamLanguage(getPID()); 822 } 823 824 } catch (IOException e) { 825 826 Common.setExceptionMessage(e); 827 } 828 829 return parameters; 830 } 831 832 /** 833 * 834 */ formatIDString(int pid, int id, int subid)835 private String formatIDString(int pid, int id, int subid) 836 { 837 String str = ""; 838 839 str += "{0x" + Common.adaptString(Integer.toHexString(pid).toUpperCase(), 4); 840 str += "-0x" + Common.adaptString(Integer.toHexString(id).toUpperCase(), 2); 841 str += "-0x" + Common.adaptString(Integer.toHexString(subid).toUpperCase(), 2); 842 str += "}"; 843 844 return str; 845 } 846 847 /** 848 * 849 */ initVideo(JobCollection collection, String _name, int _buffersize, int _lfn, int _parsertype)850 public void initVideo(JobCollection collection, String _name, int _buffersize, int _lfn, int _parsertype) 851 { 852 getSettings(collection); 853 854 parentname = _name; 855 lfn = _lfn; 856 buffersize = _buffersize; 857 sourcetype = _parsertype; 858 859 setFileName(); 860 861 es_streamtype = CommonParsing.MPEG_VIDEO; 862 MPGVideotype[0] = -1; 863 864 try { 865 out = new IDDBufferedOutputStream(new FileOutputStream(FileName), buffersize); 866 867 if (CreateM2sIndex) 868 out.InitIdd(FileName, 1); 869 870 if (CreateInfoIndex) 871 out.InitInfo(FileName); 872 873 pts_log = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(FileName + ".pts"), 65535)); 874 pts_log.write(CommonParsing.PTSVideoHeader); 875 876 packet = new ByteArrayOutputStream(); 877 vidbuf = new ByteArrayOutputStream(); 878 vptsbytes = new ByteArrayOutputStream(); 879 vpts = new DataOutputStream(vptsbytes); 880 881 } catch (IOException e) { 882 883 Common.setExceptionMessage(e); 884 } 885 } 886 initVideo2(JobCollection collection, String _name)887 public void initVideo2(JobCollection collection, String _name) 888 { 889 getSettings(collection); 890 891 parentname = _name; 892 setFileName(); 893 894 first = true; 895 MPGVideotype[0] = -1; 896 897 try { 898 out = new IDDBufferedOutputStream(new FileOutputStream(FileName), buffersize); 899 900 if (CreateM2sIndex) 901 out.InitIdd(FileName, 1); 902 903 if (CreateInfoIndex) 904 out.InitInfo(FileName); 905 906 pts_log = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(FileName + ".pts"), 65535)); 907 pts_log.write(CommonParsing.PTSVideoHeader); 908 909 packet.reset(); 910 vidbuf.reset(); 911 vptsbytes.reset(); 912 913 } catch (IOException e) { 914 915 Common.setExceptionMessage(e); 916 } 917 } 918 919 920 /** 921 * clean Up for next foreign inputfile in case of multiple 922 */ resetVideo()923 public void resetVideo() 924 { 925 if (vidbuf != null) 926 vidbuf.reset(); 927 928 if (packet != null) 929 packet.reset(); 930 931 if (vptsbytes != null) 932 vptsbytes.reset(); 933 934 first = true; 935 } 936 closeVideo(JobProcessing job_processing, String workouts)937 public String closeVideo(JobProcessing job_processing, String workouts) 938 { 939 String logfile = "-1"; 940 941 List cell = job_processing.getCellTimes(); 942 943 int[] clv = job_processing.getStatusVariables(); 944 945 try { 946 947 if (AddSequenceEndcode && job_processing.getExportedVideoFrameNumber() > 0) 948 { 949 if (MPGVideotype[0] < 2) 950 { 951 out.write(Video.getSequenceEndCode()); 952 953 job_processing.countMediaFilesExportLength(+4); 954 job_processing.countAllMediaFilesExportLength(+4); 955 } 956 957 else // h264 958 { 959 out.write(new byte[]{0, 0, 0, 1, 0xA}); 960 961 job_processing.countMediaFilesExportLength(+5); 962 job_processing.countAllMediaFilesExportLength(+5); 963 } 964 } 965 966 packet.close(); 967 vidbuf.flush(); 968 vidbuf.close(); 969 out.flush(); 970 out.close(); 971 pts_log.flush(); 972 pts_log.close(); 973 vpts.flush(); 974 vpts.close(); 975 vptsbytes.flush(); 976 vptsbytes.close(); 977 978 String videofile = ""; 979 980 if (new File(FileName).length() < 10) 981 { 982 new File(FileName).delete(); 983 984 if (!WriteVideo && new File(FileName + ".pts").length() > 16) 985 logfile = FileName + ".pts"; 986 987 else 988 new File(FileName + ".pts").delete(); 989 } 990 991 else 992 { 993 //int ot = (RenameVideo || CreateD2vIndex || SplitProjectFile) ? 0 : 2; 994 int ot = (RenameVideo || CreateD2vIndex || SplitProjectFile) ? 0 : 3; 995 996 videofile = parentname; 997 998 if (AppendPidToFileName) 999 videofile += formatIDString(getPID(), getID(), subID()); 1000 1001 videofile += videoext[MPGVideotype[0] + ot]; 1002 File newfile = new File(videofile); 1003 1004 if (newfile.exists()) 1005 newfile.delete(); 1006 1007 Common.renameTo(new File(FileName), newfile); 1008 1009 logfile = FileName + ".pts"; 1010 1011 CommonParsing.setVideoHeader(job_processing, videofile, logfile, clv, MPGVideotype); 1012 1013 /** 1014 * celltimes.txt 1015 */ 1016 if (CreateCellTimes && !cell.isEmpty()) 1017 { 1018 BufferedWriter cellout = new BufferedWriter(new FileWriter(workouts + "CellTimes.txt")); 1019 1020 for (int i = 0; i < cell.size(); i++) 1021 { 1022 cellout.write(cell.get(i).toString()); 1023 cellout.newLine(); 1024 } 1025 1026 cellout.close(); 1027 1028 Common.setMessage(Resource.getString("demux.msg.celltimes", workouts)); 1029 1030 long fl = new File(workouts + "CellTimes.txt").length(); 1031 1032 job_processing.countMediaFilesExportLength(fl); 1033 job_processing.countAllMediaFilesExportLength(fl); 1034 } 1035 1036 cell.clear(); 1037 } 1038 1039 if (CreateM2sIndex) 1040 { 1041 if (new File(videofile).exists()) 1042 { 1043 String tmpFN = videofile.toString().substring(0, videofile.toString().lastIndexOf(".")); 1044 out.renameVideoIddTo(tmpFN); 1045 } 1046 1047 else 1048 out.deleteIdd(); 1049 } 1050 1051 if (CreateInfoIndex) 1052 { 1053 if (new File(videofile).exists()) 1054 { 1055 String tmpFN = videofile.toString(); 1056 out.renameVideoInfoTo(tmpFN); 1057 } 1058 1059 else 1060 out.deleteInfo(); 1061 } 1062 1063 } catch (IOException e) { 1064 1065 Common.setExceptionMessage(e); 1066 } 1067 1068 return logfile; 1069 } 1070 1071 1072 1073 /** 1074 * temporary redirected access to goptest from video-es 1075 */ writeVideoES(JobProcessing job_processing, IDDBufferedOutputStream _out, byte[] _vidbuf, byte[] _vptsbytes, DataOutputStream _pts_log, String _parentname, int[] _MPGVideotype, List _CutpointList, List _ChapterpointList, boolean _doWrite)1076 public void writeVideoES(JobProcessing job_processing, IDDBufferedOutputStream _out, byte[] _vidbuf, byte[] _vptsbytes, DataOutputStream _pts_log, String _parentname, int[] _MPGVideotype, List _CutpointList, List _ChapterpointList, boolean _doWrite) 1077 { 1078 job_processing.getGop().goptest(job_processing, _out, _vidbuf, _vptsbytes, _pts_log, _parentname, _MPGVideotype, _CutpointList, _ChapterpointList, _doWrite); 1079 } 1080 1081 /** 1082 * write video 1083 * data = 1 pespacket from demux 1084 */ writeVideo(JobProcessing job_processing, byte[] pes_packet, boolean pes_hasHeader, List CutpointList, List ChapterpointList)1085 public void writeVideo(JobProcessing job_processing, byte[] pes_packet, boolean pes_hasHeader, List CutpointList, List ChapterpointList) 1086 { 1087 writeVideo(job_processing, pes_packet, 0, pes_packet.length, pes_hasHeader, CutpointList, ChapterpointList); 1088 } 1089 1090 /** 1091 * write video 1092 * data = 1 pespacket from demux 1093 */ writeVideo(JobProcessing job_processing, byte[] pes_packet, int pes_packetoffset, int pes_payloadlength, boolean pes_hasHeader, List CutpointList, List ChapterpointList)1094 public void writeVideo(JobProcessing job_processing, byte[] pes_packet, int pes_packetoffset, int pes_payloadlength, boolean pes_hasHeader, List CutpointList, List ChapterpointList) 1095 { 1096 int pes_extensionlength = 0; 1097 int offset = pes_packetoffset; 1098 byte[] data = null; 1099 1100 int[] clv = job_processing.getStatusVariables(); 1101 1102 pack++; 1103 1104 if (!pes_hasHeader) 1105 { 1106 if (job_processing.getPvaVideoPts() != -1) 1107 { 1108 offset -= 4; 1109 pes_extensionlength += 4; 1110 } 1111 } 1112 1113 else 1114 { 1115 if (CommonParsing.validateStartcode(pes_packet, offset) < 0) 1116 { 1117 Common.setMessage(Resource.getString("demux.error.video.startcode") + " " + pack + " (" + Integer.toHexString(PID) + "/" + Integer.toHexString(pes_ID) + "/" + Integer.toHexString(newID) + "/" + es_streamtype + ")"); 1118 return; 1119 } 1120 1121 pes_ID = CommonParsing.getPES_IdField(pes_packet, offset); 1122 pes_payloadlength = CommonParsing.getPES_LengthField(pes_packet, offset); 1123 1124 if (pes_streamtype == CommonParsing.MPEG1PS_TYPE) 1125 { 1126 skiploop: 1127 while(true) 1128 { 1129 switch (0xC0 & pes_packet[6 + offset]) 1130 { 1131 case 0x40: 1132 offset += 2; 1133 continue skiploop; 1134 1135 case 0x80: 1136 offset += 3; 1137 continue skiploop; 1138 1139 case 0xC0: 1140 offset++; 1141 continue skiploop; 1142 1143 case 0: 1144 break; 1145 } 1146 1147 switch (0x30 & pes_packet[6 + offset]) 1148 { 1149 case 0x20: //PTS 1150 pes_extensionlength = 5; 1151 break skiploop; 1152 1153 case 0x30: //PTS + DTS 1154 pes_extensionlength = 10; 1155 break skiploop; 1156 1157 case 0x10: //DTS 1158 offset += 5; 1159 break skiploop; 1160 1161 case 0: 1162 offset++; 1163 break skiploop; 1164 } 1165 } 1166 } 1167 1168 else 1169 { 1170 pes_extensionlength = CommonParsing.getPES_ExtensionLengthField(pes_packet, offset); 1171 1172 if ((0x80 & pes_packet[7 + offset]) == 0) 1173 { 1174 offset += pes_extensionlength; 1175 pes_extensionlength = 0; 1176 } 1177 1178 offset += 3; 1179 } 1180 1181 pes_payloadlength -= (offset - pes_packetoffset + pes_extensionlength); 1182 offset += 6; 1183 } 1184 1185 /** 1186 * read pts 1187 */ 1188 if (pes_extensionlength > 0 && pes_payloadlength >= 0) 1189 { 1190 //--> �ndern 1191 pts = !pes_hasHeader ? job_processing.getPvaVideoPts() : CommonParsing.getPTSfromBytes(pes_packet, offset); //returns 32bit 1192 1193 pts -= job_processing.getNextFileStartPts(); 1194 1195 pts &= 0xFFFFFFFFL; // trim to 32bit 1196 1197 if ( (pts & 0xFF000000L) == 0xFF000000L ) 1198 ptsover = true; // bit 33 was set 1199 1200 if (ptsover && pts < 0xF0000000L) 1201 1202 pts |= 0x100000000L; 1203 //<-- 1204 pts += ptsoffset; 1205 1206 if (Debug) 1207 System.out.println(" pdv PTS: " + pts); 1208 1209 isPTSwritten = false; 1210 } 1211 1212 try { 1213 1214 if (pes_payloadlength <= 0) 1215 Common.setMessage(Resource.getString("demux.error.video.payload") + " (" + pack + "/" + pes_packet.length + "/" + offset + "/" + pes_extensionlength + "/" + pes_payloadlength + ")"); 1216 1217 else 1218 packet.write(pes_packet, offset + pes_extensionlength, pes_payloadlength); 1219 1220 packet.flush(); 1221 1222 /** simple demux 1223 byte[] ddd = new byte[pes_payloadlength]; 1224 System.arraycopy(pes_packet, offset + pes_extensionlength, ddd, 0, pes_payloadlength); 1225 1226 job_processing.getGop().h264test(job_processing, out, ddd, vptsbytes.toByteArray(), pts_log, parentname, MPGVideotype, CutpointList, ChapterpointList); 1227 1228 packet.reset(); 1229 1230 if (1 == 1) 1231 return; 1232 **/ 1233 data = packet.toByteArray(); 1234 1235 packet.reset(); 1236 1237 boolean gop = false; 1238 boolean packetfirstframe = true; 1239 1240 int nal_unit = 0; 1241 int nal_ref = 0; 1242 1243 packloop: 1244 for (int i = 0, j = 0, k = 0, id, returncode; i < data.length - 3; i++) 1245 { 1246 if ((returncode = CommonParsing.validateStartcode(data, i)) < 0) 1247 { 1248 i += (-returncode) - 1; 1249 continue packloop; 1250 } 1251 1252 id = CommonParsing.getPES_IdField(data, i); 1253 1254 // mpeg4 part 1255 //optional deactivator 1256 if (!EnableHDDemux) 1257 isH264 = false; 1258 1259 if (isH264) 1260 { 1261 if (i == 0 || data[i - 1] != 0 || (0x80 & id) != 0) // not 00 00 00 01 0XXX-XXXX 1262 continue packloop; 1263 1264 i--; // return to startcode 1265 nal_ref = 3 & id>>5; 1266 nal_unit = 0x1F & id; 1267 1268 if (Debug) 1269 System.out.println("i " + i + " /NAL ref " + nal_ref + " /unit " + nal_unit); 1270 1271 switch (nal_unit) 1272 { 1273 case 1: // non IDR pic data 1274 case 5: // IDR pic data 1275 1276 if (!isPTSwritten && pts != -1) 1277 { 1278 vpts.writeLong(pts); 1279 vpts.writeLong((long) k); 1280 vpts.flush(); 1281 1282 isPTSwritten = true; 1283 } 1284 break; 1285 1286 case 7: // sequence param set // 9-7-8-6-1 ; 9-7-6-8-6-1 1287 1288 vidbuf.write(data, 0, j); // save last data until run-in 1289 1290 if (!first) 1291 job_processing.getGop().h264test(job_processing, out, vidbuf.toByteArray(), vptsbytes.toByteArray(), pts_log, parentname, MPGVideotype, CutpointList, ChapterpointList); 1292 1293 vptsbytes.reset(); 1294 vidbuf.reset(); 1295 1296 if (!isPTSwritten && pts != -1) 1297 { 1298 vpts.writeLong(pts); 1299 vpts.writeLong((long) 0); 1300 vpts.flush(); 1301 1302 isPTSwritten = true; 1303 } 1304 1305 first = false; 1306 gop = true; 1307 1308 // save new data from run-in 1309 // expects there's no next seq param set 1310 vidbuf.write(data, j, data.length - j); 1311 1312 break; 1313 1314 case 9: // run-in 1315 j = i; 1316 k = vidbuf.size(); 1317 break; 1318 } 1319 1320 i += 2; 1321 continue packloop; 1322 } 1323 // 1324 1325 // 00 00 00 01 + 0XX0-1001 lead-in, toggle with mpeg1-2, shall be set once 1326 else if (MPGVideotype[0] < 0) 1327 { 1328 isH264 = i > 0 && data[i - 1] == 0 && (0x9F & id) == 9; 1329 } 1330 1331 if (isH264) // last return, never been called after that 1332 continue packloop; 1333 1334 1335 1336 /** 1337 * new frame at first 1338 */ 1339 if (!isPTSwritten && packetfirstframe && id == CommonParsing.PICTURE_START_CODE) 1340 { 1341 if (MPGVideotype[0] < 0) 1342 MPGVideotype[0] = 0; 1343 1344 if (misshead && i < 3) 1345 { 1346 misshead = false; 1347 continue packloop; 1348 } 1349 1350 if (pts != -1) 1351 { 1352 vpts.writeLong(pts); 1353 vpts.writeLong((long)vidbuf.size()); 1354 vpts.flush(); 1355 } 1356 1357 isPTSwritten = true; 1358 packetfirstframe = false; 1359 i += 8; 1360 } 1361 1362 else if (id == CommonParsing.SEQUENCE_HEADER_CODE || id == CommonParsing.SEQUENCE_END_CODE || id == CommonParsing.GROUP_START_CODE) 1363 { 1364 if (MPGVideotype[0] < 0) 1365 MPGVideotype[0] = 0; 1366 1367 if (id == CommonParsing.SEQUENCE_HEADER_CODE) 1368 seqhead = true; 1369 1370 if (id == CommonParsing.GROUP_START_CODE && seqhead && vidbuf.size() < 400) 1371 { 1372 seqhead = false; 1373 continue packloop; 1374 } 1375 1376 vidbuf.write(data, j, i); 1377 1378 if (!first) 1379 job_processing.getGop().goptest(job_processing, out, vidbuf.toByteArray(), vptsbytes.toByteArray(), pts_log, parentname, MPGVideotype, CutpointList, ChapterpointList); 1380 1381 vptsbytes.reset(); 1382 vidbuf.reset(); 1383 1384 /** 1385 * split size reached 1386 */ 1387 if (job_processing.getSplitSize() > 0 && job_processing.getSplitSize() < job_processing.getAllMediaFilesExportLength()) 1388 return; 1389 1390 /** 1391 * d2v split reached 1392 */ 1393 if (SplitProjectFile && job_processing.getProjectFileExportLength() > job_processing.getProjectFileSplitSize()) 1394 { 1395 int part = job_processing.getProjectFileD2V().getPart() + 1; 1396 String newpart = parentname + "[" + part + "].mpv"; 1397 1398 /** 1399 * sequence end code 1400 */ 1401 if (WriteVideo && AddSequenceEndcode && job_processing.getExportedVideoFrameNumber() > 0 ) 1402 { 1403 out.write(Video.getSequenceEndCode()); 1404 1405 job_processing.countMediaFilesExportLength(+4); 1406 job_processing.countAllMediaFilesExportLength(+4); 1407 } 1408 1409 out.flush(); 1410 out.close(); 1411 //System.gc(); 1412 1413 out = new IDDBufferedOutputStream( new FileOutputStream(newpart), buffersize); 1414 1415 /** 1416 * M2S idd 1417 */ 1418 if (CreateM2sIndex) 1419 out.InitIdd(newpart, 1); 1420 1421 if (CreateInfoIndex) 1422 out.InitInfo(newpart); 1423 1424 job_processing.getProjectFileD2V().setFile(newpart); 1425 job_processing.setProjectFileExportLength(0); 1426 } 1427 1428 if (!isPTSwritten && packetfirstframe) 1429 { 1430 if (pts != -1) 1431 { 1432 vpts.writeLong(pts); 1433 vpts.writeLong(vidbuf.size()); 1434 vpts.flush(); 1435 } 1436 1437 isPTSwritten = true; 1438 } 1439 1440 1441 if (id == CommonParsing.SEQUENCE_END_CODE) 1442 { 1443 Common.setMessage(Resource.getString("demux.msg.skip.sec") + " " + clv[6]); 1444 1445 first = true; 1446 job_processing.setSequenceHeader(false); 1447 1448 i += 3; 1449 continue packloop; 1450 } 1451 else 1452 vidbuf.write(data, i, data.length - i); 1453 1454 1455 if (id == CommonParsing.GROUP_START_CODE) 1456 { 1457 job_processing.setSequenceHeader(false); 1458 1459 if (job_processing.getSplitPart() > 0) 1460 first = false; 1461 } 1462 else if (id == CommonParsing.SEQUENCE_HEADER_CODE) 1463 { 1464 job_processing.setSequenceHeader(true); 1465 first = false; 1466 } 1467 1468 gop = true; 1469 misshead = false; 1470 break packloop; 1471 } 1472 } // end packloop 1473 1474 if (!gop) 1475 { 1476 if (data.length > 2) 1477 { 1478 vidbuf.write(data, 0, data.length - 3); 1479 packet.write(data, data.length - 3, 3); 1480 misshead = true; 1481 } 1482 else 1483 vidbuf.write(data); 1484 } 1485 1486 } catch (IOException e) { 1487 1488 Common.setExceptionMessage(e); 1489 } 1490 1491 if (vidbuf.size() > 6144000) 1492 { 1493 vptsbytes.reset(); 1494 vidbuf.reset(); 1495 packet.reset(); 1496 1497 Common.setMessage(Resource.getString("demux.error.gop.toobig")); 1498 1499 misshead = false; 1500 first = true; 1501 } 1502 } 1503 1504 /** 1505 * simply write the packet 1506 */ writePacket(byte[] packet)1507 private int writePacket(byte[] packet) throws IOException 1508 { 1509 return writePacket(packet, 0, packet.length); 1510 } 1511 1512 /** 1513 * simply write the packet 1514 */ writePacket(byte[] packet, int offset, int length)1515 private int writePacket(byte[] packet, int offset, int length) throws IOException 1516 { 1517 if (offset < 0 || offset >= packet.length) 1518 { 1519 Common.setMessage("!> packet writing: index out of bounds, ignore it.. (" + Integer.toHexString(getPID()) + " / " + Integer.toHexString(getID()) + " / " + Integer.toHexString(getnewID()) + " / " + getPackCount() + " -- " + packet.length + " / " + offset + " / " + length + ") @ PTS " + Common.formatTime_1(lastPTS / 90)); 1520 return 0; 1521 } 1522 1523 if (offset + length > packet.length) 1524 { 1525 Common.setMessage("!> packet writing: length index out of bounds, shortened.. (" + Integer.toHexString(getPID()) + " / " + Integer.toHexString(getID()) + " / " + Integer.toHexString(getnewID()) + " / " + getPackCount() + " -- " + packet.length + " / " + offset + " / " + length + ") @ PTS " + Common.formatTime_1(lastPTS / 90)); 1526 length = packet.length - offset; 1527 } 1528 1529 out.write(packet, offset, length); 1530 1531 return length; 1532 } 1533 1534 } 1535 1536