/********** This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. (See .) This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA **********/ // "liveMedia" // Copyright (c) 1996-2016 Live Networks, Inc. All rights reserved. // A parser for an Ogg file. // Implementation #include "OggFileParser.hh" #include "OggDemuxedTrack.hh" #include // for "gettimeofday() PacketSizeTable::PacketSizeTable(unsigned number_page_segments) : numCompletedPackets(0), totSizes(0), nextPacketNumToDeliver(0), lastPacketIsIncomplete(False) { size = new unsigned[number_page_segments]; for (unsigned i = 0; i < number_page_segments; ++i) size[i] = 0; } PacketSizeTable::~PacketSizeTable() { delete[] size; } OggFileParser::OggFileParser(OggFile& ourFile, FramedSource* inputSource, FramedSource::onCloseFunc* onEndFunc, void* onEndClientData, OggDemux* ourDemux) : StreamParser(inputSource, onEndFunc, onEndClientData, continueParsing, this), fOurFile(ourFile), fInputSource(inputSource), fOnEndFunc(onEndFunc), fOnEndClientData(onEndClientData), fOurDemux(ourDemux), fNumUnfulfilledTracks(0), fPacketSizeTable(NULL), fCurrentTrackNumber(0), fSavedPacket(NULL) { if (ourDemux == NULL) { // Initialization fCurrentParseState = PARSING_START_OF_FILE; continueParsing(); } else { fCurrentParseState = PARSING_AND_DELIVERING_PAGES; // In this case, parsing (of page data) doesn't start until a client starts reading from a track. } } OggFileParser::~OggFileParser() { delete[] fSavedPacket; delete fPacketSizeTable; Medium::close(fInputSource); } void OggFileParser::continueParsing(void* clientData, unsigned char* ptr, unsigned size, struct timeval presentationTime) { ((OggFileParser*)clientData)->continueParsing(); } void OggFileParser::continueParsing() { if (fInputSource != NULL) { if (fInputSource->isCurrentlyAwaitingData()) return; // Our input source is currently being read. Wait until that read completes if (!parse()) { // We didn't complete the parsing, because we had to read more data from the source, // or because we're waiting for another read from downstream. // Once that happens, we'll get called again. return; } } // We successfully parsed the file. Call our 'done' function now: if (fOnEndFunc != NULL) (*fOnEndFunc)(fOnEndClientData); } Boolean OggFileParser::parse() { try { while (1) { switch (fCurrentParseState) { case PARSING_START_OF_FILE: { if (parseStartOfFile()) return True; } case PARSING_AND_DELIVERING_PAGES: { parseAndDeliverPages(); } case DELIVERING_PACKET_WITHIN_PAGE: { if (deliverPacketWithinPage()) return False; } } } } catch (int /*e*/) { #ifdef DEBUG fprintf(stderr, "OggFileParser::parse() EXCEPTION (This is normal behavior - *not* an error)\n"); #endif return False; // the parsing got interrupted } } Boolean OggFileParser::parseStartOfFile() { #ifdef DEBUG fprintf(stderr, "parsing start of file\n"); #endif // Read and parse each 'page', until we see the first non-BOS page, or until we have // collected all required headers for Vorbis, Theora, or Opus track(s) (if any). u_int8_t header_type_flag; do { header_type_flag = parseInitialPage(); } while ((header_type_flag&0x02) != 0 || needHeaders()); #ifdef DEBUG fprintf(stderr, "Finished parsing start of file\n"); #endif return True; } static u_int32_t byteSwap(u_int32_t x) { return (x<<24)|((x<<8)&0x00FF0000)|((x>>8)&0x0000FF00)|(x>>24); } u_int8_t OggFileParser::parseInitialPage() { u_int8_t header_type_flag; u_int32_t bitstream_serial_number; parseStartOfPage(header_type_flag, bitstream_serial_number); // If this is a BOS page, examine the first 8 bytes of the first 'packet', to see whether // the track data type is one that we know how to stream: OggTrack* track; if ((header_type_flag&0x02) != 0) { // BOS char const* mimeType = NULL; // if unknown if (fPacketSizeTable != NULL && fPacketSizeTable->size[0] >= 8) { // sanity check char buf[8]; testBytes((u_int8_t*)buf, 8); if (strncmp(&buf[1], "vorbis", 6) == 0) { mimeType = "audio/VORBIS"; ++fNumUnfulfilledTracks; } else if (strncmp(buf, "OpusHead", 8) == 0) { mimeType = "audio/OPUS"; ++fNumUnfulfilledTracks; } else if (strncmp(&buf[1], "theora", 6) == 0) { mimeType = "video/THEORA"; ++fNumUnfulfilledTracks; } } // Add a new track descriptor for this track: track = new OggTrack; track->trackNumber = bitstream_serial_number; track->mimeType = mimeType; fOurFile.addTrack(track); } else { // not a BOS page // Because this is not a BOS page, the specified track should already have been seen: track = fOurFile.lookup(bitstream_serial_number); } if (track != NULL) { // sanity check #ifdef DEBUG fprintf(stderr, "This track's MIME type: %s\n", track->mimeType == NULL ? "(unknown)" : track->mimeType); #endif if (track->mimeType != NULL && (strcmp(track->mimeType, "audio/VORBIS") == 0 || strcmp(track->mimeType, "video/THEORA") == 0 || strcmp(track->mimeType, "audio/OPUS") == 0)) { // Special-case handling of Vorbis, Theora, or Opus tracks: // Make a copy of each packet, until we get the three special headers that we need: Boolean isVorbis = strcmp(track->mimeType, "audio/VORBIS") == 0; Boolean isTheora = strcmp(track->mimeType, "video/THEORA") == 0; for (unsigned j = 0; j < fPacketSizeTable->numCompletedPackets && track->weNeedHeaders(); ++j) { unsigned const packetSize = fPacketSizeTable->size[j]; if (packetSize == 0) continue; // sanity check delete[] fSavedPacket/*if any*/; fSavedPacket = new u_int8_t[packetSize]; getBytes(fSavedPacket, packetSize); fPacketSizeTable->totSizes -= packetSize; // The start of the packet tells us whether its a header that we know about: Boolean headerIsKnown = False; unsigned index = 0; if (isVorbis) { u_int8_t const firstByte = fSavedPacket[0]; headerIsKnown = firstByte == 1 || firstByte == 3 || firstByte == 5; index = (firstByte-1)/2; // 1, 3, or 5 => 0, 1, or 2 } else if (isTheora) { u_int8_t const firstByte = fSavedPacket[0]; headerIsKnown = firstByte == 0x80 || firstByte == 0x81 || firstByte == 0x82; index = firstByte &~0x80; // 0x80, 0x81, or 0x82 => 0, 1, or 2 } else { // Opus if (strncmp((char const*)fSavedPacket, "OpusHead", 8) == 0) { headerIsKnown = True; index = 0; // "identification" header } else if (strncmp((char const*)fSavedPacket, "OpusTags", 8) == 0) { headerIsKnown = True; index = 1; // "comment" header } } if (headerIsKnown) { #ifdef DEBUG char const* headerName[3] = { "identification", "comment", "setup" }; fprintf(stderr, "Saved %d-byte %s \"%s\" header\n", packetSize, track->mimeType, headerName[index]); #endif // This is a header, but first check it for validity: if (!validateHeader(track, fSavedPacket, packetSize)) continue; // Save this header (deleting any old header of the same type that we'd saved before) delete[] track->vtoHdrs.header[index]; track->vtoHdrs.header[index] = fSavedPacket; fSavedPacket = NULL; track->vtoHdrs.headerSize[index] = packetSize; if (!track->weNeedHeaders()) { // We now have all of the needed Vorbis, Theora, or Opus headers for this track: --fNumUnfulfilledTracks; } // Note: The above code won't work if a required header is fragmented over // more than one 'page'. We assume that that won't ever happen... } } } } // Skip over any remaining packet data bytes: if (fPacketSizeTable->totSizes > 0) { #ifdef DEBUG fprintf(stderr, "Skipping %d remaining packet data bytes\n", fPacketSizeTable->totSizes); #endif skipBytes(fPacketSizeTable->totSizes); } return header_type_flag; } // A simple bit vector class for reading bits in little-endian order. // (We can't use our usual "BitVector" class, because that's big-endian.) class LEBitVector { public: LEBitVector(u_int8_t const* p, unsigned numBytes) : fPtr(p), fEnd(&p[numBytes]), fNumBitsRemainingInCurrentByte(8) { } u_int32_t getBits(unsigned numBits/*<=32*/) { if (noMoreBits()) { return 0; } else if (numBits == fNumBitsRemainingInCurrentByte) { u_int32_t result = (*fPtr++)>>(8-fNumBitsRemainingInCurrentByte); fNumBitsRemainingInCurrentByte = 8; return result; } else if (numBits < fNumBitsRemainingInCurrentByte) { u_int8_t mask = 0xFF>>(8-numBits); u_int32_t result = ((*fPtr)>>(8-fNumBitsRemainingInCurrentByte)) & mask; fNumBitsRemainingInCurrentByte -= numBits; return result; } else { // numBits > fNumBitsRemainingInCurrentByte // Do two recursive calls to get the result: unsigned nbr = fNumBitsRemainingInCurrentByte; u_int32_t firstBits = getBits(nbr); u_int32_t nextBits = getBits(numBits - nbr); return (nextBits< 32) { (void)getBits(32); numBits -= 32; } (void)getBits(numBits); } unsigned numBitsRemaining() { return (fEnd-fPtr-1)*8 + fNumBitsRemainingInCurrentByte; } Boolean noMoreBits() const { return fPtr >= fEnd; } private: u_int8_t const* fPtr; u_int8_t const* fEnd; unsigned fNumBitsRemainingInCurrentByte; // 1..8 }; static unsigned ilog(int n) { if (n < 0) return 0; unsigned x = (unsigned)n; unsigned result = 0; while (x > 0) { ++result; x >>= 1; } return result; } static unsigned lookup1_values(unsigned codebook_entries, unsigned codebook_dimensions) { // "the greatest integer value for which [return_value] to the power of [codebook_dimensions] // is less than or equal to [codebook_entries]" unsigned return_value = 0; unsigned powerValue; do { ++return_value; // Compute powerValue = return_value ** codebook_dimensions if (return_value == 1) powerValue = 1; // optimization else { powerValue = 1; for (unsigned i = 0; i < codebook_dimensions; ++i) { powerValue *= return_value; } } } while (powerValue <= codebook_entries); return_value -= 1; return return_value; } static Boolean parseVorbisSetup_codebook(LEBitVector& bv) { if (bv.noMoreBits()) return False; unsigned sync = bv.getBits(24); if (sync != 0x564342) return False; unsigned codebook_dimensions = bv.getBits(16); unsigned codebook_entries = bv.getBits(24); unsigned ordered = bv.getBits(1); #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\t\t\tcodebook_dimensions: %d; codebook_entries: %d, ordered: %d\n", codebook_dimensions, codebook_entries, ordered); #endif if (!ordered) { unsigned sparse = bv.getBits(1); #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\t\t\t!ordered: sparse %d\n", sparse); #endif for (unsigned i = 0; i < codebook_entries; ++i) { unsigned codewordLength; if (sparse) { unsigned flag = bv.getBits(1); if (flag) { codewordLength = bv.getBits(5) + 1; } else { codewordLength = 0; } } else { codewordLength = bv.getBits(5) + 1; } #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\t\t\t\tcodeword length[%d]:\t%d\n", i, codewordLength); #else codewordLength = codewordLength; // to prevent compiler warning #endif } } else { // ordered #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\t\t\tordered:\n"); #endif unsigned current_entry = 0; unsigned current_length = bv.getBits(5) + 1; do { unsigned number = bv.getBits(ilog(codebook_entries - current_entry)); #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\t\t\t\tcodeword length[%d..%d]:\t%d\n", current_entry, current_entry + number - 1, current_length); #endif current_entry += number; if (current_entry > codebook_entries) { fprintf(stderr, "Vorbis codebook parsing error: current_entry %d > codebook_entries %d!\n", current_entry, codebook_entries); return False; } ++current_length; } while (current_entry < codebook_entries); } unsigned codebook_lookup_type = bv.getBits(4); #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\t\t\tcodebook_lookup_type: %d\n", codebook_lookup_type); #endif if (codebook_lookup_type > 2) { fprintf(stderr, "Vorbis codebook parsing error: codebook_lookup_type %d!\n", codebook_lookup_type); return False; } else if (codebook_lookup_type > 0) { // 1 or 2 bv.skipBits(32+32); // "codebook_minimum_value" and "codebook_delta_value" unsigned codebook_value_bits = bv.getBits(4) + 1; bv.skipBits(1); // "codebook_lookup_p" unsigned codebook_lookup_values; if (codebook_lookup_type == 1) { codebook_lookup_values = lookup1_values(codebook_entries, codebook_dimensions); } else { // 2 codebook_lookup_values = codebook_entries*codebook_dimensions; } bv.skipBits(codebook_lookup_values*codebook_value_bits); // "codebook_multiplicands" } return True; } static Boolean parseVorbisSetup_codebooks(LEBitVector& bv) { if (bv.noMoreBits()) return False; unsigned vorbis_codebook_count = bv.getBits(8) + 1; #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\tCodebooks: vorbis_codebook_count: %d\n", vorbis_codebook_count); #endif for (unsigned i = 0; i < vorbis_codebook_count; ++i) { #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\t\tCodebook %d:\n", i); #endif if (!parseVorbisSetup_codebook(bv)) return False; } return True; } static Boolean parseVorbisSetup_timeDomainTransforms(LEBitVector& bv) { if (bv.noMoreBits()) return False; unsigned vorbis_time_count = bv.getBits(6) + 1; #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\tTime domain transforms: vorbis_time_count: %d\n", vorbis_time_count); #endif for (unsigned i = 0; i < vorbis_time_count; ++i) { unsigned val = bv.getBits(16); if (val != 0) { fprintf(stderr, "Vorbis Time domain transforms, read non-zero value %d\n", val); return False; } } return True; } static Boolean parseVorbisSetup_floors(LEBitVector& bv) { if (bv.noMoreBits()) return False; unsigned vorbis_floor_count = bv.getBits(6) + 1; #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\tFloors: vorbis_floor_count: %d\n", vorbis_floor_count); #endif for (unsigned i = 0; i < vorbis_floor_count; ++i) { unsigned floorType = bv.getBits(16); if (floorType == 0) { bv.skipBits(8+16+16+6+8); unsigned floor0_number_of_books = bv.getBits(4) + 1; bv.skipBits(floor0_number_of_books*8); } else if (floorType == 1) { unsigned floor1_partitions = bv.getBits(5); unsigned* floor1_partition_class_list = new unsigned[floor1_partitions]; unsigned maximum_class = 0, j; for (j = 0; j < floor1_partitions; ++j) { floor1_partition_class_list[j] = bv.getBits(4); if (floor1_partition_class_list[j] > maximum_class) maximum_class = floor1_partition_class_list[j]; } unsigned* floor1_class_dimensions = new unsigned[maximum_class + 1]; for (j = 0; j <= maximum_class; ++j) { floor1_class_dimensions[j] = bv.getBits(3) + 1; unsigned floor1_class_subclasses = bv.getBits(2); if (floor1_class_subclasses != 0) { bv.skipBits(8); // "floor1_class_masterbooks[j]" } unsigned twoExp_floor1_class_subclasses = 1 << floor1_class_subclasses; bv.skipBits(twoExp_floor1_class_subclasses*8); // "floor1_subclass_books[j][*]" } bv.skipBits(2); // "floor1_multiplier" unsigned rangebits = bv.getBits(4); for (j = 0; j < floor1_partitions; ++j) { unsigned current_class_number = floor1_partition_class_list[j]; bv.skipBits(floor1_class_dimensions[current_class_number] * rangebits); } delete[] floor1_partition_class_list; delete[] floor1_class_dimensions; } else { // floorType > 1 fprintf(stderr, "Vorbis Floors, read bad floor type %d\n", floorType); return False; } } return True; } static Boolean parseVorbisSetup_residues(LEBitVector& bv) { if (bv.noMoreBits()) return False; unsigned vorbis_residue_count = bv.getBits(6) + 1; #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\tResidues: vorbis_residue_count: %d\n", vorbis_residue_count); #endif for (unsigned i = 0; i < vorbis_residue_count; ++i) { unsigned vorbis_residue_type = bv.getBits(16); if (vorbis_residue_type > 2) { fprintf(stderr, "Vorbis Residues, read bad vorbis_residue_type: %d\n", vorbis_residue_type); return False; } else { bv.skipBits(24+24+24); // "residue_begin", "residue_end", "residue_partition_size" unsigned residue_classifications = bv.getBits(6) + 1; bv.skipBits(8); // "residue_classbook" u_int8_t* residue_cascade = new u_int8_t[residue_classifications]; unsigned j; for (j = 0; j < residue_classifications; ++j) { u_int8_t high_bits = 0; u_int8_t low_bits = bv.getBits(3); unsigned bitflag = bv.getBits(1); if (bitflag) { high_bits = bv.getBits(5); } residue_cascade[j] = (high_bits<<3) | low_bits; } for (j = 0; j < residue_classifications; ++j) { u_int8_t const cascade = residue_cascade[j]; u_int8_t mask = 0x80; while (mask != 0) { if ((cascade&mask) != 0) bv.skipBits(8); // "residue_books[j][*]" mask >>= 1; } } delete[] residue_cascade; } } return True; } static Boolean parseVorbisSetup_mappings(LEBitVector& bv, unsigned audio_channels) { if (bv.noMoreBits()) return False; unsigned vorbis_mapping_count = bv.getBits(6) + 1; #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\tMappings: vorbis_mapping_count: %d\n", vorbis_mapping_count); #endif for (unsigned i = 0; i < vorbis_mapping_count; ++i) { unsigned vorbis_mapping_type = bv.getBits(16); if (vorbis_mapping_type != 0) { fprintf(stderr, "Vorbis Mappings, read bad vorbis_mapping_type: %d\n", vorbis_mapping_type); return False; } unsigned vorbis_mapping_submaps = 1; if (bv.getBits(1)) vorbis_mapping_submaps = bv.getBits(4) + 1; if (bv.getBits(1)) { // "square polar channel mapping is in use" unsigned vorbis_mapping_coupling_steps = bv.getBits(8) + 1; for (unsigned j = 0; j < vorbis_mapping_coupling_steps; ++j) { unsigned ilog_audio_channels_minus_1 = ilog(audio_channels - 1); bv.skipBits(2*ilog_audio_channels_minus_1); // "vorbis_mapping_magnitude", "vorbis_mapping_angle" } } unsigned reserved = bv.getBits(2); if (reserved != 0) { fprintf(stderr, "Vorbis Mappings, read bad 'reserved' field\n"); return False; } if (vorbis_mapping_submaps > 1) { for (unsigned j = 0; j < audio_channels; ++j) { unsigned vorbis_mapping_mux = bv.getBits(4); fprintf(stderr, "\t\t\t\tvorbis_mapping_mux[%d]: %d\n", j, vorbis_mapping_mux); if (vorbis_mapping_mux >= vorbis_mapping_submaps) { fprintf(stderr, "Vorbis Mappings, read bad \"vorbis_mapping_mux\" %d (>= \"vorbis_mapping_submaps\" %d)\n", vorbis_mapping_mux, vorbis_mapping_submaps); return False; } } } bv.skipBits(vorbis_mapping_submaps*(8+8+8)); // "the floor and residue numbers" } return True; } static Boolean parseVorbisSetup_modes(LEBitVector& bv, OggTrack* track) { if (bv.noMoreBits()) return False; unsigned vorbis_mode_count = bv.getBits(6) + 1; unsigned ilog_vorbis_mode_count_minus_1 = ilog(vorbis_mode_count - 1); #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\tModes: vorbis_mode_count: %d (ilog(%d-1):%d)\n", vorbis_mode_count, vorbis_mode_count, ilog_vorbis_mode_count_minus_1); #endif track->vtoHdrs.vorbis_mode_count = vorbis_mode_count; track->vtoHdrs.ilog_vorbis_mode_count_minus_1 = ilog_vorbis_mode_count_minus_1; track->vtoHdrs.vorbis_mode_blockflag = new u_int8_t[vorbis_mode_count]; for (unsigned i = 0; i < vorbis_mode_count; ++i) { track->vtoHdrs.vorbis_mode_blockflag[i] = (u_int8_t)bv.getBits(1); #ifdef DEBUG_SETUP_HEADER fprintf(stderr, "\t\tMode %d: vorbis_mode_blockflag: %d\n", i, track->vtoHdrs.vorbis_mode_blockflag[i]); #endif bv.skipBits(16+16+8); // "vorbis_mode_windowtype", "vorbis_mode_transformtype", "vorbis_mode_mapping" } return True; } static Boolean parseVorbisSetupHeader(OggTrack* track, u_int8_t const* p, unsigned headerSize) { LEBitVector bv(p, headerSize); do { if (!parseVorbisSetup_codebooks(bv)) break; if (!parseVorbisSetup_timeDomainTransforms(bv)) break; if (!parseVorbisSetup_floors(bv)) break; if (!parseVorbisSetup_residues(bv)) break; if (!parseVorbisSetup_mappings(bv, track->numChannels)) break; if (!parseVorbisSetup_modes(bv, track)) break; unsigned framingFlag = bv.getBits(1); if (framingFlag == 0) { fprintf(stderr, "Vorbis \"setup\" header did not end with a 'framing flag'!\n"); break; } return True; } while (0); // An error occurred: return False; } #ifdef DEBUG #define CHECK_PTR if (p >= pEnd) return False #define printComment(p, len) do { for (unsigned k = 0; k < len; ++k) { CHECK_PTR; fprintf(stderr, "%c", *p++); } } while (0) #endif static Boolean validateCommentHeader(u_int8_t const *p, unsigned headerSize, unsigned isOpus = 0) { if (headerSize < 15+isOpus) { // need 7+isOpus + 4(vendor_length) + 4(user_comment_list_length) fprintf(stderr, "\"comment\" header is too short (%d bytes)\n", headerSize); return False; } #ifdef DEBUG u_int8_t const* pEnd = &p[headerSize]; p += 7+isOpus; u_int32_t vendor_length = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4; fprintf(stderr, "\tvendor_string:"); printComment(p, vendor_length); fprintf(stderr, "\n"); u_int32_t user_comment_list_length = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4; for (unsigned i = 0; i < user_comment_list_length; ++i) { CHECK_PTR; u_int32_t length = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4; fprintf(stderr, "\tuser_comment[%d]:", i); printComment(p, length); fprintf(stderr, "\n"); } #endif return True; } static unsigned blocksizeFromExponent(unsigned exponent) { unsigned result = 1; for (unsigned i = 0; i < exponent; ++i) result = 2*result; return result; } Boolean OggFileParser::validateHeader(OggTrack* track, u_int8_t const* p, unsigned headerSize) { // Assert: headerSize >= 7 (because we've already checked "XXXXXX" or "OpusXXXX") if (strcmp(track->mimeType, "audio/VORBIS") == 0) { u_int8_t const firstByte = p[0]; if (firstByte == 1) { // "identification" header if (headerSize < 30) { fprintf(stderr, "Vorbis \"identification\" header is too short (%d bytes)\n", headerSize); return False; } else if ((p[29]&0x1) != 1) { fprintf(stderr, "Vorbis \"identification\" header: 'framing_flag' is not set\n"); return False; } p += 7; u_int32_t vorbis_version = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4; if (vorbis_version != 0) { fprintf(stderr, "Vorbis \"identification\" header has a bad 'vorbis_version': 0x%08x\n", vorbis_version); return False; } u_int8_t audio_channels = *p++; if (audio_channels == 0) { fprintf(stderr, "Vorbis \"identification\" header: 'audio_channels' is 0!\n"); return False; } track->numChannels = audio_channels; u_int32_t audio_sample_rate = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4; if (audio_sample_rate == 0) { fprintf(stderr, "Vorbis \"identification\" header: 'audio_sample_rate' is 0!\n"); return False; } track->samplingFrequency = audio_sample_rate; p += 4; // skip over 'bitrate_maximum' u_int32_t bitrate_nominal = (p[3]<<24)|(p[2]<<16)|(p[1]<<8)|p[0]; p += 4; if (bitrate_nominal > 0) track->estBitrate = (bitrate_nominal+500)/1000; // round p += 4; // skip over 'bitrate_maximum' // Note the two 'block sizes' (samples per packet), and their durations in microseconds: u_int8_t blocksizeBits = *p++; unsigned& blocksize_0 = track->vtoHdrs.blocksize[0]; // alias unsigned& blocksize_1 = track->vtoHdrs.blocksize[1]; // alias blocksize_0 = blocksizeFromExponent(blocksizeBits&0x0F); blocksize_1 = blocksizeFromExponent(blocksizeBits>>4); double uSecsPerSample = 1000000.0/(track->samplingFrequency*2); // Why the "2"? I don't know, but it seems to be necessary track->vtoHdrs.uSecsPerPacket[0] = (unsigned)(uSecsPerSample*blocksize_0); track->vtoHdrs.uSecsPerPacket[1] = (unsigned)(uSecsPerSample*blocksize_1); #ifdef DEBUG fprintf(stderr, "\t%u Hz, %u-channel, %u kbps (est), block sizes: %u,%u (%u,%u us)\n", track->samplingFrequency, track->numChannels, track->estBitrate, blocksize_0, blocksize_1, track->vtoHdrs.uSecsPerPacket[0], track->vtoHdrs.uSecsPerPacket[1]); #endif // To be valid, "blocksize_0" must be <= "blocksize_1", and both must be in [64,8192]: if (!(blocksize_0 <= blocksize_1 && blocksize_0 >= 64 && blocksize_1 <= 8192)) { fprintf(stderr, "Invalid Vorbis \"blocksize_0\" (%d) and/or \"blocksize_1\" (%d)!\n", blocksize_0, blocksize_1); return False; } } else if (firstByte == 3) { // "comment" header if (!validateCommentHeader(p, headerSize)) return False; } else if (firstByte == 5) { // "setup" header // Parse the "setup" header to get the values that we want: // "vorbis_mode_count", and "vorbis_mode_blockflag" for each mode. Unfortunately these come // near the end of the header, so we have to parse lots of other crap first. p += 7; if (!parseVorbisSetupHeader(track, p, headerSize)) { fprintf(stderr, "Failed to parse Vorbis \"setup\" header!\n"); return False; } } } else if (strcmp(track->mimeType, "video/THEORA") == 0) { u_int8_t const firstByte = p[0]; if (firstByte == 0x80) { // "identification" header if (headerSize < 42) { fprintf(stderr, "Theora \"identification\" header is too short (%d bytes)\n", headerSize); return False; } else if ((p[41]&0x7) != 0) { fprintf(stderr, "Theora \"identification\" header: 'res' bits are non-zero\n"); return False; } track->vtoHdrs.KFGSHIFT = ((p[40]&3)<<3) | (p[41]>>5); u_int32_t FRN = (p[22]<<24) | (p[23]<<16) | (p[24]<<8) | p[25]; // Frame rate numerator u_int32_t FRD = (p[26]<<24) | (p[27]<<16) | (p[28]<<8) | p[29]; // Frame rate numerator #ifdef DEBUG fprintf(stderr, "\tKFGSHIFT %d, Frame rate numerator %d, Frame rate denominator %d\n", track->vtoHdrs.KFGSHIFT, FRN, FRD); #endif if (FRN == 0 || FRD == 0) { fprintf(stderr, "Theora \"identification\" header: Bad FRN and/or FRD values: %d, %d\n", FRN, FRD); return False; } track->vtoHdrs.uSecsPerFrame = (unsigned)((1000000.0*FRD)/FRN); #ifdef DEBUG fprintf(stderr, "\t\t=> %u microseconds per frame\n", track->vtoHdrs.uSecsPerFrame); #endif } else if (firstByte == 0x81) { // "comment" header if (!validateCommentHeader(p, headerSize)) return False; } else if (firstByte == 0x82) { // "setup" header // We don't care about the contents of the Theora "setup" header; just assume it's valid } } else { // Opus audio if (strncmp((char const*)p, "OpusHead", 8) == 0) { // "identification" header // Just check the size, and the 'major' number of the version byte: if (headerSize < 19 || (p[8]&0xF0) != 0) return False; } else { // comment header if (!validateCommentHeader(p, headerSize, 1/*isOpus*/)) return False; } } return True; } void OggFileParser::parseAndDeliverPages() { #ifdef DEBUG fprintf(stderr, "parsing and delivering data\n"); #endif while (parseAndDeliverPage()) {} } Boolean OggFileParser::parseAndDeliverPage() { u_int8_t header_type_flag; u_int32_t bitstream_serial_number; parseStartOfPage(header_type_flag, bitstream_serial_number); OggDemuxedTrack* demuxedTrack = fOurDemux->lookupDemuxedTrack(bitstream_serial_number); if (demuxedTrack == NULL) { // this track is not being read #ifdef DEBUG fprintf(stderr, "\tIgnoring page from unread track; skipping %d remaining packet data bytes\n", fPacketSizeTable->totSizes); #endif skipBytes(fPacketSizeTable->totSizes); return True; } else if (fPacketSizeTable->totSizes == 0) { // This page is empty (has no packets). Skip it and continue #ifdef DEBUG fprintf(stderr, "\t[track: %s] Skipping empty page\n", demuxedTrack->MIMEtype()); #endif return True; } // Start delivering packets next: demuxedTrack->fCurrentPageIsContinuation = (header_type_flag&0x01) != 0; fCurrentTrackNumber = bitstream_serial_number; fCurrentParseState = DELIVERING_PACKET_WITHIN_PAGE; saveParserState(); return False; } Boolean OggFileParser::deliverPacketWithinPage() { OggDemuxedTrack* demuxedTrack = fOurDemux->lookupDemuxedTrack(fCurrentTrackNumber); if (demuxedTrack == NULL) return False; // should not happen unsigned packetNum = fPacketSizeTable->nextPacketNumToDeliver; unsigned packetSize = fPacketSizeTable->size[packetNum]; if (!demuxedTrack->isCurrentlyAwaitingData()) { // Someone has been reading this stream, but isn't right now. // We can't deliver this frame until he asks for it, so punt for now. // The next time he asks for a frame, he'll get it. #ifdef DEBUG fprintf(stderr, "\t[track: %s] Deferring delivery of packet %d (%d bytes%s)\n", demuxedTrack->MIMEtype(), packetNum, packetSize, packetNum == fPacketSizeTable->numCompletedPackets ? " (incomplete)" : ""); #endif return True; } // Deliver the next packet: #ifdef DEBUG fprintf(stderr, "\t[track: %s] Delivering packet %d (%d bytes%s)\n", demuxedTrack->MIMEtype(), packetNum, packetSize, packetNum == fPacketSizeTable->numCompletedPackets ? " (incomplete)" : ""); #endif unsigned numBytesDelivered = packetSize < demuxedTrack->maxSize() ? packetSize : demuxedTrack->maxSize(); getBytes(demuxedTrack->to(), numBytesDelivered); u_int8_t firstByte = numBytesDelivered > 0 ? demuxedTrack->to()[0] : 0x00; u_int8_t secondByte = numBytesDelivered > 1 ? demuxedTrack->to()[1] : 0x00; demuxedTrack->to() += numBytesDelivered; if (demuxedTrack->fCurrentPageIsContinuation) { // the previous page's read was incomplete demuxedTrack->frameSize() += numBytesDelivered; } else { // This is the first delivery for this "doGetNextFrame()" call. demuxedTrack->frameSize() = numBytesDelivered; } if (packetSize > demuxedTrack->maxSize()) { demuxedTrack->numTruncatedBytes() += packetSize - demuxedTrack->maxSize(); } demuxedTrack->maxSize() -= numBytesDelivered; // Figure out the duration and presentation time of this frame. unsigned durationInMicroseconds; OggTrack* track = fOurFile.lookup(demuxedTrack->fOurTrackNumber); if (strcmp(track->mimeType, "audio/VORBIS") == 0) { if ((firstByte&0x01) != 0) { // This is a header packet durationInMicroseconds = 0; } else { // This is a data packet. // Parse the first byte to figure out its duration. // Extract the next "track->vtoHdrs.ilog_vorbis_mode_count_minus_1" bits of the first byte: u_int8_t const mask = 0xFE<<(track->vtoHdrs.ilog_vorbis_mode_count_minus_1); u_int8_t const modeNumber = (firstByte&~mask)>>1; if (modeNumber >= track->vtoHdrs.vorbis_mode_count) { fprintf(stderr, "Error: Bad mode number %d (>= vorbis_mode_count %d) in Vorbis packet!\n", modeNumber, track->vtoHdrs.vorbis_mode_count); durationInMicroseconds = 0; } else { unsigned blockNumber = track->vtoHdrs.vorbis_mode_blockflag[modeNumber]; durationInMicroseconds = track->vtoHdrs.uSecsPerPacket[blockNumber]; } } } else if (strcmp(track->mimeType, "video/THEORA") == 0) { if ((firstByte&0x80) != 0) { // This is a header packet durationInMicroseconds = 0; } else { // This is a data packet. durationInMicroseconds = track->vtoHdrs.uSecsPerFrame; } } else { // "audio/OPUS" if (firstByte == 0x4F/*'O'*/ && secondByte == 0x70/*'p*/) { // This is a header packet durationInMicroseconds = 0; } else { // This is a data packet. // Parse the first byte to figure out the duration of each frame, and then (if necessary) // parse the second byte to figure out how many frames are in this packet: u_int8_t config = firstByte >> 3; u_int8_t c = firstByte & 0x03; unsigned const configDuration[32] = { // in microseconds 10000, 20000, 40000, 60000, // config 0..3 10000, 20000, 40000, 60000, // config 4..7 10000, 20000, 40000, 60000, // config 8..11 10000, 20000, // config 12..13 10000, 20000, // config 14..15 2500, 5000, 10000, 20000, // config 16..19 2500, 5000, 10000, 20000, // config 20..23 2500, 5000, 10000, 20000, // config 24..27 2500, 5000, 10000, 20000 // config 28..31 }; unsigned const numFramesInPacket = c == 0 ? 1 : c == 3 ? (secondByte&0x3F) : 2; durationInMicroseconds = numFramesInPacket*configDuration[config]; } } if (demuxedTrack->nextPresentationTime().tv_sec == 0 && demuxedTrack->nextPresentationTime().tv_usec == 0) { // This is the first delivery. Initialize "demuxedTrack->nextPresentationTime()": gettimeofday(&demuxedTrack->nextPresentationTime(), NULL); } demuxedTrack->presentationTime() = demuxedTrack->nextPresentationTime(); demuxedTrack->durationInMicroseconds() = durationInMicroseconds; demuxedTrack->nextPresentationTime().tv_usec += durationInMicroseconds; while (demuxedTrack->nextPresentationTime().tv_usec >= 1000000) { ++demuxedTrack->nextPresentationTime().tv_sec; demuxedTrack->nextPresentationTime().tv_usec -= 1000000; } saveParserState(); // And check whether there's a next packet in this page: if (packetNum == fPacketSizeTable->numCompletedPackets) { // This delivery was for an incomplete packet, at the end of the page. // Return without completing delivery: fCurrentParseState = PARSING_AND_DELIVERING_PAGES; return False; } if (packetNum < fPacketSizeTable->numCompletedPackets-1 || fPacketSizeTable->lastPacketIsIncomplete) { // There is at least one more packet (possibly incomplete) left in this packet. // Deliver it next: ++fPacketSizeTable->nextPacketNumToDeliver; } else { // Start parsing a new page next: fCurrentParseState = PARSING_AND_DELIVERING_PAGES; } FramedSource::afterGetting(demuxedTrack); // completes delivery return True; } void OggFileParser::parseStartOfPage(u_int8_t& header_type_flag, u_int32_t& bitstream_serial_number) { saveParserState(); // First, make sure we start with the 'capture_pattern': 0x4F676753 ('OggS'): while (test4Bytes() != 0x4F676753) { skipBytes(1); saveParserState(); // ensures forward progress through the file } skipBytes(4); #ifdef DEBUG fprintf(stderr, "\nSaw Ogg page header:\n"); #endif u_int8_t stream_structure_version = get1Byte(); if (stream_structure_version != 0) { fprintf(stderr, "Saw page with unknown Ogg file version number: 0x%02x\n", stream_structure_version); } header_type_flag = get1Byte(); #ifdef DEBUG fprintf(stderr, "\theader_type_flag: 0x%02x (", header_type_flag); if (header_type_flag&0x01) fprintf(stderr, "continuation "); if (header_type_flag&0x02) fprintf(stderr, "bos "); if (header_type_flag&0x04) fprintf(stderr, "eos "); fprintf(stderr, ")\n"); #endif u_int32_t granule_position1 = byteSwap(get4Bytes()); u_int32_t granule_position2 = byteSwap(get4Bytes()); bitstream_serial_number = byteSwap(get4Bytes()); u_int32_t page_sequence_number = byteSwap(get4Bytes()); u_int32_t CRC_checksum = byteSwap(get4Bytes()); u_int8_t number_page_segments = get1Byte(); #ifdef DEBUG fprintf(stderr, "\tgranule_position 0x%08x%08x, bitstream_serial_number 0x%08x, page_sequence_number 0x%08x, CRC_checksum 0x%08x, number_page_segments %d\n", granule_position2, granule_position1, bitstream_serial_number, page_sequence_number, CRC_checksum, number_page_segments); #else // Dummy statements to prevent 'unused variable' compiler warnings: #define DUMMY_STATEMENT(x) do {x = x;} while (0) DUMMY_STATEMENT(granule_position1); DUMMY_STATEMENT(granule_position2); DUMMY_STATEMENT(page_sequence_number); DUMMY_STATEMENT(CRC_checksum); #endif // Look at the "segment_table" to count the sizes of the packets in this page: delete fPacketSizeTable/*if any*/; fPacketSizeTable = new PacketSizeTable(number_page_segments); u_int8_t lacing_value = 0; #ifdef DEBUG fprintf(stderr, "\tsegment_table\n"); #endif for (unsigned i = 0; i < number_page_segments; ++i) { lacing_value = get1Byte(); #ifdef DEBUG fprintf(stderr, "\t\t%d:\t%d", i, lacing_value); #endif fPacketSizeTable->totSizes += lacing_value; fPacketSizeTable->size[fPacketSizeTable->numCompletedPackets] += lacing_value; if (lacing_value < 255) { // This completes a packet: #ifdef DEBUG fprintf(stderr, " (->%d)", fPacketSizeTable->size[fPacketSizeTable->numCompletedPackets]); #endif ++fPacketSizeTable->numCompletedPackets; } #ifdef DEBUG fprintf(stderr, "\n"); #endif } fPacketSizeTable->lastPacketIsIncomplete = lacing_value == 255; }