diff --git a/README.md b/README.md index e3804c55..4723c860 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ And can be read from the server with: |--------|--------|------| |RTSP|UDP, UDP-Multicast, TCP, RTSPS|H264, H265, VP8, VP9, AV1, MPEG2, M-JPEG, MP3, MPEG4 Audio (AAC), Opus, G711, G722, LPCM and any RTP-compatible codec| |RTMP|RTMP, RTMPS|H264, MPEG4 Audio (AAC)| -|HLS|Low-Latency HLS, MP4-based HLS, legacy HLS|H264, H265, MPEG4 Audio (AAC)| +|HLS|Low-Latency HLS, MP4-based HLS, legacy HLS|H264, H265, MPEG4 Audio (AAC), Opus| |WebRTC||H264, VP8, VP9, Opus, G711, G722| Features: @@ -908,7 +908,7 @@ where `mystream` is the name of a stream that is being published. Although the server can produce HLS with a variety of video and audio codecs (that are listed at the beginning of the README), not all browsers can read all codecs. You can check what codecs your browser can read by visiting this page: -https://jsfiddle.net/7nwxmLto +https://jsfiddle.net/4msrhudv If you want to increase the compatibility of the stream in order to support most browsers, you have to re-encode it by using the H264 and AAC codecs, for instance by using _FFmpeg_: @@ -1067,9 +1067,9 @@ Related projects Standards -* RTSP 1.0 https://datatracker.ietf.org/doc/html/rfc2326 -* RTSP 2.0 https://datatracker.ietf.org/doc/html/rfc7826 +* RTSP/RTP standards https://github.com/aler9/gortsplib#links * HTTP 1.1 https://datatracker.ietf.org/doc/html/rfc2616 * HLS https://datatracker.ietf.org/doc/html/rfc8216 * HLS v2 https://datatracker.ietf.org/doc/html/draft-pantos-hls-rfc8216bis +* Opus in MP4/ISOBMFF https://opus-codec.org/docs/opus_in_isobmff.html * Golang project layout https://github.com/golang-standards/project-layout diff --git a/go.mod b/go.mod index 3ad7b178..8d94754c 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.18 require ( code.cloudfoundry.org/bytefmt v0.0.0 github.com/abema/go-mp4 v0.0.0 - github.com/aler9/gortsplib/v2 v2.0.0-20221229123705-ce25207cb823 + github.com/aler9/gortsplib/v2 v2.0.0-20230103153002-0ce435414414 github.com/asticode/go-astits v1.10.1-0.20220319093903-4abe66a9b757 github.com/fsnotify/fsnotify v1.4.9 github.com/gin-gonic/gin v1.8.1 diff --git a/go.sum b/go.sum index 97b50798..97e4a9c1 100644 --- a/go.sum +++ b/go.sum @@ -4,8 +4,8 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2c github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/aler9/go-mp4 v0.0.0-20221229200349-f3d01e787968 h1:wU8pLx4dc8bLB+JuVPWuGp+BoMkOabj98a0RmO3gqvw= github.com/aler9/go-mp4 v0.0.0-20221229200349-f3d01e787968/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws= -github.com/aler9/gortsplib/v2 v2.0.0-20221229123705-ce25207cb823 h1:EFq9LqgA15drNgXj3hNlmAouxjMYb9jyyBq6hmjDO8U= -github.com/aler9/gortsplib/v2 v2.0.0-20221229123705-ce25207cb823/go.mod h1:lMdAxc6daduSzVwh75yQkvH9UHCYHpng/vJ8uXKFzdA= +github.com/aler9/gortsplib/v2 v2.0.0-20230103153002-0ce435414414 h1:pVyJ7Uuk5kdU/RhCepxJQJEC9hsrFgxIIw1mIHn02Zs= +github.com/aler9/gortsplib/v2 v2.0.0-20230103153002-0ce435414414/go.mod h1:lMdAxc6daduSzVwh75yQkvH9UHCYHpng/vJ8uXKFzdA= github.com/aler9/writerseeker v0.0.0-20220601075008-6f0e685b9c82 h1:9WgSzBLo3a9ToSVV7sRTBYZ1GGOZUpq4+5H3SN0UZq4= github.com/aler9/writerseeker v0.0.0-20220601075008-6f0e685b9c82/go.mod h1:qsMrZCbeBf/mCLOeF16KDkPu4gktn/pOWyaq1aYQE7U= github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8= diff --git a/internal/core/formatprocessor.go b/internal/core/formatprocessor.go index ca714694..03beb70f 100644 --- a/internal/core/formatprocessor.go +++ b/internal/core/formatprocessor.go @@ -25,6 +25,9 @@ func newFormatProcessor(forma format.Format, generateRTPPackets bool) (formatPro case *format.MPEG4Audio: return newFormatProcessorMPEG4Audio(forma, generateRTPPackets) + case *format.Opus: + return newFormatProcessorOpus(forma, generateRTPPackets) + default: return newFormatProcessorGeneric(forma, generateRTPPackets) } diff --git a/internal/core/formatprocessor_opus.go b/internal/core/formatprocessor_opus.go new file mode 100644 index 00000000..c96c1801 --- /dev/null +++ b/internal/core/formatprocessor_opus.go @@ -0,0 +1,89 @@ +package core + +import ( + "fmt" + "time" + + "github.com/aler9/gortsplib/v2/pkg/format" + "github.com/aler9/gortsplib/v2/pkg/formatdecenc/rtpsimpleaudio" + "github.com/pion/rtp" +) + +type dataOpus struct { + rtpPackets []*rtp.Packet + ntp time.Time + pts time.Duration + frame []byte +} + +func (d *dataOpus) getRTPPackets() []*rtp.Packet { + return d.rtpPackets +} + +func (d *dataOpus) getNTP() time.Time { + return d.ntp +} + +type formatProcessorOpus struct { + format *format.Opus + encoder *rtpsimpleaudio.Encoder + decoder *rtpsimpleaudio.Decoder +} + +func newFormatProcessorOpus( + forma *format.Opus, + allocateEncoder bool, +) (*formatProcessorOpus, error) { + t := &formatProcessorOpus{ + format: forma, + } + + if allocateEncoder { + t.encoder = forma.CreateEncoder() + } + + return t, nil +} + +func (t *formatProcessorOpus) process(dat data, hasNonRTSPReaders bool) error { //nolint:dupl + tdata := dat.(*dataOpus) + + if tdata.rtpPackets != nil { + pkt := tdata.rtpPackets[0] + + // remove padding + pkt.Header.Padding = false + pkt.PaddingSize = 0 + + if pkt.MarshalSize() > maxPacketSize { + return fmt.Errorf("payload size (%d) is greater than maximum allowed (%d)", + pkt.MarshalSize(), maxPacketSize) + } + + // decode from RTP + if hasNonRTSPReaders { + if t.decoder == nil { + t.decoder = t.format.CreateDecoder() + } + + frame, pts, err := t.decoder.Decode(pkt) + if err != nil { + return err + } + + tdata.frame = frame + tdata.pts = pts + } + + // route packet as is + return nil + } + + pkt, err := t.encoder.Encode(tdata.frame, tdata.pts) + if err != nil { + return err + } + + tdata.rtpPackets = []*rtp.Packet{pkt} + return nil +} diff --git a/internal/core/formatprocessor_vp8.go b/internal/core/formatprocessor_vp8.go index cd771a27..83bf07ac 100644 --- a/internal/core/formatprocessor_vp8.go +++ b/internal/core/formatprocessor_vp8.go @@ -1,4 +1,4 @@ -package core +package core //nolint:dupl import ( "fmt" diff --git a/internal/core/formatprocessor_vp9.go b/internal/core/formatprocessor_vp9.go index 85e7ea84..65ce704d 100644 --- a/internal/core/formatprocessor_vp9.go +++ b/internal/core/formatprocessor_vp9.go @@ -1,4 +1,4 @@ -package core +package core //nolint:dupl import ( "fmt" diff --git a/internal/core/hls_index.html b/internal/core/hls_index.html index 3c4be2c9..544a104a 100644 --- a/internal/core/hls_index.html +++ b/internal/core/hls_index.html @@ -20,7 +20,7 @@ html, body { - +