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