~ubuntu-branches/ubuntu/utopic/libjaudiotagger-java/utopic

« back to all changes in this revision

Viewing changes to src/org/jaudiotagger/audio/ogg/OggVorbisTagReader.java

  • Committer: Bazaar Package Importer
  • Author(s): Damien Raude-Morvan
  • Date: 2011-04-28 23:52:43 UTC
  • mfrom: (3.1.4 sid)
  • Revision ID: james.westby@ubuntu.com-20110428235243-pzalvw6lncis3ukf
Tags: 2.0.3-1
* d/control: Drop Depends on default-jre per Debian Java Policy as its
  a library package.
* d/watch: Fix to directly monitor SVN tags.
* Switch to 3.0 (quilt) format.
* Bump Standards-Version to 3.9.2 (no changes needed).

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * Entagged Audio Tag library
3
 
 * Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
4
 
 * Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
5
 
 * 
6
 
 * This library is free software; you can redistribute it and/or
7
 
 * modify it under the terms of the GNU Lesser General Public
8
 
 * License as published by the Free Software Foundation; either
9
 
 * version 2.1 of the License, or (at your option) any later version.
10
 
 *  
11
 
 * This library is distributed in the hope that it will be useful,
12
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
 
 * Lesser General Public License for more details.
15
 
 * 
16
 
 * You should have received a copy of the GNU Lesser General Public
17
 
 * License along with this library; if not, write to the Free Software
18
 
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19
 
 */
20
 
package org.jaudiotagger.audio.ogg;
21
 
 
22
 
import org.jaudiotagger.audio.exceptions.CannotReadException;
23
 
import org.jaudiotagger.audio.generic.Utils;
24
 
import org.jaudiotagger.audio.ogg.util.OggPageHeader;
25
 
import org.jaudiotagger.audio.ogg.util.VorbisHeader;
26
 
import org.jaudiotagger.audio.ogg.util.VorbisPacketType;
27
 
import org.jaudiotagger.fix.Fix;
28
 
import org.jaudiotagger.logging.ErrorMessage;
29
 
import org.jaudiotagger.tag.Tag;
30
 
import org.jaudiotagger.tag.vorbiscomment.VorbisCommentReader;
31
 
import org.jaudiotagger.tag.vorbiscomment.VorbisCommentTag;
32
 
 
33
 
import java.io.ByteArrayOutputStream;
34
 
import java.io.IOException;
35
 
import java.io.RandomAccessFile;
36
 
import java.util.ArrayList;
37
 
import java.util.List;
38
 
import java.util.logging.Logger;
39
 
 
40
 
/**
41
 
 * Read Vorbis Comment Tag within ogg
42
 
 * <p/>
43
 
 * Vorbis is the audiostream within an ogg file, Vorbis uses VorbisComments as its tag
44
 
 */
45
 
public class OggVorbisTagReader
46
 
{
47
 
    // Logger Object
48
 
    public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.ogg");
49
 
 
50
 
    private VorbisCommentReader vorbisCommentReader;
51
 
 
52
 
    public OggVorbisTagReader()
53
 
    {
54
 
        vorbisCommentReader = new VorbisCommentReader();
55
 
    }
56
 
 
57
 
    public OggVorbisTagReader(Fix fix)
58
 
    {
59
 
        Fix fix1 = fix;
60
 
        vorbisCommentReader = new VorbisCommentReader(fix);
61
 
    }
62
 
 
63
 
 
64
 
    /**
65
 
     * Read the Logical VorbisComment Tag from the file
66
 
     * <p/>
67
 
     * <p>Read the CommenyTag, within an OggVorbis file the VorbisCommentTag is mandatory
68
 
     *
69
 
     * @param raf
70
 
     * @return
71
 
     * @throws CannotReadException
72
 
     * @throws IOException
73
 
     */
74
 
    public Tag read(RandomAccessFile raf) throws CannotReadException, IOException
75
 
    {
76
 
        logger.info("Starting to read ogg vorbis tag from file:");
77
 
        byte[] rawVorbisCommentData = readRawPacketData(raf);
78
 
 
79
 
        //Begin tag reading
80
 
        VorbisCommentTag tag = vorbisCommentReader.read(rawVorbisCommentData, true);
81
 
        logger.fine("CompletedReadCommentTag");
82
 
        return tag;
83
 
    }
84
 
 
85
 
    /**
86
 
     * Retrieve the Size of the VorbisComment packet including the oggvorbis header
87
 
     *
88
 
     * @param raf
89
 
     * @return
90
 
     * @throws CannotReadException
91
 
     * @throws IOException
92
 
     */
93
 
    public int readOggVorbisRawSize(RandomAccessFile raf) throws CannotReadException, IOException
94
 
    {
95
 
        byte[] rawVorbisCommentData = readRawPacketData(raf);
96
 
        return rawVorbisCommentData.length + VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH;
97
 
    }
98
 
 
99
 
    /**
100
 
     * Retrieve the raw VorbisComment packet data, does not include the OggVorbis header
101
 
     *
102
 
     * @param raf
103
 
     * @return
104
 
     * @throws CannotReadException if unable to find vorbiscomment header
105
 
     * @throws IOException
106
 
     */
107
 
    public byte[] readRawPacketData(RandomAccessFile raf) throws CannotReadException, IOException
108
 
    {
109
 
        logger.fine("Read 1st page");
110
 
        //1st page = codec infos
111
 
        OggPageHeader pageHeader = OggPageHeader.read(raf);
112
 
        //Skip over data to end of page header 1
113
 
        raf.seek(raf.getFilePointer() + pageHeader.getPageLength());
114
 
 
115
 
        logger.fine("Read 2nd page");
116
 
        //2nd page = comment, may extend to additional pages or not , may also have setup header
117
 
        pageHeader = OggPageHeader.read(raf);
118
 
 
119
 
        //Now at start of packets on page 2 , check this is the vorbis comment header 
120
 
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
121
 
        raf.read(b);
122
 
        if (!isVorbisCommentHeader(b))
123
 
        {
124
 
            throw new CannotReadException("Cannot find comment block (no vorbiscomment header)");
125
 
        }
126
 
 
127
 
        //Convert the comment raw data which maybe over many pages back into raw packet
128
 
        byte[] rawVorbisCommentData = convertToVorbisCommentPacket(pageHeader, raf);
129
 
        return rawVorbisCommentData;
130
 
    }
131
 
 
132
 
 
133
 
    /**
134
 
     * Is this a Vorbis Comment header, check
135
 
     * <p/>
136
 
     * Note this check only applies to Vorbis Comments embedded within an OggVorbis File which is why within here
137
 
     *
138
 
     * @param headerData
139
 
     * @return true if the headerData matches a VorbisComment header i.e is a Vorbis header of type COMMENT_HEADER
140
 
     */
141
 
    public boolean isVorbisCommentHeader(byte[] headerData)
142
 
    {
143
 
        String vorbis = Utils.getString(headerData, VorbisHeader.FIELD_CAPTURE_PATTERN_POS, VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH, "ISO-8859-1");
144
 
        return !(headerData[VorbisHeader.FIELD_PACKET_TYPE_POS] != VorbisPacketType.COMMENT_HEADER.getType() || !vorbis.equals(VorbisHeader.CAPTURE_PATTERN));
145
 
    }
146
 
 
147
 
    /**
148
 
     * Is this a Vorbis SetupHeader check
149
 
     *
150
 
     * @param headerData
151
 
     * @return true if matches vorbis setupheader
152
 
     */
153
 
    public boolean isVorbisSetupHeader(byte[] headerData)
154
 
    {
155
 
        String vorbis = Utils.getString(headerData, VorbisHeader.FIELD_CAPTURE_PATTERN_POS, VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH, "ISO-8859-1");
156
 
        return !(headerData[VorbisHeader.FIELD_PACKET_TYPE_POS] != VorbisPacketType.SETUP_HEADER.getType() || !vorbis.equals(VorbisHeader.CAPTURE_PATTERN));
157
 
    }
158
 
 
159
 
    /**
160
 
     * The Vorbis Comment may span multiple pages so we we need to identify the pages they contain and then
161
 
     * extract the packet data from the pages
162
 
     * @param startVorbisCommentPage
163
 
     * @param raf
164
 
     * @throws org.jaudiotagger.audio.exceptions.CannotReadException
165
 
     * @throws java.io.IOException
166
 
     * @return
167
 
     */
168
 
    private byte[] convertToVorbisCommentPacket(OggPageHeader startVorbisCommentPage, RandomAccessFile raf) throws IOException, CannotReadException
169
 
    {
170
 
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
171
 
        byte[] b = new byte[startVorbisCommentPage.getPacketList().get(0).getLength() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH)];
172
 
        raf.read(b);
173
 
        baos.write(b);
174
 
 
175
 
        //Because there is at least one other packet (SetupHeaderPacket) this means the Comment Packet has finished
176
 
        //on this page so thats all we need and we can return
177
 
        if (startVorbisCommentPage.getPacketList().size() > 1)
178
 
        {
179
 
            logger.info("Comments finish on 2nd Page because there is another packet on this page");
180
 
            return baos.toByteArray();
181
 
        }
182
 
 
183
 
        //There is only the VorbisComment packet on page if it has completed on this page we can return
184
 
        if (!startVorbisCommentPage.isLastPacketIncomplete())
185
 
        {
186
 
            logger.info("Comments finish on 2nd Page because this packet is complete");
187
 
            return baos.toByteArray();
188
 
        }
189
 
 
190
 
        //The VorbisComment extends to the next page, so should be at end of page already
191
 
        //so carry on reading pages until we get to the end of comment
192
 
        while (true)
193
 
        {
194
 
            logger.info("Reading next page");
195
 
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
196
 
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
197
 
            raf.read(b);
198
 
            baos.write(b);
199
 
 
200
 
            //Because there is at least one other packet (SetupHeaderPacket) this means the Comment Packet has finished
201
 
            //on this page so thats all we need and we can return
202
 
            if (nextPageHeader.getPacketList().size() > 1)
203
 
            {
204
 
                logger.info("Comments finish on Page because there is another packet on this page");
205
 
                return baos.toByteArray();
206
 
            }
207
 
 
208
 
            //There is only the VorbisComment packet on page if it has completed on this page we can return
209
 
            if (!nextPageHeader.isLastPacketIncomplete())
210
 
            {
211
 
                logger.info("Comments finish on Page because this packet is complete");
212
 
                return baos.toByteArray();
213
 
            }
214
 
        }
215
 
    }
