Coverage Report - org.jaudiotagger.audio.ogg.util.OggPageHeader
 
Classes in this File Line Coverage Branch Coverage Complexity
OggPageHeader
96%
78/81
80%
16/20
1.5
OggPageHeader$HeaderTypeFlag
100%
9/9
N/A
1.5
OggPageHeader$PacketStartAndLength
61%
8/13
N/A
1.5
 
 1  
 /*
 2  
  * Entagged Audio Tag library
 3  
  * Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
 4  
  * 
 5  
  * This library is free software; you can redistribute it and/or
 6  
  * modify it under the terms of the GNU Lesser General Public
 7  
  * License as published by the Free Software Foundation; either
 8  
  * version 2.1 of the License, or (at your option) any later version.
 9  
  *  
 10  
  * This library is distributed in the hope that it will be useful,
 11  
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 13  
  * Lesser General Public License for more details.
 14  
  * 
 15  
  * You should have received a copy of the GNU Lesser General Public
 16  
  * License along with this library; if not, write to the Free Software
 17  
  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 18  
  */
 19  
 package org.jaudiotagger.audio.ogg.util;
 20  
 
 21  
 import org.jaudiotagger.audio.exceptions.CannotReadException;
 22  
 import org.jaudiotagger.audio.generic.Utils;
 23  
 import org.jaudiotagger.logging.ErrorMessage;
 24  
 
 25  
 import java.io.IOException;
 26  
 import java.io.RandomAccessFile;
 27  
 import java.util.ArrayList;
 28  
 import java.util.Arrays;
 29  
 import java.util.List;
 30  
 import java.util.logging.Logger;
 31  
 import java.util.logging.Level;
 32  
 import java.nio.ByteBuffer;
 33  
 
 34  
 
 35  
 /**
 36  
  * $Id: OggPageHeader.java 822 2009-10-27 08:52:55Z paultaylor $
 37  
  * <p/>
 38  
  * reference:http://xiph.org/ogg/doc/framing.html
 39  
  *
 40  
  * @author Raphael Slinckx (KiKiDonK)
 41  
  * @version 16 d�cembre 2003
 42  
  */
 43  
 public class OggPageHeader
 44  
 {
 45  
     // Logger Object
 46  4
     public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.ogg.atom");
 47  
 
 48  
     //Capture pattern at start of header
 49  4
     public static final byte[] CAPTURE_PATTERN = {'O', 'g', 'g', 'S'};
 50  
 
 51  
     //Ogg Page header is always 27 bytes plus the size of the segment table which is variable
 52  
     public static final int OGG_PAGE_HEADER_FIXED_LENGTH = 27;
 53  
 
 54  
     //Can have upto 255 segments in a page
 55  
     public static final int MAXIMUM_NO_OF_SEGMENT_SIZE = 255;
 56  
 
 57  
     //Each segment can be upto 255 bytes
 58  
     public static final int MAXIMUM_SEGMENT_SIZE = 255;
 59  
 
 60  
     //Maximum size of pageheader (27 + 255 = 282)
 61  
     public static final int MAXIMUM_PAGE_HEADER_SIZE = OGG_PAGE_HEADER_FIXED_LENGTH + MAXIMUM_NO_OF_SEGMENT_SIZE;
 62  
 
 63  
     //Maximum size of page data following the page header (255 * 255 = 65025)
 64  
     public static final int MAXIMUM_PAGE_DATA_SIZE = MAXIMUM_NO_OF_SEGMENT_SIZE * MAXIMUM_SEGMENT_SIZE;
 65  
 
 66  
     //Maximum size of page includes header and data (282 + 65025 = 65307 bytes)
 67  
     public static final int MAXIMUM_PAGE_SIZE = MAXIMUM_PAGE_HEADER_SIZE + MAXIMUM_PAGE_DATA_SIZE;
 68  
 
 69  
     //Starting positions of the various attributes
 70  
     public static final int FIELD_CAPTURE_PATTERN_POS = 0;
 71  
     public static final int FIELD_STREAM_STRUCTURE_VERSION_POS = 4;
 72  
     public static final int FIELD_HEADER_TYPE_FLAG_POS = 5;
 73  
     public static final int FIELD_ABSOLUTE_GRANULE_POS = 6;
 74  
     public static final int FIELD_STREAM_SERIAL_NO_POS = 14;
 75  
     public static final int FIELD_PAGE_SEQUENCE_NO_POS = 18;
 76  
     public static final int FIELD_PAGE_CHECKSUM_POS = 22;
 77  
     public static final int FIELD_PAGE_SEGMENTS_POS = 26;
 78  
     public static final int FIELD_SEGMENT_TABLE_POS = 27;
 79  
 
 80  
     //Length of various attributes
 81  
     public static final int FIELD_CAPTURE_PATTERN_LENGTH = 4;
 82  
     public static final int FIELD_STREAM_STRUCTURE_VERSION_LENGTH = 1;
 83  
     public static final int FIELD_HEADER_TYPE_FLAG_LENGTH = 1;
 84  
     public static final int FIELD_ABSOLUTE_GRANULE_LENGTH = 8;
 85  
     public static final int FIELD_STREAM_SERIAL_NO_LENGTH = 4;
 86  
     public static final int FIELD_PAGE_SEQUENCE_NO_LENGTH = 4;
 87  
     public static final int FIELD_PAGE_CHECKSUM_LENGTH = 4;
 88  
     public static final int FIELD_PAGE_SEGMENTS_LENGTH = 1;
 89  
 
 90  
     private byte[] rawHeaderData;
 91  
     private double absoluteGranulePosition;
 92  
     private int checksum;
 93  
     private byte headerTypeFlag;
 94  
 
 95  18499
     private boolean isValid = false;
 96  18499
     private int pageLength = 0;
 97  
     private int pageSequenceNumber, streamSerialNumber;
 98  
     private byte[] segmentTable;
 99  
 
 100  18499
     private List<PacketStartAndLength> packetList = new ArrayList<PacketStartAndLength>();
 101  18499
     private boolean lastPacketIncomplete = false;
 102  
 
 103  
     /**
 104  
      * Read next PageHeader from Buffer
 105  
      *
 106  
      * @param byteBuffer
 107  
      * @return
 108  
      * @throws IOException
 109  
      * @throws CannotReadException
 110  
      */
 111  
     public static OggPageHeader read(ByteBuffer byteBuffer) throws IOException, CannotReadException
 112  
        {
 113  
            //byteBuffer
 114  14234
            int start = byteBuffer.position();
 115  14234
            logger.fine("Trying to read OggPage at:" + start);
 116  
 
 117  14234
            byte[] b = new byte[OggPageHeader.CAPTURE_PATTERN.length];
 118  14234
            byteBuffer.get(b);
 119  14234
            if (!(Arrays.equals(b, OggPageHeader.CAPTURE_PATTERN)))
 120  
            {
 121  0
                throw new CannotReadException(ErrorMessage.OGG_HEADER_CANNOT_BE_FOUND.getMsg(new String(b)));
 122  
            }
 123  
 
 124  14234
            byteBuffer.position(start + OggPageHeader.FIELD_PAGE_SEGMENTS_POS);
 125  14234
            int pageSegments = byteBuffer.get() & 0xFF; //unsigned
 126  14234
            byteBuffer.position(start);
 127  
 
 128  14234
            b = new byte[OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageSegments];
 129  14234
            byteBuffer.get(b);
 130  14234
            OggPageHeader pageHeader = new OggPageHeader(b);
 131  
 
 132  
            //Now just after PageHeader, ready for Packet Data
 133  14234
            return pageHeader;
 134  
        }
 135  
 
 136  
     /**
 137  
      * Read next PageHeader from file
 138  
      * @param raf
 139  
      * @return
 140  
      * @throws IOException
 141  
      * @throws CannotReadException
 142  
      */
 143  
     public static OggPageHeader read(RandomAccessFile raf) throws IOException, CannotReadException
 144  
     {
 145  4093
         long start = raf.getFilePointer();
 146  4093
         logger.fine("Trying to read OggPage at:" + start);
 147  
 
 148  4093
         byte[] b = new byte[OggPageHeader.CAPTURE_PATTERN.length];
 149  4093
         raf.read(b);
 150  4093
         if (!(Arrays.equals(b, OggPageHeader.CAPTURE_PATTERN)))
 151  
         {
 152  0
             throw new CannotReadException(ErrorMessage.OGG_HEADER_CANNOT_BE_FOUND.getMsg(new String(b)));
 153  
         }
 154  
 
 155  4093
         raf.seek(start + OggPageHeader.FIELD_PAGE_SEGMENTS_POS);
 156  4093
         int pageSegments = raf.readByte() & 0xFF; //unsigned
 157  4093
         raf.seek(start);
 158  
 
 159  4093
         b = new byte[OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageSegments];
 160  4093
         raf.read(b);
 161  
 
 162  
 
 163  4093
         OggPageHeader pageHeader = new OggPageHeader(b);
 164  
 
 165  
         //Now just after PageHeader, ready for Packet Data
 166  4093
         return pageHeader;
 167  
     }
 168  
 
 169  
     public OggPageHeader(byte[] b)
 170  18499
     {
 171  18499
         this.rawHeaderData = b;
 172  18499
         int streamStructureRevision = b[FIELD_STREAM_STRUCTURE_VERSION_POS];
 173  18499
         headerTypeFlag = b[FIELD_HEADER_TYPE_FLAG_POS];
 174  18499
         if (streamStructureRevision == 0)
 175  
         {
 176  18499
             this.absoluteGranulePosition = 0;
 177  166491
             for (int i = 0; i < FIELD_ABSOLUTE_GRANULE_LENGTH; i++)
 178  
             {
 179  147992
                 this.absoluteGranulePosition += u(b[i + FIELD_ABSOLUTE_GRANULE_POS]) * Math.pow(2, 8 * i);
 180  
             }
 181  
 
 182  18499
             streamSerialNumber = Utils.getIntLE(b, FIELD_STREAM_SERIAL_NO_POS, 17);
 183  18499
             pageSequenceNumber = Utils.getIntLE(b, FIELD_PAGE_SEQUENCE_NO_POS, 21);
 184  18499
             checksum = Utils.getIntLE(b, FIELD_PAGE_CHECKSUM_POS, 25);
 185  18499
             int pageSegments = u(b[FIELD_PAGE_SEGMENTS_POS]);
 186  
 
 187  18499
             this.segmentTable = new byte[b.length - OGG_PAGE_HEADER_FIXED_LENGTH];
 188  18499
             int packetLength = 0;
 189  18499
             Integer segmentLength = null;
 190  730513
             for (int i = 0; i < segmentTable.length; i++)
 191  
             {
 192  712014
                 segmentTable[i] = b[OGG_PAGE_HEADER_FIXED_LENGTH + i];
 193  712014
                 segmentLength = u(segmentTable[i]);
 194  712014
                 this.pageLength += segmentLength;
 195  712014
                 packetLength += segmentLength;
 196  
 
 197  712014
                 if (segmentLength < MAXIMUM_SEGMENT_SIZE)
 198  
                 {
 199  327048
                     packetList.add(new PacketStartAndLength(pageLength - packetLength, packetLength));
 200  327048
                     packetLength = 0;
 201  
                 }
 202  
             }
 203  
 
 204  
             //If last segment value is 255 this packet continues onto next page
 205  
             //and will not have been added to the packetStartAndEnd list yet
 206  18499
             if(segmentLength!=null)
 207  
             {
 208  18499
                 if (segmentLength == MAXIMUM_SEGMENT_SIZE)
 209  
                 {
 210  9330
                     packetList.add(new PacketStartAndLength(pageLength - packetLength, packetLength));
 211  9330
                     lastPacketIncomplete = true;
 212  
                 }
 213  
             }
 214  18499
             isValid = true;
 215  
         }
 216  
 
 217  18499
         if(logger.isLoggable(Level.CONFIG))
 218  
         {
 219  1242
             logger.config("Constructed OggPage:" + this.toString());
 220  
         }
 221  18499
     }
 222  
 
 223  
     private int u(int i)
 224  
     {
 225  878505
         return i & 0xFF;
 226  
     }
 227  
 
 228  
     /**
 229  
      * @return true if the last packet on this page extends to the next page
 230  
      */
 231  
     public boolean isLastPacketIncomplete()
 232  
     {
 233  4579
         return lastPacketIncomplete;
 234  
     }
 235  
 
 236  
     public double getAbsoluteGranulePosition()
 237  
     {
 238  172
         logger.fine("Number Of Samples: " + absoluteGranulePosition);
 239  172
         return this.absoluteGranulePosition;
 240  
     }
 241  
 
 242  
 
 243  
     public int getCheckSum()
 244  
     {
 245  40
         return checksum;
 246  
     }
 247  
 
 248  
 
 249  
     public byte getHeaderType()
 250  
     {
 251  36
         return headerTypeFlag;
 252  
     }
 253  
 
 254  
 
 255  
     public int getPageLength()
 256  
     {
 257  45843
         logger.fine("This page length: " + pageLength);
 258  45843
         return this.pageLength;
 259  
     }
 260  
 
 261  
     public int getPageSequence()
 262  
     {
 263  4081
         return pageSequenceNumber;
 264  
     }
 265  
 
 266  
     public int getSerialNumber()
 267  
     {
 268  3698
         return streamSerialNumber;
 269  
     }
 270  
 
 271  
     public byte[] getSegmentTable()
 272  
     {
 273  356
         return this.segmentTable;
 274  
     }
 275  
 
 276  
     public boolean isValid()
 277  
     {
 278  0
         return isValid;
 279  
     }
 280  
 
 281  
     /**
 282  
      * @return a list of packet start position and size within this page.
 283  
      */
 284  
     public List<PacketStartAndLength> getPacketList()
 285  
     {
 286  6574
         return packetList;
 287  
     }
 288  
 
 289  
     /**
 290  
      * @return the raw header data that this pageheader is derived from
 291  
      */
 292  
     public byte[] getRawHeaderData()
 293  
     {
 294  28424
         return rawHeaderData;
 295  
     }
 296  
 
 297  
     public String toString()
 298  
     {
 299  3658
         String out = "Ogg Page Header:isValid:" + isValid + ":type:" + headerTypeFlag + ":oggPageHeaderLength:" + rawHeaderData.length + ":length:" + pageLength + ":seqNo:" + getPageSequence() + ":packetIncomplete:" + isLastPacketIncomplete() + ":serNum:" + this.getSerialNumber();
 300  
 
 301  3658
         for (PacketStartAndLength packet : getPacketList())
 302  
         {
 303  38024
             out += packet.toString();
 304  
         }
 305  3658
         return out;
 306  
     }
 307  
 
 308  
     /**
 309  
      * Within the page specifies the start and length of each packet
 310  
      * in the page offset from the end of the pageheader (after the segment table)
 311  
      */
 312  
     public static class PacketStartAndLength
 313  
     {
 314  336378
         private Integer startPosition = 0;
 315  336378
         private Integer length = 0;
 316  
 
 317  
         public PacketStartAndLength(int startPosition, int length)
 318  336378
         {
 319  336378
             this.startPosition = startPosition;
 320  336378
             this.length = length;
 321  336378
         }
 322  
 
 323  
         public int getStartPosition()
 324  
         {
 325  0
             return startPosition;
 326  
         }
 327  
 
 328  
         public void setStartPosition(int startPosition)
 329  
         {
 330  0
             this.startPosition = startPosition;
 331  0
         }
 332  
 
 333  
         public int getLength()
 334  
         {
 335  4289
             return length;
 336  
         }
 337  
 
 338  
         public void setLength(int length)
 339  
         {
 340  0
             this.length = length;
 341  0
         }
 342  
 
 343  
         public String toString()
 344  
         {
 345  38024
             return "NextPkt(start:" + startPosition + ":length:" + length + "),";
 346  
         }
 347  
     }
 348  
 
 349  
     /**
 350  
      * This represents all the flags that can be set in the headerType field.
 351  
      * Note these values can be ORED together. For example the last packet in
 352  
      * a file would normally have a value of 0x5 because both the CONTINUED_PACKET
 353  
      * bit and the END_OF_BITSTREAM bit would be set.
 354  
      */
 355  4
     public static enum HeaderTypeFlag
 356  
     {
 357  4
         FRESH_PACKET((byte) 0x0),
 358  4
         CONTINUED_PACKET((byte) 0x1),
 359  4
         START_OF_BITSTREAM((byte) 0x2),
 360  4
         END_OF_BITSTREAM((byte) 0x4);
 361  
 
 362  
         byte fileValue;
 363  
 
 364  
         HeaderTypeFlag(byte fileValue)
 365  16
         {
 366  16
             this.fileValue = fileValue;
 367  16
         }
 368  
 
 369  
         /**
 370  
          * @return the value that should be written to file to enable this flag
 371  
          */
 372  
         public byte getFileValue()
 373  
         {
 374  138
             return fileValue;
 375  
         }
 376  
     }
 377  
 }
 378