mirror of
https://github.com/m1k1o/neko.git
synced 2024-07-24 14:40:50 +12:00
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:
@ -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)
|
||||
|
@ -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) {
|
||||
|
Reference in New Issue
Block a user