216
 
 
217
 
    /**
218
 
     * The Vorbis Setup Header may span multiple(2) pages, athough it doesnt normally. We pass the start of the
219
 
     * file offset of the OggPage it belongs on, it probably won't be first packet.
220
 
     * @param fileOffsetOfStartingOggPage
221
 
     * @param raf
222
 
     * @throws org.jaudiotagger.audio.exceptions.CannotReadException
223
 
     * @throws java.io.IOException
224
 
     * @return
225
 
     */
226
 
    public byte[] convertToVorbisSetupHeaderPacket(long fileOffsetOfStartingOggPage, RandomAccessFile raf) throws IOException, CannotReadException
227
 
    {
228
 
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
229
 
 
230
 
        //Seek to specified offset
231
 
        raf.seek(fileOffsetOfStartingOggPage);
232
 
 
233
 
        //Read Page
234
 
        OggPageHeader setupPageHeader = OggPageHeader.read(raf);
235
 
 
236
 
        //Assume that if multiple packets first packet is VorbisComment and second packet
237
 
        //is setupheader
238
 
        if (setupPageHeader.getPacketList().size() > 1)
239
 
        {
240
 
            raf.skipBytes(setupPageHeader.getPacketList().get(0).getLength());
241
 
        }
242
 
 
243
 
        //Now should be at start of next packet, check this is the vorbis setup header
244
 
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
245
 
        raf.read(b);
246
 
        if (!isVorbisSetupHeader(b))
247
 
        {
248
 
            throw new CannotReadException("Unable to find setup header(2), unable to write ogg file");
249
 
        }
250
 
 
251
 
        //Go back to start of setupheader data
252
 
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
253
 
 
254
 
        //Read data
255
 
        if (setupPageHeader.getPacketList().size() > 1)
256
 
        {
257
 
            b = new byte[setupPageHeader.getPacketList().get(1).getLength()];
258
 
            raf.read(b);
259
 
            baos.write(b);
260
 
        }
261
 
        else
262
 
        {
263
 
            b = new byte[setupPageHeader.getPacketList().get(0).getLength()];
264
 
            raf.read(b);
265
 
            baos.write(b);
266
 
        }
267
 
 
268
 
        //Return Data
269
 
        if (!setupPageHeader.isLastPacketIncomplete() || setupPageHeader.getPacketList().size() > 2)
270
 
        {
271
 
            logger.info("Setupheader finishes on this page");
272
 
            return baos.toByteArray();
273
 
        }
274
 
 
275
 
        //The Setupheader extends to the next page, so should be at end of page already
276
 
        //so carry on reading pages until we get to the end of comment
277
 
        while (true)
278
 
        {
279
 
            logger.info("Reading another page");
280
 
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
281
 
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
282
 
            raf.read(b);
283
 
            baos.write(b);
284
 
 
285
 
            //Because there is at least one other packet this means the Setupheader Packet has finished
286
 
            //on this page so thats all we need and we can return
287
 
            if (nextPageHeader.getPacketList().size() > 1)
288
 
            {
289
 
                logger.info("Setupheader finishes on this page");
290
 
                return baos.toByteArray();
291
 
            }
292
 
 
293
 
            //There is only the Setupheader packet on page if it has completed on this page we can return
294
 
            if (!nextPageHeader.isLastPacketIncomplete())
295
 
            {
296
 
                logger.info("Setupheader finish on Page because this packet is complete");
297
 
                return baos.toByteArray();
298
 
            }
299
 
        }
300
 
    }
301
 
 
302
 
 
303
 
    /**
304
 
     * The Vorbis Setup Header may span multiple(2) pages, athough it doesnt normally. We pass the start of the
305
 
     * file offset of the OggPage it belongs on, it probably won't be first packet, also returns any addditional
306
 
     * packets that immediately follow the setup header in original file
307
 
     * @param fileOffsetOfStartingOggPage
308
 
     * @param raf
309
 
     * @throws org.jaudiotagger.audio.exceptions.CannotReadException
310
 
     * @throws java.io.IOException
311
 
     * @return
312
 
     */
