4
4
package flexfec
5
5
6
6
import (
7
+ "errors"
8
+ "sync"
9
+
7
10
"github.com/pion/interceptor"
8
11
"github.com/pion/rtp"
9
12
)
10
13
14
+ // streamState holds the state for a single stream.
15
+ type streamState struct {
16
+ mu sync.Mutex
17
+ flexFecEncoder FlexEncoder
18
+ packetBuffer []rtp.Packet
19
+ }
20
+
11
21
// FecInterceptor implements FlexFec.
12
22
type FecInterceptor struct {
13
23
interceptor.NoOp
14
- flexFecEncoder FlexEncoder
15
- packetBuffer []rtp.Packet
16
- minNumMediaPackets uint32
24
+ mu sync.Mutex
25
+ streams map [uint32 ]* streamState
26
+ numMediaPackets uint32
27
+ numFecPackets uint32
28
+ encoderFactory EncoderFactory
17
29
}
18
30
19
- // FecOption can be used to set initial options on Fec encoder interceptors.
20
- type FecOption func (d * FecInterceptor ) error
21
-
22
31
// FecInterceptorFactory creates new FecInterceptors.
23
32
type FecInterceptorFactory struct {
24
33
opts []FecOption
@@ -31,54 +40,89 @@ func NewFecInterceptor(opts ...FecOption) (*FecInterceptorFactory, error) {
31
40
32
41
// NewInterceptor constructs a new FecInterceptor.
33
42
func (r * FecInterceptorFactory ) NewInterceptor (_ string ) (interceptor.Interceptor , error ) {
34
- // Hardcoded for now:
35
- // Min num media packets to encode FEC -> 5
36
- // Min num fec packets -> 1
37
-
38
43
interceptor := & FecInterceptor {
39
- packetBuffer : make ([]rtp.Packet , 0 ),
40
- minNumMediaPackets : 5 ,
44
+ streams : make (map [uint32 ]* streamState ),
45
+ numMediaPackets : 5 ,
46
+ numFecPackets : 2 ,
47
+ encoderFactory : FlexEncoder03Factory {},
48
+ }
49
+
50
+ for _ , opt := range r .opts {
51
+ if err := opt (interceptor ); err != nil {
52
+ return nil , err
53
+ }
41
54
}
42
55
43
56
return interceptor , nil
44
57
}
45
58
59
+ // UnbindLocalStream removes the stream state for a specific SSRC.
60
+ func (r * FecInterceptor ) UnbindLocalStream (info * interceptor.StreamInfo ) {
61
+ r .mu .Lock ()
62
+ defer r .mu .Unlock ()
63
+
64
+ delete (r .streams , info .SSRC )
65
+ }
66
+
46
67
// BindLocalStream lets you modify any outgoing RTP packets. It is called once for per LocalStream. The returned method
47
68
// will be called once per rtp packet.
48
69
func (r * FecInterceptor ) BindLocalStream (
49
70
info * interceptor.StreamInfo , writer interceptor.RTPWriter ,
50
71
) interceptor.RTPWriter {
51
- // Chromium supports version flexfec-03 of existing draft, this is the one we will configure by default
52
- // although we should support configuring the latest (flexfec-20) as well.
53
- r .flexFecEncoder = NewFlexEncoder03 (info .PayloadType , info .SSRC )
72
+ if info .PayloadTypeForwardErrorCorrection == 0 || info .SSRCForwardErrorCorrection == 0 {
73
+ return writer
74
+ }
75
+
76
+ mediaSSRC := info .SSRC
77
+
78
+ r .mu .Lock ()
79
+ stream := & streamState {
80
+ // Chromium supports version flexfec-03 of existing draft, this is the one we will configure by default
81
+ // although we should support configuring the latest (flexfec-20) as well.
82
+ flexFecEncoder : r .encoderFactory .NewEncoder (info .PayloadTypeForwardErrorCorrection , info .SSRCForwardErrorCorrection ),
83
+ packetBuffer : make ([]rtp.Packet , 0 ),
84
+ }
85
+ r .streams [mediaSSRC ] = stream
86
+ r .mu .Unlock ()
54
87
55
88
return interceptor .RTPWriterFunc (
56
89
func (header * rtp.Header , payload []byte , attributes interceptor.Attributes ) (int , error ) {
57
- r .packetBuffer = append (r .packetBuffer , rtp.Packet {
90
+ // Ignore non-media packets
91
+ if header .SSRC != mediaSSRC {
92
+ return writer .Write (header , payload , attributes )
93
+ }
94
+
95
+ var fecPackets []rtp.Packet
96
+ stream .mu .Lock ()
97
+ stream .packetBuffer = append (stream .packetBuffer , rtp.Packet {
58
98
Header : * header ,
59
99
Payload : payload ,
60
100
})
61
101
62
- // Send the media RTP packet
63
- result , err := writer .Write (header , payload , attributes )
102
+ // Check if we have enough packets to generate FEC
103
+ if len (stream .packetBuffer ) == int (r .numMediaPackets ) {
104
+ fecPackets = stream .flexFecEncoder .EncodeFec (stream .packetBuffer , r .numFecPackets )
105
+ // Reset the packet buffer now that we've sent the corresponding FEC packets.
106
+ stream .packetBuffer = nil
107
+ }
108
+ stream .mu .Unlock ()
64
109
65
- // Send the FEC packets
66
- var fecPackets []rtp.Packet
67
- if len (r .packetBuffer ) == int (r .minNumMediaPackets ) {
68
- fecPackets = r .flexFecEncoder .EncodeFec (r .packetBuffer , 2 )
110
+ var errs []error
111
+ result , err := writer .Write (header , payload , attributes )
112
+ if err != nil {
113
+ errs = append (errs , err )
114
+ }
69
115
70
- for i := range fecPackets {
71
- fecResult , fecErr := writer . Write ( & ( fecPackets [ i ]. Header ), fecPackets [ i ]. Payload , attributes )
116
+ for _ , packet := range fecPackets {
117
+ header := packet . Header
72
118
73
- if fecErr != nil && fecResult == 0 {
74
- break
75
- }
119
+ _ , err = writer . Write ( & header , packet . Payload , attributes )
120
+ if err != nil {
121
+ errs = append ( errs , err )
76
122
}
77
- // Reset the packet buffer now that we've sent the corresponding FEC packets.
78
- r .packetBuffer = nil
79
123
}
80
124
81
- return result , err
125
+ return result , errors . Join ( errs ... )
82
126
},
83
127
)
84
128
}
0 commit comments