| /********** |
| 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 3 of the License, or (at your |
| option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
| |
| 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-2020 Live Networks, Inc. All rights reserved. |
| // A parser for an Ogg file. |
| // Implementation |
| |
| #include "OggFileParser.hh" |
| #include "OggDemuxedTrack.hh" |
| #include <GroupsockHelper.hh> // 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<<nbr) | firstBits; |
| } |
| } |
| |
| void skipBits(unsigned numBits) { |
| while (numBits > 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 "<packet_type>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; |
| } |