313
 
    public byte[] convertToVorbisSetupHeaderPacketAndAdditionalPackets(long fileOffsetOfStartingOggPage, RandomAccessFile raf) throws IOException, CannotReadException
314
 
    {
315
 
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
316
 
 
317
 
        //Seek to specified offset
318
 
        raf.seek(fileOffsetOfStartingOggPage);
319
 
 
320
 
        //Read Page
321
 
        OggPageHeader setupPageHeader = OggPageHeader.read(raf);
322
 
 
323
 
        //Assume that if multiple packets first packet is VorbisComment and second packet
324
 
        //is setupheader
325
 
        if (setupPageHeader.getPacketList().size() > 1)
326
 
        {
327
 
            raf.skipBytes(setupPageHeader.getPacketList().get(0).getLength());
328
 
        }
329
 
 
330
 
        //Now should be at start of next packet, check this is the vorbis setup header
331
 
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
332
 
        raf.read(b);
333
 
        if (!isVorbisSetupHeader(b))
334
 
        {
335
 
            throw new CannotReadException("Unable to find setup header(2), unable to write ogg file");
336
 
        }
337
 
 
338
 
        //Go back to start of setupheader data
339
 
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
340
 
 
341
 
        //Read data
342
 
        if (setupPageHeader.getPacketList().size() > 1)
343
 
        {
344
 
            b = new byte[setupPageHeader.getPacketList().get(1).getLength()];
345
 
            raf.read(b);
346
 
            baos.write(b);
347
 
        }
348
 
        else
349
 
        {
350
 
            b = new byte[setupPageHeader.getPacketList().get(0).getLength()];
351
 
            raf.read(b);
352
 
            baos.write(b);
353
 
        }
354
 
 
355
 
        //Return Data
356
 
        if (!setupPageHeader.isLastPacketIncomplete() || setupPageHeader.getPacketList().size() > 2)
357
 
        {
358
 
            logger.info("Setupheader finishes on this page");
359
 
            if (setupPageHeader.getPacketList().size() > 2)
360
 
            {
361
 
                for (int i = 2; i < setupPageHeader.getPacketList().size(); i++)
362
 
                {
363
 
                    b = new byte[setupPageHeader.getPacketList().get(i).getLength()];
364
 
                    raf.read(b);
365
 
                    baos.write(b);
366
 
                }
367
 
            }
368
 
            return baos.toByteArray();
369
 
        }
370
 
 
371
 
        //The Setupheader extends to the next page, so should be at end of page already
372
 
        //so carry on reading pages until we get to the end of comment
373
 
        while (true)
374
 
        {
375
 
            logger.info("Reading another page");
376
 
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
377
 
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
378
 
            raf.read(b);
379
 
            baos.write(b);
380
 
 
381
 
            //Because there is at least one other packet this means the Setupheader Packet has finished
382
 
            //on this page so thats all we need and we can return
383
 
            if (nextPageHeader.getPacketList().size() > 1)
384
 
            {
385
 
                logger.info("Setupheader finishes on this page");
386
 
                return baos.toByteArray();
387
 
            }
388
 
 
389
 
            //There is only the Setupheader packet on page if it has completed on this page we can return
390
 
            if (!nextPageHeader.isLastPacketIncomplete())
391
 
            {
392
 
                logger.info("Setupheader finish on Page because this packet is complete");
393
 
                return baos.toByteArray();
394
 
            }
395
 
        }
396
 
    }
397
 
 
398
 
 
399
 
    /**
400
 
     * Calculate the size of the packet data for the comment and setup headers
401
 
     *
402
 
     * @param raf
403
 
     * @return
404
 
     * @throws CannotReadException
405
 
     * @throws IOException
406
 
     */
407
 
    public OggVorbisHeaderSizes readOggVorbisHeaderSizes(RandomAccessFile raf) throws CannotReadException, IOException
408
 
    {
409
 
        logger.fine("Started to read comment and setup header sizes:");
410
 
 
411
 
        //Stores filepointers so return file in same state
412
 
        long filepointer = raf.getFilePointer();
413
 
 
414
 
        //Extra Packets on same page as setup header
415
 
        List<OggPageHeader.PacketStartAndLength> extraPackets = new ArrayList<OggPageHeader.PacketStartAndLength>();
416
 
 
417
 
        long commentHeaderStartPosition;
418
 
        long setupHeaderStartPosition;
419
 
        int commentHeaderSize = 0;
420
 
        int setupHeaderSize;
421
 
        //1st page = codec infos
422
 
        OggPageHeader pageHeader = OggPageHeader.read(raf);
423
 
        //Skip over data to end of page header 1
424
 
        raf.seek(raf.getFilePointer() + pageHeader.getPageLength());
425
 
 
426
 
        //2nd page = comment, may extend to additional pages or not , may also have setup header
427
 
        pageHeader = OggPageHeader.read(raf);
428
 
        commentHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length);
429
 
 
430
 
        //Now at start of packets on page 2 , check this is the vorbis comment header
431
 
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
432
 
        raf.read(b);
433
 
        if (!isVorbisCommentHeader(b))
434
 
        {
435
 
            throw new CannotReadException("Cannot find comment block (no vorbiscomment header)");
436
 
        }
437
 
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
438
 
        logger.info("Found start of comment header at:" + raf.getFilePointer());
439
 
 
440
 
        //Calculate Comment Size (not inc header)
441
 
        while (true)
442
 
        {
443
 
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();
444
 
            commentHeaderSize += packetList.get(0).getLength();
445
 
            raf.skipBytes(packetList.get(0).getLength());
446
 
 
447
 
            //If this page contains multiple packets or if this last packet is complete then the Comment header
448
 
            //end son this page and we can break
449
 
            if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
450
 
            {
451
 
                //done comment size
452
 
                logger.info("Found end of comment:size:" + commentHeaderSize + "finishes at file position:" + raf.getFilePointer());
453
 
                break;
454
 
            }
455
 
            pageHeader = OggPageHeader.read(raf);
456
 
        }
457
 
 
458
 
        //If there are no more packets on this page we need to go to next page to get the setup header
459
 
        OggPageHeader.PacketStartAndLength       packet;
460
 
        if(pageHeader.getPacketList().size()==1)
461
 
        {
462
 
            pageHeader = OggPageHeader.read(raf);
463
 
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();                       
464
 
            packet     = pageHeader.getPacketList().get(0);
465
 
 
466
 
            //Now at start of next packet , check this is the vorbis setup header
467
 
            b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
468
 
            raf.read(b);
469
 
            if (!isVorbisSetupHeader(b))
470
 
            {
471
 
                throw new CannotReadException(ErrorMessage.OGG_VORBIS_NO_VORBIS_HEADER_FOUND.getMsg());
472
 
            }
473
 
            raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
474
 
            logger.info("Found start of vorbis setup header at file position:" + raf.getFilePointer());
475
 
 
476
 
            //Set this to the  start of the OggPage that setupheader was found on
477
 
            setupHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length);
478
 
 
479
 
            //Add packet data to size to the setup header size
480
 
            setupHeaderSize = packet.getLength();
481
 
            logger.fine("Adding:" + packet.getLength() + " to setup header size");
482
 
 
483
 
            //Skip over the packet data
484
 
            raf.skipBytes(packet.getLength());
485
 
 
486
 
            //If there are other packets that follow this one, or if the last packet is complete then we must have
