Sample listeners chan (#33)

* switch to sample listeners.

* add streamsink total bytes to metrics.

* on rtcp to chan.

* change logs.

* streamsink add real bitrate.

* add timing information to sample.

* bitrate buckets.

* switch to WriteSample.
This commit is contained in:
Miroslav Šedivý
2023-03-07 00:08:53 +01:00
committed by GitHub
parent 38fc21aabc
commit 17bfd2d58f
5 changed files with 178 additions and 72 deletions

View File

@ -321,8 +321,13 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, bitrate int,
videoAuto = false
}
videoRtcp := make(chan []rtcp.Packet, 1)
// video track
videoTrack, err := NewTrack(logger, videoCodec, connection, WithVideoAuto(videoAuto))
videoTrack, err := NewTrack(logger, videoCodec, connection,
WithVideoAuto(videoAuto),
WithRtcpChan(videoRtcp),
)
if err != nil {
return nil, err
}
@ -570,7 +575,9 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, bitrate int,
if err = video.RemoveReceiver(videoTrack); err != nil {
logger.Err(err).Msg("failed to remove video receiver")
}
audioTrack.RemoveStream()
audioTrack.Shutdown()
videoTrack.Shutdown()
close(videoRtcp)
}
manager.metrics.SetState(session, state)
@ -651,17 +658,24 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, bitrate int,
})
})
videoTrack.OnRTCP(func(packets []rtcp.Packet) {
for _, p := range packets {
if rtcpPacket, ok := p.(*rtcp.ReceiverReport); ok {
l := len(rtcpPacket.Reports)
if l > 0 {
// use only last report
manager.metrics.SetReceiverReport(session, rtcpPacket.Reports[l-1])
go func() {
for {
packets, ok := <-videoRtcp
if !ok {
break
}
for _, p := range packets {
if rtcpPacket, ok := p.(*rtcp.ReceiverReport); ok {
l := len(rtcpPacket.Reports)
if l > 0 {
// use only last report
manager.metrics.SetReceiverReport(session, rtcpPacket.Reports[l-1])
}
}
}
}
})
}()
go func() {
ticker := time.NewTicker(5 * time.Second)

View File

@ -16,9 +16,11 @@ import (
)
type Track struct {
logger zerolog.Logger
track *webrtc.TrackLocalStaticSample
listener func(sample types.Sample)
logger zerolog.Logger
track *webrtc.TrackLocalStaticSample
rtcpCh chan []rtcp.Packet
sample chan types.Sample
videoAuto bool
videoAutoMu sync.RWMutex
@ -27,9 +29,6 @@ type Track struct {
stream types.StreamSinkManager
streamMu sync.Mutex
onRtcp func([]rtcp.Packet)
onRtcpMu sync.RWMutex
bitrateChange func(int) (bool, error)
videoChange func(string) (bool, error)
}
@ -42,6 +41,12 @@ func WithVideoAuto(auto bool) option {
}
}
func WithRtcpChan(rtcp chan []rtcp.Packet) option {
return func(t *Track) {
t.rtcpCh = rtcp
}
}
func NewTrack(logger zerolog.Logger, codec codec.RTPCodec, connection *webrtc.PeerConnection, opts ...option) (*Track, error) {
id := codec.Type.String()
track, err := webrtc.NewTrackLocalStaticSample(codec.Capability, id, "stream")
@ -54,49 +59,66 @@ func NewTrack(logger zerolog.Logger, codec codec.RTPCodec, connection *webrtc.Pe
t := &Track{
logger: logger,
track: track,
rtcpCh: make(chan []rtcp.Packet),
sample: make(chan types.Sample),
}
for _, opt := range opts {
opt(t)
}
t.listener = func(sample types.Sample) {
err := track.WriteSample(media.Sample{
Data: sample.Data,
Duration: sample.Duration,
})
if err != nil && !errors.Is(err, io.ErrClosedPipe) {
logger.Warn().Err(err).Msg("failed to write sample to track")
}
}
sender, err := connection.AddTrack(t.track)
if err != nil {
return nil, err
}
go t.rtcpReader(sender)
go t.sampleReader()
return t, nil
}
func (t *Track) Shutdown() {
t.RemoveStream()
close(t.sample)
}
func (t *Track) rtcpReader(sender *webrtc.RTPSender) {
for {
packets, _, err := sender.ReadRTCP()
if err != nil {
if err == io.EOF || err == io.ErrClosedPipe {
if errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) {
t.logger.Debug().Msg("track rtcp reader closed")
return
}
t.logger.Err(err).Msg("RTCP read error")
t.logger.Warn().Err(err).Msg("failed to read track rtcp")
continue
}
t.onRtcpMu.RLock()
if t.onRtcp != nil {
go t.onRtcp(packets)
if t.rtcpCh != nil {
t.rtcpCh <- packets
}
}
}
func (t *Track) sampleReader() {
for {
sample, ok := <-t.sample
if !ok {
t.logger.Debug().Msg("track sample reader closed")
return
}
err := t.track.WriteSample(media.Sample{
Data: sample.Data,
Duration: sample.Duration,
Timestamp: sample.Timestamp,
})
if err != nil && !errors.Is(err, io.ErrClosedPipe) {
t.logger.Warn().Err(err).Msg("failed to write sample to track")
}
t.onRtcpMu.RUnlock()
}
}
@ -117,9 +139,9 @@ func (t *Track) SetStream(stream types.StreamSinkManager) (bool, error) {
var err error
if t.stream != nil {
err = t.stream.MoveListenerTo(&t.listener, stream)
err = t.stream.MoveListenerTo(t, stream)
} else {
err = stream.AddListener(&t.listener)
err = stream.AddListener(t)
}
if err != nil {
return false, err
@ -138,7 +160,7 @@ func (t *Track) RemoveStream() {
return
}
err := t.stream.RemoveListener(&t.listener)
err := t.stream.RemoveListener(t)
if err != nil {
t.logger.Warn().Err(err).Msg("failed to remove listener from stream")
}
@ -157,9 +179,9 @@ func (t *Track) SetPaused(paused bool) {
var err error
if paused {
err = t.stream.RemoveListener(&t.listener)
err = t.stream.RemoveListener(t)
} else {
err = t.stream.AddListener(&t.listener)
err = t.stream.AddListener(t)
}
if err != nil {
t.logger.Warn().Err(err).Msg("failed to change listener state")
@ -169,11 +191,8 @@ func (t *Track) SetPaused(paused bool) {
t.paused = paused
}
func (t *Track) OnRTCP(f func([]rtcp.Packet)) {
t.onRtcpMu.Lock()
defer t.onRtcpMu.Unlock()
t.onRtcp = f
func (t *Track) WriteSample(sample types.Sample) {
t.sample <- sample
}
func (t *Track) SetBitrate(bitrate int) (bool, error) {