Skip to content
GitLab
Explore
Sign in
Primary navigation
Search or go to…
Project
L
libjitsi
Manage
Activity
Members
Code
Repository
Branches
Commits
Tags
Repository graph
Compare revisions
Deploy
Releases
Model registry
Analyze
Contributor analytics
Repository analytics
Model experiments
Help
Help
Support
GitLab documentation
Compare GitLab plans
Community forum
Contribute to GitLab
Provide feedback
Keyboard shortcuts
?
Snippets
Groups
Projects
Show more breadcrumbs
ZRTP
libjitsi
Commits
8ef8afd4
Commit
8ef8afd4
authored
10 years ago
by
George Politis
Browse files
Options
Downloads
Patches
Plain Diff
First implementation of the BasicRTCPTerminationStrategy
parent
4825611c
No related branches found
Branches containing commit
No related tags found
Tags containing commit
No related merge requests found
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
src/org/jitsi/impl/neomedia/rtcp/termination/strategies/BasicRTCPTerminationStrategy.java
+326
-0
326 additions, 0 deletions
.../termination/strategies/BasicRTCPTerminationStrategy.java
with
326 additions
and
0 deletions
src/org/jitsi/impl/neomedia/rtcp/termination/strategies/BasicRTCPTerminationStrategy.java
0 → 100644
+
326
−
0
View file @
8ef8afd4
/*
* Jitsi, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package
org.jitsi.impl.neomedia.rtcp.termination.strategies
;
import
net.sf.fmj.media.rtp.*
;
import
org.jitsi.impl.neomedia.rtcp.*
;
import
org.jitsi.impl.neomedia.rtp.translator.*
;
import
org.jitsi.service.neomedia.*
;
import
java.util.*
;
import
java.util.concurrent.*
;
/**
* Created by gp on 16/07/14.
*/
public
class
BasicRTCPTerminationStrategy
implements
RTCPTerminationStrategy
,
RTCPPacketTransformer
,
RTCPReportBuilder
{
private
RTCPTransmitter
rtcpTransmitter
;
/**
* A cache of media receiver feedback. It contains both receiver report
* blocks and REMB packets.
*/
private
final
Map
<
Integer
,
FeedbackCacheEntry
>
feedbackCache
;
/**
* The cache processor that will be making the RTCP reports coming from
* the bridge.
*/
private
FeedbackCacheProcessor
feedbackCacheProcessor
;
public
BasicRTCPTerminationStrategy
()
{
this
.
feedbackCache
=
new
ConcurrentHashMap
<
Integer
,
FeedbackCacheEntry
>();
reset
();
}
/**
* The <tt>RTPTranslator</tt> associated with this strategy.
*/
private
RTPTranslator
translator
;
@Override
public
RTCPPacket
[]
makeReports
()
{
if
(
rtcpTransmitter
==
null
)
throw
new
IllegalStateException
(
"rtcpTransmitter is not set"
);
RTPTranslator
t
=
this
.
translator
;
if
(
t
==
null
||
!(
t
instanceof
RTPTranslatorImpl
))
return
new
RTCPPacket
[
0
];
// Use the SSRC of the bridge that is announced and the endpoints won't
// drop the packet.
int
localSSRC
=
(
int
)
((
RTPTranslatorImpl
)
t
).
getLocalSSRC
(
null
);
Vector
<
RTCPPacket
>
packets
=
new
Vector
<
RTCPPacket
>();
long
time
=
System
.
currentTimeMillis
();
RTCPReportBlock
reports
[]
=
makeReceiverReports
(
time
);
RTCPReportBlock
firstrep
[]
=
reports
;
// If the number of sources for which reception statistics are being
// reported exceeds 31, the number that will fit into one SR or RR
// packet, then additional RR packets SHOULD follow the initial report
// packet.
if
(
reports
.
length
>
31
)
{
firstrep
=
new
RTCPReportBlock
[
31
];
System
.
arraycopy
(
reports
,
0
,
firstrep
,
0
,
31
);
}
packets
.
addElement
(
new
RTCPRRPacket
(
localSSRC
,
firstrep
));
if
(
firstrep
!=
reports
)
{
for
(
int
offset
=
31
;
offset
<
reports
.
length
;
offset
+=
31
)
{
if
(
reports
.
length
-
offset
<
31
)
firstrep
=
new
RTCPReportBlock
[
reports
.
length
-
offset
];
System
.
arraycopy
(
reports
,
offset
,
firstrep
,
0
,
firstrep
.
length
);
RTCPRRPacket
rrp
=
new
RTCPRRPacket
(
localSSRC
,
firstrep
);
packets
.
addElement
(
rrp
);
}
}
// Include REMB.
if
(
this
.
feedbackCacheProcessor
==
null
)
{
this
.
feedbackCacheProcessor
=
new
FeedbackCacheProcessor
(
feedbackCache
);
// TODO(gp) make percentile configurable.
this
.
feedbackCacheProcessor
.
setPercentile
(
70
);
}
RTCPPacket
[]
bestReceiverFeedback
=
feedbackCacheProcessor
.
makeReports
(
localSSRC
);
if
(
bestReceiverFeedback
!=
null
&&
bestReceiverFeedback
.
length
!=
0
)
{
for
(
RTCPPacket
packet
:
bestReceiverFeedback
)
{
if
(
packet
.
type
==
RTCPPacketType
.
PSFB
&&
packet
instanceof
RTCPFBPacket
&&
((
RTCPFBPacket
)
packet
).
fmt
==
RTCPPSFBFormat
.
REMB
)
{
packets
.
add
(
packet
);
}
}
}
// TODO(gp) for RTCP compound packets MUST contain an SDES packet.
// Copy into an array and return.
RTCPPacket
[]
res
=
new
RTCPPacket
[
packets
.
size
()];
packets
.
copyInto
(
res
);
return
res
;
}
private
RTCPReportBlock
[]
makeReceiverReports
(
long
time
)
{
Vector
<
RTCPReportBlock
>
reports
=
new
Vector
<
RTCPReportBlock
>();
// Make receiver reports for all known SSRCs.
for
(
Enumeration
<
SSRCInfo
>
elements
=
rtcpTransmitter
.
cache
.
cache
.
elements
();
elements
.
hasMoreElements
();)
{
SSRCInfo
info
=
elements
.
nextElement
();
if
(!
info
.
ours
&&
info
.
sender
)
{
int
ssrc
=
info
.
ssrc
;
long
lastseq
=
info
.
maxseq
+
info
.
cycles
;
int
jitter
=
(
int
)
info
.
jitter
;
long
lsr
=
(
int
)
((
info
.
lastSRntptimestamp
&
0x0000ffffffff0000
L
)
>>
16
);
long
dlsr
=
(
int
)
((
time
-
info
.
lastSRreceiptTime
)
*
65.536000000000001
D
);
int
packetslost
=
(
int
)
(((
lastseq
-
info
.
baseseq
)
+
1L
)
-
info
.
received
);
if
(
packetslost
<
0
)
packetslost
=
0
;
double
frac
=
(
double
)
(
packetslost
-
info
.
prevlost
)
/
(
double
)
(
lastseq
-
info
.
prevmaxseq
);
if
(
frac
<
0.0
D
)
frac
=
0.0
D
;
int
fractionlost
=(
int
)
(
frac
*
256
D
);
RTCPReportBlock
receiverReport
=
new
RTCPReportBlock
(
ssrc
,
fractionlost
,
packetslost
,
lastseq
,
jitter
,
lsr
,
dlsr
);
info
.
prevmaxseq
=
(
int
)
lastseq
;
info
.
prevlost
=
packetslost
;
reports
.
addElement
(
receiverReport
);
}
}
// Copy into an array and return.
RTCPReportBlock
res
[]
=
new
RTCPReportBlock
[
reports
.
size
()];
reports
.
copyInto
(
res
);
return
res
;
}
@Override
public
void
reset
()
{
/* Nothing to do here */
}
@Override
public
void
setRTCPTransmitter
(
RTCPTransmitter
rtcpTransmitter
)
{
this
.
rtcpTransmitter
=
rtcpTransmitter
;
}
@Override
public
RTCPPacketTransformer
getRTCPPacketTransformer
()
{
return
this
;
}
@Override
public
RTCPReportBuilder
getRTCPReportBuilder
()
{
return
this
;
}
@Override
public
void
setRTPTranslator
(
RTPTranslator
translator
)
{
this
.
translator
=
translator
;
}
@Override
public
RTCPCompoundPacket
transformRTCPPacket
(
RTCPCompoundPacket
inPacket
)
{
// Removes receiver report blocks from RRs and SRs and kills REMBs.
if
(
inPacket
==
null
||
inPacket
.
packets
==
null
||
inPacket
.
packets
.
length
==
0
)
{
return
inPacket
;
}
Vector
<
RTCPPacket
>
outPackets
=
new
Vector
<
RTCPPacket
>(
inPacket
.
packets
.
length
);
// These are the data that are of interest to us : RR report blocks and
// REMBs. We'll also need the SSRC of the RTCP report sender.
RTCPReportBlock
[]
reports
=
null
;
RTCPREMBPacket
remb
=
null
;
Integer
ssrc
=
0
;
// Modify the incoming RTCP packet and/or update the
// <tt>feedbackCache</tt>.
for
(
RTCPPacket
p
:
inPacket
.
packets
)
{
switch
(
p
.
type
)
{
case
RTCPPacketType
.
RR
:
// Grab the receiver report blocks to put them into the
// cache after the loop is done and mute the RR.
RTCPRRPacket
rr
=
(
RTCPRRPacket
)
p
;
reports
=
rr
.
reports
;
ssrc
=
Integer
.
valueOf
(
rr
.
ssrc
);
break
;
case
RTCPPacketType
.
SR
:
// Grab the receiver report blocks to put them into the
// cache after the loop is done; mute the receiver report
// blocks.
RTCPSRPacket
sr
=
(
RTCPSRPacket
)
p
;
outPackets
.
add
(
sr
);
reports
=
sr
.
reports
;
ssrc
=
Integer
.
valueOf
(
sr
.
ssrc
);
sr
.
reports
=
new
RTCPReportBlock
[
0
];
break
;
case
RTCPPacketType
.
PSFB
:
RTCPFBPacket
psfb
=
(
RTCPFBPacket
)
p
;
switch
(
psfb
.
fmt
)
{
case
RTCPPSFBFormat
.
REMB
:
// NOT adding the REMB in the outPacket as we mute
// REMBs from the peers.
//
// We put it into the feedback cache instead.
remb
=
(
RTCPREMBPacket
)
p
;
ssrc
=
Integer
.
valueOf
((
int
)
remb
.
senderSSRC
);
break
;
default
:
// Pass through everything else, like PLIs and NACKs
outPackets
.
add
(
psfb
);
break
;
}
break
;
default
:
// Pass through everything else, like PLIs and NACKs
outPackets
.
add
(
p
);
break
;
}
}
// Update the cache with the new data we've gathered.
if
(
ssrc
!=
0
&&
((
reports
!=
null
&&
reports
.
length
!=
0
)
||
remb
!=
null
))
{
long
lastUpdate
=
System
.
currentTimeMillis
();
FeedbackCacheEntry
item
=
new
FeedbackCacheEntry
();
item
.
reports
=
reports
;
item
.
remb
=
remb
;
item
.
lastUpdate
=
lastUpdate
;
if
(
reports
==
null
||
reports
.
length
==
0
||
remb
==
null
)
{
// Complete the cache item from the cache item already in the
// cache, if needed.
if
(
feedbackCache
.
containsKey
(
ssrc
))
{
FeedbackCacheEntry
base
=
feedbackCache
.
get
(
ssrc
);
if
(
base
!=
null
&&
(
reports
==
null
||
reports
.
length
==
0
))
{
item
.
reports
=
base
.
reports
;
}
if
(
base
!=
null
&&
remb
==
null
)
{
item
.
remb
=
base
.
remb
;
}
}
}
feedbackCache
.
put
(
ssrc
,
item
);
}
RTCPPacket
[]
outarr
=
new
RTCPPacket
[
outPackets
.
size
()];
outPackets
.
copyInto
(
outarr
);
RTCPCompoundPacket
outPacket
=
new
RTCPCompoundPacket
(
outarr
);
return
outPacket
;
}
}
\ No newline at end of file
This diff is collapsed.
Click to expand it.
Preview
0%
Loading
Try again
or
attach a new file
.
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Save comment
Cancel
Please
register
or
sign in
to comment