media

package
v0.21.37 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 19, 2026 License: MPL-2.0 Imports: 21 Imported by: 0

README

media

Implemented:

Everything is io.Reader and io.Writer

We follow GO std lib and providing interface for Reader/Writer when it comes reading and writing media.
This optimized and made easier usage of RTP framework, by providing end user standard library io.Reader io.Writer to pass his media.

In other words chaining reader or writer allows to build interceptors, encoders, decoders without introducing overhead of contention or many memory allocations

Features:

  • Simple SDP build with formats alaw,ulaw,dtmf
  • RTP/RTCP receiving and logging
  • Extendable MediaSession handling for RTP/RTCP handling (ex microphone,speaker)
  • DTMF encoder, decoder via RFC4733
  • Minimal SDP package for audio
  • Media Session, RTP Session handling
  • RTCP monitoring
  • SDP codec fields manipulating
  • ... who knows

Concepts

  • Media Session represents mapping between SDP media description and creates session based on local/remote addr
  • RTP Session is creating RTP/RTCP session. It is using media session underneath to add networking layer.
  • RTP Packet Reader is depackatizing RTP packets and providing payload as io.Reader. Normally it should be chained to RTP Session
  • RTP Packet Writer is packatizing payload to RTP packets as io.Writer. Normally it should be chained to RTP Session

IO flow

Reader: AudioDecoder<->RTPPacketReader<->RTPSession<->MediaSession

Writer: AudioEncoder<->RTPPackerWriter<->RTPSession<->MediaSession

more docs...

Documentation

Overview

Example (Audio2RTPGenerator)

Example on how to generate RTP packets from audio bytes

package main

import (
	"bytes"
	"fmt"
	"os"

	"github.com/pion/rtp"
)

type rtpBuffer struct {
	buf []rtp.Packet
}

func (b *rtpBuffer) WriteRTP(p *rtp.Packet) error {
	b.buf = append(b.buf, *p)
	return nil
}

// Example on how to generate RTP packets from audio bytes
func main() {
	// Create some audio
	audioAlawBuf := make([]byte, 4*160)
	copy(audioAlawBuf, bytes.Repeat([]byte("0123456789"), CodecAudioAlaw.Samples16()*2/10))

	// Create Packet writer and pass RTP buff
	rtpBuf := &rtpBuffer{}
	rtpGenerator := NewRTPPacketWriter(rtpBuf, CodecAudioAlaw)
	WriteAll(rtpGenerator, audioAlawBuf, 160)

	// Now we have RTP packets ready to use from audio
	for _, p := range rtpBuf.buf {
		fmt.Fprint(os.Stderr, p.String())
	}
	fmt.Println(len(rtpBuf.buf))
}
Output:
4

Index

Examples

Constants

Variables

View Source
var (
	// Here are some codec constants that can be reused
	CodecAudioUlaw          = Codec{PayloadType: 0, SampleRate: 8000, SampleDur: 20 * time.Millisecond, NumChannels: 1, Name: "PCMU"}
	CodecAudioAlaw          = Codec{PayloadType: 8, SampleRate: 8000, SampleDur: 20 * time.Millisecond, NumChannels: 1, Name: "PCMA"}
	CodecAudioOpus          = Codec{PayloadType: 96, SampleRate: 48000, SampleDur: 20 * time.Millisecond, NumChannels: 2, Name: "opus"}
	CodecTelephoneEvent8000 = Codec{PayloadType: 101, SampleRate: 8000, SampleDur: 20 * time.Millisecond, NumChannels: 1, Name: "telephone-event"}

	// Video codecs
	// H.264 is the most common video codec for SIP
	CodecVideoH264 = Codec{PayloadType: 99, SampleRate: 90000, SampleDur: 33 * time.Millisecond, NumChannels: 1, Name: "H264"}
	// VP8 is commonly used in WebRTC
	CodecVideoVP8 = Codec{PayloadType: 103, SampleRate: 90000, SampleDur: 33 * time.Millisecond, NumChannels: 1, Name: "VP8"}
	// VP9 is a newer codec
	CodecVideoVP9 = Codec{PayloadType: 106, SampleRate: 90000, SampleDur: 33 * time.Millisecond, NumChannels: 1, Name: "VP9"}
)
View Source
var (
	// RTPPortStart and RTPPortEnd allows defining rtp port range for media
	RTPPortStart = 0
	RTPPortEnd   = 0

	// When reading RTP use at least MTU size. Increase this
	RTPBufSize = 1500

	RTPDebug  = false
	RTCPDebug = false

	// RTPProfileSAVPDisable disables offering RTP/SAVP and keeps standard RTP/AVP for backward compatibilit needs
	//
	// Experimental
	RTPProfileSAVPDisable = false
)
View Source
var (
	ErrRTPSequenceOutOfOrder = errors.New("out of order")
	ErrRTPSequenceBad        = errors.New("bad sequence")
	ErrRTPSequnceDuplicate   = errors.New("sequence duplicate")
)
View Source
var (
	DefaultOnReadRTCP  func(pkt rtcp.Packet, rtpStats RTPReadStats)  = nil
	DefaultOnWriteRTCP func(pkt rtcp.Packet, rtpStats RTPWriteStats) = nil
)