487
 
            //got the size of the setup header.
488
 
            if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
489
 
            {
490
 
                logger.info("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
491
 
                if (packetList.size() > 1)
492
 
                {
493
 
                    extraPackets = packetList.subList(1, packetList.size());
494
 
                }
495
 
            }
496
 
            //The setup header continues onto the next page
497
 
            else
498
 
            {
499
 
                pageHeader = OggPageHeader.read(raf);
500
 
                packetList = pageHeader.getPacketList();
501
 
                while (true)
502
 
                {
503
 
                    setupHeaderSize += packetList.get(0).getLength();
504
 
                    logger.fine("Adding:" + packetList.get(0).getLength() + " to setup header size");
505
 
                    raf.skipBytes(packetList.get(0).getLength());
506
 
                    if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
507
 
                    {
508
 
                        //done setup size
509
 
                        logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
510
 
                        if (packetList.size() > 1)
511
 
                        {
512
 
                            extraPackets = packetList.subList(1, packetList.size());
513
 
                        }
514
 
                        break;
515
 
                    }
516
 
                    //Continues onto another page
517
 
                    pageHeader = OggPageHeader.read(raf);
518
 
                }
519
 
            }
520
 
        }
521
 
        //else its next packet on this page
522
 
        else
523
 
        {
524
 
            packet     = pageHeader.getPacketList().get(1);
525
 
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();
526
 
 
527
 
            //Now at start of next packet , check this is the vorbis setup header
528
 
            b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
529
 
            raf.read(b);
530
 
            if (!isVorbisSetupHeader(b))
531
 
            {
532
 
                logger.warning("Expecting but got:"+new String(b)+ "at "+(raf.getFilePointer()  - b.length));
533
 
                throw new CannotReadException(ErrorMessage.OGG_VORBIS_NO_VORBIS_HEADER_FOUND.getMsg());
534
 
            }
535
 
            raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
536
 
            logger.info("Found start of vorbis setup header at file position:" + raf.getFilePointer());
537
 
 
538
 
            //Set this to the  start of the OggPage that setupheader was found on
539
 
            setupHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length)
540
 
                    - pageHeader.getPacketList().get(0).getLength();
541
 
 
542
 
            //Add packet data to size to the setup header size
543
 
            setupHeaderSize = packet.getLength();
544
 
            logger.fine("Adding:" + packet.getLength() + " to setup header size");
545
 
 
546
 
            //Skip over the packet data
547
 
            raf.skipBytes(packet.getLength());
548
 
 
549
 
             //If there are other packets that follow this one, or if the last packet is complete then we must have
550
 
            //got the size of the setup header.
551
 
            if (packetList.size() > 2 || !pageHeader.isLastPacketIncomplete())
552
 
            {
553
 
                logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
554
 
                if (packetList.size() > 2)
555
 
                {
556
 
                    extraPackets = packetList.subList(2, packetList.size());
557
 
                }
558
 
            }
559
 
            //The setup header continues onto the next page
560
 
            else
561
 
            {
562
 
                pageHeader = OggPageHeader.read(raf);
563
 
                packetList = pageHeader.getPacketList();
564
 
                while (true)
565
 
                {
566
 
                    setupHeaderSize += packetList.get(0).getLength();
567
 
                    logger.fine("Adding:" + packetList.get(0).getLength() + " to setup header size");
568
 
                    raf.skipBytes(packetList.get(0).getLength());
569
 
                    if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
570
 
                    {
571
 
                        //done setup size
572
 
                        logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
573
 
                        if (packetList.size() > 1)
574
 
                        {
575
 
                            extraPackets = packetList.subList(1, packetList.size());
576
 
                        }
577
 
                        break;
578
 
                    }
579
 
                    //Continues onto another page
580
 
                    pageHeader = OggPageHeader.read(raf);
581
 
                }
582
 
            }
583
 
        }
584
 
 
585
 
        //Reset filepointer to location that it was in at start of method
586
 
        raf.seek(filepointer);
587
 
        return new OggVorbisHeaderSizes(commentHeaderStartPosition, setupHeaderStartPosition, commentHeaderSize, setupHeaderSize, extraPackets);
588
 
    }
589
 
 
590
 
    /**
591
 
     * Find the length of the raw packet data and the start position of the ogg page header they start in
592
 
     * for the two OggVorbisHeader we need to know about when writing data (sizes included vorbis header)
593
 
     */
594
 
    public static class OggVorbisHeaderSizes
595
 
    {
596
 
        private long commentHeaderStartPosition;
597
 
        private long setupHeaderStartPosition;
598
 
        private int commentHeaderSize;
599
 
        private int setupHeaderSize;
600
 
        private List<OggPageHeader.PacketStartAndLength> packetList;
601
 
 
602
 
        OggVorbisHeaderSizes(long commentHeaderStartPosition, long setupHeaderStartPosition, int commentHeaderSize, int setupHeaderSize, List<OggPageHeader.PacketStartAndLength> packetList)
603
 
        {
604
 
            this.packetList = packetList;
605
 
            this.commentHeaderStartPosition = commentHeaderStartPosition;
606
 
            this.setupHeaderStartPosition = setupHeaderStartPosition;
607
 
            this.commentHeaderSize = commentHeaderSize;
608
 
            this.setupHeaderSize = setupHeaderSize;
609
 
        }
610
 
 
611
 
        /**
612
 
         * @return the size of the raw packet data for the vorbis comment header (includes vorbis header)
613
 
         */
614
 
        public int getCommentHeaderSize()
615
 
        {
616
 
            return commentHeaderSize;
617
 
        }
618
 
 
619
 
        /**
620
 
         * @return he size of the raw packet data for the vorbis setup header (includes vorbis header)
621
 
         */
622
 
        public int getSetupHeaderSize()
623
 
        {
624
 
            return setupHeaderSize;
625
 
        }
626
 
 
627
 
        /**
628
 
         * Return the size required by all the extra packets on same page as setup header, usually there are
629
 
         * no packets immediately after the setup packet.
630
 
         *
631
 
         * @return extra data size required for additional packets on same page
632
 
         */
633
 
        public int getExtraPacketDataSize()
634
 
        {
635
 
            int extraPacketSize = 0;
636
 
            for (OggPageHeader.PacketStartAndLength packet : packetList)
637
 
            {
638
 
                extraPacketSize += packet.getLength();
639
 
            }
640
 
            return extraPacketSize;
641
 
        }
642
 
 
643
 
        /**
644
 
         * @return the start position in the file of the ogg header which contains the start of the Vorbis Comment
645
 
         */
646
 
        public long getCommentHeaderStartPosition()
647
 
        {
648
 
            return commentHeaderStartPosition;
649
 
        }
650
 
 
651
 
        /**
652
 
         * @return the start position in the file of the ogg header which contains the start of the Setup Header
653
 
         */
654
 
        public long getSetupHeaderStartPosition()
655
 
        {
656
 
            return setupHeaderStartPosition;
657
 
        }
658
 
 
659
 
        public List<OggPageHeader.PacketStartAndLength> getExtraPacketList()
660
 
        {
661
 
            return packetList;
662
 
        }
663
 
    }
664
 
}
665
 
 
 
