FileDocCategorySizeDatePackage
OggVorbisTagReader.javaAPI DocJaudiotagger 2.0.428714Wed Jun 08 11:59:18 BST 2011org.jaudiotagger.audio.ogg

OggVorbisTagReader

public class OggVorbisTagReader extends Object
Read Vorbis Comment Tag within ogg

Vorbis is the audiostream within an ogg file, Vorbis uses VorbisComments as its tag

Fields Summary
public static Logger
logger
private org.jaudiotagger.tag.vorbiscomment.VorbisCommentReader
vorbisCommentReader
Constructors Summary
public OggVorbisTagReader()


     
    
        vorbisCommentReader = new VorbisCommentReader();
    
Methods Summary
private byte[]convertToVorbisCommentPacket(org.jaudiotagger.audio.ogg.util.OggPageHeader startVorbisCommentPage, java.io.RandomAccessFile raf)
The Vorbis Comment may span multiple pages so we we need to identify the pages they contain and then extract the packet data from the pages

param
startVorbisCommentPage
param
raf
throws
org.jaudiotagger.audio.exceptions.CannotReadException
throws
java.io.IOException
return

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        byte[] b = new byte[startVorbisCommentPage.getPacketList().get(0).getLength() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH)];
        raf.read(b);
        baos.write(b);

        //Because there is at least one other packet (SetupHeaderPacket) this means the Comment Packet has finished
        //on this page so thats all we need and we can return
        if (startVorbisCommentPage.getPacketList().size() > 1)
        {
            logger.config("Comments finish on 2nd Page because there is another packet on this page");
            return baos.toByteArray();
        }

        //There is only the VorbisComment packet on page if it has completed on this page we can return
        if (!startVorbisCommentPage.isLastPacketIncomplete())
        {
            logger.config("Comments finish on 2nd Page because this packet is complete");
            return baos.toByteArray();
        }

        //The VorbisComment extends to the next page, so should be at end of page already
        //so carry on reading pages until we get to the end of comment
        while (true)
        {
            logger.config("Reading next page");
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
            raf.read(b);
            baos.write(b);

            //Because there is at least one other packet (SetupHeaderPacket) this means the Comment Packet has finished
            //on this page so thats all we need and we can return
            if (nextPageHeader.getPacketList().size() > 1)
            {
                logger.config("Comments finish on Page because there is another packet on this page");
                return baos.toByteArray();
            }

            //There is only the VorbisComment packet on page if it has completed on this page we can return
            if (!nextPageHeader.isLastPacketIncomplete())
            {
                logger.config("Comments finish on Page because this packet is complete");
                return baos.toByteArray();
            }
        }
    
public byte[]convertToVorbisSetupHeaderPacket(long fileOffsetOfStartingOggPage, java.io.RandomAccessFile raf)
The Vorbis Setup Header may span multiple(2) pages, athough it doesnt normally. We pass the start of the file offset of the OggPage it belongs on, it probably won't be first packet.

param
fileOffsetOfStartingOggPage
param
raf
throws
org.jaudiotagger.audio.exceptions.CannotReadException
throws
java.io.IOException
return

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        //Seek to specified offset
        raf.seek(fileOffsetOfStartingOggPage);

        //Read Page
        OggPageHeader setupPageHeader = OggPageHeader.read(raf);

        //Assume that if multiple packets first packet is VorbisComment and second packet
        //is setupheader
        if (setupPageHeader.getPacketList().size() > 1)
        {
            raf.skipBytes(setupPageHeader.getPacketList().get(0).getLength());
        }

        //Now should be at start of next packet, check this is the vorbis setup header
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
        raf.read(b);
        if (!isVorbisSetupHeader(b))
        {
            throw new CannotReadException("Unable to find setup header(2), unable to write ogg file");
        }

        //Go back to start of setupheader data
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));

        //Read data
        if (setupPageHeader.getPacketList().size() > 1)
        {
            b = new byte[setupPageHeader.getPacketList().get(1).getLength()];
            raf.read(b);
            baos.write(b);
        }
        else
        {
            b = new byte[setupPageHeader.getPacketList().get(0).getLength()];
            raf.read(b);
            baos.write(b);
        }

        //Return Data
        if (!setupPageHeader.isLastPacketIncomplete() || setupPageHeader.getPacketList().size() > 2)
        {
            logger.config("Setupheader finishes on this page");
            return baos.toByteArray();
        }

        //The Setupheader extends to the next page, so should be at end of page already
        //so carry on reading pages until we get to the end of comment
        while (true)
        {
            logger.config("Reading another page");
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
            raf.read(b);
            baos.write(b);

            //Because there is at least one other packet this means the Setupheader Packet has finished
            //on this page so thats all we need and we can return
            if (nextPageHeader.getPacketList().size() > 1)
            {
                logger.config("Setupheader finishes on this page");
                return baos.toByteArray();
            }

            //There is only the Setupheader packet on page if it has completed on this page we can return
            if (!nextPageHeader.isLastPacketIncomplete())
            {
                logger.config("Setupheader finish on Page because this packet is complete");
                return baos.toByteArray();
            }
        }
    