Functions

func CodecsFromSDPRead

func CodecsFromSDPRead(formats []string, attrs []string, codecsAudio []Codec) (int, error)

CodecsFromSDP will try to parse as much as possible, but it will return also error in case some properties could not be read You can take what is parsed or return error

func CombineSDP added in v0.21.3

func CombineSDP(sessions []*MediaSession) []byte

CombineSDP combines SDP from multiple media sessions (e.g., audio and video) into a single SDP with multiple media descriptions

func Copy

func Copy(reader io.Reader, writer io.Writer) (int64, error)

Copy is like io.Copy but it uses buffer size needed for RTP

func CopyWithBuf

func CopyWithBuf(reader io.Reader, writer io.Writer, payloadBuf []byte) (int64, error)

CopyWithBuf is simple and strict compared to io.CopyBuffer. ReadFrom and WriteTo is not considered and due to RTP buf requirement it can lead to different buffer size passing

func DTMFDecode

func DTMFDecode(payload []byte, d *DTMFEvent) error

DecodeRTPPayload decodes an RTP payload into a DTMF event

func DTMFEncode

func DTMFEncode(d DTMFEvent) []byte

func DTMFToRune

func DTMFToRune(dtmf uint8) rune

func DefaultLogger

func DefaultLogger() *slog.Logger

func ErrorIsTimeout

func ErrorIsTimeout(err error) bool

func FractionLostFloat

func FractionLostFloat(f uint8) float64

func GetCurrentNTPTimestamp

func GetCurrentNTPTimestamp() uint64

func NTPTimestamp

func NTPTimestamp(t time.Time) uint64

func NTPToTime

func NTPToTime(ntpTimestamp uint64) time.Time

func RTCPUnmarshal

func RTCPUnmarshal(data []byte, packets []rtcp.Packet) (n int, err error)

RTCPUnmarshal is improved version based on pion/rtcp where we allow caller to define and control buffer of rtcp packets. This also reduces one allocation NOTE: data is still referenced in packet buffer

func RTPUnmarshal

func RTPUnmarshal(buf []byte, p *rtp.Packet) error

Experimental

RTPUnmarshal temporarly solution to provide more optimized unmarshal version based on pion/rtp it does not preserve any buffer reference which allows reusage

TODO build RTP header unmarshaller for VOIP needs

func ReadAll

func ReadAll(reader io.Reader, sampleSize int) ([]byte, error)

func SetDefaultLogger

func SetDefaultLogger(l *slog.Logger)

SetDefaultLogger sets default logger that will be used withing sip package Must be called before any usage of library

func StringRTCP

func StringRTCP(p rtcp.Packet) string

func WriteAll

func WriteAll(w io.Writer, data []byte, sampleSize int) (int64, error)

Types

type Codec

type Codec struct {
	Name        string
	PayloadType uint8
	SampleRate  uint32
	SampleDur   time.Duration
	NumChannels int // 1 or 2
}

func CodecAudioFromList

func CodecAudioFromList(codecs []Codec) (Codec, bool)

func CodecAudioFromPayloadType

func CodecAudioFromPayloadType(payloadType uint8) (Codec, error)

func CodecAudioFromSession

func CodecAudioFromSession(s *MediaSession) Codec

func CodecFromPayloadType deprecated

func CodecFromPayloadType(payloadType uint8) Codec

Deprecated: Use CodecAudioFromPayloadType

