// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>// SPDX-License-Identifier: MIT
// Package flexfec implements FlexFEC to recover missing RTP packets due to packet loss.// https://datatracker.ietf.org/doc/html/rfc8627
package flexfecimport ()const (// BaseRTPHeaderSize represents the minium RTP packet header size in bytes.BaseRTPHeaderSize = 12// BaseFecHeaderSize represents the minium FEC payload's header size including the // required first mask.BaseFecHeaderSize = 12)// EncoderFactory is an interface for generic FEC encoders.typeEncoderFactoryinterface {NewEncoder(payloadType uint8, ssrc uint32) FlexEncoder}// FlexEncoder is the interface that FecInterceptor uses to encode Fec packets.typeFlexEncoderinterface {EncodeFec(mediaPackets []rtp.Packet, numFecPackets uint32) []rtp.Packet}// FlexEncoder20 implementation is WIP, contains bugs and no tests. Check out FlexEncoder03.typeFlexEncoder20struct { fecBaseSn uint16 payloadType uint8 ssrc uint32 coverage *ProtectionCoverage}// NewFlexEncoder returns a new FlexEncoder20.// FlexEncoder20 implementation is WIP, contains bugs and no tests. Check out FlexEncoder03.func ( uint8, uint32) *FlexEncoder20 {return &FlexEncoder20{payloadType: ,ssrc: ,fecBaseSn: uint16(1000), }}// EncodeFec returns a list of generated RTP packets with FEC payloads that protect the specified mediaPackets.// This method does not account for missing RTP packets in the mediaPackets array nor does it account for// them being passed out of order.func ( *FlexEncoder20) ( []rtp.Packet, uint32) []rtp.Packet {// Start by defining which FEC packets cover which media packetsif .coverage == nil { .coverage = NewCoverage(, ) } else { .coverage.UpdateCoverage(, ) }if .coverage == nil {returnnil }// Generate FEC payloads := make([]rtp.Packet, )for := uint32(0); < ; ++ { [] = .encodeFlexFecPacket(, [0].SequenceNumber) }return}func ( *FlexEncoder20) ( uint32, uint16) rtp.Packet { := .coverage.GetCoveredBy() := .encodeFlexFecHeader( , .coverage.ExtractMask1(), .coverage.ExtractMask2(), .coverage.ExtractMask3(), , ) := .encodeFlexFecRepairPayload(.Reset()) := rtp.Packet{Header: rtp.Header{Version: 2,Padding: false,Extension: false,Marker: false,PayloadType: .payloadType,SequenceNumber: .fecBaseSn,Timestamp: 54243243,SSRC: .ssrc,CSRC: []uint32{}, },Payload: append(, ...), } .fecBaseSn++return}func ( *FlexEncoder20) ( *util.MediaPacketIterator,uint16,uint32,uint64,uint16,) []byte {/* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |0|0|P|X| CC |M| PT recovery | length recovery | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | TS recovery | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SN base_i |k| Mask [0-14] | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |k| Mask [15-45] (optional) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Mask [46-109] (optional) | | | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ... next SN base and Mask for CSRC_i in CSRC list ... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : Repair "Payload" follows FEC Header : : : */// Get header size - This depends on the size of the bitmask. := BaseFecHeaderSizeif > 0 { += 4 }if > 0 { += 8 }// Allocate the FlexFec header := make([]byte, )// XOR the relevant fields for the header // TO DO - CHECK TO SEE IF THE MARSHALTO() call works with this. := make([]byte, )for .HasNext() { := .Next() , := .MarshalTo()if == 0 || != nil {returnnil }// XOR the first 2 bytes of the header: V, P, X, CC, M, PT fields [0] ^= [0] [1] ^= [1]// XOR the length recovery field := uint16(.MarshalSize() - BaseRTPHeaderSize) //nolint:gosec // G115 [2] ^= uint8( >> 8) //nolint:gosec // G115 [3] ^= uint8() //nolint:gosec // G115// XOR the 5th to 8th bytes of the header: the timestamp field [4] ^= [4] [5] ^= [5] [6] ^= [6] [7] ^= [7] }// Write the base SN for the batch of media packetsbinary.BigEndian.PutUint16([8:10], )// Write the bitmasks to the headerbinary.BigEndian.PutUint16([10:12], )if > 0 {binary.BigEndian.PutUint32([12:16], ) [10] |= 0b10000000 }if > 0 {binary.BigEndian.PutUint64([16:24], ) [12] |= 0b10000000 }return}func ( *FlexEncoder20) ( *util.MediaPacketIterator) []byte { := make([]byte, len(.First().Payload))for .HasNext() { := .Next().Payloadiflen() < len() {// Expected FEC packet payload is bigger that what we can currently store, // we need to resize. := make([]byte, len())copy(, ) = }for := 0; < len(); ++ { [] ^= [] } }return}
The pages are generated with Goldsv0.8.2. (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu.
PR and bug reports are welcome and can be submitted to the issue list.
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds.