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 H264or5VideoRTPSink::doSpecialFrameHandling(unsigned /*fragmentationOffset*/, unsigned char* /*frameStart*/, unsigned /*numBytesInFrame*/, struct timeval framePresentationTime, unsigned /*numRemainingBytes*/) { // Set the RTP 'M' (marker) bit iff // 1/ The most recently delivered fragment was the end of (or the only fragment of) an NAL unit, and // 2/ This NAL unit was the last NAL unit of an 'access unit' (i.e. video frame). if (fOurFragmenter != NULL) { H264or5VideoStreamFramer* framerSource = (H264or5VideoStreamFramer*)(fOurFragmenter->inputSource()); // This relies on our fragmenter's source being a "H264or5VideoStreamFramer". if (((H264or5Fragmenter*)fOurFragmenter)->lastFragmentCompletedNALUnit() && framerSource != NULL && framerSource->pictureEndMarker()) { setMarkerBit(); framerSource->pictureEndMarker() = False; } } setTimestamp(framePresentationTime); }
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; }