func CodecFromSession

func CodecFromSession(s *MediaSession) Codec

CodecFromSession returns the first codec from the session, whether audio or video It prefers filterCodecs (negotiated codecs) over Codecs

func (*Codec) SampleTimestamp

func (c *Codec) SampleTimestamp() uint32

SampleTimestamp returns number of samples as RTP Timestamp measure

func (*Codec) Samples16

func (c *Codec) Samples16() int

Samples16 returns PCM 16 bit samples size

func (*Codec) SamplesPCM

func (c *Codec) SamplesPCM(bitSize int) int

Samples is samples in pcm

func (*Codec) String

func (c *Codec) String() string

type DTMFEvent

type DTMFEvent struct {
	Event      uint8
	EndOfEvent bool
	Volume     uint8
	Duration   uint16
}

DTMFEvent represents a DTMF event

func RTPDTMFEncode8000

func RTPDTMFEncode8000(char rune) []DTMFEvent

RTPDTMFEncode8000 creates series of DTMF redudant events which should be encoded as payload It is currently only 8000 sample rate considered for telophone event

func (*DTMFEvent) String

func (ev *DTMFEvent) String() string

type MediaSession

type MediaSession struct {

	// MediaType is the type of media: "audio" or "video"
	MediaType string

	// Codecs are initial list of Codecs that would be used in SDP generation
	Codecs []Codec

	// Mode is sdp mode. Check consts sdp.ModeRecvOnly etc...
	Mode string
	// Laddr our local address which has full IP and port after media session creation
	Laddr net.UDPAddr
	// Raddr is our target remote address. Normally it is resolved by SDP parsing.
	Raddr net.UDPAddr
	// ExternalIP that should be used for building SDP
	ExternalIP net.IP

	SecureRTP int // 0 none, 1 - SDES
	// TODO support multile for offering
	SRTPAlg uint16
	// contains filtered or unexported fields
}

func NewMediaSession

func NewMediaSession(ip net.IP, port int) (s *MediaSession, e error)

func NewVideoMediaSession added in v0.21.3

func NewVideoMediaSession(ip net.IP, port int) (s *MediaSession, e error)

NewVideoMediaSession creates a new video media session

func (*MediaSession) Close

func (s *MediaSession) Close() error

func (*MediaSession) CommonCodecs

func (s *MediaSession) CommonCodecs() []Codec

CommonCodecs returns common codecs if negotiation is finished, that is Local and Remote SDP are exchanged NOTE: Not thread safe, should be called after negotiation or session must be Forked

func (*MediaSession) Fork

func (s *MediaSession) Fork() *MediaSession

Fork is special call to be used in case when there is session update It preserves pointer to same conneciton but rest is remobed After this call it still expected that

func (*MediaSession) Init

func (s *MediaSession) Init() error

Init should be called if session is created manually Use NewMediaSession for default building

func (*MediaSession) InitWithListeners

func (s *MediaSession) InitWithListeners(lRTP net.PacketConn, lRTCP net.PacketConn, raddr *net.UDPAddr)

func (*MediaSession) InitWithSDP

func (s *MediaSession) InitWithSDP(localSDP []byte) error

InitWithSDP allows creating media session with own SDP and bypassing other needs

func (*MediaSession) LocalSDP

func (s *MediaSession) LocalSDP() []byte

LocalSDP generates SDP based on local settings and remote SDP It should never be called in parallel to RemoteSDP, as it is expected serial process

func (*MediaSession) ReadRTCP

func (m *MediaSession) ReadRTCP(buf []byte, pkts []rtcp.Packet) (n int, err error)

ReadRTCP is optimized reads and unmarshals RTCP packets. Buffers is only used for unmarshaling. Caller needs to be aware of size this buffer and allign with MTU

func (*MediaSession) ReadRTCPRaw

func (m *MediaSession) ReadRTCPRaw(buf []byte) (int, error)

func (*MediaSession) ReadRTCPRawDeadline

func (m *MediaSession) ReadRTCPRawDeadline(buf []byte, t time.Time) (int, error)

func (*MediaSession) ReadRTP

func (m *MediaSession) ReadRTP(buf []byte, pkt *rtp.Packet) (int, error)

ReadRTP reads data from network and parses to pkt buffer is passed in order to avoid extra allocs

func (*MediaSession) ReadRTPRaw