public byte[]convertToVorbisSetupHeaderPacketAndAdditionalPackets(long fileOffsetOfStartingOggPage, java.io.RandomAccessFile raf)
The Vorbis Setup Header may span multiple(2) pages, athough it doesnt normally. We pass the start of the file offset of the OggPage it belongs on, it probably won't be first packet, also returns any addditional packets that immediately follow the setup header in original file

param
fileOffsetOfStartingOggPage
param
raf
throws
org.jaudiotagger.audio.exceptions.CannotReadException
throws
java.io.IOException
return

        ByteArrayOutputStream baos = new ByteArrayOutputStream();

        //Seek to specified offset
        raf.seek(fileOffsetOfStartingOggPage);

        //Read Page
        OggPageHeader setupPageHeader = OggPageHeader.read(raf);

        //Assume that if multiple packets first packet is VorbisComment and second packet
        //is setupheader
        if (setupPageHeader.getPacketList().size() > 1)
        {
            raf.skipBytes(setupPageHeader.getPacketList().get(0).getLength());
        }

        //Now should be at start of next packet, check this is the vorbis setup header
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
        raf.read(b);
        if (!isVorbisSetupHeader(b))
        {
            throw new CannotReadException("Unable to find setup header(2), unable to write ogg file");
        }

        //Go back to start of setupheader data
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));

        //Read data
        if (setupPageHeader.getPacketList().size() > 1)
        {
            b = new byte[setupPageHeader.getPacketList().get(1).getLength()];
            raf.read(b);
            baos.write(b);
        }
        else
        {
            b = new byte[setupPageHeader.getPacketList().get(0).getLength()];
            raf.read(b);
            baos.write(b);
        }

        //Return Data
        if (!setupPageHeader.isLastPacketIncomplete() || setupPageHeader.getPacketList().size() > 2)
        {
            logger.config("Setupheader finishes on this page");
            if (setupPageHeader.getPacketList().size() > 2)
            {
                for (int i = 2; i < setupPageHeader.getPacketList().size(); i++)
                {
                    b = new byte[setupPageHeader.getPacketList().get(i).getLength()];
                    raf.read(b);
                    baos.write(b);
                }
            }
            return baos.toByteArray();
        }

        //The Setupheader extends to the next page, so should be at end of page already
        //so carry on reading pages until we get to the end of comment
        while (true)
        {
            logger.config("Reading another page");
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
            raf.read(b);
            baos.write(b);

            //Because there is at least one other packet this means the Setupheader Packet has finished
            //on this page so thats all we need and we can return
            if (nextPageHeader.getPacketList().size() > 1)
            {
                logger.config("Setupheader finishes on this page");
                return baos.toByteArray();
            }

            //There is only the Setupheader packet on page if it has completed on this page we can return
            if (!nextPageHeader.isLastPacketIncomplete())
            {
                logger.config("Setupheader finish on Page because this packet is complete");
                return baos.toByteArray();
            }
        }
    
public booleanisVorbisCommentHeader(byte[] headerData)
Is this a Vorbis Comment header, check

Note this check only applies to Vorbis Comments embedded within an OggVorbis File which is why within here

param
headerData
return
true if the headerData matches a VorbisComment header i.e is a Vorbis header of type COMMENT_HEADER

        String vorbis = Utils.getString(headerData, VorbisHeader.FIELD_CAPTURE_PATTERN_POS, VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH, "ISO-8859-1");
        return !(headerData[VorbisHeader.FIELD_PACKET_TYPE_POS] != VorbisPacketType.COMMENT_HEADER.getType() || !vorbis.equals(VorbisHeader.CAPTURE_PATTERN));
    
