1/**********
2This library is free software; you can redistribute it and/or modify it under
3the terms of the GNU Lesser General Public License as published by the
4Free Software Foundation; either version 3 of the License, or (at your
5option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.)
6
7This library is distributed in the hope that it will be useful, but WITHOUT
8ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
10more details.
11
12You should have received a copy of the GNU Lesser General Public License
13along with this library; if not, write to the Free Software Foundation, Inc.,
1451 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
29H265VideoRTPSink
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
38H265VideoRTPSink::~H265VideoRTPSink() {
39}
40
41H265VideoRTPSink* H265VideoRTPSink
42::createNew(UsageEnvironment& env, Groupsock* RTPgs, unsigned char rtpPayloadFormat) {
43 return new H265VideoRTPSink(env, RTPgs, rtpPayloadFormat);
44}
45
46H265VideoRTPSink* H265VideoRTPSink
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
55H265VideoRTPSink* H265VideoRTPSink
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
98Boolean H265VideoRTPSink::sourceIsCompatibleWithUs(MediaSource& source) {
99 // Our source must be an appropriate framer:
100 return source.isH265VideoStreamFramer();
101}
102
103char 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