mediasoup 3.9.13 → 3.9.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/node/lib/ActiveSpeakerObserver.d.ts +1 -1
- package/node/lib/ActiveSpeakerObserver.d.ts.map +1 -1
- package/node/lib/AudioLevelObserver.d.ts +1 -1
- package/node/lib/AudioLevelObserver.d.ts.map +1 -1
- package/node/lib/Consumer.d.ts +4 -4
- package/node/lib/Consumer.d.ts.map +1 -1
- package/node/lib/Consumer.js +1 -1
- package/node/lib/DataConsumer.d.ts +4 -4
- package/node/lib/DataConsumer.d.ts.map +1 -1
- package/node/lib/DataConsumer.js +1 -1
- package/node/lib/DataProducer.d.ts +4 -4
- package/node/lib/DataProducer.d.ts.map +1 -1
- package/node/lib/DataProducer.js +1 -1
- package/node/lib/DirectTransport.d.ts +1 -1
- package/node/lib/DirectTransport.d.ts.map +1 -1
- package/node/lib/PipeTransport.d.ts +2 -2
- package/node/lib/PipeTransport.d.ts.map +1 -1
- package/node/lib/PipeTransport.js +1 -1
- package/node/lib/PlainTransport.d.ts +1 -1
- package/node/lib/PlainTransport.d.ts.map +1 -1
- package/node/lib/Producer.d.ts +4 -4
- package/node/lib/Producer.d.ts.map +1 -1
- package/node/lib/Producer.js +1 -1
- package/node/lib/Router.d.ts +4 -4
- package/node/lib/Router.d.ts.map +1 -1
- package/node/lib/Router.js +7 -7
- package/node/lib/RtpObserver.d.ts +3 -3
- package/node/lib/RtpObserver.d.ts.map +1 -1
- package/node/lib/RtpObserver.js +1 -1
- package/node/lib/Transport.d.ts +3 -3
- package/node/lib/Transport.d.ts.map +1 -1
- package/node/lib/Transport.js +5 -5
- package/node/lib/WebRtcTransport.d.ts +1 -1
- package/node/lib/WebRtcTransport.d.ts.map +1 -1
- package/node/lib/Worker.d.ts +3 -3
- package/node/lib/Worker.d.ts.map +1 -1
- package/node/lib/Worker.js +3 -3
- package/node/lib/index.d.ts +1 -1
- package/node/lib/index.d.ts.map +1 -1
- package/node/lib/index.js +2 -2
- package/node/lib/ortc.js +1 -0
- package/node/lib/supportedRtpCapabilities.d.ts.map +1 -1
- package/node/lib/supportedRtpCapabilities.js +15 -0
- package/package.json +6 -6
- package/worker/include/RTC/Codecs/H264_SVC.hpp +115 -0
- package/worker/include/RTC/Codecs/Tools.hpp +11 -0
- package/worker/include/RTC/Consumer.hpp +5 -4
- package/worker/include/RTC/DirectTransport.hpp +4 -0
- package/worker/include/RTC/NackGenerator.hpp +5 -2
- package/worker/include/RTC/PipeConsumer.hpp +1 -0
- package/worker/include/RTC/RTCP/CompoundPacket.hpp +2 -0
- package/worker/include/RTC/RtpDictionaries.hpp +1 -0
- package/worker/include/RTC/RtpStream.hpp +2 -0
- package/worker/include/RTC/RtpStreamRecv.hpp +7 -1
- package/worker/include/RTC/RtpStreamSend.hpp +7 -0
- package/worker/include/RTC/SimpleConsumer.hpp +1 -0
- package/worker/include/RTC/SimulcastConsumer.hpp +4 -0
- package/worker/include/RTC/SvcConsumer.hpp +1 -0
- package/worker/meson.build +4 -0
- package/worker/src/RTC/Codecs/H264_SVC.cpp +428 -0
- package/worker/src/RTC/DirectTransport.cpp +10 -1
- package/worker/src/RTC/NackGenerator.cpp +27 -9
- package/worker/src/RTC/PipeConsumer.cpp +20 -0
- package/worker/src/RTC/Producer.cpp +5 -1
- package/worker/src/RTC/RTCP/CompoundPacket.cpp +7 -0
- package/worker/src/RTC/RtpDictionaries/RtpCodecMimeType.cpp +2 -0
- package/worker/src/RTC/RtpStreamRecv.cpp +4 -3
- package/worker/src/RTC/RtpStreamSend.cpp +32 -0
- package/worker/src/RTC/SimpleConsumer.cpp +17 -0
- package/worker/src/RTC/SimulcastConsumer.cpp +45 -4
- package/worker/src/RTC/SvcConsumer.cpp +17 -0
- package/worker/src/RTC/Transport.cpp +14 -0
- package/worker/src/RTC/TransportCongestionControlClient.cpp +0 -3
- package/worker/test/include/helpers.hpp +119 -0
- package/worker/test/src/RTC/Codecs/TestH264.cpp +30 -0
- package/worker/test/src/RTC/Codecs/TestH264_SVC.cpp +181 -0
- package/worker/test/src/RTC/TestNackGenerator.cpp +3 -1
- package/worker/test/src/RTC/TestRtpPacketH264Svc.cpp +455 -0
- package/worker/test/src/RTC/TestRtpStreamRecv.cpp +4 -3
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
#define MS_CLASS "RTC::Codecs::H264_SVC"
|
|
2
|
+
|
|
3
|
+
#include "RTC/Codecs/H264_SVC.hpp"
|
|
4
|
+
#include "Logger.hpp"
|
|
5
|
+
#include "Utils.hpp"
|
|
6
|
+
|
|
7
|
+
namespace RTC
|
|
8
|
+
{
|
|
9
|
+
namespace Codecs
|
|
10
|
+
{
|
|
11
|
+
/* Class methods. */
|
|
12
|
+
|
|
13
|
+
H264_SVC::PayloadDescriptor* H264_SVC::Parse(
|
|
14
|
+
const uint8_t* data, size_t len, RTC::RtpPacket::FrameMarking* frameMarking, uint8_t frameMarkingLen)
|
|
15
|
+
{
|
|
16
|
+
MS_TRACE();
|
|
17
|
+
|
|
18
|
+
if (len < 2)
|
|
19
|
+
return nullptr;
|
|
20
|
+
|
|
21
|
+
std::unique_ptr<PayloadDescriptor> payloadDescriptor(new PayloadDescriptor());
|
|
22
|
+
|
|
23
|
+
// Use frame-marking.
|
|
24
|
+
if (frameMarking)
|
|
25
|
+
{
|
|
26
|
+
// Read fields.
|
|
27
|
+
payloadDescriptor->s = frameMarking->start;
|
|
28
|
+
payloadDescriptor->e = frameMarking->end;
|
|
29
|
+
payloadDescriptor->i = frameMarking->independent;
|
|
30
|
+
payloadDescriptor->d = frameMarking->discardable;
|
|
31
|
+
payloadDescriptor->b = frameMarking->base;
|
|
32
|
+
payloadDescriptor->tlIndex = frameMarking->tid;
|
|
33
|
+
|
|
34
|
+
payloadDescriptor->hasTlIndex = true;
|
|
35
|
+
|
|
36
|
+
if (frameMarkingLen >= 2)
|
|
37
|
+
{
|
|
38
|
+
payloadDescriptor->hasSlIndex = true;
|
|
39
|
+
payloadDescriptor->slIndex = frameMarking->lid >> 4 & 0x07;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (frameMarkingLen == 3)
|
|
43
|
+
{
|
|
44
|
+
payloadDescriptor->hasTl0picidx = true;
|
|
45
|
+
payloadDescriptor->tl0picidx = frameMarking->tl0picidx;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Detect key frame.
|
|
49
|
+
if (frameMarking->start && frameMarking->independent)
|
|
50
|
+
payloadDescriptor->isKeyFrame = true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// NOTE: Unfortunately libwebrtc produces wrong Frame-Marking (without i=1 in
|
|
54
|
+
// keyframes) when it uses H264 hardware encoder (at least in Mac):
|
|
55
|
+
// https://bugs.chromium.org/p/webrtc/issues/detail?id=10746
|
|
56
|
+
//
|
|
57
|
+
// As a temporal workaround, always do payload parsing to detect keyframes if
|
|
58
|
+
// there is no frame-marking or if there is but keyframe was not detected above.
|
|
59
|
+
if (!frameMarking || !payloadDescriptor->isKeyFrame)
|
|
60
|
+
{
|
|
61
|
+
uint8_t nal = *data & 0x1F;
|
|
62
|
+
|
|
63
|
+
switch (nal)
|
|
64
|
+
{
|
|
65
|
+
// Single NAL unit packet.
|
|
66
|
+
// IDR (instantaneous decoding picture).
|
|
67
|
+
case 1:
|
|
68
|
+
case 5:
|
|
69
|
+
case 7:
|
|
70
|
+
case 14:
|
|
71
|
+
case 20:
|
|
72
|
+
{
|
|
73
|
+
payloadDescriptor =
|
|
74
|
+
H264_SVC::ParseSingleNalu(data, len, std::move(payloadDescriptor), true);
|
|
75
|
+
|
|
76
|
+
if (payloadDescriptor == nullptr)
|
|
77
|
+
return nullptr;
|
|
78
|
+
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Aggreation packet.
|
|
83
|
+
// STAP-A.
|
|
84
|
+
case 24:
|
|
85
|
+
{
|
|
86
|
+
size_t offset{ 1 };
|
|
87
|
+
|
|
88
|
+
len -= 1;
|
|
89
|
+
|
|
90
|
+
// Iterate NAL units.
|
|
91
|
+
while (len >= 3)
|
|
92
|
+
{
|
|
93
|
+
auto naluSize = Utils::Byte::Get2Bytes(data, offset);
|
|
94
|
+
|
|
95
|
+
payloadDescriptor = H264_SVC::ParseSingleNalu(
|
|
96
|
+
(data + offset + sizeof(naluSize)),
|
|
97
|
+
(len - sizeof(naluSize)),
|
|
98
|
+
std::move(payloadDescriptor),
|
|
99
|
+
true);
|
|
100
|
+
if (payloadDescriptor == nullptr)
|
|
101
|
+
return nullptr;
|
|
102
|
+
|
|
103
|
+
if (payloadDescriptor->isKeyFrame)
|
|
104
|
+
{
|
|
105
|
+
break;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check if there is room for the indicated NAL unit size.
|
|
109
|
+
if (len < (naluSize + sizeof(naluSize)))
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
offset += naluSize + sizeof(naluSize);
|
|
113
|
+
len -= naluSize + sizeof(naluSize);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
break;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Aggreation packet.
|
|
120
|
+
// FU-A, FU-B.
|
|
121
|
+
case 28:
|
|
122
|
+
case 29:
|
|
123
|
+
{
|
|
124
|
+
uint8_t startBit = *(data + 1) & 0x80;
|
|
125
|
+
|
|
126
|
+
if (startBit == 128)
|
|
127
|
+
{
|
|
128
|
+
payloadDescriptor = H264_SVC::ParseSingleNalu(
|
|
129
|
+
(data + 1), (len - 1), std::move(payloadDescriptor), (startBit == 128 ? true : false));
|
|
130
|
+
}
|
|
131
|
+
if (payloadDescriptor == nullptr)
|
|
132
|
+
return nullptr;
|
|
133
|
+
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return payloadDescriptor.release();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
std::unique_ptr<H264_SVC::PayloadDescriptor> H264_SVC::ParseSingleNalu(
|
|
143
|
+
const uint8_t* data,
|
|
144
|
+
size_t len,
|
|
145
|
+
std::unique_ptr<H264_SVC::PayloadDescriptor> payloadDescriptor,
|
|
146
|
+
bool isStartBit)
|
|
147
|
+
{
|
|
148
|
+
uint8_t nal = *data & 0x1F;
|
|
149
|
+
|
|
150
|
+
switch (nal)
|
|
151
|
+
{
|
|
152
|
+
// Single NAL unit packet.
|
|
153
|
+
// IDR (instantaneous decoding picture).
|
|
154
|
+
case 5:
|
|
155
|
+
payloadDescriptor->isKeyFrame = true;
|
|
156
|
+
case 1:
|
|
157
|
+
{
|
|
158
|
+
payloadDescriptor->slIndex = 0;
|
|
159
|
+
payloadDescriptor->tlIndex = 0;
|
|
160
|
+
|
|
161
|
+
payloadDescriptor->hasSlIndex = false;
|
|
162
|
+
payloadDescriptor->hasTlIndex = false;
|
|
163
|
+
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case 14:
|
|
167
|
+
case 20:
|
|
168
|
+
{
|
|
169
|
+
size_t offset{ 1 };
|
|
170
|
+
uint8_t byte = data[offset];
|
|
171
|
+
|
|
172
|
+
payloadDescriptor->idr = byte >> 6 & 0x01;
|
|
173
|
+
payloadDescriptor->priorityId = byte & 0x06;
|
|
174
|
+
payloadDescriptor->isKeyFrame = (isStartBit && payloadDescriptor->idr) ? true : false;
|
|
175
|
+
|
|
176
|
+
if (len < ++offset + 1)
|
|
177
|
+
return nullptr;
|
|
178
|
+
|
|
179
|
+
byte = data[offset];
|
|
180
|
+
payloadDescriptor->noIntLayerPredFlag = byte >> 7 & 0x01;
|
|
181
|
+
payloadDescriptor->slIndex = byte >> 4 & 0x03;
|
|
182
|
+
|
|
183
|
+
if (len < ++offset + 1)
|
|
184
|
+
return nullptr;
|
|
185
|
+
|
|
186
|
+
byte = data[offset];
|
|
187
|
+
|
|
188
|
+
payloadDescriptor->tlIndex = byte >> 5 & 0x03;
|
|
189
|
+
|
|
190
|
+
payloadDescriptor->hasSlIndex = payloadDescriptor->slIndex ? true : false;
|
|
191
|
+
payloadDescriptor->hasTlIndex = payloadDescriptor->tlIndex ? true : false;
|
|
192
|
+
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
195
|
+
case 7:
|
|
196
|
+
{
|
|
197
|
+
payloadDescriptor->isKeyFrame = isStartBit ? true : false;
|
|
198
|
+
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return payloadDescriptor;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
void H264_SVC::ProcessRtpPacket(RTC::RtpPacket* packet)
|
|
206
|
+
{
|
|
207
|
+
MS_TRACE();
|
|
208
|
+
|
|
209
|
+
auto* data = packet->GetPayload();
|
|
210
|
+
auto len = packet->GetPayloadLength();
|
|
211
|
+
RtpPacket::FrameMarking* frameMarking{ nullptr };
|
|
212
|
+
uint8_t frameMarkingLen{ 0 };
|
|
213
|
+
|
|
214
|
+
// Read frame-marking.
|
|
215
|
+
packet->ReadFrameMarking(&frameMarking, frameMarkingLen);
|
|
216
|
+
|
|
217
|
+
PayloadDescriptor* payloadDescriptor =
|
|
218
|
+
H264_SVC::Parse(data, len, frameMarking, frameMarkingLen);
|
|
219
|
+
|
|
220
|
+
if (!payloadDescriptor)
|
|
221
|
+
return;
|
|
222
|
+
|
|
223
|
+
auto* payloadDescriptorHandler = new PayloadDescriptorHandler(payloadDescriptor);
|
|
224
|
+
|
|
225
|
+
packet->SetPayloadDescriptorHandler(payloadDescriptorHandler);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/* Instance methods. */
|
|
229
|
+
|
|
230
|
+
void H264_SVC::PayloadDescriptor::Dump() const
|
|
231
|
+
{
|
|
232
|
+
MS_TRACE();
|
|
233
|
+
|
|
234
|
+
MS_DUMP("<PayloadDescriptor>");
|
|
235
|
+
MS_DUMP(
|
|
236
|
+
" s:%" PRIu8 "|e:%" PRIu8 "|i:%" PRIu8 "|d:%" PRIu8 "|b:%" PRIu8,
|
|
237
|
+
this->s,
|
|
238
|
+
this->e,
|
|
239
|
+
this->i,
|
|
240
|
+
this->d,
|
|
241
|
+
this->b);
|
|
242
|
+
MS_DUMP(" hasSlIndex : %s", this->hasSlIndex ? "true" : "false");
|
|
243
|
+
MS_DUMP(" hasTlIndex : %s", this->hasTlIndex ? "true" : "false");
|
|
244
|
+
MS_DUMP(" tl0picidx : %" PRIu8, this->tl0picidx);
|
|
245
|
+
MS_DUMP(" noIntLayerPredFlag : %" PRIu8, this->noIntLayerPredFlag);
|
|
246
|
+
MS_DUMP(" isKeyFrame : %s", this->isKeyFrame ? "true" : "false");
|
|
247
|
+
MS_DUMP("</PayloadDescriptor>");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
H264_SVC::PayloadDescriptorHandler::PayloadDescriptorHandler(
|
|
251
|
+
H264_SVC::PayloadDescriptor* payloadDescriptor)
|
|
252
|
+
{
|
|
253
|
+
MS_TRACE();
|
|
254
|
+
|
|
255
|
+
this->payloadDescriptor.reset(payloadDescriptor);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
bool H264_SVC::PayloadDescriptorHandler::Process(
|
|
259
|
+
RTC::Codecs::EncodingContext* encodingContext, uint8_t* /*data*/, bool& marker)
|
|
260
|
+
{
|
|
261
|
+
MS_TRACE();
|
|
262
|
+
|
|
263
|
+
auto* context = static_cast<RTC::Codecs::H264_SVC::EncodingContext*>(encodingContext);
|
|
264
|
+
|
|
265
|
+
MS_ASSERT(context->GetTargetSpatialLayer() >= 0, "target spatial layer cannot be -1");
|
|
266
|
+
MS_ASSERT(context->GetTargetTemporalLayer() >= 0, "target temporal layer cannot be -1");
|
|
267
|
+
|
|
268
|
+
auto packetSpatialLayer = GetSpatialLayer();
|
|
269
|
+
auto packetTemporalLayer = GetTemporalLayer();
|
|
270
|
+
auto tmpSpatialLayer = context->GetCurrentSpatialLayer();
|
|
271
|
+
auto tmpTemporalLayer = context->GetCurrentTemporalLayer();
|
|
272
|
+
|
|
273
|
+
// If packet spatial or temporal layer is higher than maximum announced
|
|
274
|
+
// one, drop the packet.
|
|
275
|
+
// clang-format off
|
|
276
|
+
if (
|
|
277
|
+
packetSpatialLayer >= context->GetSpatialLayers() ||
|
|
278
|
+
packetTemporalLayer >= context->GetTemporalLayers()
|
|
279
|
+
)
|
|
280
|
+
// clang-format on
|
|
281
|
+
{
|
|
282
|
+
MS_WARN_TAG(
|
|
283
|
+
rtp, "too high packet layers %" PRIu8 ":%" PRIu8, packetSpatialLayer, packetTemporalLayer);
|
|
284
|
+
|
|
285
|
+
return false;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// clang-format off
|
|
289
|
+
bool isOldPacket = false;
|
|
290
|
+
|
|
291
|
+
// Upgrade current spatial layer if needed.
|
|
292
|
+
if (context->GetTargetSpatialLayer() > context->GetCurrentSpatialLayer())
|
|
293
|
+
{
|
|
294
|
+
if (this->payloadDescriptor->isKeyFrame)
|
|
295
|
+
{
|
|
296
|
+
MS_DEBUG_DEV(
|
|
297
|
+
"upgrading tmpSpatialLayer from %" PRIu16 " to %" PRIu16 " (packet:%" PRIu8 ":%" PRIu8
|
|
298
|
+
")",
|
|
299
|
+
context->GetCurrentSpatialLayer(),
|
|
300
|
+
context->GetTargetSpatialLayer(),
|
|
301
|
+
packetSpatialLayer,
|
|
302
|
+
packetTemporalLayer);
|
|
303
|
+
|
|
304
|
+
tmpSpatialLayer = context->GetTargetSpatialLayer();
|
|
305
|
+
tmpTemporalLayer = 0; // Just in case.
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Downgrade current spatial layer if needed.
|
|
309
|
+
else if (context->GetTargetSpatialLayer() < context->GetCurrentSpatialLayer())
|
|
310
|
+
{
|
|
311
|
+
// In K-SVC we must wait for a keyframe.
|
|
312
|
+
if (context->IsKSvc())
|
|
313
|
+
{
|
|
314
|
+
if (this->payloadDescriptor->isKeyFrame)
|
|
315
|
+
// clang-format on
|
|
316
|
+
{
|
|
317
|
+
MS_DEBUG_DEV(
|
|
318
|
+
"downgrading tmpSpatialLayer from %" PRIu16 " to %" PRIu16 " (packet:%" PRIu8
|
|
319
|
+
":%" PRIu8 ") after keyframe (K-SVC)",
|
|
320
|
+
context->GetCurrentSpatialLayer(),
|
|
321
|
+
context->GetTargetSpatialLayer(),
|
|
322
|
+
packetSpatialLayer,
|
|
323
|
+
packetTemporalLayer);
|
|
324
|
+
|
|
325
|
+
tmpSpatialLayer = context->GetTargetSpatialLayer();
|
|
326
|
+
tmpTemporalLayer = 0; // Just in case.
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
// In full SVC we do not need a keyframe.
|
|
330
|
+
else
|
|
331
|
+
{
|
|
332
|
+
// clang-format off
|
|
333
|
+
if (
|
|
334
|
+
packetSpatialLayer == context->GetTargetSpatialLayer() &&
|
|
335
|
+
this->payloadDescriptor->e
|
|
336
|
+
)
|
|
337
|
+
// clang-format on
|
|
338
|
+
{
|
|
339
|
+
MS_DEBUG_DEV(
|
|
340
|
+
"downgrading tmpSpatialLayer from %" PRIu16 " to %" PRIu16 " (packet:%" PRIu8
|
|
341
|
+
":%" PRIu8 ") without keyframe (full SVC)",
|
|
342
|
+
context->GetCurrentSpatialLayer(),
|
|
343
|
+
context->GetTargetSpatialLayer(),
|
|
344
|
+
packetSpatialLayer,
|
|
345
|
+
packetTemporalLayer);
|
|
346
|
+
|
|
347
|
+
tmpSpatialLayer = context->GetTargetSpatialLayer();
|
|
348
|
+
tmpTemporalLayer = 0; // Just in case.
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// Filter spatial layers higher than current one (unless old packet).
|
|
354
|
+
if (packetSpatialLayer > tmpSpatialLayer && !isOldPacket)
|
|
355
|
+
return false;
|
|
356
|
+
|
|
357
|
+
// Check and handle temporal layer (unless old packet).
|
|
358
|
+
if (!isOldPacket)
|
|
359
|
+
{
|
|
360
|
+
// Upgrade current temporal layer if needed.
|
|
361
|
+
if (context->GetTargetTemporalLayer() > context->GetCurrentTemporalLayer())
|
|
362
|
+
{
|
|
363
|
+
// clang-format off
|
|
364
|
+
if (
|
|
365
|
+
packetTemporalLayer >= context->GetCurrentTemporalLayer() + 1 &&
|
|
366
|
+
this->payloadDescriptor->s
|
|
367
|
+
)
|
|
368
|
+
// clang-format on
|
|
369
|
+
{
|
|
370
|
+
MS_DEBUG_DEV(
|
|
371
|
+
"upgrading tmpTemporalLayer from %" PRIu16 " to %" PRIu8 " (packet:%" PRIu8 ":%" PRIu8
|
|
372
|
+
")",
|
|
373
|
+
context->GetCurrentTemporalLayer(),
|
|
374
|
+
packetTemporalLayer,
|
|
375
|
+
packetSpatialLayer,
|
|
376
|
+
packetTemporalLayer);
|
|
377
|
+
|
|
378
|
+
tmpTemporalLayer = packetTemporalLayer;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
// Downgrade current temporal layer if needed.
|
|
382
|
+
else if (context->GetTargetTemporalLayer() < context->GetCurrentTemporalLayer())
|
|
383
|
+
{
|
|
384
|
+
// clang-format off
|
|
385
|
+
if (
|
|
386
|
+
packetTemporalLayer == context->GetTargetTemporalLayer() &&
|
|
387
|
+
this->payloadDescriptor->e
|
|
388
|
+
)
|
|
389
|
+
// clang-format on
|
|
390
|
+
{
|
|
391
|
+
MS_DEBUG_DEV(
|
|
392
|
+
"downgrading tmpTemporalLayer from %" PRIu16 " to %" PRIu16 " (packet:%" PRIu8
|
|
393
|
+
":%" PRIu8 ")",
|
|
394
|
+
context->GetCurrentTemporalLayer(),
|
|
395
|
+
context->GetTargetTemporalLayer(),
|
|
396
|
+
packetSpatialLayer,
|
|
397
|
+
packetTemporalLayer);
|
|
398
|
+
|
|
399
|
+
tmpTemporalLayer = context->GetTargetTemporalLayer();
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
// Filter temporal layers higher than current one.
|
|
404
|
+
if (packetTemporalLayer > tmpTemporalLayer)
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// Set marker bit if needed.
|
|
409
|
+
if (packetSpatialLayer == tmpSpatialLayer && this->payloadDescriptor->e)
|
|
410
|
+
marker = true;
|
|
411
|
+
|
|
412
|
+
// Update current spatial layer if needed.
|
|
413
|
+
if (tmpSpatialLayer != context->GetCurrentSpatialLayer())
|
|
414
|
+
context->SetCurrentSpatialLayer(tmpSpatialLayer);
|
|
415
|
+
|
|
416
|
+
// Update current temporal layer if needed.
|
|
417
|
+
if (tmpTemporalLayer != context->GetCurrentTemporalLayer())
|
|
418
|
+
context->SetCurrentTemporalLayer(tmpTemporalLayer);
|
|
419
|
+
|
|
420
|
+
return true;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
void H264_SVC::PayloadDescriptorHandler::Restore(uint8_t* /*data*/)
|
|
424
|
+
{
|
|
425
|
+
MS_TRACE();
|
|
426
|
+
}
|
|
427
|
+
} // namespace Codecs
|
|
428
|
+
} // namespace RTC
|
|
@@ -20,6 +20,8 @@ namespace RTC
|
|
|
20
20
|
DirectTransport::~DirectTransport()
|
|
21
21
|
{
|
|
22
22
|
MS_TRACE();
|
|
23
|
+
|
|
24
|
+
delete[] this->buffer;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
void DirectTransport::FillJson(json& jsonObject) const
|
|
@@ -102,7 +104,14 @@ namespace RTC
|
|
|
102
104
|
return;
|
|
103
105
|
}
|
|
104
106
|
|
|
105
|
-
|
|
107
|
+
// If this is the first time to reveive a RTP packet then allocate the receiving buffer now.
|
|
108
|
+
if (!this->buffer)
|
|
109
|
+
this->buffer = new uint8_t[RTC::MtuSize + 100];
|
|
110
|
+
|
|
111
|
+
// Copy the received packet into this buffer so it can be expanded later.
|
|
112
|
+
std::memcpy(this->buffer, data, static_cast<size_t>(len));
|
|
113
|
+
|
|
114
|
+
RTC::RtpPacket* packet = RTC::RtpPacket::Parse(this->buffer, len);
|
|
106
115
|
|
|
107
116
|
if (!packet)
|
|
108
117
|
{
|
|
@@ -12,15 +12,16 @@ namespace RTC
|
|
|
12
12
|
{
|
|
13
13
|
/* Static. */
|
|
14
14
|
|
|
15
|
-
constexpr size_t MaxPacketAge{ 10000u };
|
|
16
|
-
constexpr size_t MaxNackPackets{ 1000u };
|
|
17
|
-
constexpr uint32_t DefaultRtt{ 100u };
|
|
18
|
-
constexpr uint8_t MaxNackRetries{ 10u };
|
|
19
|
-
constexpr uint64_t TimerInterval{ 40u };
|
|
15
|
+
static constexpr size_t MaxPacketAge{ 10000u };
|
|
16
|
+
static constexpr size_t MaxNackPackets{ 1000u };
|
|
17
|
+
static constexpr uint32_t DefaultRtt{ 100u };
|
|
18
|
+
static constexpr uint8_t MaxNackRetries{ 10u };
|
|
19
|
+
static constexpr uint64_t TimerInterval{ 40u };
|
|
20
20
|
|
|
21
21
|
/* Instance methods. */
|
|
22
22
|
|
|
23
|
-
NackGenerator::NackGenerator(Listener* listener
|
|
23
|
+
NackGenerator::NackGenerator(Listener* listener, unsigned int sendNackDelayMs)
|
|
24
|
+
: listener(listener), sendNackDelayMs(sendNackDelayMs), rtt(DefaultRtt)
|
|
24
25
|
{
|
|
25
26
|
MS_TRACE();
|
|
26
27
|
|
|
@@ -74,9 +75,14 @@ namespace RTC
|
|
|
74
75
|
packet->GetSequenceNumber(),
|
|
75
76
|
isRecovered ? "true" : "false");
|
|
76
77
|
|
|
78
|
+
auto retries = it->second.retries;
|
|
79
|
+
|
|
77
80
|
this->nackList.erase(it);
|
|
78
81
|
|
|
79
|
-
|
|
82
|
+
if (retries != 0)
|
|
83
|
+
return true;
|
|
84
|
+
else
|
|
85
|
+
return false;
|
|
80
86
|
}
|
|
81
87
|
|
|
82
88
|
// Out of order packet or already handled NACKed packet.
|
|
@@ -183,7 +189,13 @@ namespace RTC
|
|
|
183
189
|
if (this->recoveredList.find(seq) != this->recoveredList.end())
|
|
184
190
|
continue;
|
|
185
191
|
|
|
186
|
-
this->nackList.emplace(std::make_pair(
|
|
192
|
+
this->nackList.emplace(std::make_pair(
|
|
193
|
+
seq,
|
|
194
|
+
NackInfo{
|
|
195
|
+
DepLibUV::GetTimeMs(),
|
|
196
|
+
seq,
|
|
197
|
+
seq,
|
|
198
|
+
}));
|
|
187
199
|
}
|
|
188
200
|
}
|
|
189
201
|
|
|
@@ -226,6 +238,12 @@ namespace RTC
|
|
|
226
238
|
NackInfo& nackInfo = it->second;
|
|
227
239
|
uint16_t seq = nackInfo.seq;
|
|
228
240
|
|
|
241
|
+
if (this->sendNackDelayMs > 0 && nowMs - nackInfo.createdAtMs < this->sendNackDelayMs)
|
|
242
|
+
{
|
|
243
|
+
++it;
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
|
|
229
247
|
// clang-format off
|
|
230
248
|
if (
|
|
231
249
|
filter == NackFilter::SEQ &&
|
|
@@ -259,7 +277,7 @@ namespace RTC
|
|
|
259
277
|
continue;
|
|
260
278
|
}
|
|
261
279
|
|
|
262
|
-
if (filter == NackFilter::TIME && nowMs - nackInfo.sentAtMs >= this->rtt)
|
|
280
|
+
if (filter == NackFilter::TIME && (nackInfo.sentAtMs == 0 || nowMs - nackInfo.sentAtMs >= this->rtt))
|
|
263
281
|
{
|
|
264
282
|
nackBatch.emplace_back(seq);
|
|
265
283
|
nackInfo.retries++;
|
|
@@ -312,6 +312,16 @@ namespace RTC
|
|
|
312
312
|
|
|
313
313
|
packet->AddSdesChunk(sdesChunk);
|
|
314
314
|
|
|
315
|
+
auto* dlrr = rtpStream->GetRtcpXrDelaySinceLastRr(nowMs);
|
|
316
|
+
|
|
317
|
+
if (dlrr)
|
|
318
|
+
{
|
|
319
|
+
auto* report = new RTC::RTCP::DelaySinceLastRr();
|
|
320
|
+
|
|
321
|
+
report->AddSsrcInfo(dlrr);
|
|
322
|
+
packet->AddDelaySinceLastRr(report);
|
|
323
|
+
}
|
|
324
|
+
|
|
315
325
|
this->lastRtcpSentTime = nowMs;
|
|
316
326
|
}
|
|
317
327
|
|
|
@@ -388,6 +398,16 @@ namespace RTC
|
|
|
388
398
|
rtpStream->ReceiveRtcpReceiverReport(report);
|
|
389
399
|
}
|
|
390
400
|
|
|
401
|
+
void PipeConsumer::ReceiveRtcpXrReceiverReferenceTime(RTC::RTCP::ReceiverReferenceTime* report)
|
|
402
|
+
{
|
|
403
|
+
MS_TRACE();
|
|
404
|
+
|
|
405
|
+
for (auto* rtpStream : this->rtpStreams)
|
|
406
|
+
{
|
|
407
|
+
rtpStream->ReceiveRtcpXrReceiverReferenceTime(report);
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
391
411
|
uint32_t PipeConsumer::GetTransmissionRate(uint64_t nowMs)
|
|
392
412
|
{
|
|
393
413
|
MS_TRACE();
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
|
|
19
19
|
namespace RTC
|
|
20
20
|
{
|
|
21
|
+
/* Static. */
|
|
22
|
+
|
|
23
|
+
static constexpr unsigned int SendNackDelay{ 10u }; // In ms.
|
|
24
|
+
|
|
21
25
|
/* Instance methods. */
|
|
22
26
|
|
|
23
27
|
Producer::Producer(const std::string& id, RTC::Producer::Listener* listener, json& data)
|
|
@@ -1139,7 +1143,7 @@ namespace RTC
|
|
|
1139
1143
|
}
|
|
1140
1144
|
|
|
1141
1145
|
// Create a RtpStreamRecv for receiving a media stream.
|
|
1142
|
-
auto* rtpStream = new RTC::RtpStreamRecv(this, params);
|
|
1146
|
+
auto* rtpStream = new RTC::RtpStreamRecv(this, params, SendNackDelay);
|
|
1143
1147
|
|
|
1144
1148
|
// Insert into the maps.
|
|
1145
1149
|
this->mapSsrcRtpStream[ssrc] = rtpStream;
|
|
@@ -145,5 +145,12 @@ namespace RTC
|
|
|
145
145
|
|
|
146
146
|
this->xrPacket.AddReport(report);
|
|
147
147
|
}
|
|
148
|
+
|
|
149
|
+
void CompoundPacket::AddDelaySinceLastRr(DelaySinceLastRr* report)
|
|
150
|
+
{
|
|
151
|
+
MS_TRACE();
|
|
152
|
+
|
|
153
|
+
this->xrPacket.AddReport(report);
|
|
154
|
+
}
|
|
148
155
|
} // namespace RTCP
|
|
149
156
|
} // namespace RTC
|
|
@@ -36,6 +36,7 @@ namespace RTC
|
|
|
36
36
|
{ "vp8", RtpCodecMimeType::Subtype::VP8 },
|
|
37
37
|
{ "vp9", RtpCodecMimeType::Subtype::VP9 },
|
|
38
38
|
{ "h264", RtpCodecMimeType::Subtype::H264 },
|
|
39
|
+
{ "h264-svc", RtpCodecMimeType::Subtype::H264_SVC },
|
|
39
40
|
{ "x-h264uc", RtpCodecMimeType::Subtype::X_H264UC },
|
|
40
41
|
{ "h265", RtpCodecMimeType::Subtype::H265 },
|
|
41
42
|
// Complementary codecs:
|
|
@@ -63,6 +64,7 @@ namespace RTC
|
|
|
63
64
|
{ RtpCodecMimeType::Subtype::VP8, "VP8" },
|
|
64
65
|
{ RtpCodecMimeType::Subtype::VP9, "VP9" },
|
|
65
66
|
{ RtpCodecMimeType::Subtype::H264, "H264" },
|
|
67
|
+
{ RtpCodecMimeType::Subtype::H264_SVC, "H264-SVC" },
|
|
66
68
|
{ RtpCodecMimeType::Subtype::X_H264UC, "X-H264UC" },
|
|
67
69
|
{ RtpCodecMimeType::Subtype::H265, "H265" },
|
|
68
70
|
// Complementary codecs:
|
|
@@ -182,15 +182,16 @@ namespace RTC
|
|
|
182
182
|
|
|
183
183
|
/* Instance methods. */
|
|
184
184
|
|
|
185
|
-
RtpStreamRecv::RtpStreamRecv(
|
|
186
|
-
|
|
185
|
+
RtpStreamRecv::RtpStreamRecv(
|
|
186
|
+
RTC::RtpStreamRecv::Listener* listener, RTC::RtpStream::Params& params, unsigned int sendNackDelayMs)
|
|
187
|
+
: RTC::RtpStream::RtpStream(listener, params, 10), sendNackDelayMs(sendNackDelayMs),
|
|
187
188
|
transmissionCounter(
|
|
188
189
|
params.spatialLayers, params.temporalLayers, this->params.useDtx ? 6000 : 2500)
|
|
189
190
|
{
|
|
190
191
|
MS_TRACE();
|
|
191
192
|
|
|
192
193
|
if (this->params.useNack)
|
|
193
|
-
this->nackGenerator.reset(new RTC::NackGenerator(this));
|
|
194
|
+
this->nackGenerator.reset(new RTC::NackGenerator(this, this->sendNackDelayMs));
|
|
194
195
|
|
|
195
196
|
// Run the RTP inactivity periodic timer (use a different timeout if DTX is
|
|
196
197
|
// enabled).
|
|
@@ -186,6 +186,15 @@ namespace RTC
|
|
|
186
186
|
UpdateScore(report);
|
|
187
187
|
}
|
|
188
188
|
|
|
189
|
+
void RtpStreamSend::ReceiveRtcpXrReceiverReferenceTime(RTC::RTCP::ReceiverReferenceTime* report)
|
|
190
|
+
{
|
|
191
|
+
MS_TRACE();
|
|
192
|
+
|
|
193
|
+
this->lastRrReceivedMs = DepLibUV::GetTimeMs();
|
|
194
|
+
this->lastRrTimestamp = report->GetNtpSec() << 16;
|
|
195
|
+
this->lastRrTimestamp += report->GetNtpFrac() >> 16;
|
|
196
|
+
}
|
|
197
|
+
|
|
189
198
|
RTC::RTCP::SenderReport* RtpStreamSend::GetRtcpSenderReport(uint64_t nowMs)
|
|
190
199
|
{
|
|
191
200
|
MS_TRACE();
|
|
@@ -214,6 +223,29 @@ namespace RTC
|
|
|
214
223
|
return report;
|
|
215
224
|
}
|
|
216
225
|
|
|
226
|
+
RTC::RTCP::DelaySinceLastRr::SsrcInfo* RtpStreamSend::GetRtcpXrDelaySinceLastRr(uint64_t nowMs)
|
|
227
|
+
{
|
|
228
|
+
MS_TRACE();
|
|
229
|
+
|
|
230
|
+
if (this->lastRrReceivedMs == 0u)
|
|
231
|
+
return nullptr;
|
|
232
|
+
|
|
233
|
+
// Get delay in milliseconds.
|
|
234
|
+
auto delayMs = static_cast<uint32_t>(nowMs - this->lastRrReceivedMs);
|
|
235
|
+
// Express delay in units of 1/65536 seconds.
|
|
236
|
+
uint32_t dlrr = (delayMs / 1000) << 16;
|
|
237
|
+
|
|
238
|
+
dlrr |= uint32_t{ (delayMs % 1000) * 65536 / 1000 };
|
|
239
|
+
|
|
240
|
+
auto* ssrcInfo = new RTC::RTCP::DelaySinceLastRr::SsrcInfo();
|
|
241
|
+
|
|
242
|
+
ssrcInfo->SetSsrc(GetSsrc());
|
|
243
|
+
ssrcInfo->SetDelaySinceLastReceiverReport(dlrr);
|
|
244
|
+
ssrcInfo->SetLastReceiverReport(this->lastRrTimestamp);
|
|
245
|
+
|
|
246
|
+
return ssrcInfo;
|
|
247
|
+
}
|
|
248
|
+
|
|
217
249
|
RTC::RTCP::SdesChunk* RtpStreamSend::GetRtcpSdesChunk()
|
|
218
250
|
{
|
|
219
251
|
MS_TRACE();
|