RTPSink* MatroskaFile ::createRTPSinkForTrackNumber(unsigned trackNumber, Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic) { RTPSink* result = NULL; // default value, if an error occurs do { MatroskaTrack* track = lookup(trackNumber); if (track == NULL) break; if (strcmp(track->mimeType, "audio/MPEG") == 0) { result = MPEG1or2AudioRTPSink::createNew(envir(), rtpGroupsock); } else if (strcmp(track->mimeType, "audio/AAC") == 0) { // The Matroska file's 'Codec Private' data is assumed to be the AAC configuration // information. Use this to generate a hexadecimal 'config' string for the new RTP sink: char* configStr = new char[2*track->codecPrivateSize + 1]; if (configStr == NULL) break; // 2 hex digits per byte, plus the trailing '\0' for (unsigned i = 0; i < track->codecPrivateSize; ++i) { sprintf(&configStr[2*i], "%02X", track->codecPrivate[i]); } result = MPEG4GenericRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, track->samplingFrequency, "audio", "AAC-hbr", configStr, track->numChannels); delete[] configStr; } else if (strcmp(track->mimeType, "audio/AC3") == 0) { result = AC3AudioRTPSink ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, track->samplingFrequency); } else if (strcmp(track->mimeType, "audio/OPUS") == 0) { result = SimpleRTPSink ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, 48000, "audio", "OPUS", 2, False/*only 1 Opus 'packet' in each RTP packet*/); } else if (strcmp(track->mimeType, "audio/VORBIS") == 0 || strcmp(track->mimeType, "video/THEORA") == 0) { // The Matroska file's 'Codec Private' data is assumed to be the codec configuration // information, containing the "Identification", "Comment", and "Setup" headers. // Extract these headers now: u_int8_t* identificationHeader = NULL; unsigned identificationHeaderSize = 0; u_int8_t* commentHeader = NULL; unsigned commentHeaderSize = 0; u_int8_t* setupHeader = NULL; unsigned setupHeaderSize = 0; Boolean isTheora = strcmp(track->mimeType, "video/THEORA") == 0; // otherwise, Vorbis do { u_int8_t* p = track->codecPrivate; unsigned n = track->codecPrivateSize; if (n == 0 || p == NULL) break; // we have no 'Codec Private' data u_int8_t numHeaders; getPrivByte(numHeaders); unsigned headerSize[3]; // we don't handle any more than 2+1 headers // Extract the sizes of each of these headers: unsigned sizesSum = 0; Boolean success = True; unsigned i; for (i = 0; i < numHeaders && i < 3; ++i) { unsigned len = 0; u_int8_t c; do { success = False; getPrivByte(c); success = True; len += c; } while (c == 255); if (!success || len == 0) break; headerSize[i] = len; sizesSum += len; } if (!success) break; // Compute the implicit size of the final header: if (numHeaders < 3) { int finalHeaderSize = n - sizesSum; if (finalHeaderSize <= 0) break; // error in data; give up headerSize[numHeaders] = (unsigned)finalHeaderSize; ++numHeaders; // include the final header now } else { numHeaders = 3; // The maximum number of headers that we handle } // Then, extract and classify each header: for (i = 0; i < numHeaders; ++i) { success = False; unsigned newHeaderSize = headerSize[i]; u_int8_t* newHeader = new u_int8_t[newHeaderSize]; if (newHeader == NULL) break; u_int8_t* hdr = newHeader; while (newHeaderSize-- > 0) { success = False; getPrivByte(*hdr++); success = True; } if (!success) { delete[] newHeader; break; } u_int8_t headerType = newHeader[0]; if (headerType == 1 || (isTheora && headerType == 0x80)) { // "identification" header delete[] identificationHeader; identificationHeader = newHeader; identificationHeaderSize = headerSize[i]; } else if (headerType == 3 || (isTheora && headerType == 0x81)) { // "comment" header delete[] commentHeader; commentHeader = newHeader; commentHeaderSize = headerSize[i]; } else if (headerType == 5 || (isTheora && headerType == 0x82)) { // "setup" header delete[] setupHeader; setupHeader = newHeader; setupHeaderSize = headerSize[i]; } else { delete[] newHeader; // because it was a header type that we don't understand } } if (!success) break; if (isTheora) { result = TheoraVideoRTPSink ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, identificationHeader, identificationHeaderSize, commentHeader, commentHeaderSize, setupHeader, setupHeaderSize); } else { // Vorbis result = VorbisAudioRTPSink ::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, track->samplingFrequency, track->numChannels, identificationHeader, identificationHeaderSize, commentHeader, commentHeaderSize, setupHeader, setupHeaderSize); } } while (0); delete[] identificationHeader; delete[] commentHeader; delete[] setupHeader; } else if (strcmp(track->mimeType, "video/H264") == 0) { // Use our track's 'Codec Private' data: Bytes 5 and beyond contain SPS and PPSs: u_int8_t* SPS = NULL; unsigned SPSSize = NULL; u_int8_t* PPS = NULL; unsigned PPSSize = NULL; u_int8_t* SPSandPPSBytes = NULL; unsigned numSPSandPPSBytes = 0; do { if (track->codecPrivateSize < 6) break; numSPSandPPSBytes = track->codecPrivateSize - 5; SPSandPPSBytes = &track->codecPrivate[5]; // Extract, from "SPSandPPSBytes", one SPS NAL unit, and one PPS NAL unit. // (I hope one is all we need of each.) unsigned i; u_int8_t* ptr = SPSandPPSBytes; u_int8_t* limit = &SPSandPPSBytes[numSPSandPPSBytes]; unsigned numSPSs = (*ptr++)&0x1F; CHECK_PTR; for (i = 0; i < numSPSs; ++i) { unsigned spsSize = (*ptr++)<<8; CHECK_PTR; spsSize |= *ptr++; CHECK_PTR; if (spsSize > NUM_BYTES_REMAINING) break; u_int8_t nal_unit_type = ptr[0]&0x1F; if (SPS == NULL && nal_unit_type == 7/*sanity check*/) { // save the first one SPSSize = spsSize; SPS = new u_int8_t[spsSize]; memmove(SPS, ptr, spsSize); } ptr += spsSize; } unsigned numPPSs = (*ptr++)&0x1F; CHECK_PTR; for (i = 0; i < numPPSs; ++i) { unsigned ppsSize = (*ptr++)<<8; CHECK_PTR; ppsSize |= *ptr++; CHECK_PTR; if (ppsSize > NUM_BYTES_REMAINING) break; u_int8_t nal_unit_type = ptr[0]&0x1F; if (PPS == NULL && nal_unit_type == 8/*sanity check*/) { // save the first one PPSSize = ppsSize; PPS = new u_int8_t[ppsSize]; memmove(PPS, ptr, ppsSize); } ptr += ppsSize; } } while (0); result = H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, SPS, SPSSize, PPS, PPSSize); delete[] SPS; delete[] PPS; } else if (strcmp(track->mimeType, "video/H265") == 0) { u_int8_t* VPS = NULL; unsigned VPSSize = NULL; u_int8_t* SPS = NULL; unsigned SPSSize = NULL; u_int8_t* PPS = NULL; unsigned PPSSize = NULL; u_int8_t* VPS_SPS_PPSBytes = NULL; unsigned numVPS_SPS_PPSBytes = 0; unsigned i; do { if (track->codecPrivateUsesH264FormatForH265) { // The data uses the H.264-style format (but including VPS NAL unit(s)). // The VPS,SPS,PPS NAL unit information starts at byte #5: if (track->codecPrivateSize >= 6) { numVPS_SPS_PPSBytes = track->codecPrivateSize - 5; VPS_SPS_PPSBytes = &track->codecPrivate[5]; } } else { // The data uses the proper H.265-style format. // The VPS,SPS,PPS NAL unit information starts at byte #22: if (track->codecPrivateSize >= 23) { numVPS_SPS_PPSBytes = track->codecPrivateSize - 22; VPS_SPS_PPSBytes = &track->codecPrivate[22]; } } // Extract, from "VPS_SPS_PPSBytes", one VPS NAL unit, one SPS NAL unit, and one PPS NAL unit. // (I hope one is all we need of each.) if (numVPS_SPS_PPSBytes == 0 || VPS_SPS_PPSBytes == NULL) break; // sanity check u_int8_t* ptr = VPS_SPS_PPSBytes; u_int8_t* limit = &VPS_SPS_PPSBytes[numVPS_SPS_PPSBytes]; if (track->codecPrivateUsesH264FormatForH265) { // The data uses the H.264-style format (but including VPS NAL unit(s)). while (NUM_BYTES_REMAINING > 0) { unsigned numNALUnits = (*ptr++)&0x1F; CHECK_PTR; for (i = 0; i < numNALUnits; ++i) { unsigned nalUnitLength = (*ptr++)<<8; CHECK_PTR; nalUnitLength |= *ptr++; CHECK_PTR; if (nalUnitLength > NUM_BYTES_REMAINING) break; u_int8_t nal_unit_type = (ptr[0]&0x7E)>>1; if (nal_unit_type == 32) { // VPS VPSSize = nalUnitLength; delete[] VPS; VPS = new u_int8_t[nalUnitLength]; memmove(VPS, ptr, nalUnitLength); } else if (nal_unit_type == 33) { // SPS SPSSize = nalUnitLength; delete[] SPS; SPS = new u_int8_t[nalUnitLength]; memmove(SPS, ptr, nalUnitLength); } else if (nal_unit_type == 34) { // PPS PPSSize = nalUnitLength; delete[] PPS; PPS = new u_int8_t[nalUnitLength]; memmove(PPS, ptr, nalUnitLength); } ptr += nalUnitLength; } } } else { // The data uses the proper H.265-style format. unsigned numOfArrays = *ptr++; CHECK_PTR; for (unsigned j = 0; j < numOfArrays; ++j) { ++ptr; CHECK_PTR; // skip the 'array_completeness'|'reserved'|'NAL_unit_type' byte unsigned numNalus = (*ptr++)<<8; CHECK_PTR; numNalus |= *ptr++; CHECK_PTR; for (i = 0; i < numNalus; ++i) { unsigned nalUnitLength = (*ptr++)<<8; CHECK_PTR; nalUnitLength |= *ptr++; CHECK_PTR; if (nalUnitLength > NUM_BYTES_REMAINING) break; u_int8_t nal_unit_type = (ptr[0]&0x7E)>>1; if (nal_unit_type == 32) { // VPS VPSSize = nalUnitLength; delete[] VPS; VPS = new u_int8_t[nalUnitLength]; memmove(VPS, ptr, nalUnitLength); } else if (nal_unit_type == 33) { // SPS SPSSize = nalUnitLength; delete[] SPS; SPS = new u_int8_t[nalUnitLength]; memmove(SPS, ptr, nalUnitLength); } else if (nal_unit_type == 34) { // PPS PPSSize = nalUnitLength; delete[] PPS; PPS = new u_int8_t[nalUnitLength]; memmove(PPS, ptr, nalUnitLength); } ptr += nalUnitLength; } } } } while (0); result = H265VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic, VPS, VPSSize, SPS, SPSSize, PPS, PPSSize); delete[] VPS; delete[] SPS; delete[] PPS; } else if (strcmp(track->mimeType, "video/VP8") == 0) {
VorbisAudioMatroskaFileServerMediaSubsession ::VorbisAudioMatroskaFileServerMediaSubsession(MatroskaFileServerDemux& demux, unsigned trackNumber) : FileServerMediaSubsession(demux.envir(), demux.fileName(), False), fOurDemux(demux), fTrackNumber(trackNumber), fIdentificationHeader(NULL), fIdentificationHeaderSize(0), fCommentHeader(NULL), fCommentHeaderSize(0), fSetupHeader(NULL), fSetupHeaderSize(0), fEstBitrate(96/* kbps, default guess */) { MatroskaTrack* track = fOurDemux.lookup(fTrackNumber); // The Matroska file's 'Codec Private' data is assumed to be the Vorbis configuration information, // containing the "Identification", "Comment", and "Setup" headers. Extract these headers now: do { u_int8_t* p = track->codecPrivate; unsigned n = track->codecPrivateSize; if (n == 0 || p == NULL) break; // we have no 'Codec Private' data u_int8_t numHeaders; getPrivByte(numHeaders); unsigned headerSize[3]; // we don't handle any more than 2+1 headers // Extract the sizes of each of these headers: unsigned sizesSum = 0; Boolean success = True; unsigned i; for (i = 0; i < numHeaders && i < 3; ++i) { unsigned len = 0; u_int8_t c; do { success = False; getPrivByte(c); success = True; len += c; } while (c == 255); if (!success || len == 0) break; headerSize[i] = len; sizesSum += len; } if (!success) break; // Compute the implicit size of the final header: if (numHeaders < 3) { int finalHeaderSize = n - sizesSum; if (finalHeaderSize <= 0) break; // error in data; give up headerSize[numHeaders] = (unsigned)finalHeaderSize; ++numHeaders; // include the final header now } else { numHeaders = 3; // The maximum number of headers that we handle } // Then, extract and classify each header: for (i = 0; i < numHeaders; ++i) { success = False; unsigned newHeaderSize = headerSize[i]; u_int8_t* newHeader = new u_int8_t[newHeaderSize]; if (newHeader == NULL) break; u_int8_t* hdr = newHeader; while (newHeaderSize-- > 0) { success = False; getPrivByte(*hdr++); success = True; } if (!success) { delete[] newHeader; break; } u_int8_t headerType = newHeader[0]; if (headerType == 1) { delete[] fIdentificationHeader; fIdentificationHeader = newHeader; fIdentificationHeaderSize = headerSize[i]; if (fIdentificationHeaderSize >= 28) { // Get the 'bitrate' values from this header, and use them to set "fEstBitrate": u_int32_t val; u_int8_t* p; p = &fIdentificationHeader[16]; val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian int bitrate_maximum = (int)val; if (bitrate_maximum < 0) bitrate_maximum = 0; p = &fIdentificationHeader[20]; val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian int bitrate_nominal = (int)val; if (bitrate_nominal < 0) bitrate_nominal = 0; p = &fIdentificationHeader[24]; val = ((p[3]*256 + p[2])*256 + p[1])*256 + p[0]; // i.e., little-endian int bitrate_minimum = (int)val; if (bitrate_minimum < 0) bitrate_minimum = 0; int bitrate = bitrate_nominal>0 ? bitrate_nominal : bitrate_maximum>0 ? bitrate_maximum : bitrate_minimum>0 ? bitrate_minimum : 0; if (bitrate > 0) fEstBitrate = ((unsigned)bitrate)/1000; } } else if (headerType == 3) { delete[] fCommentHeader; fCommentHeader = newHeader; fCommentHeaderSize = headerSize[i]; } else if (headerType == 5) { delete[] fSetupHeader; fSetupHeader = newHeader; fSetupHeaderSize = headerSize[i]; } else { delete[] newHeader; // because it was a header type that we don't understand } } if (!success) break; } while (0); }