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