Coverage Report - org.jaudiotagger.audio.ogg.util.OggPageHeader
 
Classes in this File Line Coverage Branch Coverage Complexity
OggPageHeader
97%
64/66
86%
12/14
0
OggPageHeader$HeaderTypeFlag
100%
9/9
N/A
0
OggPageHeader$PacketStartAndLength
62%
8/13
N/A
0
 
 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  
 
 24  
 import java.io.IOException;
 25  
 import java.io.RandomAccessFile;
 26  
 import java.util.ArrayList;
 27  
 import java.util.Arrays;
 28  
 import java.util.List;
 29  
 import java.util.logging.Logger;
 30  
 
 31  
 
 32  
 /**
 33  
  * $Id: OggPageHeader.java,v 1.14 2008/07/21 10:48:37 paultaylor Exp $
 34  
  * <p/>
 35  
  * reference:http://xiph.org/ogg/doc/framing.html
 36  
  *
 37  
  * @author Raphael Slinckx (KiKiDonK)
 38  
  * @version 16 d�cembre 2003
 39  
  */
 40  
 public class OggPageHeader
 41  
 {
 42  
     // Logger Object
 43  8
     public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.ogg.atom");
 44  
 
 45  
     //Capture pattern at start of header
 46  8
     public static final byte[] CAPTURE_PATTERN = {'O', 'g', 'g', 'S'};
 47  
 
 48  
     //Ogg Page header is always 27 bytes plus the size of the segment table which is variable
 49  
     public static final int OGG_PAGE_HEADER_FIXED_LENGTH = 27;
 50  
 
 51  
     //Can have upto 255 segments in a page
 52  
     public static final int MAXIMUM_NO_OF_SEGMENT_SIZE = 255;
 53  
 
 54  
     //Each segment can be upto 255 bytes
 55  
     public static final int MAXIMUM_SEGMENT_SIZE = 255;
 56  
 
 57  
     //Maximum size of pageheader (27 + 255 = 282)
 58  
     public static final int MAXIMUM_PAGE_HEADER_SIZE = OGG_PAGE_HEADER_FIXED_LENGTH + MAXIMUM_NO_OF_SEGMENT_SIZE;
 59  
 
 60  
     //Maximum size of page data following the page header (255 * 255 = 65025)
 61  
     public static final int MAXIMUM_PAGE_DATA_SIZE = MAXIMUM_NO_OF_SEGMENT_SIZE * MAXIMUM_SEGMENT_SIZE;
 62  
 
 63  
     //Maximum size of page includes header and data (282 + 65025 = 65307 bytes)
 64  
     public static final int MAXIMUM_PAGE_SIZE = MAXIMUM_PAGE_HEADER_SIZE + MAXIMUM_PAGE_DATA_SIZE;
 65  
 
 66  
     //Starting positions of the various attributes
 67  
     public static final int FIELD_CAPTURE_PATTERN_POS = 0;
 68  
     public static final int FIELD_STREAM_STRUCTURE_VERSION_POS = 4;
 69  
     public static final int FIELD_HEADER_TYPE_FLAG_POS = 5;
 70  
     public static final int FIELD_ABSOLUTE_GRANULE_POS = 6;
 71  
     public static final int FIELD_STREAM_SERIAL_NO_POS = 14;
 72  
     public static final int FIELD_PAGE_SEQUENCE_NO_POS = 18;
 73  
     public static final int FIELD_PAGE_CHECKSUM_POS = 22;
 74  
     public static final int FIELD_PAGE_SEGMENTS_POS = 26;
 75  
     public static final int FIELD_SEGMENT_TABLE_POS = 27;
 76  
 
 77  
     //Length of various attributes
 78  
     public static final int FIELD_CAPTURE_PATTERN_LENGTH = 4;
 79  
     public static final int FIELD_STREAM_STRUCTURE_VERSION_LENGTH = 1;
 80  
     public static final int FIELD_HEADER_TYPE_FLAG_LENGTH = 1;
 81  
     public static final int FIELD_ABSOLUTE_GRANULE_LENGTH = 8;
 82  
     public static final int FIELD_STREAM_SERIAL_NO_LENGTH = 4;
 83  
     public static final int FIELD_PAGE_SEQUENCE_NO_LENGTH = 4;
 84  
     public static final int FIELD_PAGE_CHECKSUM_LENGTH = 4;
 85  
     public static final int FIELD_PAGE_SEGMENTS_LENGTH = 1;
 86  
 
 87  
     private byte[] rawHeaderData;
 88  
     private double absoluteGranulePosition;
 89  
     private int checksum;
 90  
     private byte headerTypeFlag;
 91  
 
 92  4414
     private boolean isValid = false;
 93  4414
     private int pageLength = 0;
 94  
     private int pageSequenceNumber, streamSerialNumber;
 95  
     private byte[] segmentTable;
 96  
 
 97  4414
     private List<PacketStartAndLength> packetList = new ArrayList<PacketStartAndLength>();
 98  4414
     private boolean lastPacketIncomplete = false;
 99  
 
 100  
 
 101  
     public static OggPageHeader read(RandomAccessFile raf) throws IOException, CannotReadException
 102  
     {
 103  4371
         long start = raf.getFilePointer();
 104  4371
         logger.info("Trying to read OggPage at:" + start);
 105  
 
 106  4371
         byte[] b = new byte[OggPageHeader.CAPTURE_PATTERN.length];
 107  4371
         raf.read(b);
 108  4371
         if (!(Arrays.equals(b, OggPageHeader.CAPTURE_PATTERN)))
 109  
         {
 110  0
             throw new CannotReadException("OggS Header could not be found, not an ogg stream:" + new String(b));
 111  
         }
 112  
 
 113  4371
         raf.seek(start + OggPageHeader.FIELD_PAGE_SEGMENTS_POS);
 114  4371
         int pageSegments = raf.readByte() & 0xFF; //unsigned
 115  4371
         raf.seek(start);
 116  
 
 117  4371
         b = new byte[OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageSegments];
 118  4371
         raf.read(b);
 119  
 
 120  
 
 121  4371
         OggPageHeader pageHeader = new OggPageHeader(b);
 122  
 
 123  
         //Now just after PageHeader, ready for Packet Data
 124  4371
         return pageHeader;
 125  
     }
 126  
 
 127  
     public OggPageHeader(byte[] b)
 128  4414
     {
 129  4414
         this.rawHeaderData = b;
 130  4414
         int streamStructureRevision = b[FIELD_STREAM_STRUCTURE_VERSION_POS];
 131  4414
         headerTypeFlag = b[FIELD_HEADER_TYPE_FLAG_POS];
 132  4414
         if (streamStructureRevision == 0)
 133  
         {
 134  4414
             this.absoluteGranulePosition = 0;
 135  39726
             for (int i = 0; i < FIELD_ABSOLUTE_GRANULE_LENGTH; i++)
 136  
             {
 137  35312
                 this.absoluteGranulePosition += u(b[i + FIELD_ABSOLUTE_GRANULE_POS]) * Math.pow(2, 8 * i);
 138  
             }
 139  
 
 140  4414
             streamSerialNumber = Utils.getIntLE(b, FIELD_STREAM_SERIAL_NO_POS, 17);
 141  4414
             pageSequenceNumber = Utils.getIntLE(b, FIELD_PAGE_SEQUENCE_NO_POS, 21);
 142  4414
             checksum = Utils.getIntLE(b, FIELD_PAGE_CHECKSUM_POS, 25);
 143  4414
             int pageSegments = u(b[FIELD_PAGE_SEGMENTS_POS]);
 144  
 
 145  4414
             this.segmentTable = new byte[b.length - OGG_PAGE_HEADER_FIXED_LENGTH];
 146  4414
             int packetLength = 0;
 147  4414
             Integer segmentLength = null;
 148  161241
             for (int i = 0; i < segmentTable.length; i++)
 149  
             {
 150  156827
                 segmentTable[i] = b[OGG_PAGE_HEADER_FIXED_LENGTH + i];
 151  156827
                 segmentLength = u(segmentTable[i]);
 152  156827
                 this.pageLength += segmentLength;
 153  156827
                 packetLength += segmentLength;
 154  
 
 155  156827
                 if (segmentLength < MAXIMUM_SEGMENT_SIZE)
 156  
                 {
 157  54744
                     packetList.add(new PacketStartAndLength(pageLength - packetLength, packetLength));
 158  54744
                     packetLength = 0;
 159  
                 }
 160  
             }
 161  
 
 162  
             //If last segment value is 255 this packet continues onto next page
 163  
             //and will not have been added to the packetStartAndEnd list yet
 164  4414
             if (segmentLength == MAXIMUM_SEGMENT_SIZE)
 165  
             {
 166  2927
                 packetList.add(new PacketStartAndLength(pageLength - packetLength, packetLength));
 167  2927
                 lastPacketIncomplete = true;
 168  
             }
 169  4414
             isValid = true;
 170  
         }
 171  
 
 172  4414
         logger.info("Constructed OggPage:" + this.toString());
 173  4414
     }
 174  
 
 175  
     private int u(int i)
 176  
     {
 177  196553
         return i & 0xFF;
 178  
     }
 179  
 
 180  
     /**
 181  
      * @return true if the last packet on this page extends to the next page
 182  
      */
 183  
     public boolean isLastPacketIncomplete()
 184  
     {
 185  6708
         return lastPacketIncomplete;
 186  
     }
 187  
 
 188  
     public double getAbsoluteGranulePosition()
 189  
     {
 190  43
         logger.fine("Number Of Samples: " + absoluteGranulePosition);
 191  43
         return this.absoluteGranulePosition;
 192  
     }
 193  
 
 194  
 
 195  
     public int getCheckSum()
 196  
     {
 197  10
         return checksum;
 198  
     }
 199  
 
 200  
 
 201  
     public byte getHeaderType()
 202  
     {
 203  9
         return headerTypeFlag;
 204  
     }
 205  
 
 206  
 
 207  
     public int getPageLength()
 208  
     {
 209  4200
         logger.fine("This page length: " + pageLength);
 210  4200
         return this.pageLength;
 211  
     }
 212  
 
 213  
     public int getPageSequence()
 214  
     {
 215  6598
         return pageSequenceNumber;
 216  
     }
 217  
 
 218  
     public int getSerialNumber()
 219  
     {
 220  6521
         return streamSerialNumber;
 221  
     }
 222  
 
 223  
     public byte[] getSegmentTable()
 224  
     {
 225  88
         return this.segmentTable;
 226  
     }
 227  
 
 228  
     public boolean isValid()
 229  
     {
 230  0
         return isValid;
 231  
     }
 232  
 
 233  
     /**
 234  
      * @return a list of packet start position and size within this page.
 235  
      */
 236  
     public List<PacketStartAndLength> getPacketList()
 237  
     {
 238  7062
         return packetList;
 239  
     }
 240  
 
 241  
     /**
 242  
      * @return the raw header data that this pageheader is derived from
 243  
      */
 244  
     public byte[] getRawHeaderData()
 245  
     {
 246  3711
         return rawHeaderData;
 247  
     }
 248  
 
 249  
     public String toString()
 250  
     {
 251  6511
         String out = "Ogg Page Header:isValid:" + isValid + ":type:" + headerTypeFlag + ":oggPageHeaderLength:" + rawHeaderData.length + ":length:" + pageLength + ":seqNo:" + getPageSequence() + ":packetIncomplete:" + isLastPacketIncomplete() + ":serNum:" + this.getSerialNumber();
 252  
 
 253  6511
         for (PacketStartAndLength packet : getPacketList())
 254  
         {
 255  90151
             out += packet.toString();
 256  
         }
 257  6511
         return out;
 258  
     }
 259  
 
 260  
     /**
 261  
      * Within the page specifies the start and length of each packet
 262  
      * in the page offset from the end of the pageheader (after the segment table)
 263  
      */
 264  
     public static class PacketStartAndLength
 265  
     {
 266  57671
         private Integer startPosition = 0;
 267  57671
         private Integer length = 0;
 268  
 
 269  
         public PacketStartAndLength(int startPosition, int length)
 270  57671
         {
 271  57671
             this.startPosition = startPosition;
 272  57671
             this.length = length;
 273  57671
         }
 274  
 
 275  
         public int getStartPosition()
 276  
         {
 277  0
             return startPosition;
 278  
         }
 279  
 
 280  
         public void setStartPosition(int startPosition)
 281  
         {
 282  0
             this.startPosition = startPosition;
 283  0
         }
 284  
 
 285  
         public int getLength()
 286  
         {
 287  713
             return length;
 288  
         }
 289  
 
 290  
         public void setLength(int length)
 291  
         {
 292  0
             this.length = length;
 293  0
         }
 294  
 
 295  
         public String toString()
 296  
         {
 297  90151
             return "NextPkt(start:" + startPosition + ":length:" + length + "),";
 298  
         }
 299  
     }
 300  
 
 301  
     /**
 302  
      * This represents all the flags that can be set in the headerType field.
 303  
      * Note these values can be ORED together. For example the last packet in
 304  
      * a file would normally have a value of 0x5 because both the CONTINUED_PACKET
 305  
      * bit and the END_OF_BITSTREAM bit would be set.
 306  
      */
 307  2
     public static enum HeaderTypeFlag
 308  
     {
 309  2
         FRESH_PACKET((byte) 0x0),
 310  2
         CONTINUED_PACKET((byte) 0x1),
 311  2
         START_OF_BITSTREAM((byte) 0x2),
 312  2
         END_OF_BITSTREAM((byte) 0x4);
 313  
 
 314  
         byte fileValue;
 315  
 
 316  
         HeaderTypeFlag(byte fileValue)
 317  8
         {
 318  8
             this.fileValue = fileValue;
 319  8
         }
 320  
 
 321  
         /**
 322  
          * @return the value that should be written to file to enable this flag
 323  
          */
 324  
         public byte getFileValue()
 325  
         {
 326  35
             return fileValue;
 327  
         }
 328  
     }
 329  
 }
 330