func (m *MediaSession) ReadRTPRaw(buf []byte) (int, error)

func (*MediaSession) ReadRTPRawDeadline

func (m *MediaSession) ReadRTPRawDeadline(buf []byte, t time.Time) (int, error)

func (*MediaSession) RemoteSDP

func (s *MediaSession) RemoteSDP(sdpReceived []byte) error

func (*MediaSession) SetRemoteAddr

func (s *MediaSession) SetRemoteAddr(raddr *net.UDPAddr)

SetRemoteAddr is helper to set Raddr and rtcp address. It is not thread safe

func (*MediaSession) StartRTP

func (s *MediaSession) StartRTP(rw int8) error

func (*MediaSession) StopRTP

func (s *MediaSession) StopRTP(rw int8, dur time.Duration) error

func (*MediaSession) WriteRTCP

func (m *MediaSession) WriteRTCP(p rtcp.Packet) error

func (*MediaSession) WriteRTCPDeadline

func (m *MediaSession) WriteRTCPDeadline(p rtcp.Packet, deadline time.Time) error

func (*MediaSession) WriteRTCPRaw

func (m *MediaSession) WriteRTCPRaw(data []byte) (int, error)

func (*MediaSession) WriteRTCPs

func (m *MediaSession) WriteRTCPs(pkts []rtcp.Packet) error

Use this to write Multi RTCP packets if they can fit in MTU=1500

func (*MediaSession) WriteRTP

func (m *MediaSession) WriteRTP(p *rtp.Packet) error

func (*MediaSession) WriteRTPRaw

func (m *MediaSession) WriteRTPRaw(data []byte) (n int, err error)

type MediaStreamer

type MediaStreamer interface {
	MediaStream(s *MediaSession) error
}

type OnRTPReadStats

type OnRTPReadStats func(stats RTPReadStats)

type OnRTPWriteStats

type OnRTPWriteStats func(stats RTPWriteStats)

type RTCPReader

type RTCPReader interface {
	ReadRTCP(buf []byte, pkts []rtcp.Packet) (n int, err error)
}

type RTCPWriter

type RTCPWriter interface {
	WriteRTCP(p rtcp.Packet) error
}

type RTCPWriterRaw

type RTCPWriterRaw interface {
	WriteRTCPRaw(buf []byte) (int, error) // -> io.Writer
}

type RTPCReaderRaw

type RTPCReaderRaw interface {
	ReadRTCPRaw(buf []byte) (int, error)
}

type RTPDtmfReader

type RTPDtmfReader struct {
	// contains filtered or unexported fields
}

func NewRTPDTMFReader

func NewRTPDTMFReader(codec Codec, packetReader *RTPPacketReader, reader io.Reader) *RTPDtmfReader

RTP DTMF writer is midleware for reading DTMF events It reads from io Reader and checks packet Reader

func (*RTPDtmfReader) Read

func (w *RTPDtmfReader) Read(b []byte) (int, error)

Write is RTP io.Writer which adds more sync mechanism

func (*RTPDtmfReader) ReadDTMF

func (w *RTPDtmfReader) ReadDTMF() (rune, bool)

type RTPDtmfWriter

type RTPDtmfWriter struct {
	// contains filtered or unexported fields
}

func NewRTPDTMFWriter

func NewRTPDTMFWriter(codec Codec, rtpPacketizer *RTPPacketWriter, writer io.Writer) *RTPDtmfWriter

RTP DTMF writer is midleware for passing RTP DTMF event. If it is chained it uses to block writer while writing DTFM events

func (*RTPDtmfWriter) Write

func (w *RTPDtmfWriter) Write(b []byte) (int, error)

Write is RTP io.Writer which adds more sync mechanism

func (*RTPDtmfWriter) WriteDTMF

func (w *RTPDtmfWriter) WriteDTMF(dtmf rune) error

type RTPExtendedSequenceNumber

type RTPExtendedSequenceNumber struct {
	// contains filtered or unexported fields
}

RTPExtendedSequenceNumber is embedable/ replacable sequnce number generator For thread safety you should wrap it

func NewRTPSequencer

func NewRTPSequencer() RTPExtendedSequenceNumber

func (*RTPExtendedSequenceNumber) InitSeq

func (sn *RTPExtendedSequenceNumber) InitSeq(seq uint16)

