Skip to content

Commit 23f6323

Browse files
gpolitisbbaldino
authored andcommitted
fix: Partially handles screen-sharing simulcast.
This is the first PR from a (hopefully short) series of PRs that will implement proper support for simulcast for screen-sharing. When VP8-based screen-sharing is activated Chrome is sending 2 streams with the same resolution but with different bitrates and different temporal layer bitrate allocations. This is different from what the bridge expects, which is 3 streams with _different_ resolutions. The different resolutions imply a quality order enabling the bridge to switch from low quality to high quality and vica-versa. Since we can no longer rely on the streams having different resolutions, there's no longer an implicit order that the bridge can detect on-the-fly, so now we take this information from the signaling.
1 parent 8ae7961 commit 23f6323

File tree

5 files changed

+37
-113
lines changed

5 files changed

+37
-113
lines changed

src/main/java/org/jitsi/videobridge/cc/AdaptiveTrackProjection.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,21 @@ public boolean accept(@NotNull RawPacket rtpPacket)
184184
// suspended so that it can raise the needsKeyframe flag and also allow
185185
// it to compute a sequence number delta when the target becomes > -1.
186186

187+
RTPEncodingDesc encoding = getSource()
188+
.getMediaStreamTrackReceiver().findRTPEncodingDesc(rtpPacket);
189+
190+
if (encoding == null)
191+
{
192+
logger.warn(
193+
"Dropping an RTP packet, because the SSRC has not " +
194+
"been signaled:" + rtpPacket.getSSRCAsLong());
195+
196+
return false;
197+
}
198+
187199
int targetIndexCopy = targetIndex;
188-
boolean accept = contextCopy.accept(rtpPacket, targetIndexCopy);
200+
boolean accept = contextCopy.accept(
201+
rtpPacket, encoding.getIndex(), targetIndexCopy);
189202

190203
// We check if the context needs a keyframe regardless of whether or not
191204
// the packet was accepted.

src/main/java/org/jitsi/videobridge/cc/AdaptiveTrackProjectionContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,11 @@ public interface AdaptiveTrackProjectionContext
4545
* Determines whether an RTP packet should be accepted or not.
4646
*
4747
* @param rtpPacket the RTP packet to determine whether to accept or not.
48+
* @param incomingIndex the quality index of the incoming RTP packet.
4849
* @param targetIndex the target quality index
4950
* @return true if the packet should be accepted, false otherwise.
5051
*/
51-
boolean accept(RawPacket rtpPacket, int targetIndex);
52+
boolean accept(RawPacket rtpPacket, int incomingIndex, int targetIndex);
5253