1
/*
 
2
 * Entagged Audio Tag library
 
3
 * Copyright (c) 2003-2005 Raphaël Slinckx <raphael@slinckx.net>
 
4
 * Copyright (c) 2004-2005 Christian Laireiter <liree@web.de>
 
5
 * 
 
6
 * This library is free software; you can redistribute it and/or
 
7
 * modify it under the terms of the GNU Lesser General Public
 
8
 * License as published by the Free Software Foundation; either
 
9
 * version 2.1 of the License, or (at your option) any later version.
 
10
 *  
 
11
 * This library is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * Lesser General Public License for more details.
 
15
 * 
 
16
 * You should have received a copy of the GNU Lesser General Public
 
17
 * License along with this library; if not, write to the Free Software
 
18
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 
19
 */
 
20
package org.jaudiotagger.audio.ogg;
 
21
 
 
22
import org.jaudiotagger.audio.exceptions.CannotReadException;
 
23
import org.jaudiotagger.audio.generic.Utils;
 
24
import org.jaudiotagger.audio.ogg.util.OggPageHeader;
 
25
import org.jaudiotagger.audio.ogg.util.VorbisHeader;
 
26
import org.jaudiotagger.audio.ogg.util.VorbisPacketType;
 
27
import org.jaudiotagger.fix.Fix;
 
28
import org.jaudiotagger.logging.ErrorMessage;
 
29
import org.jaudiotagger.tag.Tag;
 
30
import org.jaudiotagger.tag.vorbiscomment.VorbisCommentReader;
 
31
import org.jaudiotagger.tag.vorbiscomment.VorbisCommentTag;
 
32
 
 
33
import java.io.ByteArrayOutputStream;
 
34
import java.io.IOException;
 
35
import java.io.RandomAccessFile;
 
36
import java.util.ArrayList;
 
37
import java.util.List;
 
38
import java.util.logging.Logger;
 
39
 
 
40
/**
 
41
 * Read Vorbis Comment Tag within ogg
 
42
 * <p/>
 
43
 * Vorbis is the audiostream within an ogg file, Vorbis uses VorbisComments as its tag
 
44
 */
 
45
public class OggVorbisTagReader
 
46
{
 
47
    // Logger Object
 
48
    public static Logger logger = Logger.getLogger("org.jaudiotagger.audio.ogg");
 
49
 
 
50
    private VorbisCommentReader vorbisCommentReader;
 
51
 
 
52
    public OggVorbisTagReader()
 
53
    {
 
54
        vorbisCommentReader = new VorbisCommentReader();
 
55
    }
 
56
 
 
57
    public OggVorbisTagReader(Fix fix)
 
58
    {
 
59
        Fix fix1 = fix;
 
60
        vorbisCommentReader = new VorbisCommentReader(fix);
 
61
    }
 
62
 
 
63
 
 
64
    /**
 
65
     * Read the Logical VorbisComment Tag from the file
 
66
     * <p/>
 
67
     * <p>Read the CommenyTag, within an OggVorbis file the VorbisCommentTag is mandatory
 
68
     *
 
69
     * @param raf
 
70
     * @return
 
71
     * @throws CannotReadException
 
72
     * @throws IOException
 
73
     */
 
74
    public Tag read(RandomAccessFile raf) throws CannotReadException, IOException
 
75
    {
 
76
        logger.info("Starting to read ogg vorbis tag from file:");
 
77
        byte[] rawVorbisCommentData = readRawPacketData(raf);
 
78
 
 
79
        //Begin tag reading
 
80
        VorbisCommentTag tag = vorbisCommentReader.read(rawVorbisCommentData, true);
 
81
        logger.fine("CompletedReadCommentTag");
 
82
        return tag;
 
83
    }
 
84
 
 
85
    /**
 
86
     * Retrieve the Size of the VorbisComment packet including the oggvorbis header
 
87
     *
 
88
     * @param raf
 
89
     * @return
 
90
     * @throws CannotReadException
 
91
     * @throws IOException
 
92
     */
 
93
    public int readOggVorbisRawSize(RandomAccessFile raf) throws CannotReadException, IOException
 
94
    {
 
95
        byte[] rawVorbisCommentData = readRawPacketData(raf);
 
96
        return rawVorbisCommentData.length + VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH;
 
97
    }
 
98
 
 
99
    /**
 
100
     * Retrieve the raw VorbisComment packet data, does not include the OggVorbis header
 
101
     *
 
102
     * @param raf
 
103
     * @return
 
104
     * @throws CannotReadException if unable to find vorbiscomment header
 
105
     * @throws IOException
 
106
     */
 
107
    public byte[] readRawPacketData(RandomAccessFile raf) throws CannotReadException, IOException
 
108
    {
 
109
        logger.fine("Read 1st page");
 
110
        //1st page = codec infos
 
111
        OggPageHeader pageHeader = OggPageHeader.read(raf);
 
112
        //Skip over data to end of page header 1
 
113
        raf.seek(raf.getFilePointer() + pageHeader.getPageLength());
 
114
 
 
115
        logger.fine("Read 2nd page");
 
116
        //2nd page = comment, may extend to additional pages or not , may also have setup header
 
117
        pageHeader = OggPageHeader.read(raf);
 
118
 
 
119
        //Now at start of packets on page 2 , check this is the vorbis comment header 
 
120
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
 
121
        raf.read(b);
 
122
        if (!isVorbisCommentHeader(b))
 
123
        {
 
124
            throw new CannotReadException("Cannot find comment block (no vorbiscomment header)");
 
125
        }
 
126
 
 
127
        //Convert the comment raw data which maybe over many pages back into raw packet
 
128
        byte[] rawVorbisCommentData = convertToVorbisCommentPacket(pageHeader, raf);
 
129
        return rawVorbisCommentData;
 
130
    }
 
131
 
 
132
 
 
133
    /**
 
134
     * Is this a Vorbis Comment header, check
 
135
     * <p/>
 
136
     * Note this check only applies to Vorbis Comments embedded within an OggVorbis File which is why within here
 
137
     *
 
138
     * @param headerData
 
139
     * @return true if the headerData matches a VorbisComment header i.e is a Vorbis header of type COMMENT_HEADER
 
140
     */
 
141
    public boolean isVorbisCommentHeader(byte[] headerData)
 
142
    {
 
143
        String vorbis = Utils.getString(headerData, VorbisHeader.FIELD_CAPTURE_PATTERN_POS, VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH, "ISO-8859-1");
 
144
        return !(headerData[VorbisHeader.FIELD_PACKET_TYPE_POS] != VorbisPacketType.COMMENT_HEADER.getType() || !vorbis.equals(VorbisHeader.CAPTURE_PATTERN));
 
145
    }
 
146
 
 
147
    /**
 
148
     * Is this a Vorbis SetupHeader check
 
149
     *
 
150
     * @param headerData
 
151
     * @return true if matches vorbis setupheader
 
152
     */
 
153
    public boolean isVorbisSetupHeader(byte[] headerData)
 
154
    {
 
155
        String vorbis = Utils.getString(headerData, VorbisHeader.FIELD_CAPTURE_PATTERN_POS, VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH, "ISO-8859-1");
 
156
        return !(headerData[VorbisHeader.FIELD_PACKET_TYPE_POS] != VorbisPacketType.SETUP_HEADER.getType() || !vorbis.equals(VorbisHeader.CAPTURE_PATTERN));
 
157
    }
 
158
 
 
159
    /**
 
160
     * The Vorbis Comment may span multiple pages so we we need to identify the pages they contain and then
 
161
     * extract the packet data from the pages
 
162
     * @param startVorbisCommentPage
 
163
     * @param raf
 
164
     * @throws org.jaudiotagger.audio.exceptions.CannotReadException
 
165
     * @throws java.io.IOException
 
166
     * @return
 
167
     */
 
168
    private byte[] convertToVorbisCommentPacket(OggPageHeader startVorbisCommentPage, RandomAccessFile raf) throws IOException, CannotReadException
 
169
    {
 
170
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
171
        byte[] b = new byte[startVorbisCommentPage.getPacketList().get(0).getLength() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH)];
 
172
        raf.read(b);
 
173
        baos.write(b);
 
174
 
 
175
        //Because there is at least one other packet (SetupHeaderPacket) this means the Comment Packet has finished
 
176
        //on this page so thats all we need and we can return
 
177
        if (startVorbisCommentPage.getPacketList().size() > 1)
 
178
        {
 
179
            logger.info("Comments finish on 2nd Page because there is another packet on this page");
 
180
            return baos.toByteArray();
 
181
        }
 
182
 
 
183
        //There is only the VorbisComment packet on page if it has completed on this page we can return
 
184
        if (!startVorbisCommentPage.isLastPacketIncomplete())
 
185
        {
 
186
            logger.info("Comments finish on 2nd Page because this packet is complete");
 
187
            return baos.toByteArray();
 
188
        }
 
189
 
 
190
        //The VorbisComment extends to the next page, so should be at end of page already
 
191
        //so carry on reading pages until we get to the end of comment
 
192
        while (true)
 
193
        {
 
194
            logger.info("Reading next page");
 
195
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
 
196
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
 
197
            raf.read(b);
 
198
            baos.write(b);
 
199
 
 
200
            //Because there is at least one other packet (SetupHeaderPacket) this means the Comment Packet has finished
 
201
            //on this page so thats all we need and we can return
 
202
            if (nextPageHeader.getPacketList().size() > 1)
 
203
            {
 
204
                logger.info("Comments finish on Page because there is another packet on this page");
 
205
                return baos.toByteArray();
 
206
            }
 
207
 
 
208
            //There is only the VorbisComment packet on page if it has completed on this page we can return
 
209
            if (!nextPageHeader.isLastPacketIncomplete())
 
210
            {
 
211
                logger.info("Comments finish on Page because this packet is complete");
 
212
                return baos.toByteArray();
 
213
            }
 
214
        }
 
215
    }
 
