1 /***************************************************************************
2 copyright : (C) 2002 - 2008 by Scott Wheeler
3 email : wheeler@kde.org
4 ***************************************************************************/
6 /***************************************************************************
7 * This library is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU Lesser General Public License version *
9 * 2.1 as published by the Free Software Foundation. *
11 * This library is distributed in the hope that it will be useful, but *
12 * 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. *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
21 * Alternatively, this file is available under the Mozilla Public *
22 * License Version 1.1. You may obtain a copy of the License at *
23 * http://www.mozilla.org/MPL/ *
24 ***************************************************************************/
30 #include "oggpageheader.h"
33 using namespace TagLib;
35 class Ogg::Page::PagePrivate
38 PagePrivate(File *f = 0, long pageOffset = -1) :
40 fileOffset(pageOffset),
42 header(f, pageOffset),
46 packetOffset = fileOffset + header.size();
47 packetSizes = header.packetSizes();
48 dataSize = header.dataSize();
56 List<int> packetSizes;
59 ByteVectorList packets;
62 ////////////////////////////////////////////////////////////////////////////////
64 ////////////////////////////////////////////////////////////////////////////////
66 Ogg::Page::Page(Ogg::File *file, long pageOffset)
68 d = new PagePrivate(file, pageOffset);
76 long Ogg::Page::fileOffset() const
81 const Ogg::PageHeader *Ogg::Page::header() const
86 int Ogg::Page::firstPacketIndex() const
88 return d->firstPacketIndex;
91 void Ogg::Page::setFirstPacketIndex(int index)
93 d->firstPacketIndex = index;
96 Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const
98 int lastPacketIndex = d->firstPacketIndex + packetCount() - 1;
99 if(index < d->firstPacketIndex || index > lastPacketIndex)
100 return DoesNotContainPacket;
102 ContainsPacketFlags flags = DoesNotContainPacket;
104 if(index == d->firstPacketIndex)
105 flags = ContainsPacketFlags(flags | BeginsWithPacket);
107 if(index == lastPacketIndex)
108 flags = ContainsPacketFlags(flags | EndsWithPacket);
110 // If there's only one page and it's complete:
112 if(packetCount() == 1 &&
113 !d->header.firstPacketContinued() &&
114 d->header.lastPacketCompleted())
116 flags = ContainsPacketFlags(flags | CompletePacket);
119 // Or if the page is (a) the first page and it's complete or (b) the last page
120 // and it's complete or (c) a page in the middle.
122 else if((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
123 (flags & EndsWithPacket && d->header.lastPacketCompleted()) ||
124 (!(flags & BeginsWithPacket) && !(flags & EndsWithPacket)))
126 flags = ContainsPacketFlags(flags | CompletePacket);
132 TagLib::uint Ogg::Page::packetCount() const
134 return d->header.packetSizes().size();
137 ByteVectorList Ogg::Page::packets() const
139 if(!d->packets.isEmpty())
144 if(d->file && d->header.isValid()) {
146 d->file->seek(d->packetOffset);
148 List<int> packetSizes = d->header.packetSizes();
150 List<int>::ConstIterator it = packetSizes.begin();
151 for(; it != packetSizes.end(); ++it)
152 l.append(d->file->readBlock(*it));
155 debug("Ogg::Page::packets() -- attempting to read packets from an invalid page.");
160 int Ogg::Page::size() const
162 return d->header.size() + d->header.dataSize();
165 ByteVector Ogg::Page::render() const
169 data.append(d->header.render());
171 if(d->packets.isEmpty()) {
173 d->file->seek(d->packetOffset);
174 data.append(d->file->readBlock(d->dataSize));
177 debug("Ogg::Page::render() -- this page is empty!");
180 ByteVectorList::ConstIterator it = d->packets.begin();
181 for(; it != d->packets.end(); ++it)
185 // Compute and set the checksum for the Ogg page. The checksum is taken over
186 // the entire page with the 4 bytes reserved for the checksum zeroed and then
187 // inserted in bytes 22-25 of the page header.
189 ByteVector checksum = ByteVector::fromUInt(data.checksum(), false);
190 for(int i = 0; i < 4; i++)
191 data[i + 22] = checksum[i];
196 List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
197 PaginationStrategy strategy,
198 uint streamSerialNumber,
200 bool firstPacketContinued,
201 bool lastPacketCompleted,
202 bool containsLastPacket)
208 for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
209 totalSize += (*it).size();
211 if(strategy == Repaginate || totalSize + packets.size() > 255 * 256) {
212 debug("Ogg::Page::paginate() -- Sorry! Repagination is not yet implemented.");
216 // TODO: Handle creation of multiple pages here with appropriate pagination.
218 Page *p = new Page(packets, streamSerialNumber, firstPage, firstPacketContinued,
219 lastPacketCompleted, containsLastPacket);
225 ////////////////////////////////////////////////////////////////////////////////
227 ////////////////////////////////////////////////////////////////////////////////
229 Ogg::Page::Page(const ByteVectorList &packets,
230 uint streamSerialNumber,
232 bool firstPacketContinued,
233 bool lastPacketCompleted,
234 bool containsLastPacket)
239 List<int> packetSizes;
241 d->header.setFirstPageOfStream(pageNumber == 0 && !firstPacketContinued);
242 d->header.setLastPageOfStream(containsLastPacket);
243 d->header.setFirstPacketContinued(firstPacketContinued);
244 d->header.setLastPacketCompleted(lastPacketCompleted);
245 d->header.setStreamSerialNumber(streamSerialNumber);
246 d->header.setPageSequenceNumber(pageNumber);
248 // Build a page from the list of packets.
250 for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
251 packetSizes.append((*it).size());
254 d->packets = packets;
255 d->header.setPacketSizes(packetSizes);