5354
/**
5455
* @return true if this stream context needs a keyframe in order to either

src/main/java/org/jitsi/videobridge/cc/GenericAdaptiveTrackProjectionContext.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,13 @@ class GenericAdaptiveTrackProjectionContext
148148
* thread) accessing this method at a time.
149149
*
150150
* @param rtpPacket the RTP packet to determine whether to accept or not.
151+
* @param incomingIndex the quality index of the
151152
* @param targetIndex the target quality index
152153
* @return true if the packet should be accepted, false otherwise.
153154
*/
154155
@Override
155156
public synchronized boolean
156-
accept(@NotNull RawPacket rtpPacket, int targetIndex)
157+
accept(@NotNull RawPacket rtpPacket, int incomingIndex, int targetIndex)
157158
{
158159
if (targetIndex == RTPEncodingDesc.SUSPENDED_INDEX)
159160
{

src/main/java/org/jitsi/videobridge/cc/vp8/VP8AdaptiveTrackProjectionContext.java

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -191,12 +191,13 @@ public VP8AdaptiveTrackProjectionContext(
191191
* method at a time.
192192
*
193193
* @param rtpPacket the VP8 packet to decide whether or not to project.
194-
* @param targetIndex
194+
* @param incomingIndex the quality index of the incoming RTP packet
195+
* @param targetIndex the target quality index we want to achieve
195196
* @return true to project the packet, otherwise false.
196197
*/
197198
private synchronized
198199
VP8FrameProjection createVP8FrameProjection(
199-
@NotNull RawPacket rtpPacket, int targetIndex)
200+
@NotNull RawPacket rtpPacket, int incomingIndex, int targetIndex)
200201
{
201202
// Creating a new VP8 projection depends on reading and results in
202203
// writing of the last VP8 frame, therefore this method needs to be
@@ -236,7 +237,8 @@ VP8FrameProjection createVP8FrameProjection(
236237
// Lastly, check whether the quality of the frame is something that we
237238
// want to forward. We don't want to be allocating new objects unless
238239
// we're interested in the quality of this frame.
239-
if (!vp8QualityFilter.acceptFrame(rtpPacket, targetIndex, nowMs))
240+
if (!vp8QualityFilter.acceptFrame(
241+
rtpPacket, incomingIndex, targetIndex, nowMs))
240242
{
241243
return null;
242244
}
@@ -349,18 +351,21 @@ public boolean needsKeyframe()
349351
* Determines whether a packet should be accepted or not.
350352
*
351353
* @param rtpPacket the RTP packet to determine whether to project or not.
352-
* @param targetIndex the target index to achieve
354+
* @param incomingIndex the quality index of the incoming RTP packet
355+
* @param targetIndex the target quality index we want to achieve
353356
* @return true if the packet should be accepted, false otherwise.
354357
*/
355358
@Override
356-
public boolean accept(@NotNull RawPacket rtpPacket, int targetIndex)
359+
public boolean accept(
360+
@NotNull RawPacket rtpPacket, int incomingIndex, int targetIndex)
357361
{
358362
VP8FrameProjection vp8FrameProjection
359363
= lookupVP8FrameProjection(rtpPacket);
360364

361365
if (vp8FrameProjection == null)
362366
{
363-
vp8FrameProjection = createVP8FrameProjection(rtpPacket, targetIndex);
367+
vp8FrameProjection
368+
= createVP8FrameProjection(rtpPacket, incomingIndex, targetIndex);
364369
}
365370

366371
return vp8FrameProjection != null

src/main/java/org/jitsi/videobridge/cc/vp8/VP8QualityFilter.java

Lines changed: 8 additions & 104 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020
import org.jitsi.service.neomedia.*;
2121
import org.jitsi.util.*;
2222

23-
import java.util.*;
24-
2523
/**
2624
* This class is responsible for dropping VP8 simulcast/svc packets based on
2725
* their quality, i.e. packets that correspond to qualities that are above a
@@ -44,22 +42,10 @@ class VP8QualityFilter
4442
*/
4543
private static final int MIN_KEY_FRAME_WAIT_MS = 300;
4644

47-
/**
48-
* Constants that define the minimum heights that a VP8 stream needs to have
49-
* in order to be considered HD, SD or LD.
50-
*/
51-
private static final int MIN_HD_HEIGHT = 540, MIN_SD_HEIGHT = 360;
52-
5345
/**
5446
* The HD, SD, LD and suspended spatial/quality layer IDs.
5547
*/
56-
private static final int HD_LAYER_ID = 2, SD_LAYER_ID = 1,
57-
LD_LAYER_ID = 0, SUSPENDED_LAYER_ID = -1;
58-
59-
/**
60-
* A map that caches the spatial/quality layer ID of each simulcast SSRC.
61-
*/
62-
private final Map<Long, Integer> ssrcToSpatialLayerQuality = new HashMap<>();
48+
private static final int SUSPENDED_LAYER_ID = -1;
6349

6450
/**
6551
* Holds the arrival time (in millis) of the most recent keyframe group.
@@ -94,85 +80,6 @@ class VP8QualityFilter
9480
*/
9581
private int currentSpatialLayerId = SUSPENDED_LAYER_ID;
9682

97-
/**
98-
* Gets the spatial/quality layer ID of a packet. Before this method is able
99-
* to return a meaningful value (not -1) it has to have received at least
100-
* one keyframe from the SSRC that is queried.
101-
*
102-
* @param firstPacketOfFrame the RTP packet whose spatial/quality layer
103-
* index needs to be returned.
104-
* @param isKeyframe a boolean that indicates whether or not the packet that
105-
* is specified as an argument is a keyframe
106-
* @return the spatial/quality index of the packet that is specified as an
107-
* argument, or -1 in case it's impossible to resolve.
108-
*/
109-
private int getSpatialLayerIndex(
110-
@NotNull RawPacket firstPacketOfFrame, boolean isKeyframe)
111-
{
112-
long ssrc = firstPacketOfFrame.getSSRCAsLong();
113-
if (ssrcToSpatialLayerQuality.containsKey(ssrc))
114-
{
115-
return ssrcToSpatialLayerQuality.get(ssrc);
116-
}
117-
else if (isKeyframe)
118-
{
119-
int spatialLayerId
120-
= getSpatialLayerIndexFromKeyframe(firstPacketOfFrame);
121-
if (spatialLayerId > -1)
122-
{
123-
ssrcToSpatialLayerQuality.put(ssrc, spatialLayerId);
124-
}
125-
126-
return spatialLayerId;
127-
}
128-
{
129-
return -1;
130-
}
131-
}
132-
133-
/**
134-
* Gets the spatial/quality layer ID of a packet. The specified packet MUST
135-
* be a VP8 keyframe because the frame height, which allows us to map the
136-
* different simulcast layers into spatial/quality layer IDs, are found in
137-
* the keyframe header.
138-
*
139-
* @param firstPacketOfKeyframe the first packet of a keyframe to map/get
140-
* its spatial/quality layer id.
141-
* @return the spatial/quality layer id of the packet.
142-
*/
143-
private static int getSpatialLayerIndexFromKeyframe(
144-
@NotNull RawPacket firstPacketOfKeyframe)
145-
{
146-
byte[] buf = firstPacketOfKeyframe.getBuffer();
147-
int payloadOffset = firstPacketOfKeyframe.getPayloadOffset(),
148-
payloadLen = firstPacketOfKeyframe.getPayloadLength(),
149-
payloadHeaderLen = 3;
150-
151-
int payloadDescriptorLen = DePacketizer
152-
.VP8PayloadDescriptor.getSize(buf, payloadOffset, payloadLen);
153-
154-
int height = DePacketizer.VP8KeyframeHeader.getHeight(
155-
buf, payloadOffset + payloadDescriptorLen + payloadHeaderLen);
156-
157-
if (height >= MIN_HD_HEIGHT)
158-
{
159-
return HD_LAYER_ID;
160-
}
161-
else if (height >= MIN_SD_HEIGHT)
162-
{
163-
return SD_LAYER_ID;
164-
}
165-
else if (height > -1)
166-
{
167-
return LD_LAYER_ID;
168-
}
169-
else
170-
{
171-
// Some error occurred.
172-
return -1;
173-
}
174-
}
175-
17683
/**
17784
* @return true if a the target spatial/quality layer id has changed and a
17885
* keyframe hasn't been received yet, false otherwise.
@@ -192,13 +99,15 @@ boolean needsKeyframe()
19299
* method at a time.
193100
*
194101
* @param firstPacketOfFrame the first packet of the VP8 frame.
102+
* @param incomingIndex the quality index of the incoming RTP packet
195103
* @param externalTargetIndex the target quality index that the user of this
196104
* instance wants to achieve.
197105
* @param nowMs the current time (in millis)
198106
* @return true to accept the VP8 frame, otherwise false.
199107
*/
200108
synchronized boolean acceptFrame(
201109
@NotNull RawPacket firstPacketOfFrame,
110+
int incomingIndex,
202111
int externalTargetIndex, long nowMs)
203112
{
204113
// We make local copies of the externalTemporalLayerIdTarget and the
@@ -245,14 +154,15 @@ synchronized boolean acceptFrame(
245154
temporalLayerIdOfFrame = 0;
246155
}
247156

157+
int spatialLayerId = getSpatialLayerId(incomingIndex);
248158
if (DePacketizer.isKeyFrame(buf, payloadOff, payloadLen))
249159
{
250-
return acceptKeyframe(firstPacketOfFrame, nowMs);
160+
return acceptKeyframe(spatialLayerId, nowMs);
251161
}
252162
else if (currentSpatialLayerId > SUSPENDED_LAYER_ID)
253163
{
254164
if (!isInSwitchingPhase(nowMs)
255-
&& isPossibleToSwitch(firstPacketOfFrame))
165+
&& isPossibleToSwitch(firstPacketOfFrame, spatialLayerId))
256166
{
257167
needsKeyframe = true;
258168
}
@@ -311,10 +221,8 @@ private synchronized boolean isInSwitchingPhase(long nowMs)
311221
* method for specific details).
312222
*/
313223
private synchronized boolean isPossibleToSwitch(
314-
@NotNull RawPacket firstPacketOfFrame)
224+
@NotNull RawPacket firstPacketOfFrame, int spatialLayerId)
315225
{
316-
int spatialLayerId
317-
= getSpatialLayerIndex(firstPacketOfFrame, false);
318226
if (spatialLayerId == -1)
319227
{
320228
// We failed to resolve the spatial/quality layer of the packet.
@@ -347,19 +255,15 @@ else if (spatialLayerId < currentSpatialLayerId
347255
* synchronized keyword because there's only one thread accessing this
348256
* method at a time.
349257
*
350-
* @param firstPacketOfKeyframe the first packet of a VP8 keyframe.
351258
* @param nowMs the current time (in millis)
352259
* @return true to accept the VP8 keyframe, otherwise false.
353260
*/
354261
private synchronized boolean acceptKeyframe(
355-
@NotNull RawPacket firstPacketOfKeyframe, long nowMs)
262+
int spatialLayerIdOfKeyframe, long nowMs)
356263
{
357264
// This branch writes the {@link #currentSpatialLayerId} and it
358265
// determines whether or not we should switch to another simulcast
359266
// stream.
360-
361-
int spatialLayerIdOfKeyframe
362-
= getSpatialLayerIndex(firstPacketOfKeyframe, true);
363267
if (spatialLayerIdOfKeyframe < 0)
364268
{
365269
// something went terribly wrong, normally we should be able to

0 commit comments

Comments
 (0)