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 | // H.264 Video RTP Sources |
19 | // Implementation |
20 | |
21 | #include "H264VideoRTPSource.hh" |
22 | #include "Base64.hh" |
23 | |
24 | ////////// H264BufferedPacket and H264BufferedPacketFactory ////////// |
25 | |
26 | class H264BufferedPacket: public BufferedPacket { |
27 | public: |
28 | H264BufferedPacket(H264VideoRTPSource& ourSource); |
29 | virtual ~H264BufferedPacket(); |
30 | |
31 | private: // redefined virtual functions |
32 | virtual unsigned nextEnclosedFrameSize(unsigned char*& framePtr, |
33 | unsigned dataSize); |
34 | private: |
35 | H264VideoRTPSource& fOurSource; |
36 | }; |
37 | |
38 | class H264BufferedPacketFactory: public BufferedPacketFactory { |
39 | private: // redefined virtual functions |
40 | virtual BufferedPacket* createNewPacket(MultiFramedRTPSource* ourSource); |
41 | }; |
42 | |
43 | |
44 | ///////// H264VideoRTPSource implementation //////// |
45 | |
46 | H264VideoRTPSource* |
47 | H264VideoRTPSource::createNew(UsageEnvironment& env, Groupsock* RTPgs, |
48 | unsigned char rtpPayloadFormat, |
49 | unsigned rtpTimestampFrequency) { |
50 | return new H264VideoRTPSource(env, RTPgs, rtpPayloadFormat, |
51 | rtpTimestampFrequency); |
52 | } |
53 | |
54 | H264VideoRTPSource |
55 | ::H264VideoRTPSource(UsageEnvironment& env, Groupsock* RTPgs, |
56 | unsigned char rtpPayloadFormat, |
57 | unsigned rtpTimestampFrequency) |
58 | : MultiFramedRTPSource(env, RTPgs, rtpPayloadFormat, rtpTimestampFrequency, |
59 | new H264BufferedPacketFactory) { |
60 | } |
61 | |
62 | H264VideoRTPSource::~H264VideoRTPSource() { |
63 | } |
64 | |
65 | Boolean H264VideoRTPSource |
66 | ::(BufferedPacket* packet, |
67 | unsigned& ) { |
68 | unsigned char* = packet->data(); |
69 | unsigned packetSize = packet->dataSize(); |
70 | unsigned numBytesToSkip; |
71 | |
72 | // Check the 'nal_unit_type' for special 'aggregation' or 'fragmentation' packets: |
73 | if (packetSize < 1) return False; |
74 | fCurPacketNALUnitType = (headerStart[0]&0x1F); |
75 | switch (fCurPacketNALUnitType) { |
76 | case 24: { // STAP-A |
77 | numBytesToSkip = 1; // discard the type byte |
78 | break; |
79 | } |
80 | case 25: case 26: case 27: { // STAP-B, MTAP16, or MTAP24 |
81 | numBytesToSkip = 3; // discard the type byte, and the initial DON |
82 | break; |
83 | } |
84 | case 28: case 29: { // // FU-A or FU-B |
85 | // For these NALUs, the first two bytes are the FU indicator and the FU header. |
86 | // If the start bit is set, we reconstruct the original NAL header into byte 1: |
87 | if (packetSize < 2) return False; |
88 | unsigned char startBit = headerStart[1]&0x80; |
89 | unsigned char endBit = headerStart[1]&0x40; |
90 | if (startBit) { |
91 | fCurrentPacketBeginsFrame = True; |
92 | |
93 | headerStart[1] = (headerStart[0]&0xE0)|(headerStart[1]&0x1F); |
94 | numBytesToSkip = 1; |
95 | } else { |
96 | // The start bit is not set, so we skip both the FU indicator and header: |
97 | fCurrentPacketBeginsFrame = False; |
98 | numBytesToSkip = 2; |
99 | } |
100 | fCurrentPacketCompletesFrame = (endBit != 0); |
101 | break; |
102 | } |
103 | default: { |
104 | // This packet contains one complete NAL unit: |
105 | fCurrentPacketBeginsFrame = fCurrentPacketCompletesFrame = True; |
106 | numBytesToSkip = 0; |
107 | break; |
108 | } |
109 | } |
110 | |
111 | resultSpecialHeaderSize = numBytesToSkip; |
112 | return True; |
113 | } |
114 | |
115 | char const* H264VideoRTPSource::MIMEtype() const { |
116 | return "video/H264" ; |
117 | } |
118 | |
119 | SPropRecord* parseSPropParameterSets(char const* sPropParameterSetsStr, |
120 | // result parameter: |
121 | unsigned& numSPropRecords) { |
122 | // Make a copy of the input string, so we can replace the commas with '\0's: |
123 | char* inStr = strDup(sPropParameterSetsStr); |
124 | if (inStr == NULL) { |
125 | numSPropRecords = 0; |
126 | return NULL; |
127 | } |
128 | |
129 | // Count the number of commas (and thus the number of parameter sets): |
130 | numSPropRecords = 1; |
131 | char* s; |
132 | for (s = inStr; *s != '\0'; ++s) { |
133 | if (*s == ',') { |
134 | ++numSPropRecords; |
135 | *s = '\0'; |
136 | } |
137 | } |
138 | |
139 | // Allocate and fill in the result array: |
140 | SPropRecord* resultArray = new SPropRecord[numSPropRecords]; |
141 | s = inStr; |
142 | for (unsigned i = 0; i < numSPropRecords; ++i) { |
143 | resultArray[i].sPropBytes = base64Decode(s, resultArray[i].sPropLength); |
144 | s += strlen(s) + 1; |
145 | } |
146 | |
147 | delete[] inStr; |
148 | return resultArray; |
149 | } |
150 | |
151 | |
152 | ////////// H264BufferedPacket and H264BufferedPacketFactory implementation ////////// |
153 | |
154 | H264BufferedPacket::H264BufferedPacket(H264VideoRTPSource& ourSource) |
155 | : fOurSource(ourSource) { |
156 | } |
157 | |
158 | H264BufferedPacket::~H264BufferedPacket() { |
159 | } |
160 | |
161 | unsigned H264BufferedPacket |
162 | ::nextEnclosedFrameSize(unsigned char*& framePtr, unsigned dataSize) { |
163 | unsigned resultNALUSize = 0; // if an error occurs |
164 | |
165 | switch (fOurSource.fCurPacketNALUnitType) { |
166 | case 24: case 25: { // STAP-A or STAP-B |
167 | // The first two bytes are NALU size: |
168 | if (dataSize < 2) break; |
169 | resultNALUSize = (framePtr[0]<<8)|framePtr[1]; |
170 | framePtr += 2; |
171 | break; |
172 | } |
173 | case 26: { // MTAP16 |
174 | // The first two bytes are NALU size. The next three are the DOND and TS offset: |
175 | if (dataSize < 5) break; |
176 | resultNALUSize = (framePtr[0]<<8)|framePtr[1]; |
177 | framePtr += 5; |
178 | break; |
179 | } |
180 | case 27: { // MTAP24 |
181 | // The first two bytes are NALU size. The next four are the DOND and TS offset: |
182 | if (dataSize < 6) break; |
183 | resultNALUSize = (framePtr[0]<<8)|framePtr[1]; |
184 | framePtr += 6; |
185 | break; |
186 | } |
187 | default: { |
188 | // Common case: We use the entire packet data: |
189 | return dataSize; |
190 | } |
191 | } |
192 | |
193 | return (resultNALUSize <= dataSize) ? resultNALUSize : dataSize; |
194 | } |
195 | |
196 | BufferedPacket* H264BufferedPacketFactory |
197 | ::createNewPacket(MultiFramedRTPSource* ourSource) { |
198 | return new H264BufferedPacket((H264VideoRTPSource&)(*ourSource)); |
199 | } |
200 | |