public booleanisVorbisSetupHeader(byte[] headerData)
Is this a Vorbis SetupHeader check

param
headerData
return
true if matches vorbis setupheader

        String vorbis = Utils.getString(headerData, VorbisHeader.FIELD_CAPTURE_PATTERN_POS, VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH, "ISO-8859-1");
        return !(headerData[VorbisHeader.FIELD_PACKET_TYPE_POS] != VorbisPacketType.SETUP_HEADER.getType() || !vorbis.equals(VorbisHeader.CAPTURE_PATTERN));
    
public org.jaudiotagger.tag.Tagread(java.io.RandomAccessFile raf)
Read the Logical VorbisComment Tag from the file

Read the CommenyTag, within an OggVorbis file the VorbisCommentTag is mandatory

param
raf
return
throws
CannotReadException
throws
IOException

        logger.config("Starting to read ogg vorbis tag from file:");
        byte[] rawVorbisCommentData = readRawPacketData(raf);

        //Begin tag reading
        VorbisCommentTag tag = vorbisCommentReader.read(rawVorbisCommentData, true);
        logger.fine("CompletedReadCommentTag");
        return tag;
    
public org.jaudiotagger.audio.ogg.OggVorbisTagReader$OggVorbisHeaderSizesreadOggVorbisHeaderSizes(java.io.RandomAccessFile raf)
Calculate the size of the packet data for the comment and setup headers

param
raf
return
throws
CannotReadException
throws
IOException

        logger.fine("Started to read comment and setup header sizes:");

        //Stores filepointers so return file in same state
        long filepointer = raf.getFilePointer();

        //Extra Packets on same page as setup header
        List<OggPageHeader.PacketStartAndLength> extraPackets = new ArrayList<OggPageHeader.PacketStartAndLength>();

        long commentHeaderStartPosition;
        long setupHeaderStartPosition;
        int commentHeaderSize = 0;
        int setupHeaderSize;
        //1st page = codec infos
        OggPageHeader pageHeader = OggPageHeader.read(raf);
        //Skip over data to end of page header 1
        raf.seek(raf.getFilePointer() + pageHeader.getPageLength());

        //2nd page = comment, may extend to additional pages or not , may also have setup header
        pageHeader = OggPageHeader.read(raf);
        commentHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length);

        //Now at start of packets on page 2 , check this is the vorbis comment header
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
        raf.read(b);
        if (!isVorbisCommentHeader(b))
        {
            throw new CannotReadException("Cannot find comment block (no vorbiscomment header)");
        }
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
        logger.config("Found start of comment header at:" + raf.getFilePointer());

        //Calculate Comment Size (not inc header)
        while (true)
        {
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();
            commentHeaderSize += packetList.get(0).getLength();
            raf.skipBytes(packetList.get(0).getLength());

            //If this page contains multiple packets or if this last packet is complete then the Comment header
            //end son this page and we can break
            if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
            {
                //done comment size
                logger.config("Found end of comment:size:" + commentHeaderSize + "finishes at file position:" + raf.getFilePointer());
                break;
            }
            pageHeader = OggPageHeader.read(raf);
        }

        //If there are no more packets on this page we need to go to next page to get the setup header
        OggPageHeader.PacketStartAndLength       packet;
        if(pageHeader.getPacketList().size()==1)
        {
            pageHeader = OggPageHeader.read(raf);
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();                       
            packet     = pageHeader.getPacketList().get(0);

            //Now at start of next packet , check this is the vorbis setup header
            b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
            raf.read(b);
            if (!isVorbisSetupHeader(b))
            {
                throw new CannotReadException(ErrorMessage.OGG_VORBIS_NO_VORBIS_HEADER_FOUND.getMsg());
            }
            raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
            logger.config("Found start of vorbis setup header at file position:" + raf.getFilePointer());

            //Set this to the  start of the OggPage that setupheader was found on
            setupHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length);

            //Add packet data to size to the setup header size
            setupHeaderSize = packet.getLength();
            logger.fine("Adding:" + packet.getLength() + " to setup header size");

            //Skip over the packet data
            raf.skipBytes(packet.getLength());

            //If there are other packets that follow this one, or if the last packet is complete then we must have
            //got the size of the setup header.
            if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
            {
                logger.config("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
                if (packetList.size() > 1)
                {
                    extraPackets = packetList.subList(1, packetList.size());
                }
            }
            //The setup header continues onto the next page
            else
            {
                pageHeader = OggPageHeader.read(raf);
                packetList = pageHeader.getPacketList();
                while (true)
                {
                    setupHeaderSize += packetList.get(0).getLength();
                    logger.fine("Adding:" + packetList.get(0).getLength() + " to setup header size");
                    raf.skipBytes(packetList.get(0).getLength());
                    if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
                    {
                        //done setup size
                        logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
                        if (packetList.size() > 1)
                        {
                            extraPackets = packetList.subList(1, packetList.size());
                        }
                        break;
                    }
                    //Continues onto another page
                    pageHeader = OggPageHeader.read(raf);
                }
            }
        }
        //else its next packet on this page
        else
        {
            packet     = pageHeader.getPacketList().get(1);
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();

            //Now at start of next packet , check this is the vorbis setup header
            b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
            raf.read(b);
            if (!isVorbisSetupHeader(b))
            {
                logger.warning("Expecting but got:"+new String(b)+ "at "+(raf.getFilePointer()  - b.length));
                throw new CannotReadException(ErrorMessage.OGG_VORBIS_NO_VORBIS_HEADER_FOUND.getMsg());
            }
            raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
            logger.config("Found start of vorbis setup header at file position:" + raf.getFilePointer());

            //Set this to the  start of the OggPage that setupheader was found on
            setupHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length)
                    - pageHeader.getPacketList().get(0).getLength();

            //Add packet data to size to the setup header size
            setupHeaderSize = packet.getLength();
            logger.fine("Adding:" + packet.getLength() + " to setup header size");

            //Skip over the packet data
            raf.skipBytes(packet.getLength());

             //If there are other packets that follow this one, or if the last packet is complete then we must have
            //got the size of the setup header.
            if (packetList.size() > 2 || !pageHeader.isLastPacketIncomplete())
            {
                logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
                if (packetList.size() > 2)
                {
                    extraPackets = packetList.subList(2, packetList.size());
                }
            }
            //The setup header continues onto the next page
            else
            {
                pageHeader = OggPageHeader.read(raf);
                packetList = pageHeader.getPacketList();
                while (true)
                {
                    setupHeaderSize += packetList.get(0).getLength();
                    logger.fine("Adding:" + packetList.get(0).getLength() + " to setup header size");
                    raf.skipBytes(packetList.get(0).getLength());
                    if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
                    {
                        //done setup size
                        logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
                        if (packetList.size() > 1)
                        {
                            extraPackets = packetList.subList(1, packetList.size());
                        }
                        break;
                    }
                    //Continues onto another page
                    pageHeader = OggPageHeader.read(raf);
                }
            }
        }

        //Reset filepointer to location that it was in at start of method
        raf.seek(filepointer);
        return new OggVorbisHeaderSizes(commentHeaderStartPosition, setupHeaderStartPosition, commentHeaderSize, setupHeaderSize, extraPackets);
    
