support reading and proxying VP9 tracks with HLS (#2162)
This commit is contained in:
parent
9051ccc219
commit
40b4fa1e8e
|
@ -28,7 +28,7 @@ Live streams can be published to the server with:
|
|||
|[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 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, H265, H264|Opus, MPEG-4 Audio (AAC)|
|
||||
|[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)|
|
||||
|[Raspberry Pi Cameras](#raspberry-pi-cameras)||H264||
|
||||
|
||||
|
@ -40,7 +40,7 @@ And can be read from the server with:
|
|||
|[WebRTC](#webrtc)|Browser-based, WHEP|AV1, VP9, VP8, H264|Opus, G722, G711|
|
||||
|[RTSP](#rtsp)|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](#rtmp)|RTMP, RTMPS, Enhanced RTMP|H264|MPEG-4 Audio (AAC), MPEG-1/2 Audio (MP3)|
|
||||
|[HLS](#hls)|Low-Latency HLS, MP4-based HLS, legacy HLS|AV1, H265, H264|Opus, MPEG-4 Audio (AAC)|
|
||||
|[HLS](#hls)|Low-Latency HLS, MP4-based HLS, legacy HLS|AV1, VP9, H265, H264|Opus, MPEG-4 Audio (AAC)|
|
||||
|
||||
**Features**
|
||||
|
||||
|
|
6
go.mod
6
go.mod
|
@ -6,9 +6,9 @@ require (
|
|||
code.cloudfoundry.org/bytefmt v0.0.0
|
||||
github.com/abema/go-mp4 v0.11.0
|
||||
github.com/alecthomas/kong v0.8.0
|
||||
github.com/bluenviron/gohlslib v0.3.1-0.20230806000705-4ca609772363
|
||||
github.com/bluenviron/gortsplib/v3 v3.9.1-0.20230806001215-926a452fb745
|
||||
github.com/bluenviron/mediacommon v0.7.1-0.20230805234008-34d20294a26b
|
||||
github.com/bluenviron/gohlslib v0.3.1-0.20230806182538-54b835e0ef2c
|
||||
github.com/bluenviron/gortsplib/v3 v3.9.1-0.20230806185830-e6e9e8490907
|
||||
github.com/bluenviron/mediacommon v0.7.1-0.20230806185229-f060a1e5295b
|
||||
github.com/datarhei/gosrt v0.5.3
|
||||
github.com/fsnotify/fsnotify v1.6.0
|
||||
github.com/gin-gonic/gin v1.9.1
|
||||
|
|
12
go.sum
12
go.sum
|
@ -14,12 +14,12 @@ github.com/asticode/go-astits v1.12.0 h1:BiefTgVEyPgEB8nT6J+Sys/uxE4H/a04SW/aedp
|
|||
github.com/asticode/go-astits v1.12.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
|
||||
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYhJeJ2aZxADI2tGADS15AzIF8MQ8XAhT4=
|
||||
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
|
||||
github.com/bluenviron/gohlslib v0.3.1-0.20230806000705-4ca609772363 h1:ysN/BZSY9Q7Ditvro4zvSLLYyxTPyiiVl0ALeY/rHHQ=
|
||||
github.com/bluenviron/gohlslib v0.3.1-0.20230806000705-4ca609772363/go.mod h1:2aXz4lSl2pToXk9zYmWhdhR02OiRRZCQiFdrQcIWLBc=
|
||||
github.com/bluenviron/gortsplib/v3 v3.9.1-0.20230806001215-926a452fb745 h1:LR8F5ng75c9KxWLrPWj8bskXCR7AJXv8gm4duKfzUjc=
|
||||
github.com/bluenviron/gortsplib/v3 v3.9.1-0.20230806001215-926a452fb745/go.mod h1:7owCGeF8gQfziPCWL9UbL+Yqogl13gKJeRI8jhnpadg=
|
||||
github.com/bluenviron/mediacommon v0.7.1-0.20230805234008-34d20294a26b h1:/csYQQDmZyXw0CVATJSPBkwT3JYap418W7LLX7mscxw=
|
||||
github.com/bluenviron/mediacommon v0.7.1-0.20230805234008-34d20294a26b/go.mod h1:LR4w8cpvzo2ZcmBwXcentvBj7ZlyF9g9xP4dDbt8uJw=
|
||||
github.com/bluenviron/gohlslib v0.3.1-0.20230806182538-54b835e0ef2c h1:wztDQy5iTijIZz4v+mkbJkTIHoQ/MHtdMSPirTQy5uQ=
|
||||
github.com/bluenviron/gohlslib v0.3.1-0.20230806182538-54b835e0ef2c/go.mod h1:SoCWetcOsLi70kcUBBrckYFfmLPJuqmXVGP10OlEc70=
|
||||
github.com/bluenviron/gortsplib/v3 v3.9.1-0.20230806185830-e6e9e8490907 h1:EPRg+yYqp7eZr9ROvjSvHJf7TIcu31spCGKbjHzpilM=
|
||||
github.com/bluenviron/gortsplib/v3 v3.9.1-0.20230806185830-e6e9e8490907/go.mod h1:tK2JtJRkKUIhmZEyRWCd8rtiqbirO4eMXt7fN5Yont4=
|
||||
github.com/bluenviron/mediacommon v0.7.1-0.20230806185229-f060a1e5295b h1:53WQf0Kam0/Rj4bnTWhpo6n6cCrRE84tjf9xrwq8mWo=
|
||||
github.com/bluenviron/mediacommon v0.7.1-0.20230806185229-f060a1e5295b/go.mod h1:LR4w8cpvzo2ZcmBwXcentvBj7ZlyF9g9xP4dDbt8uJw=
|
||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
|
||||
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
|
||||
|
|
|
@ -176,7 +176,7 @@ func TestHLSRead(t *testing.T) {
|
|||
"#EXT-X-SERVER-CONTROL:CAN-BLOCK-RELOAD=YES,PART-HOLD-BACK=2\\.50000,CAN-SKIP-UNTIL=6\\.00000\n"+
|
||||
"#EXT-X-PART-INF:PART-TARGET=1\\.00000\n"+
|
||||
"#EXT-X-MEDIA-SEQUENCE:1\n"+
|
||||
"#EXT-X-MAP:URI=\"init.mp4\"\n"+
|
||||
"#EXT-X-MAP:URI=\".*?_init.mp4\"\n"+
|
||||
"#EXT-X-GAP\n"+
|
||||
"#EXTINF:1\\.00000,\n"+
|
||||
"gap.mp4\n"+
|
||||
|
@ -196,10 +196,10 @@ func TestHLSRead(t *testing.T) {
|
|||
"#EXTINF:1\\.00000,\n"+
|
||||
"gap.mp4\n"+
|
||||
"#EXT-X-PROGRAM-DATE-TIME:.+?Z\n"+
|
||||
"#EXT-X-PART:DURATION=1\\.00000,URI=\"part0.mp4\",INDEPENDENT=YES\n"+
|
||||
"#EXT-X-PART:DURATION=1\\.00000,URI=\".*?_part0.mp4\",INDEPENDENT=YES\n"+
|
||||
"#EXTINF:1\\.00000,\n"+
|
||||
"seg7.mp4\n"+
|
||||
"#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\"part1.mp4\"\n", string(cnt))
|
||||
".*?_seg7.mp4\n"+
|
||||
"#EXT-X-PRELOAD-HINT:TYPE=PART,URI=\".*?_part1.mp4\"\n", string(cnt))
|
||||
|
||||
/*trak := <-c.track
|
||||
|
||||
|
|
|
@ -371,6 +371,41 @@ func (m *hlsMuxer) createVideoTrack(stream *stream.Stream) (*media.Media, *gohls
|
|||
}
|
||||
}
|
||||
|
||||
var videoFormatVP9 *formats.VP9
|
||||
videoMedia = stream.Medias().FindFormat(&videoFormatVP9)
|
||||
|
||||
if videoFormatVP9 != nil {
|
||||
startPTSFilled := false
|
||||
var startPTS time.Duration
|
||||
|
||||
stream.AddReader(m, videoMedia, videoFormatVP9, func(unit formatprocessor.Unit) {
|
||||
m.ringBuffer.Push(func() error {
|
||||
tunit := unit.(*formatprocessor.UnitVP9)
|
||||
|
||||
if tunit.Frame == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !startPTSFilled {
|
||||
startPTSFilled = true
|
||||
startPTS = tunit.PTS
|
||||
}
|
||||
|
||||
pts := tunit.PTS - startPTS
|
||||
err := m.muxer.WriteVP9(tunit.NTP, pts, tunit.Frame)
|
||||
if err != nil {
|
||||
return fmt.Errorf("muxer error: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
return videoMedia, &gohlslib.Track{
|
||||
Codec: &codecs.VP9{},
|
||||
}
|
||||
}
|
||||
|
||||
var videoFormatH265 *formats.H265
|
||||
videoMedia = stream.Medias().FindFormat(&videoFormatH265)
|
||||
|
||||
|
|
|
@ -92,6 +92,22 @@ func (s *hlsSource) run(ctx context.Context, cnf *conf.PathConf, reloadConf chan
|
|||
})
|
||||
})
|
||||
|
||||
case *codecs.VP9:
|
||||
medi = &media.Media{
|
||||
Type: media.TypeVideo,
|
||||
Formats: []formats.Format{&formats.VP9{}},
|
||||
}
|
||||
|
||||
c.OnDataVP9(track, func(pts time.Duration, frame []byte) {
|
||||
stream.WriteUnit(medi, medi.Formats[0], &formatprocessor.UnitVP9{
|
||||
BaseUnit: formatprocessor.BaseUnit{
|
||||
NTP: time.Now(),
|
||||
},
|
||||
PTS: pts,
|
||||
Frame: frame,
|
||||
})
|
||||
})
|
||||
|
||||
case *codecs.H264:
|
||||
medi = &media.Media{
|
||||
Type: media.TypeVideo,
|
||||
|
|
Loading…
Reference in New Issue