Example #1
0
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);
}