1 #include "ogg.h"
2 #include "ogg_crc.h"
3 #include <string.h>
4 
5 /********************************************************
6  Audio Tools, a module and set of tools for manipulating audio data
7  Copyright (C) 2007-2014  Brian Langenberger
8 
9  This program is free software; you can redistribute it and/or modify
10  it under the terms of the GNU General Public License as published by
11  the Free Software Foundation; either version 2 of the License, or
12  (at your option) any later version.
13 
14  This program is distributed in the hope that it will be useful,
15  but WITHOUT ANY WARRANTY; without even the implied warranty of
16  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  GNU General Public License for more details.
18 
19  You should have received a copy of the GNU General Public License
20  along with this program; if not, write to the Free Software
21  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
22 *******************************************************/
23 
24 ogg_status
25 read_ogg_page_header(BitstreamReader *ogg_stream,
26                      struct ogg_page_header *header) {
27     int i;
28     struct bs_callback callback;
29 
30     if ((header->magic_number =
31          ogg_stream->read(ogg_stream, 32)) != 0x5367674F) {
32         return OGG_INVALID_MAGIC_NUMBER;
33     }
34 
35     if ((header->version = ogg_stream->read(ogg_stream, 8)) != 0) {
36         return OGG_INVALID_STREAM_VERSION;
37     }
38 
39     header->packet_continuation = ogg_stream->read(ogg_stream, 1);
40     header->stream_beginning = ogg_stream->read(ogg_stream, 1);
41     header->stream_end = ogg_stream->read(ogg_stream, 1);
42     ogg_stream->skip(ogg_stream, 5);
43     header->granule_position = ogg_stream->read_signed_64(ogg_stream, 64);
44     header->bitstream_serial_number = ogg_stream->read(ogg_stream, 32);
45     header->sequence_number = ogg_stream->read(ogg_stream, 32);
46 
47     /*the checksum field is *not* checksummed itself, naturally
48       those 4 bytes are treated as 0*/
49     ogg_stream->pop_callback(ogg_stream, &callback);
50     if (!setjmp(*br_try(ogg_stream))) {
51         header->checksum = ogg_stream->read(ogg_stream, 32);
52         br_etry(ogg_stream);
53         ogg_stream->push_callback(ogg_stream, &callback);
54     } else {
55         /*restore callback before propagating read error*/
56         br_etry(ogg_stream);
57         ogg_stream->push_callback(ogg_stream, &callback);
58         br_abort(ogg_stream);
59     }
60     ogg_stream->call_callbacks(ogg_stream, 0);
61     ogg_stream->call_callbacks(ogg_stream, 0);
62     ogg_stream->call_callbacks(ogg_stream, 0);
63     ogg_stream->call_callbacks(ogg_stream, 0);
64 
65     header->segment_count = ogg_stream->read(ogg_stream, 8);
66     for (i = 0; i < header->segment_count; i++) {
67         header->segment_lengths[i] = ogg_stream->read(ogg_stream, 8);
68     }
69 
70     return OGG_OK;
71 }
72 
73 ogg_status
74 read_ogg_page(BitstreamReader *ogg_stream,
75               struct ogg_page *page)
76 {
77     uint32_t checksum = 0;
78 
79     if (!setjmp(*br_try(ogg_stream))) {
80         uint8_t i;
81         ogg_status result;
82 
83         /*attach checksum calculator to stream*/
84         ogg_stream->add_callback(ogg_stream, (bs_callback_f)ogg_crc, &checksum);
85 
86         /*read header*/
87         if ((result = read_ogg_page_header(ogg_stream,
88                                            &(page->header))) != OGG_OK) {
89             /*abort if error in header*/
90             ogg_stream->pop_callback(ogg_stream, NULL);
91             br_etry(ogg_stream);
92             return result;
93         }
94 
95         /*populate segments based on lengths in header*/
96         for (i = 0; i < page->header.segment_count; i++) {
97             ogg_stream->read_bytes(ogg_stream,
98                                    page->segment[i],
99                                    page->header.segment_lengths[i]);
100         }
101 
102         /*remove checksum calculator from stream*/
103         ogg_stream->pop_callback(ogg_stream, NULL);
104 
105         /*no more I/O needed for page*/
106         br_etry(ogg_stream);
107 
108         /*validate checksum*/
109         if (checksum == page->header.checksum) {
110             return OGG_OK;
111         } else {
112             return OGG_CHECKSUM_MISMATCH;
113         }
114     } else {
115         ogg_stream->pop_callback(ogg_stream, NULL);
116         br_etry(ogg_stream);
117         return OGG_PREMATURE_EOF;
118     }
119 }
120 
121 void
122 write_ogg_page_header(BitstreamWriter *ogg_stream,
123                       const struct ogg_page_header *header)
124 {
125     uint8_t i;
126     struct bs_callback callback;
127 
128     ogg_stream->write(ogg_stream, 32, header->magic_number);
129     ogg_stream->write(ogg_stream, 8, header->version);
130     ogg_stream->write(ogg_stream, 1, header->packet_continuation);
131     ogg_stream->write(ogg_stream, 1, header->stream_beginning);
132     ogg_stream->write(ogg_stream, 1, header->stream_end);
133     ogg_stream->write(ogg_stream, 5, 0);
134     ogg_stream->write_signed_64(ogg_stream, 64, header->granule_position);
135     ogg_stream->write(ogg_stream, 32, header->bitstream_serial_number);
136     ogg_stream->write(ogg_stream, 32, header->sequence_number);
137 
138     /*the checksum field is *not* checksummed itself, naturally
139       those 4 bytes are treated as 0*/
140     ogg_stream->pop_callback(ogg_stream, &callback);
141     ogg_stream->write(ogg_stream, 32, header->checksum);
142     ogg_stream->push_callback(ogg_stream, &callback);
143     ogg_stream->call_callbacks(ogg_stream, 0);
144     ogg_stream->call_callbacks(ogg_stream, 0);
145     ogg_stream->call_callbacks(ogg_stream, 0);
146     ogg_stream->call_callbacks(ogg_stream, 0);
147 
148     ogg_stream->write(ogg_stream, 8, header->segment_count);
149     for (i = 0; i < header->segment_count; i++)
150         ogg_stream->write(ogg_stream, 8, header->segment_lengths[i]);
151 }
152 
153 void
154 write_ogg_page(BitstreamWriter *ogg_stream,
155                const struct ogg_page *page)
156 {
157     bw_pos_t *checksum_pos;
158     bw_pos_t *page_end;
159     uint32_t checksum = 0;
160     uint8_t i;
161 
162     /*attach checksum calculator to stream*/
163     ogg_stream->add_callback(ogg_stream,
164                              (bs_callback_f)ogg_crc,
165                              &checksum);
166 
167     /*write page header*/
168     ogg_stream->write(ogg_stream, 32, page->header.magic_number);
169     ogg_stream->write(ogg_stream, 8, page->header.version);
170     ogg_stream->write(ogg_stream, 1, page->header.packet_continuation);
171     ogg_stream->write(ogg_stream, 1, page->header.stream_beginning);
172     ogg_stream->write(ogg_stream, 1, page->header.stream_end);
173     ogg_stream->write(ogg_stream, 5, 0);
174     ogg_stream->write_signed_64(ogg_stream, 64,
175                                 page->header.granule_position);
176     ogg_stream->write(ogg_stream, 32, page->header.bitstream_serial_number);
177     ogg_stream->write(ogg_stream, 32, page->header.sequence_number);
178 
179     /*the checksum field is *not* checksummed itself, naturally
180       those 4 bytes are treated as 0*/
181     checksum_pos = ogg_stream->getpos(ogg_stream);
182     ogg_stream->write(ogg_stream, 8, 0);
183     ogg_stream->write(ogg_stream, 8, 0);
184     ogg_stream->write(ogg_stream, 8, 0);
185     ogg_stream->write(ogg_stream, 8, 0);
186 
187     ogg_stream->write(ogg_stream, 8, page->header.segment_count);
188     for (i = 0; i < page->header.segment_count; i++)
189         ogg_stream->write(ogg_stream, 8, page->header.segment_lengths[i]);
190 
191     /*write segments*/
192     for (i = 0; i < page->header.segment_count; i++) {
193         ogg_stream->write_bytes(ogg_stream,
194                                 page->segment[i],
195                                 page->header.segment_lengths[i]);
196     }
197 
198     /*pop checksum calculator*/
199     ogg_stream->pop_callback(ogg_stream, NULL);
200 
201     /*go back and populate actual checksum*/
202     page_end = ogg_stream->getpos(ogg_stream);
203     ogg_stream->setpos(ogg_stream, checksum_pos);
204     checksum_pos->del(checksum_pos);
205     ogg_stream->write(ogg_stream, 32, checksum);
206     ogg_stream->setpos(ogg_stream, page_end);
207     page_end->del(page_end);
208 }
209 
210 
211 OggPacketIterator*
212 oggiterator_open(FILE *stream)
213 {
214     OggPacketIterator *iterator = malloc(sizeof(OggPacketIterator));
215     iterator->reader = br_open(stream, BS_LITTLE_ENDIAN);
216 
217     /*force next read to read in a new page*/
218     iterator->page.header.segment_count = 0;
219     iterator->current_segment = 1;
220     iterator->page.header.stream_end = 0;
221     return iterator;
222 }
223 
224 void
225 oggiterator_close(OggPacketIterator *iterator)
226 {
227     iterator->reader->close(iterator->reader);
228     free(iterator);
229 }
230 
231 ogg_status
232 oggiterator_next_segment(OggPacketIterator *iterator,
233                          uint8_t **segment_data,
234                          uint8_t *segment_size)
235 {
236     if (iterator->current_segment < iterator->page.header.segment_count) {
237         /*return Ogg segment from current page*/
238         *segment_size =
239             iterator->page.header.segment_lengths[iterator->current_segment];
240         *segment_data =
241             iterator->page.segment[iterator->current_segment];
242         iterator->current_segment++;
243         return OGG_OK;
244     } else {
245         ogg_status result;
246 
247         /*current page's segments exhausted
248           so read another unless the page is marked as the last*/
249         if (!iterator->page.header.stream_end) {
250             if ((result = read_ogg_page(iterator->reader,
251                                         &(iterator->page))) == OGG_OK) {
252                 iterator->current_segment = 0;
253                 return oggiterator_next_segment(iterator,
254                                                 segment_data,
255                                                 segment_size);
256             } else {
257                 return result;
258             }
259         } else {
260             return OGG_STREAM_FINISHED;
261         }
262     }
263 }
264 
265 BitstreamReader*
266 oggiterator_next_packet(OggPacketIterator *iterator,
267                         bs_endianness endianness,
268                         ogg_status *result)
269 {
270     BitstreamQueue *packet = br_open_queue(endianness);
271     uint8_t *segment_data;
272     uint8_t segment_length;
273 
274     do {
275         if ((*result = oggiterator_next_segment(iterator,
276                                                 &segment_data,
277                                                 &segment_length)) == OGG_OK) {
278             packet->push(packet, segment_length, segment_data);
279         }
280     } while ((*result == OGG_OK) && (segment_length == 255));
281 
282     if (*result == OGG_OK) {
283         BitstreamReader *converted =
284             packet->substream((BitstreamReader*)packet, packet->size(packet));
285         packet->close(packet);
286         return converted;
287     } else {
288         packet->close(packet);
289         return NULL;
290     }
291 }
292 
293 
294 char *
295 ogg_strerror(ogg_status err) {
296     switch (err) {
297     case OGG_OK:                    /*not an actual error*/
298         return "no error";
299     case OGG_STREAM_FINISHED:       /*not an actual error*/
300         return "stream finished";
301     case OGG_INVALID_MAGIC_NUMBER:
302         return "invalid magic number";
303     case OGG_INVALID_STREAM_VERSION:
304         return "invalid stream version";
305     case OGG_CHECKSUM_MISMATCH:
306         return "checksum mismatch";
307     case OGG_PREMATURE_EOF:
308         return "premature EOF reading Ogg stream";
309     }
310     return "unknown error"; /*shouldn't get here*/
311 }
312 
313 #ifndef STANDALONE
314 PyObject*
315 ogg_exception(ogg_status err) {
316     switch (err) {
317     case OGG_PREMATURE_EOF:
318     case OGG_STREAM_FINISHED:       /*not an actual error*/
319         return PyExc_IOError;
320     case OGG_INVALID_MAGIC_NUMBER:
321     case OGG_INVALID_STREAM_VERSION:
322     case OGG_CHECKSUM_MISMATCH:
323         return PyExc_ValueError;
324     case OGG_OK:                    /*not an actual error*/
325     default:
326         return PyExc_ValueError;
327     }
328 }
329 #endif
330 
331 #ifdef EXECUTABLE
332 int main(int argc, char *argv[]) {
333     /*perform simple round-trip using page reader and writer*/
334 
335     BitstreamReader *reader = br_open(stdin, BS_LITTLE_ENDIAN);
336     BitstreamWriter *writer = bw_open(stdout, BS_LITTLE_ENDIAN);
337     struct ogg_page page;
338 
339     do {
340         ogg_status result;
341         if ((result = read_ogg_page(reader, &page)) == OGG_OK) {
342             write_ogg_page(writer, &page);
343         } else {
344             fprintf(stderr, "*** Error: %s", ogg_strerror(result));
345             goto error;
346         }
347     } while (!page.header.stream_end);
348 
349     reader->close(reader);
350     writer->close(writer);
351     return 0;
352 
353 error:
354     reader->close(reader);
355     writer->close(writer);
356     return 1;
357 }
358 #endif
359