func (*RTPExtendedSequenceNumber) NextSeqNumber

func (s *RTPExtendedSequenceNumber) NextSeqNumber() uint16

func (*RTPExtendedSequenceNumber) ReadExtendedSeq

func (sn *RTPExtendedSequenceNumber) ReadExtendedSeq() uint64

func (*RTPExtendedSequenceNumber) UpdateSeq

func (sn *RTPExtendedSequenceNumber) UpdateSeq(seq uint16) error

Based on https://datatracker.ietf.org/doc/html/rfc1889#appendix-A.2

type RTPPacketReader

type RTPPacketReader struct {

	// PacketHeader is stored after calling Read
	// Safe to read only in same goroutine as Read
	PacketHeader rtp.Header
	// contains filtered or unexported fields
}

RTPPacketReader reads RTP packet and extracts payload and header

func NewRTPPacketReader

func NewRTPPacketReader(reader RTPReader, codec Codec) *RTPPacketReader

func NewRTPPacketReaderSession

func NewRTPPacketReaderSession(sess *RTPSession) *RTPPacketReader

NewRTPPacketReaderSession just helper constructor

func (*RTPPacketReader) Read

func (r *RTPPacketReader) Read(b []byte) (int, error)

Read Implements io.Reader and extracts Payload from RTP packet has no input queue or sorting control of packets Buffer is used for reading headers and Headers are stored in PacketHeader

NOTE: Consider that if you are passsing smaller buffer than RTP header+payload, io.ErrShortBuffer is returned

func (*RTPPacketReader) Reader

func (r *RTPPacketReader) Reader() RTPReader

func (*RTPPacketReader) UpdateRTPSession

func (r *RTPPacketReader) UpdateRTPSession(rtpSess *RTPSession)

func (*RTPPacketReader) UpdateReader

func (r *RTPPacketReader) UpdateReader(reader RTPReader)

type RTPPacketWriter

type RTPPacketWriter struct {

	// After each write packet header is saved for more reading
	PacketHeader rtp.Header

	// SSRC is readOnly and it is not changed
	SSRC uint32
	// contains filtered or unexported fields
}

RTPPacketWriter packetize any payload before pushing to active media session It creates SSRC as identifier and all packets sent will be with this SSRC For multiple streams, multiple RTP Writer needs to be created

func NewRTPPacketWriter

func NewRTPPacketWriter(writer RTPWriter, codec Codec) *RTPPacketWriter

RTPPacketWriter packetize payload in RTP packet before passing on media session Not having: - random Timestamp - allow different clock rate - CSRC contribution source - Silence detection and marker set updateClockRate- Padding and encryyption

func NewRTPPacketWriterSession

func NewRTPPacketWriterSession(sess *RTPSession) *RTPPacketWriter

NewRTPPacketWriterSession creates RTPPacketWriter and attaches RTP Session expected values

func (*RTPPacketWriter) DelayTimestamp

func (p *RTPPacketWriter) DelayTimestamp(ofsset uint32)

func (*RTPPacketWriter) InitTimestamp

func (p *RTPPacketWriter) InitTimestamp() uint32

InitTimestamp returns init RTP timestamp

func (*RTPPacketWriter) ResetTimestamp

func (p *RTPPacketWriter) ResetTimestamp()

ResetTimestamp can mark new stream comming. If stream is continuous it will add timestamp difference MUST Not be called during stream Write

func (*RTPPacketWriter) UpdateRTPSession

func (w *RTPPacketWriter) UpdateRTPSession(rtpSess *RTPSession)

UpdateRTPSession updates rtp writer from current rtp session due to REINVITE It is expected that this is now new RTP Session and it is expected tha: - Statistics will be reset (SSRC=0) -> Fresh Start of Quality monitoring - Should not lead inacurate reporting - In case CODEC change than RTP should reset stats anyway

func (*RTPPacketWriter) Write

func (p *RTPPacketWriter) Write(b []byte) (int, error)

Write implements io.Writer and does payload RTP packetization Media clock rate is determined For more control or dynamic payload WriteSamples can be used It is not thread safe and order of payload frames is required

func (*RTPPacketWriter) WriteSamples

func (p *RTPPacketWriter) WriteSamples(payload []byte, sampleRateTimestamp uint32, marker bool, payloadType uint8) (int, error)

