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 there is more than one page and the page is
120 // (a) the first page and it's complete or
121 // (b) the last page and it's complete or
122 // (c) a page in the middle.
123 else if(packetCount() > 1 &&
124 ((flags & BeginsWithPacket && !d->header.firstPacketContinued()) ||
125 (flags & EndsWithPacket && d->header.lastPacketCompleted()) ||
126 (!(flags & BeginsWithPacket) && !(flags & EndsWithPacket))))
128 flags = ContainsPacketFlags(flags | CompletePacket);
134 TagLib::uint Ogg::Page::packetCount() const
136 return d->header.packetSizes().size();
139 ByteVectorList Ogg::Page::packets() const
141 if(!d->packets.isEmpty())
146 if(d->file && d->header.isValid()) {
148 d->file->seek(d->packetOffset);
150 List<int> packetSizes = d->header.packetSizes();
152 List<int>::ConstIterator it = packetSizes.begin();
153 for(; it != packetSizes.end(); ++it)
154 l.append(d->file->readBlock(*it));
157 debug("Ogg::Page::packets() -- attempting to read packets from an invalid page.");
162 int Ogg::Page::size() const
164 return d->header.size() + d->header.dataSize();
167 ByteVector Ogg::Page::render() const
171 data.append(d->header.render());
173 if(d->packets.isEmpty()) {
175 d->file->seek(d->packetOffset);
176 data.append(d->file->readBlock(d->dataSize));
179 debug("Ogg::Page::render() -- this page is empty!");
182 ByteVectorList::ConstIterator it = d->packets.begin();
183 for(; it != d->packets.end(); ++it)
187 // Compute and set the checksum for the Ogg page. The checksum is taken over
188 // the entire page with the 4 bytes reserved for the checksum zeroed and then
189 // inserted in bytes 22-25 of the page header.
191 ByteVector checksum = ByteVector::fromUInt(data.checksum(), false);
192 for(int i = 0; i < 4; i++)
193 data[i + 22] = checksum[i];
198 List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
199 PaginationStrategy strategy,
200 uint streamSerialNumber,
202 bool firstPacketContinued,
203 bool lastPacketCompleted,
204 bool containsLastPacket)
210 for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
211 totalSize += (*it).size();
213 // Handle creation of multiple pages with appropriate pagination.
214 if(strategy == Repaginate || totalSize + packets.size() > 255 * 255) {
216 // SPLITSIZE must be a multiple of 255 in order to get the lacing values right
217 // create pages of about 8KB each
218 #define SPLITSIZE (32*255)
222 for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
223 bool continued = false;
225 // mark very first packet?
226 if(firstPacketContinued && it==packets.begin()) {
231 ByteVector packetBuf;
232 packetBuf.append(*it);
234 while(packetBuf.size() > SPLITSIZE) {
236 ByteVector packetForOnePage;
237 packetForOnePage.resize(SPLITSIZE);
238 std::copy(packetBuf.begin(), packetBuf.begin() + SPLITSIZE, packetForOnePage.begin());
240 ByteVectorList packetList;
241 packetList.append(packetForOnePage);
242 Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued, false, false);
247 packetBuf = packetBuf.mid(SPLITSIZE);
250 ByteVectorList::ConstIterator jt = it;
252 bool lastPacketInList = (jt == packets.end());
254 // output a page for the rest (we output one packet per page, so this one should be completed)
255 ByteVectorList packetList;
256 packetList.append(packetBuf);
258 bool isVeryLastPacket = false;
259 if(containsLastPacket) {
260 // mark the very last output page as last of stream
261 ByteVectorList::ConstIterator jt = it;
263 if(jt == packets.end()) {
264 isVeryLastPacket = true;
268 Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued,
269 lastPacketInList ? lastPacketCompleted : true,
277 Page *p = new Page(packets, streamSerialNumber, firstPage, firstPacketContinued,
278 lastPacketCompleted, containsLastPacket);
285 Ogg::Page* Ogg::Page::getCopyWithNewPageSequenceNumber(int sequenceNumber)
287 Page *pResultPage = NULL;
289 // TODO: a copy constructor would be helpful
292 pResultPage = new Page(
294 d->header.streamSerialNumber(),
296 d->header.firstPacketContinued(),
297 d->header.lastPacketCompleted(),
298 d->header.lastPageOfStream());
302 pResultPage = new Page(d->file, d->fileOffset);
303 pResultPage->d->header.setPageSequenceNumber(sequenceNumber);
308 ////////////////////////////////////////////////////////////////////////////////
310 ////////////////////////////////////////////////////////////////////////////////
312 Ogg::Page::Page(const ByteVectorList &packets,
313 uint streamSerialNumber,
315 bool firstPacketContinued,
316 bool lastPacketCompleted,
317 bool containsLastPacket)
322 List<int> packetSizes;
324 d->header.setFirstPageOfStream(pageNumber == 0 && !firstPacketContinued);
325 d->header.setLastPageOfStream(containsLastPacket);
326 d->header.setFirstPacketContinued(firstPacketContinued);
327 d->header.setLastPacketCompleted(lastPacketCompleted);
328 d->header.setStreamSerialNumber(streamSerialNumber);
329 d->header.setPageSequenceNumber(pageNumber);
331 // Build a page from the list of packets.
333 for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
334 packetSizes.append((*it).size());
337 d->packets = packets;
338 d->header.setPacketSizes(packetSizes);