support publishing VP9 tracks with RTMP (#2247)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+20
-1
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user