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