Understanding the Attributes and StreamInfo types for passing metadata between interceptors
The Pion Interceptor library uses two key types for passing metadata: Attributes for generic key-value storage, and StreamInfo for media stream context. These types enable communication between interceptors and provide essential stream information.
// Attributes are a generic key/value store used by interceptors.type Attributes map[any]any// Get returns the attribute associated with key.func (a Attributes) Get(key any) any { return a[key]}// Set sets the attribute associated with key to the given value.func (a Attributes) Set(key any, val any) { a[key] = val}
Attributes provides helper methods for common RTP/RTCP operations:
// GetRTPHeader gets the RTP header if present. If it is not present, it will be// unmarshalled from the raw byte slice and stored in the attributes.func (a Attributes) GetRTPHeader(raw []byte) (*rtp.Header, error) { if val, ok := a[rtpHeaderKey]; ok { if header, ok := val.(*rtp.Header); ok { return header, nil } return nil, errInvalidType } header := &rtp.Header{} if _, err := header.Unmarshal(raw); err != nil { return nil, err } a[rtpHeaderKey] = header return header, nil}
These helper methods cache the unmarshalled data in the attributes map, avoiding redundant parsing if multiple interceptors need to access the same headers or packets.
func (i *MyInterceptor) BindRTCPReader(reader RTCPReader) RTCPReader { return RTCPReaderFunc(func(b []byte, a Attributes) (int, Attributes, error) { // Get the parsed RTCP packets (cached after first access) packets, err := a.GetRTCPPackets(b) if err != nil { return 0, a, err } // Process each packet for _, pkt := range packets { switch p := pkt.(type) { case *rtcp.SenderReport: fmt.Printf("Sender Report: SSRC=%d, PacketCount=%d\n", p.SSRC, p.PacketCount) case *rtcp.ReceiverReport: fmt.Printf("Receiver Report: SSRC=%d\n", p.SSRC) } } return reader.Read(b, a) })}
Use typed keys to avoid conflicts between interceptors:
type myKeyType intconst ( timestampKey myKeyType = iota sequenceKey metadataKey)// In one interceptorattributes.Set(timestampKey, time.Now())// In another interceptorif val := attributes.Get(timestampKey); val != nil { if ts, ok := val.(time.Time); ok { // Use the timestamp }}
Using typed constants as keys prevents accidental conflicts between interceptors and provides type safety.
// RTCPFeedback signals the connection to use additional RTCP packet types.// https://draft.ortc.org/#dom-rtcrtcpfeedbacktype RTCPFeedback struct { // Type is the type of feedback. // see: https://draft.ortc.org/#dom-rtcrtcpfeedback // valid: ack, ccm, nack, goog-remb, transport-cc Type string // The parameter value depends on the type. // For example, type="nack" parameter="pli" will send Picture Loss Indicator packets. Parameter string}
SSRC: Synchronization source identifier for the main streamSSRCRetransmission: SSRC for retransmitted packets (RFC 4588)SSRCForwardErrorCorrection: SSRC for FEC packets
PayloadType: RTP payload type for the main streamPayloadTypeRetransmission: Payload type for retransmitted packetsPayloadTypeForwardErrorCorrection: Payload type for FEC packets
func (i *MyInterceptor) BindLocalStream(info *StreamInfo, writer RTPWriter) RTPWriter { return RTPWriterFunc(func(header *rtp.Header, payload []byte, attributes Attributes) (int, error) { // Check payload type switch header.PayloadType { case info.PayloadType: // Handle main stream case info.PayloadTypeRetransmission: // Handle retransmission case info.PayloadTypeForwardErrorCorrection: // Handle FEC } return writer.Write(header, payload, attributes) })}
Codec information
MimeType: Media codec type (e.g., “video/VP8”, “audio/opus”)ClockRate: RTP timestamp clock rateChannels: Number of audio channels (0 for video)SDPFmtpLine: Format-specific parameters from SDP
func (i *MyInterceptor) BindRemoteStream(info *StreamInfo, reader RTPReader) RTPReader { if strings.HasPrefix(info.MimeType, "video/") { fmt.Printf("Video codec: %s at %d Hz\n", info.MimeType, info.ClockRate) } else if strings.HasPrefix(info.MimeType, "audio/") { fmt.Printf("Audio codec: %s at %d Hz, %d channels\n", info.MimeType, info.ClockRate, info.Channels) } if info.SDPFmtpLine != "" { fmt.Printf("Format params: %s\n", info.SDPFmtpLine) } return reader}