001/* 002 * $RCSfile: CodestreamManipulator.java,v $ 003 * $Revision: 1.1 $ 004 * $Date: 2005/02/11 05:02:24 $ 005 * $State: Exp $ 006 * 007 * Class: CodestreamManipulator 008 * 009 * Description: Manipulates codestream to create tile-parts etc 010 * 011 * 012 * 013 * COPYRIGHT: 014 * 015 * This software module was originally developed by Raphaël Grosbois and 016 * Diego Santa Cruz (Swiss Federal Institute of Technology-EPFL); Joel 017 * Askelöf (Ericsson Radio Systems AB); and Bertrand Berthelot, David 018 * Bouchard, Félix Henry, Gerard Mozelle and Patrice Onno (Canon Research 019 * Centre France S.A) in the course of development of the JPEG2000 020 * standard as specified by ISO/IEC 15444 (JPEG 2000 Standard). This 021 * software module is an implementation of a part of the JPEG 2000 022 * Standard. Swiss Federal Institute of Technology-EPFL, Ericsson Radio 023 * Systems AB and Canon Research Centre France S.A (collectively JJ2000 024 * Partners) agree not to assert against ISO/IEC and users of the JPEG 025 * 2000 Standard (Users) any of their rights under the copyright, not 026 * including other intellectual property rights, for this software module 027 * with respect to the usage by ISO/IEC and Users of this software module 028 * or modifications thereof for use in hardware or software products 029 * claiming conformance to the JPEG 2000 Standard. Those intending to use 030 * this software module in hardware or software products are advised that 031 * their use may infringe existing patents. The original developers of 032 * this software module, JJ2000 Partners and ISO/IEC assume no liability 033 * for use of this software module or modifications thereof. No license 034 * or right to this software module is granted for non JPEG 2000 Standard 035 * conforming products. JJ2000 Partners have full right to use this 036 * software module for his/her own purpose, assign or donate this 037 * software module to any third party and to inhibit third parties from 038 * using this software module for non JPEG 2000 Standard conforming 039 * products. This copyright notice must be included in all copies or 040 * derivative works of this software module. 041 * 042 * Copyright (c) 1999/2000 JJ2000 Partners. 043 * */ 044package jj2000.j2k.util; 045 046import java.io.ByteArrayOutputStream; 047import java.io.File; 048import java.io.IOException; 049import java.util.Vector; 050 051import jj2000.j2k.codestream.Markers; 052import jj2000.j2k.io.BEBufferedRandomAccessFile; 053import jj2000.j2k.io.BufferedRandomAccessFile; 054 055/** 056 * This class takes a legal JPEG 2000 codestream and performs some 057 * manipulation on it. Currently the manipulations supported are: Tile-parts 058 * */ 059public class CodestreamManipulator{ 060 061 /** Flag indicating whether packed packet headers in main header is used 062 * */ 063 private boolean ppmUsed; 064 065 /** Flag indicating whether packed packet headers in tile headers is used 066 * */ 067 private boolean pptUsed; 068 069 /** Flag indicating whether SOP marker was only intended for parsing in 070 * This class and should be removed */ 071 private boolean tempSop; 072 073 /** Flag indicating whether EPH marker was only intended for parsing in 074 * This class and should be removed */ 075 private boolean tempEph; 076 077 /** The number of tiles in the image */ 078 private int nt; 079 080 /** The number of packets per tile-part */ 081 private int pptp; 082 083 /** The name of the outfile */ 084 private File file; 085 086 /** The length of a SOT plus a SOD marker */ 087 private static int TP_HEAD_LEN = 14; 088 089 /** The maximum number of a tile part index (TPsot) */ 090 private static int MAX_TPSOT = 16; 091 092 /** The maximum number of tile parts in any tile */ 093 private int maxtp; 094 095 /** The number of packets per tile */ 096 private int[] ppt = new int[nt]; 097 098 /** The positions of the SOT, SOP and EPH markers */ 099 private Integer[] positions; 100 101 /** The main header */ 102 private byte[] mainHeader; 103 104 /** Buffers containing the tile parts */ 105 private byte[][][] tileParts; 106 107 /** Buffers containing the original tile headers */ 108 private byte[][] tileHeaders; 109 110 /** Buffers contaning the packet headers */ 111 private byte[][][] packetHeaders; 112 113 /** Buffers containing the packet data */ 114 private byte[][][] packetData; 115 116 /** Buffers containing the SOP marker segments */ 117 private byte[][][] sopMarkSeg; 118 119 /** 120 * Instantiates a codestream manipulator.. 121 * 122 * @param outname The name of the original outfile 123 * 124 * @param nt The number of tiles in the image 125 * 126 * @param pptp Packets per tile-part. If zero, no division into tileparts 127 * is performed 128 * 129 * @param ppm Flag indicating that PPM marker is used 130 * 131 * @param ppt Flag indicating that PPT marker is used 132 * 133 * @param tempSop Flag indicating whether SOP merker should be removed 134 * 135 * @param tempEph Flag indicating whether EPH merker should be removed 136 * */ 137 public CodestreamManipulator(File file, int nt, int pptp, boolean ppm, 138 boolean ppt, boolean tempSop, 139 boolean tempEph) { 140 this.file = file; 141 this.nt=nt; 142 this.pptp = pptp; 143 this.ppmUsed = ppm; 144 this.pptUsed = ppt; 145 this.tempSop = tempSop; 146 this.tempEph = tempEph; 147 } 148 149 /** 150 * This method performs the actual manipulation of the codestream which is 151 * the reparsing for tile parts and packed packet headers 152 * 153 * @return The number of bytes that the file has increased by 154 * 155 * @exception java.io.IOException If an I/O error ocurred. 156 * */ 157 public int doCodestreamManipulation() throws IOException{ 158 int addedHeaderBytes=0; 159 ppt = new int[nt]; 160 tileParts = new byte[nt][][]; 161 tileHeaders = new byte[nt][]; 162 packetHeaders = new byte[nt][][]; 163 packetData = new byte[nt][][]; 164 sopMarkSeg = new byte[nt][][]; 165 166 // If neither packed packet header nor tile parts are used, return 0 167 if( ppmUsed == false && pptUsed == false && pptp == 0) 168 return 0; 169 170 // Open file for reading and writing 171 BEBufferedRandomAccessFile fi = 172 new BEBufferedRandomAccessFile(file, "rw+"); 173 addedHeaderBytes -= fi.length(); 174 175 // Parse the codestream for SOT, SOP and EPH markers 176 parseAndFind(fi); 177 178 // Read and buffer the tile headers, packet headers and packet data 179 readAndBuffer(fi); 180 181 // Close file and overwrite with new file 182 fi.close(); 183 fi = new BEBufferedRandomAccessFile(file, "rw"); 184 185 // Create tile-parts 186 createTileParts(); 187 188 // Write new codestream 189 writeNewCodestream(fi); 190 191 // Close file 192 fi.flush(); 193 addedHeaderBytes += fi.length(); 194 fi.close(); 195 196 return addedHeaderBytes; 197 } 198 199 /** 200 * This method parses the codestream for SOT, SOP and EPH markers and 201 * removes header header bits signalling SOP and EPH markers if packed 202 * packet headers are used 203 * 204 * @param fi The file to parse the markers from 205 * 206 * @exception java.io.IOException If an I/O error ocurred. 207 * */ 208 private void parseAndFind(BufferedRandomAccessFile fi) throws IOException{ 209 int length,pos,i,t,sop=0,eph=0; 210 short marker; 211 int halfMarker; 212 int tileEnd; 213 Vector markPos = new Vector(); 214 215 // Find position of first SOT marker 216 marker = (short)fi.readUnsignedShort(); // read SOC marker 217 marker = (short)fi.readUnsignedShort(); 218 while(marker != Markers.SOT){ 219 pos = fi.getPos(); 220 length = fi.readUnsignedShort(); 221 222 // If SOP and EPH markers were only used for parsing in this 223 // class remove SOP and EPH markers from Scod field 224 if(marker == Markers.COD){ 225 int scod = fi.readUnsignedByte(); 226 if(tempSop) 227 scod &= 0xfd; // Remove bits indicating SOP 228 if(tempEph) 229 scod &= 0xfb; // Remove bits indicating SOP 230 fi.seek(pos +2); 231 fi.write(scod); 232 } 233 234 fi.seek(pos + length); 235 marker = (short)fi.readUnsignedShort(); 236 } 237 pos = fi.getPos(); 238 fi.seek(pos-2); 239 240 // Find all packet headers, packed data and tile headers 241 for(t=0;t<nt;t++){ 242 // Read SOT marker 243 fi.readUnsignedShort(); // Skip SOT 244 pos = fi.getPos(); 245 markPos.addElement(new Integer(fi.getPos())); 246 fi.readInt(); // Skip Lsot and Isot 247 length = fi.readInt(); // Read Psot 248 fi.readUnsignedShort(); // Skip TPsot & TNsot 249 tileEnd = pos + length-2; // Last byte of tile 250 251 // Find position of SOD marker 252 marker = (short)fi.readUnsignedShort(); 253 while(marker != Markers.SOD){ 254 pos = fi.getPos(); 255 length = fi.readUnsignedShort(); 256 257 // If SOP and EPH markers were only used for parsing in this 258 // class remove SOP and EPH markers from Scod field 259 if(marker == Markers.COD){ 260 int scod = fi.readUnsignedByte(); 261 if(tempSop) 262 scod &= 0xfd; // Remove bits indicating SOP 263 if(tempEph) 264 scod &= 0xfb; // Remove bits indicating SOP 265 fi.seek(pos +2); 266 fi.write(scod); 267 } 268 fi.seek(pos + length); 269 marker = (short)fi.readUnsignedShort(); 270 } 271 272 // Find all SOP and EPH markers in tile 273 sop = 0; 274 eph = 0; 275 276 i = fi.getPos(); 277 while(i<tileEnd){ 278 halfMarker = (short) fi.readUnsignedByte(); 279 if(halfMarker == (short)0xff){ 280 marker = (short)((halfMarker<<8) + 281 fi.readUnsignedByte()); 282 i++; 283 if(marker == Markers.SOP){ 284 markPos.addElement(new Integer(fi.getPos())); 285 ppt[t]++; 286 sop++; 287 fi.skipBytes(4); 288 i+=4; 289 } 290 291 if(marker == Markers.EPH){ 292 markPos.addElement(new Integer(fi.getPos())); 293 eph++; 294 } 295 } 296 i++; 297 } 298 } 299 markPos.addElement(new Integer(fi.getPos()+2)); 300 positions = new Integer[markPos.size()]; 301 markPos.copyInto(positions); 302 } 303 304 /** 305 * This method reads and buffers the tile headers, packet headers and 306 * packet data. 307 * 308 * @param fi The file to read the headers and data from 309 * 310 * @exception java.io.IOException If an I/O error ocurred. 311 * */ 312 private void readAndBuffer(BufferedRandomAccessFile fi)throws IOException{ 313 int p,prem,length,t,markIndex; 314 315 // Buffer main header 316 fi.seek(0); 317 length = ((Integer)positions[0]).intValue()-2; 318 mainHeader = new byte[length]; 319 fi.readFully(mainHeader,0,length); 320 markIndex = 0; 321 322 for(t=0; t<nt; t++){ 323 prem = ppt[t]; 324 325 packetHeaders[t] = new byte[prem][]; 326 packetData[t] = new byte[prem][]; 327 sopMarkSeg[t] = new byte[prem][]; 328 329 // Read tile header 330 length = positions[ markIndex+1 ].intValue() - 331 positions[ markIndex ].intValue(); 332 tileHeaders[t] = new byte[length]; 333 fi.readFully(tileHeaders[t],0,length); 334 markIndex++; 335 336 for(p=0; p<prem; p++){ 337 // Read packet header 338 length = positions[ markIndex+1 ].intValue() - 339 positions[ markIndex ].intValue(); 340 341 if(tempSop){ // SOP marker is skipped 342 length -= Markers.SOP_LENGTH; 343 fi.skipBytes(Markers.SOP_LENGTH); 344 } 345 else{ // SOP marker is read and buffered 346 length -= Markers.SOP_LENGTH; 347 sopMarkSeg[t][p] = new byte[Markers.SOP_LENGTH]; 348 fi.readFully(sopMarkSeg[t][p],0,Markers.SOP_LENGTH); 349 } 350 351 if(!tempEph){ // EPH marker is kept in header 352 length += Markers.EPH_LENGTH; 353 } 354 355 packetHeaders[t][p] = new byte[length]; 356 fi.readFully(packetHeaders[t][p],0,length); 357 markIndex++; 358 359 // Read packet data 360 length = positions[ markIndex+1 ].intValue() - 361 positions[ markIndex ].intValue(); 362 363 length -= Markers.EPH_LENGTH; 364 if(tempEph){ // EPH marker is used and is skipped 365 fi.skipBytes(Markers.EPH_LENGTH); 366 } 367 368 packetData[t][p] = new byte[length]; 369 fi.readFully(packetData[t][p],0,length); 370 markIndex++; 371 } 372 } 373 } 374 375 /** 376 * This method creates the tileparts from the buffered tile headers, 377 * packet headers and packet data 378 * 379 * @exception java.io.IOException If an I/O error ocurred. 380 * */ 381 private void createTileParts() throws IOException{ 382 int i,prem,t,length; 383 int pIndex,phIndex; 384 int tppStart; 385 int tilePart; 386 int p,np, nomnp; 387 int numTileParts; 388 int numPackets; 389 ByteArrayOutputStream temp = new ByteArrayOutputStream(); 390 byte[] tempByteArr; 391 392 // Create tile parts 393 tileParts = new byte[nt][][]; 394 maxtp = 0; 395 396 for(t=0; t<nt; t++){ 397 // Calculate number of tile parts. If tileparts are not used, 398 // put all packets in the first tilepart 399 if(pptp == 0) 400 pptp = ppt[t]; 401 prem = ppt[t]; 402 numTileParts = (int)Math.ceil(((double)prem)/pptp); 403 numPackets = packetHeaders[t].length; 404 maxtp = (numTileParts>maxtp) ? numTileParts:maxtp; 405 tileParts[t] = new byte[numTileParts][]; 406 407 // Create all the tile parts for tile t 408 tppStart = 0; 409 pIndex = 0; 410 p=0; 411 phIndex = 0; 412 for(tilePart=0;tilePart<numTileParts;tilePart++){ 413 414 // Calculate number of packets in this tilepart 415 nomnp = (pptp > prem) ? prem:pptp; 416 np = nomnp; 417 418 // Write tile part header 419 if(tilePart == 0){ 420 // Write original tile part header up to SOD marker 421 temp.write(tileHeaders[t],0, 422 tileHeaders[t].length-2); 423 }else{ 424 // Write empty header of length TP_HEAD_LEN-2 425 temp.write(new byte[TP_HEAD_LEN-2],0,TP_HEAD_LEN-2); 426 } 427 428 // Write PPT marker segments if PPT used 429 if(pptUsed){ 430 int pptLength = 3; // Zppt and Lppt 431 int pptIndex = 0; 432 int phLength; 433 434 p = pIndex; 435 while(np > 0){ 436 phLength = packetHeaders[t][p].length; 437 438 // If the total legth of the packet headers is greater 439 // than MAX_LPPT, several PPT markers are needed 440 if(pptLength + phLength > Markers.MAX_LPPT){ 441 temp.write(Markers.PPT>>>8); 442 temp.write(Markers.PPT); 443 temp.write(pptLength>>>8); 444 temp.write(pptLength); 445 temp.write(pptIndex++); 446 for(i=pIndex; i < p ; i++){ 447 temp.write(packetHeaders[t][i],0, 448 packetHeaders[t][i].length); 449 } 450 pptLength = 3; // Zppt and Lppt 451 pIndex = p; 452 } 453 pptLength += phLength; 454 p++; 455 np--; 456 } 457 // Write last PPT marker 458 temp.write(Markers.PPT>>>8); 459 temp.write(Markers.PPT); 460 temp.write(pptLength>>>8); 461 temp.write(pptLength); 462 temp.write(pptIndex); 463 for(i=pIndex; i < p ; i++){ 464 465 temp.write(packetHeaders[t][i],0, 466 packetHeaders[t][i].length); 467 } 468 } 469 pIndex = p; 470 np = nomnp; 471 472 // Write SOD marker 473 temp.write(Markers.SOD>>>8); 474 temp.write(Markers.SOD); 475 476 // Write packet data and packet headers if PPT and PPM not used 477 for(p=tppStart; p<tppStart+np ; p++){ 478 if(!tempSop){ 479 temp.write(sopMarkSeg[t][p],0,Markers.SOP_LENGTH); 480 } 481 482 if(!(ppmUsed || pptUsed)){ 483 temp.write(packetHeaders[t][p],0, 484 packetHeaders[t][p].length); 485 } 486 487 temp.write(packetData[t][p], 0, packetData[t][p].length); 488 } 489 tppStart += np; 490 491 // Edit tile part header 492 tempByteArr = temp.toByteArray(); 493 tileParts[t][tilePart] = tempByteArr; 494 length = temp.size(); 495 496 if(tilePart == 0){ 497 // Edit first tile part header 498 tempByteArr[6] = (byte)(length>>>24); // Psot 499 tempByteArr[7] = (byte)(length>>>16); 500 tempByteArr[8] = (byte)(length>>>8); 501 tempByteArr[9] = (byte)(length); 502 tempByteArr[10] = (byte)(0); // TPsot 503 tempByteArr[11] = (byte)(numTileParts); // TNsot 504 }else{ 505 // Edit tile part header 506 tempByteArr[0] = (byte)(Markers.SOT>>>8); // SOT 507 tempByteArr[1] = (byte)(Markers.SOT); 508 tempByteArr[2] = (byte)(0); // Lsot 509 tempByteArr[3] = (byte)(10); 510 tempByteArr[4] = (byte)(t >> 8); // Lsot 511 tempByteArr[5] = (byte)(t); // Isot 512 tempByteArr[6] = (byte)(length>>>24); // Psot 513 tempByteArr[7] = (byte)(length>>>16); 514 tempByteArr[8] = (byte)(length>>>8); 515 tempByteArr[9] = (byte)(length); 516 tempByteArr[10] = (byte)(tilePart); //TPsot 517 tempByteArr[11] = (byte)(numTileParts); // TNsot 518 } 519 temp.reset(); 520 prem -= np; 521 } 522 } 523 temp.close(); 524 } 525 526 /** 527 * This method writes the new codestream to the file. 528 * 529 * @param fi The file to write the new codestream to 530 * 531 * @exception java.io.IOException If an I/O error ocurred. 532 * */ 533 private void writeNewCodestream(BufferedRandomAccessFile fi) 534 throws IOException{ 535 int i,t,p,tp; 536 int numTiles = tileParts.length; 537 int[][] packetHeaderLengths = new int[numTiles][maxtp]; 538 byte[] temp; 539 int length; 540 541 // Write main header up to SOT marker 542 fi.write(mainHeader,0,mainHeader.length); 543 544 // If PPM used write all packet headers in PPM markers 545 if(ppmUsed){ 546 ByteArrayOutputStream ppmMarkerSegment = 547 new ByteArrayOutputStream(); 548 int numPackets; 549 int totNumPackets; 550 int ppmIndex=0; 551 int ppmLength; 552 int pStart,pStop; 553 int prem[] = new int[numTiles]; 554 555 // Set number of remaining packets 556 for(t=0 ; t<numTiles ; t++) 557 prem[t] = packetHeaders[t].length; 558 559 // Calculate Nppm values 560 for(tp=0; tp<maxtp ; tp++){ 561 for(t=0 ; t<numTiles ; t++){ 562 563 if(tileParts[t].length > tp){ 564 totNumPackets = packetHeaders[t].length; 565 // Calculate number of packets in this tilepart 566 numPackets = 567 (tp == tileParts[t].length-1) ? 568 prem[t] : pptp; 569 570 pStart = totNumPackets - prem[t]; 571 pStop = pStart + numPackets; 572 573 // Calculate number of packet header bytes for this 574 // tile part 575 for(p=pStart ; p < pStop ; p++) 576 packetHeaderLengths[t][tp] += 577 packetHeaders[t][p].length; 578 579 prem[t] -= numPackets; 580 } 581 } 582 } 583 584 // Write first PPM marker 585 ppmMarkerSegment.write(Markers.PPM >>> 8); 586 ppmMarkerSegment.write(Markers.PPM); 587 ppmMarkerSegment.write(0); // Temporary Lppm value 588 ppmMarkerSegment.write(0); // Temporary Lppm value 589 ppmMarkerSegment.write(0); // zppm 590 ppmLength = 3; 591 ppmIndex++; 592 593 // Set number of remaining packets 594 for(t=0 ; t<numTiles ; t++) 595 prem[t] = packetHeaders[t].length; 596 597 // Write all PPM markers and information 598 for(tp=0; tp<maxtp ; tp++){ 599 for(t=0 ; t<numTiles ; t++){ 600 601 if(tileParts[t].length > tp){ 602 totNumPackets = packetHeaders[t].length; 603 604 // Calculate number of packets in this tilepart 605 numPackets = 606 (tp == tileParts[t].length-1) ? prem[t] : pptp; 607 608 pStart = totNumPackets - prem[t]; 609 pStop = pStart + numPackets; 610 611 // If Nppm value wont fit in current PPM marker segment 612 // write current PPM marker segment and start new 613 if(ppmLength + 4 > Markers.MAX_LPPM){ 614 // Write current PPM marker 615 temp = ppmMarkerSegment.toByteArray(); 616 length = temp.length-2; 617 temp[2] = (byte)(length >>> 8); 618 temp[3] = (byte)length; 619 fi.write(temp,0,length+2); 620 621 // Start new PPM marker segment 622 ppmMarkerSegment.reset(); 623 ppmMarkerSegment.write(Markers.PPM >>> 8); 624 ppmMarkerSegment.write(Markers.PPM); 625 ppmMarkerSegment.write(0); // Temporary Lppm value 626 ppmMarkerSegment.write(0); // Temporary Lppm value 627 ppmMarkerSegment.write(ppmIndex++); // zppm 628 ppmLength = 3; 629 } 630 631 // Write Nppm value 632 length = packetHeaderLengths[t][tp]; 633 ppmMarkerSegment.write(length>>>24); 634 ppmMarkerSegment.write(length>>>16); 635 ppmMarkerSegment.write(length>>>8); 636 ppmMarkerSegment.write(length); 637 ppmLength += 4; 638 639 // Write packet headers 640 for(p=pStart ; p < pStop ; p++){ 641 length = packetHeaders[t][p].length; 642 643 // If next packet header value wont fit in 644 // current PPM marker segment write current PPM 645 // marker segment and start new 646 if(ppmLength + length > Markers.MAX_LPPM){ 647 // Write current PPM marker 648 temp = ppmMarkerSegment.toByteArray(); 649 length = temp.length-2; 650 temp[2] = (byte)(length >>> 8); 651 temp[3] = (byte)length; 652 fi.write(temp,0,length+2); 653 654 // Start new PPM marker segment 655 ppmMarkerSegment.reset(); 656 ppmMarkerSegment.write(Markers.PPM >>> 8); 657 ppmMarkerSegment.write(Markers.PPM); 658 ppmMarkerSegment.write(0); // Temp Lppm value 659 ppmMarkerSegment.write(0); // Temp Lppm value 660 ppmMarkerSegment.write(ppmIndex++); // zppm 661 ppmLength = 3; 662 } 663 664 // write packet header 665 ppmMarkerSegment.write(packetHeaders[t][p],0, 666 packetHeaders[t][p].length); 667 ppmLength+=packetHeaders[t][p].length; 668 } 669 prem[t]-=numPackets; 670 } 671 } 672 } 673 // Write last PPM marker segment 674 temp = ppmMarkerSegment.toByteArray(); 675 length = temp.length-2; 676 temp[2] = (byte)(length >>> 8); 677 temp[3] = (byte)length; 678 fi.write(temp,0,length+2); 679 } 680 681 // Write tile parts interleaved 682 for(tp=0;tp<maxtp;tp++) 683 for(t=0 ; t<nt ;t++){ 684 if(tileParts[t].length >= tp){ 685 temp = tileParts[t][tp]; 686 length = temp.length; 687 fi.write(temp,0,length); 688 } 689 } 690 fi.writeShort(Markers.EOC); 691 } 692} 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713