diff --git a/README.md b/README.md index 883a3b81..2faf499b 100644 --- a/README.md +++ b/README.md @@ -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** diff --git a/go.mod b/go.mod index 4e23d192..1a3232bf 100644 --- a/go.mod +++ b/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 diff --git a/go.sum b/go.sum index 000901e1..72220f57 100644 --- a/go.sum +++ b/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= diff --git a/internal/core/hls_manager_test.go b/internal/core/hls_manager_test.go index e6add10c..e5b66295 100644 --- a/internal/core/hls_manager_test.go +++ b/internal/core/hls_manager_test.go @@ -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 diff --git a/internal/core/hls_muxer.go b/internal/core/hls_muxer.go index 7792943c..e489f54f 100644 --- a/internal/core/hls_muxer.go +++ b/internal/core/hls_muxer.go @@ -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) diff --git a/internal/core/hls_source.go b/internal/core/hls_source.go index 9bb53c90..23dd6925 100644 --- a/internal/core/hls_source.go +++ b/internal/core/hls_source.go @@ -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,