Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / ogg / oggpage.cpp
1 /***************************************************************************
2     copyright            : (C) 2002 - 2008 by Scott Wheeler
3     email                : wheeler@kde.org
4  ***************************************************************************/
5
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.                     *
10  *                                                                         *
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.                       *
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
19  *   USA                                                                   *
20  *                                                                         *
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  ***************************************************************************/
25
26 #include <tstring.h>
27 #include <tdebug.h>
28
29 #include "oggpage.h"
30 #include "oggpageheader.h"
31 #include "oggfile.h"
32
33 using namespace TagLib;
34
35 class Ogg::Page::PagePrivate
36 {
37 public:
38   PagePrivate(File *f = 0, long pageOffset = -1) :
39     file(f),
40     fileOffset(pageOffset),
41     packetOffset(0),
42     header(f, pageOffset),
43     firstPacketIndex(-1)
44   {
45     if(file) {
46       packetOffset = fileOffset + header.size();
47       packetSizes = header.packetSizes();
48       dataSize = header.dataSize();
49     }
50   }
51
52   File *file;
53   long fileOffset;
54   long packetOffset;
55   int dataSize;
56   List<int> packetSizes;
57   PageHeader header;
58   int firstPacketIndex;
59   ByteVectorList packets;
60 };
61
62 ////////////////////////////////////////////////////////////////////////////////
63 // public members
64 ////////////////////////////////////////////////////////////////////////////////
65
66 Ogg::Page::Page(Ogg::File *file, long pageOffset)
67 {
68   d = new PagePrivate(file, pageOffset);
69 }
70
71 Ogg::Page::~Page()
72 {
73   delete d;
74 }
75
76 long Ogg::Page::fileOffset() const
77 {
78   return d->fileOffset;
79 }
80
81 const Ogg::PageHeader *Ogg::Page::header() const
82 {
83   return &d->header;
84 }
85
86 int Ogg::Page::firstPacketIndex() const
87 {
88   return d->firstPacketIndex;
89 }
90
91 void Ogg::Page::setFirstPacketIndex(int index)
92 {
93   d->firstPacketIndex = index;
94 }
95
96 Ogg::Page::ContainsPacketFlags Ogg::Page::containsPacket(int index) const
97 {
98   int lastPacketIndex = d->firstPacketIndex + packetCount() - 1;
99   if(index < d->firstPacketIndex || index > lastPacketIndex)
100     return DoesNotContainPacket;
101
102   ContainsPacketFlags flags = DoesNotContainPacket;
103
104   if(index == d->firstPacketIndex)
105     flags = ContainsPacketFlags(flags | BeginsWithPacket);
106
107   if(index == lastPacketIndex)
108     flags = ContainsPacketFlags(flags | EndsWithPacket);
109
110   // If there's only one page and it's complete:
111
112   if(packetCount() == 1 &&
113      !d->header.firstPacketContinued() &&
114      d->header.lastPacketCompleted())
115   {
116     flags = ContainsPacketFlags(flags | CompletePacket);
117   }
118
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))))
127   {
128     flags = ContainsPacketFlags(flags | CompletePacket);
129   }
130
131   return flags;
132 }
133
134 TagLib::uint Ogg::Page::packetCount() const
135 {
136   return d->header.packetSizes().size();
137 }
138
139 ByteVectorList Ogg::Page::packets() const
140 {
141   if(!d->packets.isEmpty())
142     return d->packets;
143
144   ByteVectorList l;
145
146   if(d->file && d->header.isValid()) {
147
148     d->file->seek(d->packetOffset);
149
150     List<int> packetSizes = d->header.packetSizes();
151
152     List<int>::ConstIterator it = packetSizes.begin();
153     for(; it != packetSizes.end(); ++it)
154       l.append(d->file->readBlock(*it));
155   }
156   else
157     debug("Ogg::Page::packets() -- attempting to read packets from an invalid page.");
158
159   return l;
160 }
161
162 int Ogg::Page::size() const
163 {
164   return d->header.size() + d->header.dataSize();
165 }
166
167 ByteVector Ogg::Page::render() const
168 {
169   ByteVector data;
170
171   data.append(d->header.render());
172
173   if(d->packets.isEmpty()) {
174     if(d->file) {
175       d->file->seek(d->packetOffset);
176       data.append(d->file->readBlock(d->dataSize));
177     }
178     else
179       debug("Ogg::Page::render() -- this page is empty!");
180   }
181   else {
182     ByteVectorList::ConstIterator it = d->packets.begin();
183     for(; it != d->packets.end(); ++it)
184       data.append(*it);
185   }
186
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.
190
191   ByteVector checksum = ByteVector::fromUInt(data.checksum(), false);
192   for(int i = 0; i < 4; i++)
193     data[i + 22] = checksum[i];
194
195   return data;
196 }
197
198 List<Ogg::Page *> Ogg::Page::paginate(const ByteVectorList &packets,
199                                       PaginationStrategy strategy,
200                                       uint streamSerialNumber,
201                                       int firstPage,
202                                       bool firstPacketContinued,
203                                       bool lastPacketCompleted,
204                                       bool containsLastPacket)
205 {
206   List<Page *> l;
207
208   int totalSize = 0;
209
210   for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it)
211     totalSize += (*it).size();
212
213   // Handle creation of multiple pages with appropriate pagination.
214   if(strategy == Repaginate || totalSize + packets.size() > 255 * 255) {
215
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)
219
220     int pageIndex = 0;
221
222     for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
223       bool continued = false;
224
225       // mark very first packet?
226       if(firstPacketContinued && it==packets.begin()) {
227         continued = true;
228       }
229
230       // append to buf
231       ByteVector packetBuf;
232       packetBuf.append(*it);
233
234       while(packetBuf.size() > SPLITSIZE) {
235         // output a Page
236         ByteVector packetForOnePage;
237         packetForOnePage.resize(SPLITSIZE);
238         std::copy(packetBuf.begin(), packetBuf.begin() + SPLITSIZE, packetForOnePage.begin());
239
240         ByteVectorList packetList;
241         packetList.append(packetForOnePage);
242         Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued, false, false);
243         l.append(p);
244
245         pageIndex++;
246         continued = true;
247         packetBuf = packetBuf.mid(SPLITSIZE);
248       }
249
250       ByteVectorList::ConstIterator jt = it;
251       ++jt;
252       bool lastPacketInList = (jt == packets.end());
253
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);
257
258       bool isVeryLastPacket = false;
259       if(containsLastPacket) {
260         // mark the very last output page as last of stream
261         ByteVectorList::ConstIterator jt = it;
262         ++jt;
263         if(jt == packets.end()) {
264           isVeryLastPacket = true;
265         }
266       }
267
268       Page *p = new Page(packetList, streamSerialNumber, firstPage+pageIndex, continued,
269                          lastPacketInList ? lastPacketCompleted : true, 
270                          isVeryLastPacket);
271       pageIndex++;
272
273       l.append(p);
274     }
275   }
276   else {
277     Page *p = new Page(packets, streamSerialNumber, firstPage, firstPacketContinued,
278                        lastPacketCompleted, containsLastPacket);
279     l.append(p);
280   }
281
282   return l;
283 }
284
285 Ogg::Page* Ogg::Page::getCopyWithNewPageSequenceNumber(int sequenceNumber)
286 {
287   Page *pResultPage = NULL;
288
289   // TODO: a copy constructor would be helpful
290
291   if(d->file == 0) {
292     pResultPage = new Page(
293         d->packets,
294         d->header.streamSerialNumber(),
295         sequenceNumber,
296         d->header.firstPacketContinued(),
297         d->header.lastPacketCompleted(),
298         d->header.lastPageOfStream());
299   }
300   else
301   {
302     pResultPage = new Page(d->file, d->fileOffset);
303     pResultPage->d->header.setPageSequenceNumber(sequenceNumber);
304   }
305   return pResultPage;
306 }
307
308 ////////////////////////////////////////////////////////////////////////////////
309 // protected members
310 ////////////////////////////////////////////////////////////////////////////////
311
312 Ogg::Page::Page(const ByteVectorList &packets,
313                 uint streamSerialNumber,
314                 int pageNumber,
315                 bool firstPacketContinued,
316                 bool lastPacketCompleted,
317                 bool containsLastPacket)
318 {
319   d = new PagePrivate;
320
321   ByteVector data;
322   List<int> packetSizes;
323
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);
330
331   // Build a page from the list of packets.
332
333   for(ByteVectorList::ConstIterator it = packets.begin(); it != packets.end(); ++it) {
334     packetSizes.append((*it).size());
335     data.append(*it);
336   }
337   d->packets = packets;
338   d->header.setPacketSizes(packetSizes);
339 }
340