216
 
 
217
    /**
 
218
     * The Vorbis Setup Header may span multiple(2) pages, athough it doesnt normally. We pass the start of the
 
219
     * file offset of the OggPage it belongs on, it probably won't be first packet.
 
220
     * @param fileOffsetOfStartingOggPage
 
221
     * @param raf
 
222
     * @throws org.jaudiotagger.audio.exceptions.CannotReadException
 
223
     * @throws java.io.IOException
 
224
     * @return
 
225
     */
 
226
    public byte[] convertToVorbisSetupHeaderPacket(long fileOffsetOfStartingOggPage, RandomAccessFile raf) throws IOException, CannotReadException
 
227
    {
 
228
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
229
 
 
230
        //Seek to specified offset
 
231
        raf.seek(fileOffsetOfStartingOggPage);
 
232
 
 
233
        //Read Page
 
234
        OggPageHeader setupPageHeader = OggPageHeader.read(raf);
 
235
 
 
236
        //Assume that if multiple packets first packet is VorbisComment and second packet
 
237
        //is setupheader
 
238
        if (setupPageHeader.getPacketList().size() > 1)
 
239
        {
 
240
            raf.skipBytes(setupPageHeader.getPacketList().get(0).getLength());
 
241
        }
 
242
 
 
243
        //Now should be at start of next packet, check this is the vorbis setup header
 
244
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
 
245
        raf.read(b);
 
246
        if (!isVorbisSetupHeader(b))
 
247
        {
 
248
            throw new CannotReadException("Unable to find setup header(2), unable to write ogg file");
 
249
        }
 
250
 
 
251
        //Go back to start of setupheader data
 
252
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
 
253
 
 
254
        //Read data
 
255
        if (setupPageHeader.getPacketList().size() > 1)
 
256
        {
 
257
            b = new byte[setupPageHeader.getPacketList().get(1).getLength()];
 
258
            raf.read(b);
 
259
            baos.write(b);
 
260
        }
 
261
        else
 
262
        {
 
263
            b = new byte[setupPageHeader.getPacketList().get(0).getLength()];
 
264
            raf.read(b);
 
265
            baos.write(b);
 
266
        }
 
267
 
 
268
        //Return Data
 
269
        if (!setupPageHeader.isLastPacketIncomplete() || setupPageHeader.getPacketList().size() > 2)
 
270
        {
 
271
            logger.info("Setupheader finishes on this page");
 
272
            return baos.toByteArray();
 
273
        }
 
274
 
 
275
        //The Setupheader extends to the next page, so should be at end of page already
 
276
        //so carry on reading pages until we get to the end of comment
 
277
        while (true)
 
278
        {
 
279
            logger.info("Reading another page");
 
280
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
 
281
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
 
282
            raf.read(b);
 
283
            baos.write(b);
 
284
 
 
285
            //Because there is at least one other packet this means the Setupheader Packet has finished
 
286
            //on this page so thats all we need and we can return
 
287
            if (nextPageHeader.getPacketList().size() > 1)
 
288
            {
 
289
                logger.info("Setupheader finishes on this page");
 
290
                return baos.toByteArray();
 
291
            }
 
292
 
 
293
            //There is only the Setupheader packet on page if it has completed on this page we can return
 
294
            if (!nextPageHeader.isLastPacketIncomplete())
 
295
            {
 
296
                logger.info("Setupheader finish on Page because this packet is complete");
 
297
                return baos.toByteArray();
 
298
            }
 
299
        }
 
300
    }
 
301
 
 
302
 
 
303
    /**
 
304
     * The Vorbis Setup Header may span multiple(2) pages, athough it doesnt normally. We pass the start of the
 
305
     * file offset of the OggPage it belongs on, it probably won't be first packet, also returns any addditional
 
306
     * packets that immediately follow the setup header in original file
 
307
     * @param fileOffsetOfStartingOggPage
 
308
     * @param raf
 
309
     * @throws org.jaudiotagger.audio.exceptions.CannotReadException
 
310
     * @throws java.io.IOException
 
311
     * @return
 
312
     */
 
313
    public byte[] convertToVorbisSetupHeaderPacketAndAdditionalPackets(long fileOffsetOfStartingOggPage, RandomAccessFile raf) throws IOException, CannotReadException
 