WriteSamples allows to skip default packet rate. This is useful if you need to write different payload but keeping same SSRC

func (*RTPPacketWriter) Writer

func (w *RTPPacketWriter) Writer() RTPWriter

type RTPReadStats

type RTPReadStats struct {
	SSRC                   uint32
	FirstPktSequenceNumber uint16
	LastSequenceNumber     uint16

	// tracks first pkt seq in this interval to calculate loss of packets
	IntervalFirstPktSeqNum uint16
	IntervalPacketsCount   uint16

	PacketsCount uint64
	OctetCount   uint64

	// RTP reading stats
	SampleRate uint32

	// Round TRIP Time based on LSR and DLSR
	RTT time.Duration
	// contains filtered or unexported fields
}

Some of fields here are exported (as readonly) intentionally

type RTPReader

type RTPReader interface {
	ReadRTP(buf []byte, p *rtp.Packet) (int, error)
}

type RTPReaderRaw

type RTPReaderRaw interface {
	ReadRTPRaw(buf []byte) (int, error)
}

type RTPSession

type RTPSession struct {
	// Keep pointers at top to reduce GC
	Sess *MediaSession
	// contains filtered or unexported fields
}

func NewRTPSession

func NewRTPSession(sess *MediaSession) *RTPSession

RTP session creates new RTP reader/writer from session

func (*RTPSession) Close

func (s *RTPSession) Close() error

func (*RTPSession) Monitor

func (s *RTPSession) Monitor() error

Monitor starts reading RTCP and monitoring media quality

func (*RTPSession) MonitorBackground

func (s *RTPSession) MonitorBackground() error

MonitorBackground is helper to keep monitoring in background MUST Be called after session REMOTE SDP is parsed

func (*RTPSession) OnReadRTCP

func (s *RTPSession) OnReadRTCP(f func(pkt rtcp.Packet, rtpStats RTPReadStats))

func (*RTPSession) OnWriteRTCP

func (s *RTPSession) OnWriteRTCP(f func(pkt rtcp.Packet, rtpStats RTPWriteStats))

func (*RTPSession) ReadRTP

func (s *RTPSession) ReadRTP(b []byte, readPkt *rtp.Packet) (n int, err error)

ReadRTP reads RTP NOTE: For RTCP we may read some properties of media session. Do not run this until full media session is negotiated. For updating media, media session forking must be done!

func (*RTPSession) ReadRTPRaw

func (s *RTPSession) ReadRTPRaw(buf []byte) (int, error)

func (*RTPSession) ReadStats

func (s *RTPSession) ReadStats() RTPReadStats

func (*RTPSession) WriteRTP

func (s *RTPSession) WriteRTP(pkt *rtp.Packet) error

func (*RTPSession) WriteRTPRaw

func (s *RTPSession) WriteRTPRaw(buf []byte) (int, error)

func (*RTPSession) WriteStats

func (s *RTPSession) WriteStats() RTPWriteStats

type RTPStatsReader

type RTPStatsReader struct {
	// Reader should be your AudioReade or any other interceptor RTP reader that is reading audio stream
	Reader     io.Reader
	RTPSession *RTPSession
	// OnRTPReadStats is fired each time on Read RTP. Must not block
	OnRTPReadStats OnRTPReadStats
}

func (*RTPStatsReader) Read

func (i *RTPStatsReader) Read(b []byte) (int, error)

type RTPStatsWriter

type RTPStatsWriter struct {
	// Writer should be your Writer or any other interceptor RTP writer that is reading audio stream
	Writer     io.Writer
	RTPSession *RTPSession
	// ONRTPWriteStats is fired each time on Read RTP. Must not block
	OnRTPWriteStats OnRTPWriteStats
}

func (*RTPStatsWriter) Write

func (i *RTPStatsWriter) Write(b []byte) (int, error)

type RTPWriteStats

type RTPWriteStats struct {
	SSRC uint32

	// RTCP stats
	PacketsCount uint64
	OctetCount   uint64
	// contains filtered or unexported fields
}

Some of fields here are exported (as readonly) intentionally

type RTPWriter

type RTPWriter interface {
	WriteRTP(p *rtp.Packet) error
}

type RTPWriterRaw

type RTPWriterRaw interface {
	WriteRTPRaw(buf []byte) (int, error) // -> io.Writer
}

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL