Skip to content

Commit 663fc83

Browse files
author
Jagger Yu
authored
[QBOX] Support TrackLocal FlexFEC (pion#9)
1 parent 62e4e71 commit 663fc83

File tree

6 files changed

+128
-2
lines changed

6 files changed

+128
-2
lines changed

mediaengine.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ const (
4747
// MimeTypePCMA PCMA MIME type
4848
// Note: Matching should be case insensitive.
4949
MimeTypePCMA = "audio/PCMA"
50+
// MimeTypeFlexFEC03 FlexFEC03 MIME type
51+
// Note: Matching should be case insensitive.
52+
MimeTypeFlexFEC03 = "video/flexfec-03"
5053
)
5154

5255
type mediaEngineHeaderExtension struct {

rtpcodec.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,3 +145,13 @@ func codecParametersAssociatedSearch(needle RTPCodecParameters, haystack []RTPCo
145145

146146
return RTPCodecParameters{}, codecMatchNone
147147
}
148+
149+
// Do a search by mime type in the list of codecs
150+
func codecParametersSearchByMimeType(mimeType string, haystack []RTPCodecParameters) (codecs []RTPCodecParameters) {
151+
for i, c := range haystack {
152+
if c.MimeType == mimeType {
153+
codecs = append(codecs, haystack[i])
154+
}
155+
}
156+
return codecs
157+
}

rtpcodingparameters.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,11 @@ type RTPRtxParameters struct {
99
SSRC SSRC `json:"ssrc"`
1010
}
1111

12+
// RTPFecParameters dictionary contains information relating to FEC settings.
13+
type RTPFecParameters struct {
14+
SSRC SSRC `json:"ssrc"`
15+
}
16+
1217
// RTPCodingParameters provides information relating to both encoding and decoding.
1318
// This is a subset of the RFC since Pion WebRTC doesn't implement encoding/decoding itself
1419
// http://draft.ortc.org/#dom-rtcrtpcodingparameters
@@ -17,4 +22,5 @@ type RTPCodingParameters struct {
1722
SSRC SSRC `json:"ssrc"`
1823
PayloadType PayloadType `json:"payloadType"`
1924
RTX RTPRtxParameters `json:"rtx"`
25+
FEC RTPFecParameters `json:"fec,omitempty"`
2026
}

rtpsender.go

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ type trackEncoding struct {
3232
rtxSrtpStream *srtpWriterFuture
3333
rtxRtcpInterceptor interceptor.RTCPReader
3434
rtxStreamInfo interceptor.StreamInfo
35+
36+
fecSsrc SSRC
37+
fecSrtpStream *srtpWriterFuture
38+
fecRtcpInterceptor interceptor.RTCPReader
39+
fecStreamInfo interceptor.StreamInfo
3540
}
3641

3742
// RTPSender allows an application to control how a given Track is encoded and transmitted to a remote peer
@@ -125,6 +130,7 @@ func (r *RTPSender) getParameters() RTPSendParameters {
125130
SSRC: trackEncoding.ssrc,
126131
PayloadType: r.payloadType,
127132
RTX: RTPRtxParameters{SSRC: trackEncoding.rtxSsrc},
133+
FEC: RTPFecParameters{SSRC: trackEncoding.fecSsrc},
128134
},
129135
})
130136
}
@@ -223,6 +229,13 @@ func (r *RTPSender) addEncoding(track TrackLocal) {
223229
}
224230
}
225231

232+
if r.api.settingEngine.trackLocalFlexfec {
233+
codecs := r.api.mediaEngine.getCodecsByKind(track.Kind())
234+
if len(codecParametersSearchByMimeType(MimeTypeFlexFEC03, codecs)) > 0 {
235+
trackEncoding.fecSsrc = SSRC(randutil.NewMathRandomGenerator().Uint32())
236+
}
237+
}
238+
226239
r.trackEncodings = append(r.trackEncodings, trackEncoding)
227240
}
228241

@@ -314,8 +327,14 @@ func (r *RTPSender) Send(parameters RTPSendParameters) error {
314327
return errRTPSenderTrackRemoved
315328
}
316329

317-
for idx, trackEncoding := range r.trackEncodings {
330+
for idx := range r.trackEncodings {
331+
trackEncoding := r.trackEncodings[idx]
332+
srtpStream := &srtpWriterFuture{ssrc: parameters.Encodings[idx].SSRC, rtpSender: r}
318333
writeStream := &interceptorToTrackLocalWriter{}
334+
fecCodecs := codecParametersSearchByMimeType(MimeTypeFlexFEC03, r.api.mediaEngine.getCodecsByKind(r.kind))
335+
336+
trackEncoding.srtpStream = srtpStream
337+
trackEncoding.ssrc = parameters.Encodings[idx].SSRC
319338
trackEncoding.context = &baseTrackLocalContext{
320339
id: r.id,
321340
params: r.api.mediaEngine.getRTPParametersByKind(trackEncoding.track.Kind(), []RTPTransceiverDirection{RTPTransceiverDirectionSendonly}),
@@ -337,7 +356,18 @@ func (r *RTPSender) Send(parameters RTPSendParameters) error {
337356
codec.RTPCodecCapability,
338357
parameters.HeaderExtensions,
339358
)
340-
srtpStream := trackEncoding.srtpStream
359+
360+
if len(fecCodecs) > 0 {
361+
trackEncoding.streamInfo.Attributes.Set("flexfec-03", struct{}{})
362+
}
363+
364+
trackEncoding.rtcpInterceptor = r.api.interceptor.BindRTCPReader(
365+
interceptor.RTCPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, _ interceptor.Attributes, err error) {
366+
n, err = trackEncoding.srtpStream.Read(in)
367+
return n, a, err
368+
}),
369+
)
370+
341371
rtpInterceptor := r.api.interceptor.BindLocalStream(
342372
&trackEncoding.streamInfo,
343373
interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes interceptor.Attributes) (int, error) {
@@ -376,6 +406,37 @@ func (r *RTPSender) Send(parameters RTPSendParameters) error {
376406
}),
377407
)
378408
}
409+
410+
if len(fecCodecs) > 0 &&
411+
parameters.Encodings[idx].FEC.SSRC != 0 {
412+
fecSrtpStream := &srtpWriterFuture{ssrc: parameters.Encodings[idx].FEC.SSRC, rtpSender: r}
413+
414+
trackEncoding.fecSrtpStream = fecSrtpStream
415+
trackEncoding.fecSsrc = parameters.Encodings[idx].FEC.SSRC
416+
417+
trackEncoding.fecStreamInfo = *createStreamInfo(
418+
r.id+"_fec",
419+
parameters.Encodings[idx].FEC.SSRC,
420+
fecCodecs[0].PayloadType,
421+
fecCodecs[0].RTPCodecCapability,
422+
parameters.HeaderExtensions,
423+
)
424+
trackEncoding.fecStreamInfo.Attributes.Set("apt_ssrc", uint32(parameters.Encodings[idx].SSRC))
425+
426+
trackEncoding.fecRtcpInterceptor = r.api.interceptor.BindRTCPReader(
427+
interceptor.RTCPReaderFunc(func(in []byte, a interceptor.Attributes) (n int, attributes interceptor.Attributes, err error) {
428+
n, err = trackEncoding.fecSrtpStream.Read(in)
429+
return n, a, err
430+
}),
431+
)
432+
433+
r.api.interceptor.BindLocalStream(
434+
&trackEncoding.fecStreamInfo,
435+
interceptor.RTPWriterFunc(func(header *rtp.Header, payload []byte, _ interceptor.Attributes) (int, error) {
436+
return fecSrtpStream.WriteRTP(header, payload)
437+
}),
438+
)
439+
}
379440
}
380441

381442
close(r.sendCalled)
@@ -415,6 +476,10 @@ func (r *RTPSender) Stop() error {
415476
r.api.interceptor.UnbindLocalStream(&trackEncoding.rtxStreamInfo)
416477
errs = append(errs, trackEncoding.rtxSrtpStream.Close())
417478
}
479+
if trackEncoding.fecSrtpStream != nil {
480+
r.api.interceptor.UnbindLocalStream(&trackEncoding.fecStreamInfo)
481+
errs = append(errs, trackEncoding.fecSrtpStream.Close())
482+
}
418483
}
419484