314
    {
 
315
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
 
316
 
 
317
        //Seek to specified offset
 
318
        raf.seek(fileOffsetOfStartingOggPage);
 
319
 
 
320
        //Read Page
 
321
        OggPageHeader setupPageHeader = OggPageHeader.read(raf);
 
322
 
 
323
        //Assume that if multiple packets first packet is VorbisComment and second packet
 
324
        //is setupheader
 
325
        if (setupPageHeader.getPacketList().size() > 1)
 
326
        {
 
327
            raf.skipBytes(setupPageHeader.getPacketList().get(0).getLength());
 
328
        }
 
329
 
 
330
        //Now should be at start of next packet, check this is the vorbis setup header
 
331
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
 
332
        raf.read(b);
 
333
        if (!isVorbisSetupHeader(b))
 
334
        {
 
335
            throw new CannotReadException("Unable to find setup header(2), unable to write ogg file");
 
336
        }
 
337
 
 
338
        //Go back to start of setupheader data
 
339
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
 
340
 
 
341
        //Read data
 
342
        if (setupPageHeader.getPacketList().size() > 1)
 
343
        {
 
344
            b = new byte[setupPageHeader.getPacketList().get(1).getLength()];
 
345
            raf.read(b);
 
346
            baos.write(b);
 
347
        }
 
348
        else
 
349
        {
 
350
            b = new byte[setupPageHeader.getPacketList().get(0).getLength()];
 
351
            raf.read(b);
 
352
            baos.write(b);
 
353
        }
 
354
 
 
355
        //Return Data
 
356
        if (!setupPageHeader.isLastPacketIncomplete() || setupPageHeader.getPacketList().size() > 2)
 
357
        {
 
358
            logger.info("Setupheader finishes on this page");
 
359
            if (setupPageHeader.getPacketList().size() > 2)
 
360
            {
 
361
                for (int i = 2; i < setupPageHeader.getPacketList().size(); i++)
 
362
                {
 
363
                    b = new byte[setupPageHeader.getPacketList().get(i).getLength()];
 
364
                    raf.read(b);
 
365
                    baos.write(b);
 
366
                }
 
367
            }
 
368
            return baos.toByteArray();
 
369
        }
 
370
 
 
371
        //The Setupheader extends to the next page, so should be at end of page already
 
372
        //so carry on reading pages until we get to the end of comment
 
373
        while (true)
 
374
        {
 
375
            logger.info("Reading another page");
 
376
            OggPageHeader nextPageHeader = OggPageHeader.read(raf);
 
377
            b = new byte[nextPageHeader.getPacketList().get(0).getLength()];
 
378
            raf.read(b);
 
379
            baos.write(b);
 
380
 
 
381
            //Because there is at least one other packet this means the Setupheader Packet has finished
 
382
            //on this page so thats all we need and we can return
 
383
            if (nextPageHeader.getPacketList().size() > 1)
 
384
            {
 
385
                logger.info("Setupheader finishes on this page");
 
386
                return baos.toByteArray();
 
387
            }
 
388
 
 
389
            //There is only the Setupheader packet on page if it has completed on this page we can return
 
390
            if (!nextPageHeader.isLastPacketIncomplete())
 
391
            {
 
392
                logger.info("Setupheader finish on Page because this packet is complete");
 
393
                return baos.toByteArray();
 
394
            }
 
395
        }
 
396
    }
 
397
 
 
398
 
 
399
    /**
 
400
     * Calculate the size of the packet data for the comment and setup headers
 
401
     *
 
402
     * @param raf
 
403
     * @return
 
404
     * @throws CannotReadException
 
405
     * @throws IOException
 
406
     */
 
407
    public OggVorbisHeaderSizes readOggVorbisHeaderSizes(RandomAccessFile raf) throws CannotReadException, IOException
 
408
    {
 
409
        logger.fine("Started to read comment and setup header sizes:");
 
410
 
 
411
        //Stores filepointers so return file in same state
 
412
        long filepointer = raf.getFilePointer();
 
413
 
 
414
        //Extra Packets on same page as setup header
 
415
        List<OggPageHeader.PacketStartAndLength> extraPackets = new ArrayList<OggPageHeader.PacketStartAndLength>();
 
416
 
 
417
        long commentHeaderStartPosition;
 
418
        long setupHeaderStartPosition;
 
419
        int commentHeaderSize = 0;
 
420
        int setupHeaderSize;
 
421
        //1st page = codec infos
 
422
        OggPageHeader pageHeader = OggPageHeader.read(raf);
 
423
        //Skip over data to end of page header 1
 
424
        raf.seek(raf.getFilePointer() + pageHeader.getPageLength());
 
425
 
 
426
        //2nd page = comment, may extend to additional pages or not , may also have setup header
 
427
        pageHeader = OggPageHeader.read(raf);
 
428
        commentHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length);
 
429
 
 
430
        //Now at start of packets on page 2 , check this is the vorbis comment header
 
431
        byte[] b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
 
432
        raf.read(b);
 
433
        if (!isVorbisCommentHeader(b))
 
434
        {
 
435
            throw new CannotReadException("Cannot find comment block (no vorbiscomment header)");
 
436
        }
 
437
        raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
 
438
        logger.info("Found start of comment header at:" + raf.getFilePointer());
 
439
 
 
440
        //Calculate Comment Size (not inc header)
 
441
        while (true)
 
442
        {
 
443
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();
 
444
            commentHeaderSize += packetList.get(0).getLength();
 
445
            raf.skipBytes(packetList.get(0).getLength());
 
446
 
 
447
            //If this page contains multiple packets or if this last packet is complete then the Comment header
 
448
            //end son this page and we can break
 
449
            if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
 
450
            {
 
451
                //done comment size
 
452
                logger.info("Found end of comment:size:" + commentHeaderSize + "finishes at file position:" + raf.getFilePointer());
 
453
                break;
 
454
            }
 
455
            pageHeader = OggPageHeader.read(raf);
 
456
        }
 
457
 
 
458
        //If there are no more packets on this page we need to go to next page to get the setup header
 
459
        OggPageHeader.PacketStartAndLength       packet;
 
460
        if(pageHeader.getPacketList().size()==1)
 
461
        {
 
462
            pageHeader = OggPageHeader.read(raf);
 
463
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();                       
 
464
            packet     = pageHeader.getPacketList().get(0);
 
465
 
 
466
            //Now at start of next packet , check this is the vorbis setup header
 
467
            b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
 
468
            raf.read(b);
 
469
            if (!isVorbisSetupHeader(b))
 
470
            {
 
471
                throw new CannotReadException(ErrorMessage.OGG_VORBIS_NO_VORBIS_HEADER_FOUND.getMsg());
 
472
            }
 
473
            raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
 
474
            logger.info("Found start of vorbis setup header at file position:" + raf.getFilePointer());
 
475
 
 
476
            //Set this to the  start of the OggPage that setupheader was found on
 
477
            setupHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length);
 
478
 
 
479
            //Add packet data to size to the setup header size
 
480
            setupHeaderSize = packet.getLength();
 
481
            logger.fine("Adding:" + packet.getLength() + " to setup header size");
 
482
 
 
483
            //Skip over the packet data
 
484
            raf.skipBytes(packet.getLength());
 
485
 
 
486
            //If there are other packets that follow this one, or if the last packet is complete then we must have
 
487
            //got the size of the setup header.
 
488
            if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
 
