diff --git a/opus/opus.go b/opus/opus.go index 4b2af7c..d1e1109 100644 --- a/opus/opus.go +++ b/opus/opus.go @@ -64,7 +64,7 @@ func Decode(w media.PCM16Writer, targetChannels int, logger logger.Logger) (Writ }, nil } -func Encode(w Writer, channels int, logger logger.Logger) (media.PCM16Writer, error) { +func Encode(w Writer, channels int, useDtx bool, logger logger.Logger) (media.PCM16Writer, error) { enc, err := opus.NewEncoder(w.SampleRate(), channels, opus.AppVoIP) if err != nil { return nil, err @@ -73,6 +73,7 @@ func Encode(w Writer, channels int, logger logger.Logger) (media.PCM16Writer, er w: w, enc: enc, buf: make([]byte, w.SampleRate()/rtp.DefFramesPerSec*channels), + useDtx: useDtx, logger: logger, }, nil } @@ -162,6 +163,7 @@ type encoder struct { w Writer enc *opus.Encoder buf Sample + useDtx bool logger logger.Logger } @@ -178,6 +180,13 @@ func (e *encoder) WriteSample(in media.PCM16Sample) error { if err != nil { return err } + + // opus_encode() returns the number of bytes actually written to the packet. + // The return value can be negative, which indicates that an error has occurred. + // If the return value is 1 byte, then the packet does not need to be transmitted (DTX). + if n == 1 && e.useDtx { + return nil + } return e.w.WriteSample(e.buf[:n]) } diff --git a/pcm.go b/pcm.go index 1e22246..52b2ea9 100644 --- a/pcm.go +++ b/pcm.go @@ -182,6 +182,7 @@ type sampleWriter[T ~[]byte] struct { w MediaSampleWriter sampleRate int sampleDur time.Duration + lastWrite time.Time } func (w *sampleWriter[T]) String() string { @@ -199,7 +200,18 @@ func (w *sampleWriter[T]) Close() error { func (w *sampleWriter[T]) WriteSample(in T) error { data := make([]byte, len(in)) copy(data, in) - return w.w.WriteSample(media.Sample{Data: data, Duration: w.sampleDur}) + + var droppedPackets uint16 + if !w.lastWrite.IsZero() { + timeSinceLastWrite := time.Since(w.lastWrite) + tolerance := w.sampleDur / 10 + if timeSinceLastWrite > (w.sampleDur + tolerance) { + droppedPackets = uint16((timeSinceLastWrite - (w.sampleDur - tolerance)) / w.sampleDur) + } + } + + w.lastWrite = time.Now() + return w.w.WriteSample(media.Sample{Data: data, Duration: w.sampleDur, PrevDroppedPackets: droppedPackets}) } // MonoToStereo converts mono PCM from src to stereo PCM in dst.