public intreadOggVorbisRawSize(java.io.RandomAccessFile raf)
Retrieve the Size of the VorbisComment packet including the oggvorbis header

param
raf
return
throws
CannotReadException
throws
IOException

        byte[] rawVorbisCommentData = readRawPacketData(raf);
        return rawVorbisCommentData.length + VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH;
    
public byte[]readRawPacketData(java.io.RandomAccessFile raf)
Retrieve the raw VorbisComment packet data, does not include the OggVorbis header

param
raf
return
throws
CannotReadException if unable to find vorbiscomment header
throws
IOException

        logger.fine("Read 1st page");
        //1st page = codec infos
        OggPageHeader pageHeader = OggPageHeader.read(raf);
        //Skip over data to end of page header 1
        raf.seek(raf.getFilePointer() + pageHeader.getPageLength());

        logger.fine("Read 2nd page");
        //2nd page = comment, may extend to additional pages or not , may also have setup header
        pageHeader = OggPageHeader.read(raf);

        //Now at start of packets on page 2 , check this is the vorbis comment header 
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
        raf.read(b);
        if (!isVorbisCommentHeader(b))
        {
            throw new CannotReadException("Cannot find comment block (no vorbiscomment header)");
        }

        //Convert the comment raw data which maybe over many pages back into raw packet
        byte[] rawVorbisCommentData = convertToVorbisCommentPacket(pageHeader, raf);
        return rawVorbisCommentData;