489
            {
 
490
                logger.info("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
 
491
                if (packetList.size() > 1)
 
492
                {
 
493
                    extraPackets = packetList.subList(1, packetList.size());
 
494
                }
 
495
            }
 
496
            //The setup header continues onto the next page
 
497
            else
 
498
            {
 
499
                pageHeader = OggPageHeader.read(raf);
 
500
                packetList = pageHeader.getPacketList();
 
501
                while (true)
 
502
                {
 
503
                    setupHeaderSize += packetList.get(0).getLength();
 
504
                    logger.fine("Adding:" + packetList.get(0).getLength() + " to setup header size");
 
505
                    raf.skipBytes(packetList.get(0).getLength());
 
506
                    if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
 
507
                    {
 
508
                        //done setup size
 
509
                        logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
 
510
                        if (packetList.size() > 1)
 
511
                        {
 
512
                            extraPackets = packetList.subList(1, packetList.size());
 
513
                        }
 
514
                        break;
 
515
                    }
 
516
                    //Continues onto another page
 
517
                    pageHeader = OggPageHeader.read(raf);
 
518
                }
 
519
            }
 
520
        }
 
521
        //else its next packet on this page
 
522
        else
 
523
        {
 
524
            packet     = pageHeader.getPacketList().get(1);
 
525
            List<OggPageHeader.PacketStartAndLength> packetList = pageHeader.getPacketList();
 
526
 
 
527
            //Now at start of next packet , check this is the vorbis setup header
 
528
            b = new byte[VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH];
 
529
            raf.read(b);
 
530
            if (!isVorbisSetupHeader(b))
 
531
            {
 
532
                logger.warning("Expecting but got:"+new String(b)+ "at "+(raf.getFilePointer()  - b.length));
 
533
                throw new CannotReadException(ErrorMessage.OGG_VORBIS_NO_VORBIS_HEADER_FOUND.getMsg());
 
534
            }
 
535
            raf.seek(raf.getFilePointer() - (VorbisHeader.FIELD_PACKET_TYPE_LENGTH + VorbisHeader.FIELD_CAPTURE_PATTERN_LENGTH));
 
536
            logger.info("Found start of vorbis setup header at file position:" + raf.getFilePointer());
 
537
 
 
538
            //Set this to the  start of the OggPage that setupheader was found on
 
539
            setupHeaderStartPosition = raf.getFilePointer() - (OggPageHeader.OGG_PAGE_HEADER_FIXED_LENGTH + pageHeader.getSegmentTable().length)
 
540
                    - pageHeader.getPacketList().get(0).getLength();
 
541
 
 
542
            //Add packet data to size to the setup header size
 
543
            setupHeaderSize = packet.getLength();
 
544
            logger.fine("Adding:" + packet.getLength() + " to setup header size");
 
545
 
 
546
            //Skip over the packet data
 
547
            raf.skipBytes(packet.getLength());
 
548
 
 
549
             //If there are other packets that follow this one, or if the last packet is complete then we must have
 
550
            //got the size of the setup header.
 
551
            if (packetList.size() > 2 || !pageHeader.isLastPacketIncomplete())
 
552
            {
 
553
                logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
 
554
                if (packetList.size() > 2)
 
555
                {
 
556
                    extraPackets = packetList.subList(2, packetList.size());
 
557
                }
 
558
            }
 
559
            //The setup header continues onto the next page
 
560
            else
 
561
            {
 
562
                pageHeader = OggPageHeader.read(raf);
 
563
                packetList = pageHeader.getPacketList();
 
564
                while (true)
 
565
                {
 
566
                    setupHeaderSize += packetList.get(0).getLength();
 
567
                    logger.fine("Adding:" + packetList.get(0).getLength() + " to setup header size");
 
568
                    raf.skipBytes(packetList.get(0).getLength());
 
569
                    if (packetList.size() > 1 || !pageHeader.isLastPacketIncomplete())
 
570
                    {
 
571
                        //done setup size
 
572
                        logger.fine("Found end of setupheader:size:" + setupHeaderSize + "finishes at:" + raf.getFilePointer());
 
573
                        if (packetList.size() > 1)
 
574
                        {
 
575
                            extraPackets = packetList.subList(1, packetList.size());
 
576
                        }
 
577
                        break;
 
578
                    }
 
579
                    //Continues onto another page
 
580
                    pageHeader = OggPageHeader.read(raf);
 
581
                }
 
582
            }
 
583
        }
 
584
 
 
585
        //Reset filepointer to location that it was in at start of method
 
586
        raf.seek(filepointer);
 
587
        return new OggVorbisHeaderSizes(commentHeaderStartPosition, setupHeaderStartPosition, commentHeaderSize, setupHeaderSize, extraPackets);
 
588
    }
 
589
 
 
590
    /**
 
591
     * Find the length of the raw packet data and the start position of the ogg page header they start in
 
592
     * for the two OggVorbisHeader we need to know about when writing data (sizes included vorbis header)
 
593
     */
 
594
    public static class OggVorbisHeaderSizes
 
595
    {
 
596
        private long commentHeaderStartPosition;
 
597
        private long setupHeaderStartPosition;
 
598
        private int commentHeaderSize;
 
599
        private int setupHeaderSize;
 
600
        private List<OggPageHeader.PacketStartAndLength> packetList;
 
601
 
 
602
        OggVorbisHeaderSizes(long commentHeaderStartPosition, long setupHeaderStartPosition, int commentHeaderSize, int setupHeaderSize, List<OggPageHeader.PacketStartAndLength> packetList)
 
603
        {
 
604
            this.packetList = packetList;
 
605
            this.commentHeaderStartPosition = commentHeaderStartPosition;
 
606
            this.setupHeaderStartPosition = setupHeaderStartPosition;
 
607
            this.commentHeaderSize = commentHeaderSize;
 
608
            this.setupHeaderSize = setupHeaderSize;
 
609
        }
 
610
 
 
611
        /**
 
612
         * @return the size of the raw packet data for the vorbis comment header (includes vorbis header)
 
613
         */
 
614
        public int getCommentHeaderSize()
 
615
        {
 
616
            return commentHeaderSize;
 
617
        }
 
618
 
 
619
        /**
 
620
         * @return he size of the raw packet data for the vorbis setup header (includes vorbis header)
 
621
         */
 
622
        public int getSetupHeaderSize()
 
623
        {
 
624
            return setupHeaderSize;
 
625
        }
 
626
 
 
627
        /**
 
628
         * Return the size required by all the extra packets on same page as setup header, usually there are
 
629
         * no packets immediately after the setup packet.
 
630
         *
 
631
         * @return extra data size required for additional packets on same page
 
632
         */
 
633
        public int getExtraPacketDataSize()
 
634
        {
 
635
            int extraPacketSize = 0;
 
636
            for (OggPageHeader.PacketStartAndLength packet : packetList)
 
637
            {
 
638
                extraPacketSize += packet.getLength();
 
639
            }
 
640
            return extraPacketSize;
 
641
        }
 
642
 
 
643
        /**
 
644
         * @return the start position in the file of the ogg header which contains the start of the Vorbis Comment
 
645
         */
 
646
        public long getCommentHeaderStartPosition()
 
647
        {
 
648
            return commentHeaderStartPosition;
 
649
        }
 
650
 
 
651
        /**
 
652
         * @return the start position in the file of the ogg header which contains the start of the Setup Header
 
653
         */
 
654
        public long getSetupHeaderStartPosition()
 
655
        {
 
656
            return setupHeaderStartPosition;
 
657
        }
 
658
 
 
659
        public List<OggPageHeader.PacketStartAndLength> getExtraPacketList()
 
660
        {
 
661
            return packetList;
 
662
        }
 
663
    }
 
664
}
 
665