1 /**********
2 This library is free software; you can redistribute it and/or modify it under
3 the terms of the GNU Lesser General Public License as published by the
4 Free Software Foundation; either version 3 of the License, or (at your
5 option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6 
7 This library is distributed in the hope that it will be useful, but WITHOUT
8 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for
10 more details.
11 
12 You should have received a copy of the GNU Lesser General Public License
13 along with this library; if not, write to the Free Software Foundation, Inc.,
14 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
15 **********/
16 // "liveMedia"
17 // Copyright (c) 1996-2020 Live Networks, Inc.  All rights reserved.
18 // RTP sink for H.265 video
19 // Implementation
20 
21 #include "H265VideoRTPSink.hh"
22 #include "H265VideoStreamFramer.hh"
23 #include "Base64.hh"
24 #include "BitVector.hh"
25 #include "H264VideoRTPSource.hh" // for "parseSPropParameterSets()"
26 
27 ////////// H265VideoRTPSink implementation //////////
28 
29 H265VideoRTPSink
H265VideoRTPSink(UsageEnvironment & env,Groupsock * RTPgs,unsigned char rtpPayloadFormat,u_int8_t const * vps,unsigned vpsSize,u_int8_t const * sps,unsigned spsSize,u_int8_t const * pps,unsigned ppsSize)30 ::H265VideoRTPSink(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat,
31 		   u_int8_t const* vps, unsigned vpsSize,
32 		   u_int8_t const* sps, unsigned spsSize,
33 		   u_int8_t const* pps, unsigned ppsSize)
34   : H264or5VideoRTPSink(265, env, RTPgs, rtpPayloadFormat,
35 			vps, vpsSize, sps, spsSize, pps, ppsSize) {
36 }
37 
~H265VideoRTPSink()38 H265VideoRTPSink::~H265VideoRTPSink() {
39 }
40 
41 H265VideoRTPSink* H265VideoRTPSink
createNew(UsageEnvironment & env,Groupsock * RTPgs,unsigned char rtpPayloadFormat)42 ::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat) {
43   return new H265VideoRTPSink(env, RTPgs, rtpPayloadFormat);
44 }
45 
46 H265VideoRTPSink* H265VideoRTPSink
createNew(UsageEnvironment & env,Groupsock * RTPgs,unsigned char rtpPayloadFormat,u_int8_t const * vps,unsigned vpsSize,u_int8_t const * sps,unsigned spsSize,u_int8_t const * pps,unsigned ppsSize)47 ::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat,
48 	    u_int8_t const* vps, unsigned vpsSize,
49 	    u_int8_t const* sps, unsigned spsSize,
50 	    u_int8_t const* pps, unsigned ppsSize) {
51   return new H265VideoRTPSink(env, RTPgs, rtpPayloadFormat,
52 			      vps, vpsSize, sps, spsSize, pps, ppsSize);
53 }
54 
55 H265VideoRTPSink* H265VideoRTPSink
createNew(UsageEnvironment & env,Groupsock * RTPgs,unsigned char rtpPayloadFormat,char const * sPropVPSStr,char const * sPropSPSStr,char const * sPropPPSStr)56 ::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat,
57 	    char const* sPropVPSStr, char const* sPropSPSStr, char const* sPropPPSStr) {
58   u_int8_t* vps = NULL; unsigned vpsSize = 0;
59   u_int8_t* sps = NULL; unsigned spsSize = 0;
60   u_int8_t* pps = NULL; unsigned ppsSize = 0;
61 
62   // Parse each 'sProp' string, extracting and then classifying the NAL unit(s) from each one.
63   // We're 'liberal in what we accept'; it's OK if the strings don't contain the NAL unit type
64   // implied by their names (or if one or more of the strings encode multiple NAL units).
65   SPropRecord* sPropRecords[3];
66   unsigned numSPropRecords[3];
67   sPropRecords[0] = parseSPropParameterSets(sPropVPSStr, numSPropRecords[0]);
68   sPropRecords[1] = parseSPropParameterSets(sPropSPSStr, numSPropRecords[1]);
69   sPropRecords[2] = parseSPropParameterSets(sPropPPSStr, numSPropRecords[2]);
70 
71   for (unsigned j = 0; j < 3; ++j) {
72     SPropRecord* records = sPropRecords[j];
73     unsigned numRecords = numSPropRecords[j];
74 
75     for (unsigned i = 0; i < numRecords; ++i) {
76       if (records[i].sPropLength == 0) continue; // bad data
77       u_int8_t nal_unit_type = ((records[i].sPropBytes[0])&0x7E)>>1;
78       if (nal_unit_type == 32/*VPS*/) {
79 	vps = records[i].sPropBytes;
80 	vpsSize = records[i].sPropLength;
81       } else if (nal_unit_type == 33/*SPS*/) {
82 	sps = records[i].sPropBytes;
83 	spsSize = records[i].sPropLength;
84       } else if (nal_unit_type == 34/*PPS*/) {
85 	pps = records[i].sPropBytes;
86 	ppsSize = records[i].sPropLength;
87       }
88     }
89   }
90 
91   H265VideoRTPSink* result = new H265VideoRTPSink(env, RTPgs, rtpPayloadFormat,
92 						  vps, vpsSize, sps, spsSize, pps, ppsSize);
93   delete[] sPropRecords[0]; delete[] sPropRecords[1]; delete[] sPropRecords[2];
94 
95   return result;
96 }
97 
sourceIsCompatibleWithUs(MediaSource & source)98 Boolean H265VideoRTPSink::sourceIsCompatibleWithUs(MediaSource& source) {
99   // Our source must be an appropriate framer:
100   return source.isH265VideoStreamFramer();
101 }
102 
auxSDPLine()103 char const* H265VideoRTPSink::auxSDPLine() {
104   // Generate a new "a=fmtp:" line each time, using our VPS, SPS and PPS (if we have them),
105   // otherwise parameters from our framer source (in case they've changed since the last time that
106   // we were called):
107   H264or5VideoStreamFramer* framerSource = NULL;
108   u_int8_t* vps = fVPS; unsigned vpsSize = fVPSSize;
109   u_int8_t* sps = fSPS; unsigned spsSize = fSPSSize;
110   u_int8_t* pps = fPPS; unsigned ppsSize = fPPSSize;
111   if (vps == NULL || sps == NULL || pps == NULL) {
112     // We need to get VPS, SPS and PPS from our framer source:
113     if (fOurFragmenter == NULL) return NULL; // we don't yet have a fragmenter (and therefore not a source)
114     framerSource = (H264or5VideoStreamFramer*)(fOurFragmenter->inputSource());
115     if (framerSource == NULL) return NULL; // we don't yet have a source
116 
117     framerSource->getVPSandSPSandPPS(vps, vpsSize, sps, spsSize, pps, ppsSize);
118     if (vps == NULL || sps == NULL || pps == NULL) {
119       return NULL; // our source isn't ready
120     }
121   }
122 
123   // Set up the "a=fmtp:" SDP line for this stream.
124   u_int8_t* vpsWEB = new u_int8_t[vpsSize]; // "WEB" means "Without Emulation Bytes"
125   unsigned vpsWEBSize = removeH264or5EmulationBytes(vpsWEB, vpsSize, vps, vpsSize);
126   if (vpsWEBSize < 6/*'profile_tier_level' offset*/ + 12/*num 'profile_tier_level' bytes*/) {
127     // Bad VPS size => assume our source isn't ready
128     delete[] vpsWEB;
129     return NULL;
130   }
131   u_int8_t const* profileTierLevelHeaderBytes = &vpsWEB[6];
132   unsigned profileSpace  = profileTierLevelHeaderBytes[0]>>6; // general_profile_space
133   unsigned profileId = profileTierLevelHeaderBytes[0]&0x1F; // general_profile_idc
134   unsigned tierFlag = (profileTierLevelHeaderBytes[0]>>5)&0x1; // general_tier_flag
135   unsigned levelId = profileTierLevelHeaderBytes[11]; // general_level_idc
136   u_int8_t const* interop_constraints = &profileTierLevelHeaderBytes[5];
137   char interopConstraintsStr[100];
138   sprintf(interopConstraintsStr, "%02X%02X%02X%02X%02X%02X",
139 	  interop_constraints[0], interop_constraints[1], interop_constraints[2],
140 	  interop_constraints[3], interop_constraints[4], interop_constraints[5]);
141   delete[] vpsWEB;
142 
143   char* sprop_vps = base64Encode((char*)vps, vpsSize);
144   char* sprop_sps = base64Encode((char*)sps, spsSize);
145   char* sprop_pps = base64Encode((char*)pps, ppsSize);
146 
147   char const* fmtpFmt =
148     "a=fmtp:%d profile-space=%u"
149     ";profile-id=%u"
150     ";tier-flag=%u"
151     ";level-id=%u"
152     ";interop-constraints=%s"
153     ";sprop-vps=%s"
154     ";sprop-sps=%s"
155     ";sprop-pps=%s\r\n";
156   unsigned fmtpFmtSize = strlen(fmtpFmt)
157     + 3 /* max num chars: rtpPayloadType */ + 20 /* max num chars: profile_space */
158     + 20 /* max num chars: profile_id */
159     + 20 /* max num chars: tier_flag */
160     + 20 /* max num chars: level_id */
161     + strlen(interopConstraintsStr)
162     + strlen(sprop_vps)
163     + strlen(sprop_sps)
164     + strlen(sprop_pps);
165   char* fmtp = new char[fmtpFmtSize];
166   sprintf(fmtp, fmtpFmt,
167           rtpPayloadType(), profileSpace,
168 	  profileId,
169 	  tierFlag,
170 	  levelId,
171 	  interopConstraintsStr,
172 	  sprop_vps,
173 	  sprop_sps,
174 	  sprop_pps);
175 
176   delete[] sprop_vps;
177   delete[] sprop_sps;
178   delete[] sprop_pps;
179 
180   delete[] fFmtpSDPLine; fFmtpSDPLine = fmtp;
181   return fFmtpSDPLine;
182 }
183