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; }