From c4548199d904f2ec8f93b83b15c24d1f30643971 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Fri, 25 Aug 2023 16:58:46 +0200 Subject: [PATCH] support publishing VP9 tracks with RTMP --- README.md | 7 +++---- go.mod | 2 +- go.sum | 4 ++-- internal/core/rtmp_conn.go | 17 +++++++++++++++++ internal/core/rtmp_source.go | 14 ++++++++------ internal/rtmp/reader.go | 21 ++++++++++++++++++++- 6 files changed, 51 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7fd79ad3c88..c494da50ba6 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ Live streams can be published to the server with: |[WebRTC servers](#webrtc-servers)|WHEP|AV1, VP9, VP8, H264|Opus, G722, G711| |[RTSP clients](#rtsp-clients)|UDP, TCP, RTSPS|AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG and any RTP-compatible codec|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), G726, G722, G711, LPCM and any RTP-compatible codec| |[RTSP cameras and servers](#rtsp-cameras-and-servers)|UDP, UDP-Multicast, TCP, RTSPS|AV1, VP9, VP8, H265, H264, MPEG-4 Video (H263, Xvid), MPEG-1/2 Video, M-JPEG and any RTP-compatible codec|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3), G726, G722, G711, LPCM and any RTP-compatible codec| -|[RTMP clients](#rtmp-clients)|RTMP, RTMPS, Enhanced RTMP|AV1, H265, H264|MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3)| +|[RTMP clients](#rtmp-clients)|RTMP, RTMPS, Enhanced RTMP|AV1, VP9, H265, H264|MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3)| |[RTMP cameras and servers](#rtmp-cameras-and-servers)|RTMP, RTMPS, Enhanced RTMP|H264|MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3)| |[HLS cameras and servers](#hls-cameras-and-servers)|Low-Latency HLS, MP4-based HLS, legacy HLS|AV1, VP9, H265, H264|Opus, MPEG-4 Audio (AAC)| |[UDP/MPEG-TS](#udpmpeg-ts)|Unicast, broadcast, multicast|H265, H264|Opus, MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3)| @@ -46,7 +46,6 @@ And can be read from the server with: * Publish live streams to the server * Read live streams from the server -* Proxy streams from other servers or cameras, always or on-demand * Streams are automatically converted from a protocol to another. For instance, it's possible to publish a stream with RTSP and read it with HLS * Serve multiple streams at once in separate paths * Authenticate users; use internal or external authentication @@ -102,7 +101,7 @@ _rtsp-simple-server_ has been rebranded as _MediaMTX_. The reason is pretty obvi * [RTSP](#rtsp) * [RTMP](#rtmp) * [HLS](#hls) -* [Features](#features) +* [Other features](#other-features) * [Configuration](#configuration) * [Authentication](#authentication) * [Encrypt the configuration](#encrypt-the-configuration) @@ -977,7 +976,7 @@ To decrease the latency, you can: ffmpeg -i rtsp://original-stream -pix_fmt yuv420p -c:v libx264 -preset ultrafast -b:v 600k -max_muxing_queue_size 1024 -g 30 -f rtsp rtsp://localhost:$RTSP_PORT/compressed ``` -## Features +## Other features ### Configuration diff --git a/go.mod b/go.mod index 4b1d7e75380..b67e92ec1f6 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.20 require ( code.cloudfoundry.org/bytefmt v0.0.0 - github.com/abema/go-mp4 v0.12.0 + github.com/abema/go-mp4 v0.13.0 github.com/alecthomas/kong v0.8.0 github.com/bluenviron/gohlslib v1.0.0 github.com/bluenviron/gortsplib/v3 v3.10.0 diff --git a/go.sum b/go.sum index 751a4c54bf3..7ce340ed23b 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -github.com/abema/go-mp4 v0.12.0 h1:XI9PPt1BpjB3wFl18oFiX6C99uesx7F/X13Z+ga8bYY= -github.com/abema/go-mp4 v0.12.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws= +github.com/abema/go-mp4 v0.13.0 h1:gjEZLt7g0ePpYA5sUDrI2r8X+WuI8o+USkgG5wMgmkI= +github.com/abema/go-mp4 v0.13.0/go.mod h1:vPl9t5ZK7K0x68jh12/+ECWBCXoWuIDtNgPtU2f04ws= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= github.com/alecthomas/kong v0.8.0 h1:ryDCzutfIqJPnNn0omnrgHLbAggDQM2VWHikE1xqK7s= github.com/alecthomas/kong v0.8.0/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= diff --git a/internal/core/rtmp_conn.go b/internal/core/rtmp_conn.go index 5bf6bb7c5b3..65fb6e42ba8 100644 --- a/internal/core/rtmp_conn.go +++ b/internal/core/rtmp_conn.go @@ -633,6 +633,17 @@ func (c *rtmpConn) runPublish(conn *rtmp.Conn, u *url.URL) error { }) }) + case *formats.VP9: + r.OnDataVP9(func(pts time.Duration, frame []byte) { + stream.WriteUnit(videoMedia, videoFormat, &formatprocessor.UnitVP9{ + BaseUnit: formatprocessor.BaseUnit{ + NTP: time.Now(), + }, + PTS: pts, + Frame: frame, + }) + }) + case *formats.H265: r.OnDataH265(func(pts time.Duration, au [][]byte) { stream.WriteUnit(videoMedia, videoFormat, &formatprocessor.UnitH265{ @@ -654,6 +665,9 @@ func (c *rtmpConn) runPublish(conn *rtmp.Conn, u *url.URL) error { AU: au, }) }) + + default: + return fmt.Errorf("unsupported video codec: %T", videoFormat) } } @@ -686,6 +700,9 @@ func (c *rtmpConn) runPublish(conn *rtmp.Conn, u *url.URL) error { Frames: [][]byte{frame}, }) }) + + default: + return fmt.Errorf("unsupported audio codec: %T", audioFormat) } } diff --git a/internal/core/rtmp_source.go b/internal/core/rtmp_source.go index fd997db8c85..66cd22a7e0b 100644 --- a/internal/core/rtmp_source.go +++ b/internal/core/rtmp_source.go @@ -113,11 +113,6 @@ func (s *rtmpSource) runReader(u *url.URL, nconn net.Conn) error { videoFormat, audioFormat := mc.Tracks() - switch videoFormat.(type) { - case *formats.H265, *formats.AV1: - return fmt.Errorf("proxying H265 or AV1 tracks with RTMP is not supported") - } - var medias media.Medias var stream *stream.Stream @@ -128,7 +123,8 @@ func (s *rtmpSource) runReader(u *url.URL, nconn net.Conn) error { } medias = append(medias, videoMedia) - if _, ok := videoFormat.(*formats.H264); ok { + switch videoFormat.(type) { + case *formats.H264: mc.OnDataH264(func(pts time.Duration, au [][]byte) { stream.WriteUnit(videoMedia, videoFormat, &formatprocessor.UnitH264{ BaseUnit: formatprocessor.BaseUnit{ @@ -138,6 +134,9 @@ func (s *rtmpSource) runReader(u *url.URL, nconn net.Conn) error { AU: au, }) }) + + default: + return fmt.Errorf("unsupported video codec: %T", videoFormat) } } @@ -170,6 +169,9 @@ func (s *rtmpSource) runReader(u *url.URL, nconn net.Conn) error { Frames: [][]byte{frame}, }) }) + + default: + return fmt.Errorf("unsupported audio codec: %T", audioFormat) } } diff --git a/internal/rtmp/reader.go b/internal/rtmp/reader.go index b73eacbe661..5f862b71b29 100644 --- a/internal/rtmp/reader.go +++ b/internal/rtmp/reader.go @@ -21,6 +21,9 @@ import ( // OnDataAV1Func is the prototype of the callback passed to OnDataAV1(). type OnDataAV1Func func(pts time.Duration, tu [][]byte) +// OnDataVP9Func is the prototype of the callback passed to OnDataVP9(). +type OnDataVP9Func func(pts time.Duration, frame []byte) + // OnDataH26xFunc is the prototype of the callback passed to OnDataH26x(). type OnDataH26xFunc func(pts time.Duration, au [][]byte) @@ -252,7 +255,13 @@ func tracksFromMetadata(conn *Conn, payload []interface{}) (formats.Format, form videoTrack = &formats.AV1{} default: // VP9 - return nil, nil, fmt.Errorf("VP9 is not supported yet") + var vpcc mp4.VpcC + _, err := mp4.Unmarshal(bytes.NewReader(tmsg.Config), uint64(len(tmsg.Config)), &vpcc, mp4.Context{}) + if err != nil { + return nil, nil, fmt.Errorf("invalid VP9 configuration: %v", err) + } + + videoTrack = &formats.VP9{} } } @@ -449,6 +458,16 @@ func (r *Reader) OnDataAV1(cb OnDataAV1Func) { } } +// OnDataVP9 sets a callback that is called when VP9 data is received. +func (r *Reader) OnDataVP9(cb OnDataVP9Func) { + r.onDataVideo = func(msg message.Message) error { + if msg, ok := msg.(*message.ExtendedCodedFrames); ok { + cb(msg.DTS, msg.Payload) + } + return nil + } +} + // OnDataH265 sets a callback that is called when H265 data is received. func (r *Reader) OnDataH265(cb OnDataH26xFunc) { r.onDataVideo = func(msg message.Message) error {