420485
return util.FlattenErrs(errs)
@@ -476,6 +541,36 @@ func (r *RTPSender) ReadRtxRTCP() ([]rtcp.Packet, interceptor.Attributes, error)
476541
return pkts, attributes, nil
477542
}
478543

544+
// ReadFec reads incoming FEC Stream RTCP for this RTPSender
545+
func (r *RTPSender) ReadFec(b []byte) (n int, a interceptor.Attributes, err error) {
546+
if r.trackEncodings[0].fecRtcpInterceptor == nil {
547+
return 0, nil, io.ErrNoProgress
548+
}
549+
550+
select {
551+
case <-r.sendCalled:
552+
return r.trackEncodings[0].fecRtcpInterceptor.Read(b, a)
553+
case <-r.stopCalled:
554+
return 0, nil, io.ErrClosedPipe
555+
}
556+
}
557+
558+
// ReadFecRTCP is a convenience method that wraps ReadFec and unmarshals for you.
559+
func (r *RTPSender) ReadFecRTCP() ([]rtcp.Packet, interceptor.Attributes, error) {
560+
b := make([]byte, r.api.settingEngine.getReceiveMTU())
561+
i, attributes, err := r.ReadFec(b)
562+
if err != nil {
563+
return nil, nil, err
564+
}
565+
566+
pkts, err := rtcp.Unmarshal(b[:i])
567+
if err != nil {
568+
return nil, nil, err
569+
}
570+
571+
return pkts, attributes, nil
572+
}
573+
479574
// ReadSimulcast reads incoming RTCP for this RTPSender for given rid
480575
func (r *RTPSender) ReadSimulcast(b []byte, rid string) (n int, a interceptor.Attributes, err error) {
481576
select {

sdp.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,10 +392,16 @@ func addSenderSDP(
392392
if encoding.RTX.SSRC != 0 {
393393
media = media.WithValueAttribute(sdp.AttrKeySSRCGroup, fmt.Sprintf("FID %d %d", encoding.SSRC, encoding.RTX.SSRC))
394394
}
395+
if encoding.FEC.SSRC != 0 {
396+
media = media.WithValueAttribute(sdp.AttrKeySSRCGroup, fmt.Sprintf("FEC-FR %d %d", encoding.SSRC, encoding.FEC.SSRC))
397+
}
395398
media = media.WithMediaSource(uint32(encoding.SSRC), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID())
396399
if encoding.RTX.SSRC != 0 {
397400
media = media.WithMediaSource(uint32(encoding.RTX.SSRC), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID())
398401
}
402+
if encoding.FEC.SSRC != 0 {
403+
media = media.WithMediaSource(uint32(encoding.FEC.SSRC), track.StreamID() /* cname */, track.StreamID() /* streamLabel */, track.ID())
404+
}
399405
if !isPlanB {
400406
media = media.WithPropertyAttribute("msid:" + track.StreamID() + " " + track.ID())
401407
}

settingengine.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ type SettingEngine struct {
9292
srtpProtectionProfiles []dtls.SRTPProtectionProfile
9393
receiveMTU uint
9494
trackLocalRtx bool
95+
trackLocalFlexfec bool
9596
}
9697

9798
// getReceiveMTU returns the configured MTU. If SettingEngine's MTU is configured to 0 it returns the default
@@ -440,3 +441,8 @@ func (e *SettingEngine) SetSCTPMaxReceiveBufferSize(maxReceiveBufferSize uint32)
440441
func (e *SettingEngine) SetTrackLocalRtx(enable bool) {
441442
e.trackLocalRtx = enable
442443
}
444+
445+
// SetTrackLocalFlexfec allows track local use FlexFEC.
446+
func (e *SettingEngine) SetTrackLocalFlexfec(enable bool) {
447+
e.trackLocalFlexfec = enable
448+
}

0 commit comments

Comments
 (0)