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., 51 Franklin Street, Fifth Floor, Boston, MA *
19 * 02110-1301 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 <bitset>
27
28 #include <tstring.h>
29 #include <tdebug.h>
30 #include <taglib.h>
31
32 #include "oggpageheader.h"
33 #include "oggfile.h"
34
35 using namespace TagLib;
36
37 class Ogg::PageHeader::PageHeaderPrivate
38 {
39 public:
PageHeaderPrivate()40 PageHeaderPrivate() :
41 isValid(false),
42 firstPacketContinued(false),
43 lastPacketCompleted(false),
44 firstPageOfStream(false),
45 lastPageOfStream(false),
46 absoluteGranularPosition(0),
47 streamSerialNumber(0),
48 pageSequenceNumber(-1),
49 size(0),
50 dataSize(0) {}
51
52 bool isValid;
53 List<int> packetSizes;
54 bool firstPacketContinued;
55 bool lastPacketCompleted;
56 bool firstPageOfStream;
57 bool lastPageOfStream;
58 long long absoluteGranularPosition;
59 unsigned int streamSerialNumber;
60 int pageSequenceNumber;
61 int size;
62 int dataSize;
63 };
64
65 ////////////////////////////////////////////////////////////////////////////////
66 // public members
67 ////////////////////////////////////////////////////////////////////////////////
68
PageHeader(Ogg::File * file,long pageOffset)69 Ogg::PageHeader::PageHeader(Ogg::File *file, long pageOffset) :
70 d(new PageHeaderPrivate())
71 {
72 if(file && pageOffset >= 0)
73 read(file, pageOffset);
74 }
75
~PageHeader()76 Ogg::PageHeader::~PageHeader()
77 {
78 delete d;
79 }
80
isValid() const81 bool Ogg::PageHeader::isValid() const
82 {
83 return d->isValid;
84 }
85
packetSizes() const86 List<int> Ogg::PageHeader::packetSizes() const
87 {
88 return d->packetSizes;
89 }
90
setPacketSizes(const List<int> & sizes)91 void Ogg::PageHeader::setPacketSizes(const List<int> &sizes)
92 {
93 d->packetSizes = sizes;
94 }
95
firstPacketContinued() const96 bool Ogg::PageHeader::firstPacketContinued() const
97 {
98 return d->firstPacketContinued;
99 }
100
setFirstPacketContinued(bool continued)101 void Ogg::PageHeader::setFirstPacketContinued(bool continued)
102 {
103 d->firstPacketContinued = continued;
104 }
105
lastPacketCompleted() const106 bool Ogg::PageHeader::lastPacketCompleted() const
107 {
108 return d->lastPacketCompleted;
109 }
110
setLastPacketCompleted(bool completed)111 void Ogg::PageHeader::setLastPacketCompleted(bool completed)
112 {
113 d->lastPacketCompleted = completed;
114 }
115
firstPageOfStream() const116 bool Ogg::PageHeader::firstPageOfStream() const
117 {
118 return d->firstPageOfStream;
119 }
120
setFirstPageOfStream(bool first)121 void Ogg::PageHeader::setFirstPageOfStream(bool first)
122 {
123 d->firstPageOfStream = first;
124 }
125
lastPageOfStream() const126 bool Ogg::PageHeader::lastPageOfStream() const
127 {
128 return d->lastPageOfStream;
129 }
130
setLastPageOfStream(bool last)131 void Ogg::PageHeader::setLastPageOfStream(bool last)
132 {
133 d->lastPageOfStream = last;
134 }
135
absoluteGranularPosition() const136 long long Ogg::PageHeader::absoluteGranularPosition() const
137 {
138 return d->absoluteGranularPosition;
139 }
140
setAbsoluteGranularPosition(long long agp)141 void Ogg::PageHeader::setAbsoluteGranularPosition(long long agp)
142 {
143 d->absoluteGranularPosition = agp;
144 }
145
pageSequenceNumber() const146 int Ogg::PageHeader::pageSequenceNumber() const
147 {
148 return d->pageSequenceNumber;
149 }
150
setPageSequenceNumber(int sequenceNumber)151 void Ogg::PageHeader::setPageSequenceNumber(int sequenceNumber)
152 {
153 d->pageSequenceNumber = sequenceNumber;
154 }
155
streamSerialNumber() const156 unsigned int Ogg::PageHeader::streamSerialNumber() const
157 {
158 return d->streamSerialNumber;
159 }
160
setStreamSerialNumber(unsigned int n)161 void Ogg::PageHeader::setStreamSerialNumber(unsigned int n)
162 {
163 d->streamSerialNumber = n;
164 }
165
size() const166 int Ogg::PageHeader::size() const
167 {
168 return d->size;
169 }
170
dataSize() const171 int Ogg::PageHeader::dataSize() const
172 {
173 return d->dataSize;
174 }
175
render() const176 ByteVector Ogg::PageHeader::render() const
177 {
178 ByteVector data;
179
180 // capture pattern
181
182 data.append("OggS");
183
184 // stream structure version
185
186 data.append(char(0));
187
188 // header type flag
189
190 std::bitset<8> flags;
191 flags[0] = d->firstPacketContinued;
192 flags[1] = d->pageSequenceNumber == 0;
193 flags[2] = d->lastPageOfStream;
194
195 data.append(char(flags.to_ulong()));
196
197 // absolute granular position
198
199 data.append(ByteVector::fromLongLong(d->absoluteGranularPosition, false));
200
201 // stream serial number
202
203 data.append(ByteVector::fromUInt(d->streamSerialNumber, false));
204
205 // page sequence number
206
207 data.append(ByteVector::fromUInt(d->pageSequenceNumber, false));
208
209 // checksum -- this is left empty and should be filled in by the Ogg::Page
210 // class
211
212 data.append(ByteVector(4, 0));
213
214 // page segment count and page segment table
215
216 ByteVector pageSegments = lacingValues();
217
218 data.append(static_cast<unsigned char>(pageSegments.size()));
219 data.append(pageSegments);
220
221 return data;
222 }
223
224 ////////////////////////////////////////////////////////////////////////////////
225 // private members
226 ////////////////////////////////////////////////////////////////////////////////
227
read(Ogg::File * file,long pageOffset)228 void Ogg::PageHeader::read(Ogg::File *file, long pageOffset)
229 {
230 file->seek(pageOffset);
231
232 // An Ogg page header is at least 27 bytes, so we'll go ahead and read that
233 // much and then get the rest when we're ready for it.
234
235 const ByteVector data = file->readBlock(27);
236
237 // Sanity check -- make sure that we were in fact able to read as much data as
238 // we asked for and that the page begins with "OggS".
239
240 if(data.size() != 27 || !data.startsWith("OggS")) {
241 debug("Ogg::PageHeader::read() -- error reading page header");
242 return;
243 }
244
245 const std::bitset<8> flags(data[5]);
246
247 d->firstPacketContinued = flags.test(0);
248 d->firstPageOfStream = flags.test(1);
249 d->lastPageOfStream = flags.test(2);
250
251 d->absoluteGranularPosition = data.toLongLong(6, false);
252 d->streamSerialNumber = data.toUInt(14, false);
253 d->pageSequenceNumber = data.toUInt(18, false);
254
255 // Byte number 27 is the number of page segments, which is the only variable
256 // length portion of the page header. After reading the number of page
257 // segments we'll then read in the corresponding data for this count.
258
259 int pageSegmentCount = static_cast<unsigned char>(data[26]);
260
261 const ByteVector pageSegments = file->readBlock(pageSegmentCount);
262
263 // Another sanity check.
264
265 if(pageSegmentCount < 1 || int(pageSegments.size()) != pageSegmentCount)
266 return;
267
268 // The base size of an Ogg page 27 bytes plus the number of lacing values.
269
270 d->size = 27 + pageSegmentCount;
271
272 int packetSize = 0;
273
274 for(int i = 0; i < pageSegmentCount; i++) {
275 d->dataSize += static_cast<unsigned char>(pageSegments[i]);
276 packetSize += static_cast<unsigned char>(pageSegments[i]);
277
278 if(static_cast<unsigned char>(pageSegments[i]) < 255) {
279 d->packetSizes.append(packetSize);
280 packetSize = 0;
281 }
282 }
283
284 if(packetSize > 0) {
285 d->packetSizes.append(packetSize);
286 d->lastPacketCompleted = false;
287 }
288 else
289 d->lastPacketCompleted = true;
290
291 d->isValid = true;
292 }
293
lacingValues() const294 ByteVector Ogg::PageHeader::lacingValues() const
295 {
296 ByteVector data;
297
298 for(List<int>::ConstIterator it = d->packetSizes.begin(); it != d->packetSizes.end(); ++it) {
299
300 // The size of a packet in an Ogg page is indicated by a series of "lacing
301 // values" where the sum of the values is the packet size in bytes. Each of
302 // these values is a byte. A value of less than 255 (0xff) indicates the end
303 // of the packet.
304
305 data.resize(data.size() + (*it / 255), '\xff');
306
307 if(it != --d->packetSizes.end() || d->lastPacketCompleted)
308 data.append(static_cast<unsigned char>(*it % 255));
309 }
310
311 return data;
312 }
313