From f837ba6a835e751b1c2b7ddb4e5e641c78cfc410 Mon Sep 17 00:00:00 2001 From: aler9 <46489434+aler9@users.noreply.github.com> Date: Fri, 6 Jan 2023 15:39:06 +0100 Subject: [PATCH] hls source: support proxying H265 and Opus tracks --- README.md | 2 +- internal/core/hls_source.go | 24 ++++++++++++++++++++ internal/hls/codecparameters.go | 4 +++- internal/hls/fmp4/boxes_h265.go | 12 ++++++++++ internal/hls/fmp4/{opus.go => boxes_opus.go} | 0 internal/hls/fmp4/init.go | 3 +-- 6 files changed, 41 insertions(+), 4 deletions(-) create mode 100644 internal/hls/fmp4/boxes_h265.go rename internal/hls/fmp4/{opus.go => boxes_opus.go} (100%) diff --git a/README.md b/README.md index 4723c860..7e3d8bdb 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Live streams can be published to the server with: |RTSP servers and cameras|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 clients (OBS Studio)|RTMP, RTMPS|H264, H265, MPEG4 Audio (AAC)| |RTMP servers and cameras|RTMP, RTMPS|H264, MPEG4 Audio (AAC)| -|HLS servers and cameras|Low-Latency HLS, MP4-based HLS, legacy HLS|H264, MPEG4 Audio (AAC)| +|HLS servers and cameras|Low-Latency HLS, MP4-based HLS, legacy HLS|H264, H265, MPEG4 Audio (AAC), Opus| |Raspberry Pi Cameras||H264| And can be read from the server with: diff --git a/internal/core/hls_source.go b/internal/core/hls_source.go index 95fab82d..75ba5436 100644 --- a/internal/core/hls_source.go +++ b/internal/core/hls_source.go @@ -83,6 +83,18 @@ func (s *hlsSource) run(ctx context.Context) error { } }) + case *format.H265: + c.OnData(track, func(pts time.Duration, dat interface{}) { + err := stream.writeData(medi, ctrack, &formatprocessor.DataH265{ + PTS: pts, + AU: dat.([][]byte), + NTP: time.Now(), + }) + if err != nil { + s.Log(logger.Warn, "%v", err) + } + }) + case *format.MPEG4Audio: c.OnData(track, func(pts time.Duration, dat interface{}) { err := stream.writeData(medi, ctrack, &formatprocessor.DataMPEG4Audio{ @@ -94,6 +106,18 @@ func (s *hlsSource) run(ctx context.Context) error { s.Log(logger.Warn, "%v", err) } }) + + case *format.Opus: + c.OnData(track, func(pts time.Duration, dat interface{}) { + err := stream.writeData(medi, ctrack, &formatprocessor.DataOpus{ + PTS: pts, + Frame: dat.([]byte), + NTP: time.Now(), + }) + if err != nil { + s.Log(logger.Warn, "%v", err) + } + }) } } diff --git a/internal/hls/codecparameters.go b/internal/hls/codecparameters.go index b86ea559..4e4053de 100644 --- a/internal/hls/codecparameters.go +++ b/internal/hls/codecparameters.go @@ -39,7 +39,9 @@ func codecParametersGenerate(track format.Format) string { func codecParametersAreSupported(codecs string) bool { for _, codec := range strings.Split(codecs, ",") { if !strings.HasPrefix(codec, "avc1.") && - !strings.HasPrefix(codec, "mp4a.") { + !strings.HasPrefix(codec, "hvc1.") && + !strings.HasPrefix(codec, "mp4a.") && + codec != "opus" { return false } } diff --git a/internal/hls/fmp4/boxes_h265.go b/internal/hls/fmp4/boxes_h265.go new file mode 100644 index 00000000..a339aaac --- /dev/null +++ b/internal/hls/fmp4/boxes_h265.go @@ -0,0 +1,12 @@ +//nolint:gochecknoinits,revive,gocritic +package fmp4 + +import ( + gomp4 "github.com/abema/go-mp4" +) + +func BoxTypeHvc1() gomp4.BoxType { return gomp4.StrToBoxType("hvc1") } + +func init() { + gomp4.AddAnyTypeBoxDef(&gomp4.VisualSampleEntry{}, BoxTypeHvc1()) +} diff --git a/internal/hls/fmp4/opus.go b/internal/hls/fmp4/boxes_opus.go similarity index 100% rename from internal/hls/fmp4/opus.go rename to internal/hls/fmp4/boxes_opus.go diff --git a/internal/hls/fmp4/init.go b/internal/hls/fmp4/init.go index 9ccf3e0e..c321cabe 100644 --- a/internal/hls/fmp4/init.go +++ b/internal/hls/fmp4/init.go @@ -76,7 +76,6 @@ func (i *Init) Unmarshal(byts []byte) error { if state != waitingCodec { return nil, fmt.Errorf("unexpected box 'avc1'") } - state = waitingAvcC case "avcC": @@ -116,7 +115,7 @@ func (i *Init) Unmarshal(byts []byte) error { } state = waitingTrak - case "hev1": + case "hev1", "hvc1": if state != waitingCodec { return nil, fmt.Errorf("unexpected box 'hev1'") }