void H264or5VideoStreamParser
::removeEmulationBytes(u_int8_t* nalUnitCopy, unsigned maxSize, unsigned& nalUnitCopySize) {
  u_int8_t* nalUnitOrig = fStartOfFrame + fOutputStartCodeSize;
  unsigned const numBytesInNALunit = fTo - nalUnitOrig;
  nalUnitCopySize
    = removeH264or5EmulationBytes(nalUnitCopy, maxSize, nalUnitOrig, numBytesInNALunit);
}
char const* H264VideoRTPSink::auxSDPLine() {

  liveLogInfo(" H264VideoRTPSink::auxSDPLine \n");
  
  // Generate a new "a=fmtp:" line each time, using our SPS and PPS (if we have them),
  // otherwise parameters from our framer source (in case they've changed since the last time that
  // we were called):
  H264or5VideoStreamFramer* framerSource = NULL;
  u_int8_t* vpsDummy = NULL; unsigned vpsDummySize = 0;
  u_int8_t* sps = fSPS; unsigned spsSize = fSPSSize;
  u_int8_t* pps = fPPS; unsigned ppsSize = fPPSSize;
  if (sps == NULL || pps == NULL) {
    // We need to get SPS and PPS from our framer source:
    if (fOurFragmenter == NULL) return NULL; // we don't yet have a fragmenter (and therefore not a source)
    framerSource = (H264or5VideoStreamFramer*)(fOurFragmenter->inputSource());
    if (framerSource == NULL) return NULL; // we don't yet have a source

    framerSource->getVPSandSPSandPPS(vpsDummy, vpsDummySize, sps, spsSize, pps, ppsSize);
    if (sps == NULL || pps == NULL) return NULL; // our source isn't ready
  }

  // Set up the "a=fmtp:" SDP line for this stream:
  u_int8_t* spsWEB = new u_int8_t[spsSize]; // "WEB" means "Without Emulation Bytes"
  unsigned spsWEBSize = removeH264or5EmulationBytes(spsWEB, spsSize, sps, spsSize);
  if (spsWEBSize < 4) { // Bad SPS size => assume our source isn't ready
    delete[] spsWEB;
    return NULL;
  }
  u_int32_t profileLevelId = (spsWEB[1]<<16) | (spsWEB[2]<<8) | spsWEB[3];
  delete[] spsWEB;

  char* sps_base64 = base64Encode((char*)sps, spsSize);
  char* pps_base64 = base64Encode((char*)pps, ppsSize);

  char const* fmtpFmt =
    "a=fmtp:%d packetization-mode=1"
    ";profile-level-id=%06X"
    ";sprop-parameter-sets=%s,%s\r\n";
  unsigned fmtpFmtSize = strlen(fmtpFmt)
    + 3 /* max char len */
    + 6 /* 3 bytes in hex */
    + strlen(sps_base64) + strlen(pps_base64);
  char* fmtp = new char[fmtpFmtSize];
  sprintf(fmtp, fmtpFmt,
          rtpPayloadType(),
	  profileLevelId,
          sps_base64, pps_base64);

  delete[] sps_base64;
  delete[] pps_base64;

  delete[] fFmtpSDPLine; fFmtpSDPLine = fmtp;
  return fFmtpSDPLine;
}
void H264or5VideoStreamFramer::saveCopyOfVPS(u_int8_t* from, unsigned size) {
  if (from == NULL) return;
  delete[] fLastSeenVPS;
  fLastSeenVPS = new u_int8_t[size];
  memmove(fLastSeenVPS, from, size);

  fLastSeenVPSSize = size;

  // We also make another copy - without 'emulation bytes', to extract parameters that we need:
  u_int8_t vps[VPS_MAX_SIZE];
  unsigned vpsSize
    = removeH264or5EmulationBytes(vps, VPS_MAX_SIZE, fLastSeenVPS, fLastSeenVPSSize);

  // Extract the first 12 'profile_tier_level' bytes:
  if (vpsSize >= 6/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) {
    memmove(fProfileTierLevelHeaderBytes, &vps[6], 12);
  }
}
void H264or5VideoStreamFramer::saveCopyOfSPS(u_int8_t* from, unsigned size) {
  if (from == NULL) return;
  delete[] fLastSeenSPS;
  fLastSeenSPS = new u_int8_t[size];
  memmove(fLastSeenSPS, from, size);

  fLastSeenSPSSize = size;

  // We also make another copy - without 'emulation bytes', to extract parameters that we need:
  u_int8_t sps[SPS_MAX_SIZE];
  unsigned spsSize
    = removeH264or5EmulationBytes(sps, SPS_MAX_SIZE, fLastSeenSPS, fLastSeenSPSSize);
  if (fHNumber == 264) {
    // Extract the first 3 bytes of the SPS (after the nal_unit_header byte) as 'profile_level_id'
    if (spsSize >= 1/*'profile_level_id' offset within SPS*/ + 3/*num bytes needed*/) {
      fProfileLevelId = (sps[1]<<16) | (sps[2]<<8) | sps[3];
    }
  } else { // 265
    // Extract the first 12 'profile_tier_level' bytes:
    if (spsSize >= 3/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) {
      memmove(fProfileTierLevelHeaderBytes, &sps[3], 12);
    }
  }
}
H264StreamParser::H264StreamParser(uint8_t* sps, unsigned spsSize)
    : _sps(SPS_MAX_SIZE), _width(0), _height(0), _framerate(0)
{
    spsSize = removeH264or5EmulationBytes(_sps.data(), _sps.size(), sps, spsSize);

    BitVector bv(_sps.data(), 0, spsSize * 8);
    bv.skipBits(8); // forbidden_zero_bit; nal_ref_idc; nal_unit_type
    unsigned profile_idc = bv.getBits(8);
    bv.skipBits(8); // 6 constraint_setN_flag and "reserved_zero_2bits" at the end
    bv.skipBits(8); // level_idc
    unsigned seq_parameter_set_id = bv.get_expGolomb();
    unsigned chroma_format_idc = 1;
    Boolean separate_colour_plane_flag = False;
    if (profile_idc == 100 || // High profile
        profile_idc == 110 || // High10 profile
        profile_idc == 122 || // High422 profile
        profile_idc == 244 || // High444 Predictive profile
        profile_idc == 44 ||  // Cavlc444 profile
        profile_idc == 83 ||  // Scalable Constrained High profile (SVC)
        profile_idc == 86 ||  // Scalable High Intra profile (SVC)
        profile_idc == 118 || // Stereo High profile (MVC)
        profile_idc == 128 || // Multiview High profile (MVC)
        profile_idc == 138 || // Multiview Depth High profile (MVCD)
        profile_idc == 144)   // old High444 profile
    {
        chroma_format_idc = bv.get_expGolomb();
        if (chroma_format_idc == 3)
            separate_colour_plane_flag = bv.get1BitBoolean();
        bv.get_expGolomb(); // bit_depth_luma_minus8
        bv.get_expGolomb(); // bit_depth_chroma_minus8
        bv.skipBits(1);     // qpprime_y_zero_transform_bypass_flag
        Boolean seq_scaling_matrix_present_flag = bv.get1BitBoolean();
        if (seq_scaling_matrix_present_flag)
        {
            for (int i = 0; i < ((chroma_format_idc != 3) ? 8 : 12); ++i)
            {
                Boolean seq_scaling_list_present_flag = bv.get1BitBoolean();
                if (seq_scaling_list_present_flag)
                {
                    // Decode scaling matrices
                    unsigned sizeOfScalingList = i < 6 ? 16 : 64;
                    unsigned lastScale = 8;
                    unsigned nextScale = 8;
                    for (unsigned j = 0; j < sizeOfScalingList; ++j)
                    {
                        if (nextScale != 0)
                        {
                            unsigned delta_scale = bv.get_expGolomb();
                            nextScale = (lastScale + delta_scale + 256) % 256;
                        }
                        lastScale = (nextScale == 0) ? lastScale : nextScale;
                    }
                }
            }
        }
    }
    unsigned log2_max_frame_num_minus4 = bv.get_expGolomb();
    unsigned pic_order_cnt_type = bv.get_expGolomb();
    if (pic_order_cnt_type == 0)
    {
        bv.get_expGolomb(); // log2_max_pic_order_cnt_lsb_minus4
    }
    else if (pic_order_cnt_type == 1)
    {
        bv.skipBits(1);     // delta_pic_order_always_zero_flag
        bv.get_expGolomb(); // offset_for_non_ref_pic SIGNED!
        bv.get_expGolomb(); // offset_for_top_to_bottom_field SIGNED!
        unsigned num_ref_frames_in_pic_order_cnt_cycle = bv.get_expGolomb();
        for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i)
            bv.get_expGolomb(); // offset_for_ref_frame[i] SIGNED!
    }
    unsigned num_ref_frames = bv.get_expGolomb();
    Boolean gaps_in_frame_num_value_allowed_flag = bv.get1BitBoolean();
    unsigned pic_width_in_mbs_minus1 = bv.get_expGolomb();
    unsigned pic_height_in_map_units_minus1 = bv.get_expGolomb();
    Boolean frame_mbs_only_flag = bv.get1BitBoolean();
    if (!frame_mbs_only_flag)
        bv.skipBits(1); // mb_adaptive_frame_field_flag
    bv.skipBits(1);     // direct_8x8_inference_flag
    Boolean frame_cropping_flag = bv.get1BitBoolean();
    unsigned frame_crop_left_offset = 0, frame_crop_right_offset = 0, frame_crop_top_offset = 0,
             frame_crop_bottom_offset = 0;
    if (frame_cropping_flag)
    {
        frame_crop_left_offset = bv.get_expGolomb();
        frame_crop_right_offset = bv.get_expGolomb();
        frame_crop_top_offset = bv.get_expGolomb();
        frame_crop_bottom_offset = bv.get_expGolomb();
    }

    // Formula taken from MediaInfo
    _width = (pic_width_in_mbs_minus1 + 1) * 16;
    _height = (pic_height_in_map_units_minus1 + 1) * 16 * (2 - frame_mbs_only_flag);
    unsigned chromaArrayType = separate_colour_plane_flag ? 0 : chroma_format_idc;
    unsigned cropUnitX = subWidthChroma[chromaArrayType];
    unsigned cropUnitY = subHeightChroma[chromaArrayType] * (2 - frame_mbs_only_flag);
    _width -= (frame_crop_left_offset + frame_crop_right_offset) * cropUnitX;
    _height -= (frame_crop_top_offset + frame_crop_bottom_offset) * cropUnitY;

    Boolean vui_parameters_present_flag = bv.get1BitBoolean();
    if (vui_parameters_present_flag)
    {
        // Decode VUI
        Boolean aspect_ratio_info_present_flag = bv.get1BitBoolean();
        if (aspect_ratio_info_present_flag)
        {
            unsigned aspect_ratio_idc = bv.getBits(8);
            if (aspect_ratio_idc == 255 /*Extended_SAR*/)
                bv.skipBits(32); // sar_width(16); sar_height(16)
        }
        Boolean overscan_info_present_flag = bv.get1BitBoolean();
        if (overscan_info_present_flag)
            bv.skipBits(1); // overscan_appropriate_flag
        Boolean video_signal_type_present_flag = bv.get1BitBoolean();
        if (video_signal_type_present_flag)
        {
            bv.skipBits(4); // video_format(3); video_full_range_flag(1)
            Boolean colour_description_present_flag = bv.get1BitBoolean();
            if (colour_description_present_flag)
                bv.skipBits(24); // colour_primaries(8); transfer_characteristics(8);
                                 // matrix_coefficients(8)
        }
        Boolean chroma_loc_info_present_flag = bv.get1BitBoolean();
        if (chroma_loc_info_present_flag)
        {
            bv.get_expGolomb(); // chroma_sample_loc_type_top_field
            bv.get_expGolomb(); // chroma_sample_loc_type_bottom_field
        }
        Boolean timing_info_present_flag = bv.get1BitBoolean();
        if (timing_info_present_flag)
        {
            unsigned num_units_in_tick = bv.getBits(32);
            unsigned time_scale = bv.getBits(32);
            Boolean fixed_frame_rate_flag = bv.get1BitBoolean();
            _framerate = (double)time_scale / (double)num_units_in_tick / 2.0;
        }
    }
}
char const* H265VideoRTPSink::auxSDPLine() {
  // Generate a new "a=fmtp:" line each time, using our VPS, SPS and PPS (if we have them),
  // otherwise parameters from our framer source (in case they've changed since the last time that
  // we were called):
  H264or5VideoStreamFramer* framerSource = NULL;
  u_int8_t* vps = fVPS; unsigned vpsSize = fVPSSize;
  u_int8_t* sps = fSPS; unsigned spsSize = fSPSSize;
  u_int8_t* pps = fPPS; unsigned ppsSize = fPPSSize;
  if (vps == NULL || sps == NULL || pps == NULL) {
    // We need to get VPS, SPS and PPS from our framer source:
    if (fOurFragmenter == NULL) return NULL; // we don't yet have a fragmenter (and therefore not a source)
    framerSource = (H264or5VideoStreamFramer*)(fOurFragmenter->inputSource());
    if (framerSource == NULL) return NULL; // we don't yet have a source

    framerSource->getVPSandSPSandPPS(vps, vpsSize, sps, spsSize, pps, ppsSize);
    if (vps == NULL || sps == NULL || pps == NULL) {
      return NULL; // our source isn't ready
    }
  }

  // Set up the "a=fmtp:" SDP line for this stream.
  u_int8_t* vpsWEB = new u_int8_t[vpsSize]; // "WEB" means "Without Emulation Bytes"
  unsigned vpsWEBSize = removeH264or5EmulationBytes(vpsWEB, vpsSize, vps, vpsSize);
  if (vpsWEBSize < 6/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) {
    // Bad VPS size => assume our source isn't ready
    delete[] vpsWEB;
    return NULL;
  }
  u_int8_t const* profileTierLevelHeaderBytes = &vpsWEB[6];
  unsigned profileSpace  = profileTierLevelHeaderBytes[0]>>6; // general_profile_space
  unsigned profileId = profileTierLevelHeaderBytes[0]&0x1F; // general_profile_idc
  unsigned tierFlag = (profileTierLevelHeaderBytes[0]>>5)&0x1; // general_tier_flag
  unsigned levelId = profileTierLevelHeaderBytes[11]; // general_level_idc
  u_int8_t const* interop_constraints = &profileTierLevelHeaderBytes[5];
  char interopConstraintsStr[100];
  sprintf(interopConstraintsStr, "%02X%02X%02X%02X%02X%02X", 
	  interop_constraints[0], interop_constraints[1], interop_constraints[2],
	  interop_constraints[3], interop_constraints[4], interop_constraints[5]);
  delete[] vpsWEB;

  char* sprop_vps = base64Encode((char*)vps, vpsSize);
  char* sprop_sps = base64Encode((char*)sps, spsSize);
  char* sprop_pps = base64Encode((char*)pps, ppsSize);

  char const* fmtpFmt =
    "a=fmtp:%d profile-space=%u"
    ";profile-id=%u"
    ";tier-flag=%u"
    ";level-id=%u"
    ";interop-constraints=%s"
    ";sprop-vps=%s"
    ";sprop-sps=%s"
    ";sprop-pps=%s\r\n";
  unsigned fmtpFmtSize = strlen(fmtpFmt)
    + 3 /* max num chars: rtpPayloadType */ + 20 /* max num chars: profile_space */
    + 20 /* max num chars: profile_id */
    + 20 /* max num chars: tier_flag */
    + 20 /* max num chars: level_id */
    + strlen(interopConstraintsStr)
    + strlen(sprop_vps)
    + strlen(sprop_sps)
    + strlen(sprop_pps);
  char* fmtp = new char[fmtpFmtSize];
  sprintf(fmtp, fmtpFmt,
          rtpPayloadType(), profileSpace,
	  profileId,
	  tierFlag,
	  levelId,
	  interopConstraintsStr,
	  sprop_vps,
	  sprop_sps,
	  sprop_pps);

  delete[] sprop_vps;
  delete[] sprop_sps;
  delete[] sprop_pps;

  delete[] fFmtpSDPLine; fFmtpSDPLine = fmtp;
  return fFmtpSDPLine;
}