feat: h265

This commit is contained in:
langhuihui
2024-07-11 10:18:21 +08:00
parent de1111f1f5
commit 13e1029b1d
44 changed files with 3831 additions and 1077 deletions

6
api.go
View File

@@ -80,7 +80,7 @@ func (s *Server) api_Stream_AnnexB_(rw http.ResponseWriter, r *http.Request) {
var annexb pkg.AnnexB var annexb pkg.AnnexB
var t pkg.AVTrack var t pkg.AVTrack
err = annexb.ConvertCtx(publisher.VideoTrack.ICodecCtx, &t) t.ICodecCtx, t.SequenceFrame, err = annexb.ConvertCtx(publisher.VideoTrack.ICodecCtx)
if t.ICodecCtx == nil { if t.ICodecCtx == nil {
http.Error(rw, "unsupported codec", http.StatusInternalServerError) http.Error(rw, "unsupported codec", http.StatusInternalServerError)
return return
@@ -123,8 +123,8 @@ func (s *Server) getStreamInfo(pub *Publisher) (res *pb.StreamInfoResponse, err
Delta: pub.VideoTrack.Delta.String(), Delta: pub.VideoTrack.Delta.String(),
Gop: uint32(pub.GOP), Gop: uint32(pub.GOP),
} }
res.VideoTrack.Width = uint32(t.ICodecCtx.(pkg.IVideoCodecCtx).GetWidth()) res.VideoTrack.Width = uint32(t.ICodecCtx.(pkg.IVideoCodecCtx).Width())
res.VideoTrack.Height = uint32(t.ICodecCtx.(pkg.IVideoCodecCtx).GetHeight()) res.VideoTrack.Height = uint32(t.ICodecCtx.(pkg.IVideoCodecCtx).Height())
} }
} }
return return

View File

@@ -11,6 +11,7 @@ import (
_ "m7s.live/m7s/v5/plugin/logrotate" _ "m7s.live/m7s/v5/plugin/logrotate"
_ "m7s.live/m7s/v5/plugin/rtmp" _ "m7s.live/m7s/v5/plugin/rtmp"
_ "m7s.live/m7s/v5/plugin/rtsp" _ "m7s.live/m7s/v5/plugin/rtsp"
_ "m7s.live/m7s/v5/plugin/stress"
_ "m7s.live/m7s/v5/plugin/webrtc" _ "m7s.live/m7s/v5/plugin/webrtc"
) )

126
example/xdp/main.go Normal file
View File

@@ -0,0 +1,126 @@
// Copyright 2019 Asavie Technologies Ltd. All rights reserved.
//
// Use of this source code is governed by a BSD-style license
// that can be found in the LICENSE file in the root of the source
// tree.
/*
dumpframes demostrates how to receive frames from a network link using
github.com/asavie/xdp package, it sets up an XDP socket attached to a
particular network link and dumps all frames it receives to standard output.
*/
package main
import (
"encoding/hex"
"flag"
"fmt"
"log"
"net"
"github.com/asavie/xdp"
"github.com/asavie/xdp/examples/dumpframes/ebpf"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
)
func main() {
var linkName string
var queueID int
var protocol int64
log.SetFlags(log.Ldate | log.Ltime | log.Lmicroseconds)
flag.StringVar(&linkName, "linkname", "enp3s0", "The network link on which rebroadcast should run on.")
flag.IntVar(&queueID, "queueid", 0, "The ID of the Rx queue to which to attach to on the network link.")
flag.Int64Var(&protocol, "ip-proto", 0, "If greater than 0 and less than or equal to 255, limit xdp bpf_redirect_map to packets with the specified IP protocol number.")
flag.Parse()
interfaces, err := net.Interfaces()
if err != nil {
fmt.Printf("error: failed to fetch the list of network interfaces on the system: %v\n", err)
return
}
Ifindex := -1
for _, iface := range interfaces {
if iface.Name == linkName {
Ifindex = iface.Index
break
}
}
if Ifindex == -1 {
fmt.Printf("error: couldn't find a suitable network interface to attach to\n")
return
}
var program *xdp.Program
// Create a new XDP eBPF program and attach it to our chosen network link.
if protocol == 0 {
program, err = xdp.NewProgram(queueID + 1)
} else {
program, err = ebpf.NewIPProtoProgram(uint32(protocol), nil)
}
if err != nil {
fmt.Printf("error: failed to create xdp program: %v\n", err)
return
}
defer program.Close()
if err := program.Attach(Ifindex); err != nil {
fmt.Printf("error: failed to attach xdp program to interface: %v\n", err)
return
}
defer program.Detach(Ifindex)
// Create and initialize an XDP socket attached to our chosen network
// link.
xsk, err := xdp.NewSocket(Ifindex, queueID, nil)
if err != nil {
fmt.Printf("error: failed to create an XDP socket: %v\n", err)
return
}
// Register our XDP socket file descriptor with the eBPF program so it can be redirected packets
if err := program.Register(queueID, xsk.FD()); err != nil {
fmt.Printf("error: failed to register socket in BPF map: %v\n", err)
return
}
defer program.Unregister(queueID)
for {
// If there are any free slots on the Fill queue...
if n := xsk.NumFreeFillSlots(); n > 0 {
// ...then fetch up to that number of not-in-use
// descriptors and push them onto the Fill ring queue
// for the kernel to fill them with the received
// frames.
xsk.Fill(xsk.GetDescs(n, true))
}
// Wait for receive - meaning the kernel has
// produced one or more descriptors filled with a received
// frame onto the Rx ring queue.
log.Printf("waiting for frame(s) to be received...")
numRx, _, err := xsk.Poll(-1)
if err != nil {
fmt.Printf("error: %v\n", err)
return
}
if numRx > 0 {
// Consume the descriptors filled with received frames
// from the Rx ring queue.
rxDescs := xsk.Receive(numRx)
// Print the received frames and also modify them
// in-place replacing the destination MAC address with
// broadcast address.
for i := 0; i < len(rxDescs); i++ {
pktData := xsk.GetFrame(rxDescs[i])
pkt := gopacket.NewPacket(pktData, layers.LayerTypeEthernet, gopacket.Default)
log.Printf("received frame:\n%s%+v", hex.Dump(pktData[:]), pkt)
}
}
}
}

13
go.mod
View File

@@ -5,9 +5,12 @@ go 1.22
toolchain go1.22.1 toolchain go1.22.1
require ( require (
github.com/AlexxIT/go2rtc v1.9.4 github.com/asavie/xdp v0.3.3
github.com/cilium/ebpf v0.15.0
github.com/cnotch/ipchub v1.1.0 github.com/cnotch/ipchub v1.1.0
github.com/deepch/vdk v0.0.27
github.com/emiago/sipgo v0.22.0 github.com/emiago/sipgo v0.22.0
github.com/google/gopacket v1.1.19
github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1 github.com/grpc-ecosystem/grpc-gateway/v2 v2.19.1
github.com/pion/interceptor v0.1.29 github.com/pion/interceptor v0.1.29
github.com/pion/rtcp v1.2.14 github.com/pion/rtcp v1.2.14
@@ -16,6 +19,7 @@ require (
github.com/polarsignals/frostdb v0.0.0-20240613134636-1d823f7d7299 github.com/polarsignals/frostdb v0.0.0-20240613134636-1d823f7d7299
github.com/q191201771/naza v0.30.48 github.com/q191201771/naza v0.30.48
github.com/quic-go/quic-go v0.43.1 github.com/quic-go/quic-go v0.43.1
github.com/vishvananda/netlink v1.1.0
google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237
google.golang.org/grpc v1.64.0 google.golang.org/grpc v1.64.0
google.golang.org/protobuf v1.33.0 google.golang.org/protobuf v1.33.0
@@ -100,6 +104,7 @@ require (
github.com/thanos-io/objstore v0.0.0-20240512204237-71ef2d0cf7c4 // indirect github.com/thanos-io/objstore v0.0.0-20240512204237-71ef2d0cf7c4 // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect github.com/tklauser/numcpus v0.6.1 // indirect
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zeebo/xxh3 v1.0.2 // indirect github.com/zeebo/xxh3 v1.0.2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect go.etcd.io/bbolt v1.3.6 // indirect
@@ -116,7 +121,7 @@ require (
github.com/bluenviron/mediacommon v1.9.2 github.com/bluenviron/mediacommon v1.9.2
github.com/chromedp/chromedp v0.9.5 github.com/chromedp/chromedp v0.9.5
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect github.com/google/pprof v0.0.0-20230309165930-d61513b1440d // indirect
github.com/gorilla/websocket v1.5.1 github.com/gorilla/websocket v1.5.1
github.com/onsi/ginkgo/v2 v2.9.5 // indirect github.com/onsi/ginkgo/v2 v2.9.5 // indirect
github.com/phsym/console-slog v0.3.1 github.com/phsym/console-slog v0.3.1
@@ -126,8 +131,8 @@ require (
golang.org/x/crypto v0.24.0 // indirect golang.org/x/crypto v0.24.0 // indirect
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc // indirect
golang.org/x/mod v0.18.0 // indirect golang.org/x/mod v0.18.0 // indirect
golang.org/x/net v0.26.0 golang.org/x/net v0.26.0 // indirect
golang.org/x/sys v0.21.0 // indirect golang.org/x/sys v0.21.0
golang.org/x/tools v0.22.0 // indirect golang.org/x/tools v0.22.0 // indirect
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
) )

39
go.sum
View File

@@ -1,5 +1,3 @@
github.com/AlexxIT/go2rtc v1.9.4 h1:GC25fWz9S0XwZn/RV5Y4cV7UEGbtIWktYy8Aq96RROg=
github.com/AlexxIT/go2rtc v1.9.4/go.mod h1:3nYj8jnqS0O38cCxa96fbifX1RF6GxAjlzhfEl32zeY=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU=
github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk= github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0CRv0ky0k6m906ixxpzmDRLvX58TFUKS2eePweuyxk=
@@ -11,6 +9,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/apache/arrow/go/v16 v16.1.0 h1:dwgfOya6s03CzH9JrjCBx6bkVb4yPD4ma3haj9p7FXI= github.com/apache/arrow/go/v16 v16.1.0 h1:dwgfOya6s03CzH9JrjCBx6bkVb4yPD4ma3haj9p7FXI=
github.com/apache/arrow/go/v16 v16.1.0/go.mod h1:9wnc9mn6vEDTRIm4+27pEjQpRKuTvBaessPoEXQzxWA= github.com/apache/arrow/go/v16 v16.1.0/go.mod h1:9wnc9mn6vEDTRIm4+27pEjQpRKuTvBaessPoEXQzxWA=
github.com/asavie/xdp v0.3.3 h1:b5Aa3EkMJYBeUO5TxPTIAa4wyUqYcsQr2s8f6YLJXhE=
github.com/asavie/xdp v0.3.3/go.mod h1:Vv5p+3mZiDh7ImdSvdon3E78wXyre7df5V58ATdIYAY=
github.com/benbjohnson/immutable v0.4.0 h1:CTqXbEerYso8YzVPxmWxh2gnoRQbbB9X1quUC8+vGZA= github.com/benbjohnson/immutable v0.4.0 h1:CTqXbEerYso8YzVPxmWxh2gnoRQbbB9X1quUC8+vGZA=
github.com/benbjohnson/immutable v0.4.0/go.mod h1:iAr8OjJGLnLmVUr9MZ/rz4PWUy6Ouc2JLYuMArmvAJM= github.com/benbjohnson/immutable v0.4.0/go.mod h1:iAr8OjJGLnLmVUr9MZ/rz4PWUy6Ouc2JLYuMArmvAJM=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
@@ -27,9 +27,9 @@ github.com/chromedp/chromedp v0.9.5 h1:viASzruPJOiThk7c5bueOUY91jGLJVximoEMGoH93
github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y= github.com/chromedp/chromedp v0.9.5/go.mod h1:D4I2qONslauw/C7INoCir1BJkSwBYMyZgx8X276z3+Y=
github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic= github.com/chromedp/sysutil v1.0.0 h1:+ZxhTpfpZlmchB58ih/LBHX52ky7w2VhQVKQMucy3Ic=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww= github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/cilium/ebpf v0.15.0 h1:7NxJhNiBT3NG8pZJ3c+yfrVdHY8ScgKD27sScgjLMMk=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.15.0/go.mod h1:DHp1WyrLeiBh19Cf/tfiSMhqheEiK8fXFZ4No0P1Hso=
github.com/cnotch/apirouter v0.0.0-20200731232942-89e243a791f3/go.mod h1:5deJPLON/x/s2dLOQfuKS0lenhOIT4xX0pvtN/OEIuY= github.com/cnotch/apirouter v0.0.0-20200731232942-89e243a791f3/go.mod h1:5deJPLON/x/s2dLOQfuKS0lenhOIT4xX0pvtN/OEIuY=
github.com/cnotch/ipchub v1.1.0 h1:hH0lh2mU3AZXPiqMwA0pdtqrwo7PFIMRGush9OobMUs= github.com/cnotch/ipchub v1.1.0 h1:hH0lh2mU3AZXPiqMwA0pdtqrwo7PFIMRGush9OobMUs=
github.com/cnotch/ipchub v1.1.0/go.mod h1:2PbeBs2q2VxxTVCn1eYCDwpAWuVXbq1+N0FU7GimOH4= github.com/cnotch/ipchub v1.1.0/go.mod h1:2PbeBs2q2VxxTVCn1eYCDwpAWuVXbq1+N0FU7GimOH4=
@@ -48,6 +48,8 @@ github.com/coreos/pkg v0.0.0-20220810130054-c7d1c02cb6cf/go.mod h1:E3G3o1h8I7cfc
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/deepch/vdk v0.0.27 h1:j/SHaTiZhA47wRpaue8NRp7P9xwOOO/lunxrDJBwcao=
github.com/deepch/vdk v0.0.27/go.mod h1:JlgGyR2ld6+xOIHa7XAxJh+stSDBAkdNvIPkUIdIywk=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140 h1:y7y0Oa6UawqTFPCDw9JG6pdKt4F9pAhHv0B7FMGaGD0=
github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw= github.com/dgryski/go-metro v0.0.0-20211217172704-adc40b04c140/go.mod h1:c9O8+fpSOX1DM8cPNSkX/qsBWdkD4yd2dpciOWQjpBw=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
@@ -57,6 +59,7 @@ github.com/efficientgo/core v1.0.0-rc.2/go.mod h1:FfGdkzWarkuzOlY04VY+bGfb1lWrja
github.com/emiago/sipgo v0.22.0 h1:GaQ51m26M9QnVBVY2aDJ/mXqq/BDfZ1A+nW7XgU/4Ts= github.com/emiago/sipgo v0.22.0 h1:GaQ51m26M9QnVBVY2aDJ/mXqq/BDfZ1A+nW7XgU/4Ts=
github.com/emiago/sipgo v0.22.0/go.mod h1:a77FgPEEjJvfYWYfP3p53u+dNhWEMb/VGVS6guvBzx0= github.com/emiago/sipgo v0.22.0/go.mod h1:a77FgPEEjJvfYWYfP3p53u+dNhWEMb/VGVS6guvBzx0=
github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug= github.com/emitter-io/address v1.0.0/go.mod h1:GfZb5+S/o8694B1GMGK2imUYQyn2skszMvGNA5D84Ug=
github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
@@ -67,6 +70,8 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
@@ -97,6 +102,7 @@ github.com/google/flatbuffers v24.3.25+incompatible/go.mod h1:1AeVuKshWv4vARoZat
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -105,8 +111,10 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d h1:um9/pc7tKMINFfP1eE7Wv6PRGXlcCSJkVajF7KJw3uQ=
github.com/google/pprof v0.0.0-20230309165930-d61513b1440d/go.mod h1:79YE0hCXdHag9sBkw2o+N/YnZtTkXi0UT9Nnixa5eYk=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
@@ -119,7 +127,6 @@ github.com/hamba/avro/v2 v2.20.1/go.mod h1:xHiKXbISpb3Ovc809XdzWow+XGTn+Oyf/F9aZ
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/icholy/digest v0.1.22 h1:dRIwCjtAcXch57ei+F0HSb5hmprL873+q7PoVojdMzM= github.com/icholy/digest v0.1.22 h1:dRIwCjtAcXch57ei+F0HSb5hmprL873+q7PoVojdMzM=
github.com/icholy/digest v0.1.22/go.mod h1:uLAeDdWKIWNFMH0wqbwchbTQOmJWhzSnL7zmqSPqEEc= github.com/icholy/digest v0.1.22/go.mod h1:uLAeDdWKIWNFMH0wqbwchbTQOmJWhzSnL7zmqSPqEEc=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
@@ -134,6 +141,7 @@ github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ib
github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM=
github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -155,6 +163,7 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U= github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -326,6 +335,10 @@ github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFA
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/vishvananda/netlink v1.1.0 h1:1iyaYNBLmP6L0220aDnYQpo1QEV4t4hJ+xEEhhJH8j0=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df h1:OviZH7qLw/7ZovXvuNyL3XQl8UFofeikI1NW1Gypu7k=
github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
@@ -360,6 +373,8 @@ golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg= golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc h1:O9NuF4s+E/PvMIy+9IUZB9znFwUIXEWSstNjek6VpVg=
golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= golang.org/x/exp v0.0.0-20240531132922-fd00a4e0eefc/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
@@ -369,6 +384,7 @@ golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73r
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -394,18 +410,21 @@ golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -452,6 +471,8 @@ golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216052735-49a3e744a425/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=

View File

@@ -17,16 +17,15 @@ type AnnexB struct {
} }
func (a *AnnexB) Dump(t byte, w io.Writer) { func (a *AnnexB) Dump(t byte, w io.Writer) {
m := a.Borrow(4 + a.Size) m := a.GetAllocator().Borrow(4 + a.Size)
binary.BigEndian.PutUint32(m, uint32(a.Size)) binary.BigEndian.PutUint32(m, uint32(a.Size))
a.CopyTo(m[4:]) a.CopyTo(m[4:])
w.Write(m) w.Write(m)
} }
// DecodeConfig implements pkg.IAVFrame. // DecodeConfig implements pkg.IAVFrame.
func (a *AnnexB) ConvertCtx(ctx codec.ICodecCtx, t *AVTrack) error { func (a *AnnexB) ConvertCtx(ctx codec.ICodecCtx) (codec.ICodecCtx, IAVFrame, error) {
t.ICodecCtx = ctx.GetBase() return ctx.GetBase(), nil, nil
return nil
} }
// GetSize implements pkg.IAVFrame. // GetSize implements pkg.IAVFrame.
@@ -61,9 +60,9 @@ func (a *AnnexB) Mux(codecCtx codec.ICodecCtx, frame *AVFrame) {
if frame.IDR { if frame.IDR {
switch ctx := codecCtx.(type) { switch ctx := codecCtx.(type) {
case *codec.H264Ctx: case *codec.H264Ctx:
a.Append(ctx.SPS[0], codec.NALU_Delimiter2, ctx.PPS[0], codec.NALU_Delimiter2) a.Append(ctx.SPS(), codec.NALU_Delimiter2, ctx.PPS(), codec.NALU_Delimiter2)
case *codec.H265Ctx: case *codec.H265Ctx:
a.Append(ctx.SPS[0], codec.NALU_Delimiter2, ctx.PPS[0], codec.NALU_Delimiter2, ctx.VPS[0], codec.NALU_Delimiter2) a.Append(ctx.SPS(), codec.NALU_Delimiter2, ctx.PPS(), codec.NALU_Delimiter2, ctx.VPS(), codec.NALU_Delimiter2)
} }
} }
for i, nalu := range frame.Raw.(Nalus) { for i, nalu := range frame.Raw.(Nalus) {

View File

@@ -20,8 +20,8 @@ type (
} }
IVideoCodecCtx interface { IVideoCodecCtx interface {
codec.ICodecCtx codec.ICodecCtx
GetWidth() int Width() int
GetHeight() int Height() int
} }
IDataFrame interface { IDataFrame interface {
} }
@@ -30,7 +30,7 @@ type (
GetAllocator() *util.ScalableMemoryAllocator GetAllocator() *util.ScalableMemoryAllocator
SetAllocator(*util.ScalableMemoryAllocator) SetAllocator(*util.ScalableMemoryAllocator)
Parse(*AVTrack) error // get codec info, idr Parse(*AVTrack) error // get codec info, idr
ConvertCtx(codec.ICodecCtx, *AVTrack) error // convert codec from source stream ConvertCtx(codec.ICodecCtx) (codec.ICodecCtx, IAVFrame, error) // convert codec from source stream
Demux(codec.ICodecCtx) (any, error) // demux to raw format Demux(codec.ICodecCtx) (any, error) // demux to raw format
Mux(codec.ICodecCtx, *AVFrame) // mux from raw format Mux(codec.ICodecCtx, *AVFrame) // mux from raw format
GetTimestamp() time.Duration GetTimestamp() time.Duration
@@ -67,6 +67,10 @@ type (
var _ IAVFrame = (*AnnexB)(nil) var _ IAVFrame = (*AnnexB)(nil)
func (frame *AVFrame) Clone() {
}
func (frame *AVFrame) Reset() { func (frame *AVFrame) Reset() {
frame.Timestamp = 0 frame.Timestamp = 0
frame.Raw = nil frame.Raw = nil

View File

@@ -2,6 +2,8 @@ package codec
import ( import (
"fmt" "fmt"
"github.com/deepch/vdk/codec/aacparser"
"github.com/deepch/vdk/codec/opusparser"
) )
type ( type (
@@ -17,11 +19,10 @@ type (
AudioCtx AudioCtx
} }
OPUSCtx struct { OPUSCtx struct {
AudioCtx opusparser.CodecData
} }
AACCtx struct { AACCtx struct {
AudioCtx aacparser.CodecData
Asc []byte
} }
) )
@@ -41,10 +42,21 @@ func (ctx *AudioCtx) GetInfo() string {
return fmt.Sprintf("sample rate: %d, channels: %d, sample size: %d", ctx.SampleRate, ctx.Channels, ctx.SampleSize) return fmt.Sprintf("sample rate: %d, channels: %d, sample size: %d", ctx.SampleRate, ctx.Channels, ctx.SampleSize)
} }
func (ctx *AACCtx) GetChannels() int {
return ctx.ChannelLayout().Count()
}
func (ctx *AACCtx) GetSampleSize() int {
return 16
}
func (ctx *AACCtx) GetSampleRate() int {
return ctx.SampleRate()
}
func (ctx *AACCtx) GetBase() ICodecCtx { func (ctx *AACCtx) GetBase() ICodecCtx {
return ctx return ctx
} }
func (ctx *AACCtx) GetInfo() string {
return fmt.Sprintf("sample rate: %d, channels: %d, object type: %d", ctx.SampleRate(), ctx.GetChannels(), ctx.Config.ObjectType)
}
func (*PCMUCtx) FourCC() FourCC { func (*PCMUCtx) FourCC() FourCC {
return FourCC_ULAW return FourCC_ULAW
} }
@@ -72,3 +84,15 @@ func (*OPUSCtx) FourCC() FourCC {
func (ctx *OPUSCtx) GetBase() ICodecCtx { func (ctx *OPUSCtx) GetBase() ICodecCtx {
return ctx return ctx
} }
func (ctx *OPUSCtx) GetChannels() int {
return ctx.ChannelLayout().Count()
}
func (ctx *OPUSCtx) GetSampleSize() int {
return 16
}
func (ctx *OPUSCtx) GetSampleRate() int {
return ctx.SampleRate()
}
func (ctx *OPUSCtx) GetInfo() string {
return fmt.Sprintf("sample rate: %d, channels: %d", ctx.SampleRate(), ctx.ChannelLayout().Count())
}

View File

@@ -26,11 +26,11 @@ func (ctx *AV1Ctx) GetBase() ICodecCtx {
return ctx return ctx
} }
func (ctx *AV1Ctx) GetWidth() int { func (ctx *AV1Ctx) Width() int {
return 0 return 0
} }
func (ctx *AV1Ctx) GetHeight() int { func (ctx *AV1Ctx) Height() int {
return 0 return 0
} }

View File

@@ -3,6 +3,7 @@ package codec
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/deepch/vdk/codec/h264parser"
) )
// Start Code + NAL Unit -> NALU Header + NALU Body // Start Code + NAL Unit -> NALU Header + NALU Body
@@ -107,9 +108,7 @@ func SplitH264(payload []byte) (nalus [][]byte) {
type ( type (
H264Ctx struct { H264Ctx struct {
SPSInfo h264parser.CodecData
SPS [][]byte
PPS [][]byte
} }
) )
@@ -118,15 +117,7 @@ func (*H264Ctx) FourCC() FourCC {
} }
func (ctx *H264Ctx) GetInfo() string { func (ctx *H264Ctx) GetInfo() string {
return fmt.Sprintf("sps: % 02X,pps: % 02X", ctx.SPS[0], ctx.PPS[0]) return fmt.Sprintf("fps: %d, resolution: %s", ctx.FPS(), ctx.Resolution())
}
func (h264 *H264Ctx) GetWidth() int {
return int(h264.Width)
}
func (h264 *H264Ctx) GetHeight() int {
return int(h264.Height)
} }
func (h264 *H264Ctx) GetBase() ICodecCtx { func (h264 *H264Ctx) GetBase() ICodecCtx {

View File

@@ -1,6 +1,7 @@
package codec package codec
import "fmt" import "fmt"
import "github.com/deepch/vdk/codec/h265parser"
type H265NALUType byte type H265NALUType byte
@@ -12,100 +13,16 @@ func ParseH265NALUType(b byte) H265NALUType {
return H265NALUType(b & 0x7E >> 1) return H265NALUType(b & 0x7E >> 1)
} }
const (
// HEVC_VPS = 0x40
// HEVC_SPS = 0x42
// HEVC_PPS = 0x44
// HEVC_SEI = 0x4E
// HEVC_IDR = 0x26
// HEVC_PSLICE = 0x02
NAL_UNIT_CODED_SLICE_TRAIL_N H265NALUType = iota // 0
NAL_UNIT_CODED_SLICE_TRAIL_R // 1
NAL_UNIT_CODED_SLICE_TSA_N // 2
NAL_UNIT_CODED_SLICE_TLA // 3 // Current name in the spec: TSA_R
NAL_UNIT_CODED_SLICE_STSA_N // 4
NAL_UNIT_CODED_SLICE_STSA_R // 5
NAL_UNIT_CODED_SLICE_RADL_N // 6
NAL_UNIT_CODED_SLICE_DLP // 7 // Current name in the spec: RADL_R
NAL_UNIT_CODED_SLICE_RASL_N // 8
NAL_UNIT_CODED_SLICE_TFD // 9 // Current name in the spec: RASL_R
NAL_UNIT_RESERVED_10
NAL_UNIT_RESERVED_11
NAL_UNIT_RESERVED_12
NAL_UNIT_RESERVED_13
NAL_UNIT_RESERVED_14
NAL_UNIT_RESERVED_15
NAL_UNIT_CODED_SLICE_BLA // 16 // Current name in the spec: BLA_W_LP
NAL_UNIT_CODED_SLICE_BLANT // 17 // Current name in the spec: BLA_W_DLP
NAL_UNIT_CODED_SLICE_BLA_N_LP // 18
NAL_UNIT_CODED_SLICE_IDR // 19// Current name in the spec: IDR_W_DLP
NAL_UNIT_CODED_SLICE_IDR_N_LP // 20
NAL_UNIT_CODED_SLICE_CRA // 21
NAL_UNIT_RESERVED_22
NAL_UNIT_RESERVED_23
NAL_UNIT_RESERVED_24
NAL_UNIT_RESERVED_25
NAL_UNIT_RESERVED_26
NAL_UNIT_RESERVED_27
NAL_UNIT_RESERVED_28
NAL_UNIT_RESERVED_29
NAL_UNIT_RESERVED_30
NAL_UNIT_RESERVED_31
NAL_UNIT_VPS // 32
NAL_UNIT_SPS // 33
NAL_UNIT_PPS // 34
NAL_UNIT_ACCESS_UNIT_DELIMITER // 35
NAL_UNIT_EOS // 36
NAL_UNIT_EOB // 37
NAL_UNIT_FILLER_DATA // 38
NAL_UNIT_SEI // 39 Prefix SEI
NAL_UNIT_SEI_SUFFIX // 40 Suffix SEI
NAL_UNIT_RESERVED_41
NAL_UNIT_RESERVED_42
NAL_UNIT_RESERVED_43
NAL_UNIT_RESERVED_44
NAL_UNIT_RESERVED_45
NAL_UNIT_RESERVED_46
NAL_UNIT_RESERVED_47
NAL_UNIT_RTP_AP
NAL_UNIT_RTP_FU
NAL_UNIT_UNSPECIFIED_50
NAL_UNIT_UNSPECIFIED_51
NAL_UNIT_UNSPECIFIED_52
NAL_UNIT_UNSPECIFIED_53
NAL_UNIT_UNSPECIFIED_54
NAL_UNIT_UNSPECIFIED_55
NAL_UNIT_UNSPECIFIED_56
NAL_UNIT_UNSPECIFIED_57
NAL_UNIT_UNSPECIFIED_58
NAL_UNIT_UNSPECIFIED_59
NAL_UNIT_UNSPECIFIED_60
NAL_UNIT_UNSPECIFIED_61
NAL_UNIT_UNSPECIFIED_62
NAL_UNIT_UNSPECIFIED_63
NAL_UNIT_INVALID
)
var AudNalu = []byte{0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x10} var AudNalu = []byte{0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x10}
type ( type (
H265Ctx struct { H265Ctx struct {
H264Ctx h265parser.CodecData
VPS [][]byte
} }
) )
func (ctx *H265Ctx) GetInfo() string { func (ctx *H265Ctx) GetInfo() string {
return fmt.Sprintf("sps: % 02X,pps: % 02X,vps: % 02X", ctx.SPS[0], ctx.PPS[0], ctx.VPS[0]) return fmt.Sprintf("fps: %d, resolution: %s", ctx.FPS(), ctx.Resolution())
}
func (h265 *H265Ctx) GetHeight() int {
return int(h265.Height)
}
func (h265 *H265Ctx) GetWidth() int {
return int(h265.Width)
} }
func (*H265Ctx) FourCC() FourCC { func (*H265Ctx) FourCC() FourCC {

View File

@@ -78,8 +78,29 @@ func (t *Track) AddBytesIn(n int) {
} }
} }
func (t *AVTrack) Ready(err error) {
if !t.IsReady() {
if err != nil {
t.Error("ready", "err", err)
} else {
switch ctx := t.ICodecCtx.(type) {
case IVideoCodecCtx:
t.Info("ready", "info", t.ICodecCtx.GetInfo(), "width", ctx.Width(), "height", ctx.Height())
case IAudioCodecCtx:
t.Info("ready", "info", t.ICodecCtx.GetInfo(), "channels", ctx.GetChannels(), "sample_rate", ctx.GetSampleRate())
}
}
t.ready.Fulfill(err)
}
}
func (t *Track) Ready(err error) { func (t *Track) Ready(err error) {
if !t.IsReady() { if !t.IsReady() {
if err != nil {
t.Error("ready", "err", err)
} else {
t.Info("ready")
}
t.ready.Fulfill(err) t.ready.Fulfill(err)
} }
} }

View File

@@ -90,6 +90,10 @@ func (m *Memory) NewReader() *MemoryReader {
return &reader return &reader
} }
func (r *MemoryReader) Offset() int {
return r.Size - r.Length
}
func (r *MemoryReader) Pop() []byte { func (r *MemoryReader) Pop() []byte {
panic("ReadableBuffers Pop not allowed") panic("ReadableBuffers Pop not allowed")
} }

View File

@@ -21,7 +21,7 @@ var (
lock sync.Mutex lock sync.Mutex
poolStart = int64(uintptr(unsafe.Pointer(&memoryPool[0]))) poolStart = int64(uintptr(unsafe.Pointer(&memoryPool[0])))
blockPool = list.New() blockPool = list.New()
EnableCheckSize bool = false //EnableCheckSize bool = false
) )
type MemoryAllocator struct { type MemoryAllocator struct {
@@ -176,9 +176,9 @@ func (sma *ScalableMemoryAllocator) Malloc(size int) (memory []byte) {
if sma == nil || size > MaxBlockSize { if sma == nil || size > MaxBlockSize {
return make([]byte, size) return make([]byte, size)
} }
if EnableCheckSize { //if EnableCheckSize {
defer sma.checkSize() // defer sma.checkSize()
} //}
defer sma.addMallocCount(size) defer sma.addMallocCount(size)
var child *MemoryAllocator var child *MemoryAllocator
for _, child = range sma.children { for _, child = range sma.children {
@@ -228,9 +228,9 @@ func (sma *ScalableMemoryAllocator) Free(mem []byte) bool {
if sma == nil { if sma == nil {
return false return false
} }
if EnableCheckSize { //if EnableCheckSize {
defer sma.checkSize() // defer sma.checkSize()
} //}
ptr := int64(uintptr(unsafe.Pointer(&mem[0]))) ptr := int64(uintptr(unsafe.Pointer(&mem[0])))
size := len(mem) size := len(mem)
for i, child := range sma.children { for i, child := range sma.children {
@@ -247,57 +247,16 @@ func (sma *ScalableMemoryAllocator) Free(mem []byte) bool {
return false return false
} }
type RecyclableMemory struct { //func (r *RecyclableMemory) RemoveRecycleBytes(index int) (buf []byte) {
*ScalableMemoryAllocator // if index < 0 {
Memory // index = r.Count() + index
RecycleIndexes []int // }
} // buf = r.Buffers[index]
// if r.recycleIndexes != nil {
func (r *RecyclableMemory) SetAllocator(allocator *ScalableMemoryAllocator) { // i := slices.Index(r.recycleIndexes, index)
r.ScalableMemoryAllocator = allocator // r.recycleIndexes = slices.Delete(r.recycleIndexes, i, i+1)
} // }
// r.Buffers = slices.Delete(r.Buffers, index, index+1)
func (r *RecyclableMemory) NextN(size int) (memory []byte) { // r.Size -= len(buf)
memory = r.ScalableMemoryAllocator.Malloc(size) // return
if memory == nil { //}
memory = make([]byte, size)
} else if r.RecycleIndexes != nil {
r.RecycleIndexes = append(r.RecycleIndexes, r.Count())
}
r.AppendOne(memory)
return
}
func (r *RecyclableMemory) AddRecycleBytes(b []byte) {
if r.RecycleIndexes != nil {
r.RecycleIndexes = append(r.RecycleIndexes, r.Count())
}
r.AppendOne(b)
}
func (r *RecyclableMemory) RemoveRecycleBytes(index int) (buf []byte) {
if index < 0 {
index = r.Count() + index
}
buf = r.Buffers[index]
if r.RecycleIndexes != nil {
i := slices.Index(r.RecycleIndexes, index)
r.RecycleIndexes = slices.Delete(r.RecycleIndexes, i, i+1)
}
r.Buffers = slices.Delete(r.Buffers, index, index+1)
r.Size -= len(buf)
return
}
func (r *RecyclableMemory) Recycle() {
if r.RecycleIndexes != nil {
for _, index := range r.RecycleIndexes {
r.Free(r.Buffers[index])
}
r.RecycleIndexes = r.RecycleIndexes[:0]
} else {
for _, buf := range r.Buffers {
r.Free(buf)
}
}
}

30
pkg/util/rm_disable.go Normal file
View File

@@ -0,0 +1,30 @@
//go:build disable_rm
package util
type RecyclableMemory struct {
Memory
}
func (r *RecyclableMemory) InitRecycleIndexes(max int) {
}
func (r *RecyclableMemory) GetAllocator() *ScalableMemoryAllocator {
return nil
}
func (r *RecyclableMemory) SetAllocator(allocator *ScalableMemoryAllocator) {
}
func (r *RecyclableMemory) Recycle() {
}
func (r *RecyclableMemory) NextN(size int) (memory []byte) {
memory = make([]byte, size)
r.AppendOne(memory)
return memory
}
func (r *RecyclableMemory) AddRecycleBytes(b []byte) {
r.AppendOne(b)
}

50
pkg/util/rm_enable.go Normal file
View File

@@ -0,0 +1,50 @@
//go:build !disable_rm
package util
type RecyclableMemory struct {
allocator *ScalableMemoryAllocator
Memory
recycleIndexes []int
}
func (r *RecyclableMemory) InitRecycleIndexes(max int) {
r.recycleIndexes = make([]int, 0, max)
}
func (r *RecyclableMemory) GetAllocator() *ScalableMemoryAllocator {
return r.allocator
}
func (r *RecyclableMemory) NextN(size int) (memory []byte) {
memory = r.allocator.Malloc(size)
if r.recycleIndexes != nil {
r.recycleIndexes = append(r.recycleIndexes, r.Count())
}
r.AppendOne(memory)
return
}
func (r *RecyclableMemory) AddRecycleBytes(b []byte) {
if r.recycleIndexes != nil {
r.recycleIndexes = append(r.recycleIndexes, r.Count())
}
r.AppendOne(b)
}
func (r *RecyclableMemory) SetAllocator(allocator *ScalableMemoryAllocator) {
r.allocator = allocator
}
func (r *RecyclableMemory) Recycle() {
if r.recycleIndexes != nil {
for _, index := range r.recycleIndexes {
r.allocator.Free(r.Buffers[index])
}
r.recycleIndexes = r.recycleIndexes[:0]
} else {
for _, buf := range r.Buffers {
r.allocator.Free(buf)
}
}
}

933
pkg/util/xdp_linux.go Normal file
View File

@@ -0,0 +1,933 @@
package util
import (
"fmt"
"github.com/cilium/ebpf"
"github.com/cilium/ebpf/asm"
"github.com/vishvananda/netlink"
"golang.org/x/sys/unix"
"reflect"
"syscall"
"time"
"unsafe"
)
// DefaultSocketOptions is the default SocketOptions used by an xdp.Socket created without specifying options.
var DefaultSocketOptions = SocketOptions{
NumFrames: 128,
FrameSize: 2048,
FillRingNumDescs: 64,
CompletionRingNumDescs: 64,
RxRingNumDescs: 64,
TxRingNumDescs: 64,
}
type umemRing struct {
Producer *uint32
Consumer *uint32
Descs []uint64
}
type rxTxRing struct {
Producer *uint32
Consumer *uint32
Descs []Desc
}
// A Socket is an implementation of the AF_XDP Linux socket type for reading packets from a device.
type Socket struct {
fd int
umem []byte
fillRing umemRing
rxRing rxTxRing
txRing rxTxRing
completionRing umemRing
qidconfMap *ebpf.Map
xsksMap *ebpf.Map
program *ebpf.Program
ifindex int
numTransmitted int
numFilled int
freeRXDescs, freeTXDescs []bool
options SocketOptions
rxDescs []Desc
getTXDescs, getRXDescs []Desc
}
// SocketOptions are configuration settings used to bind an XDP socket.
type SocketOptions struct {
NumFrames int
FrameSize int
FillRingNumDescs int
CompletionRingNumDescs int
RxRingNumDescs int
TxRingNumDescs int
}
// Desc represents an XDP Rx/Tx descriptor.
type Desc unix.XDPDesc
// Stats contains various counters of the XDP socket, such as numbers of
// sent/received frames.
type Stats struct {
// Filled is the number of items consumed thus far by the Linux kernel
// from the Fill ring queue.
Filled uint64
// Received is the number of items consumed thus far by the user of
// this package from the Rx ring queue.
Received uint64
// Transmitted is the number of items consumed thus far by the Linux
// kernel from the Tx ring queue.
Transmitted uint64
// Completed is the number of items consumed thus far by the user of
// this package from the Completion ring queue.
Completed uint64
// KernelStats contains the in-kernel statistics of the corresponding
// XDP socket, such as the number of invalid descriptors that were
// submitted into Fill or Tx ring queues.
KernelStats unix.XDPStatistics
}
// DefaultSocketFlags are the flags which are passed to bind(2) system call
// when the XDP socket is bound, possible values include unix.XDP_SHARED_UMEM,
// unix.XDP_COPY, unix.XDP_ZEROCOPY.
var DefaultSocketFlags uint16
// DefaultXdpFlags are the flags which are passed when the XDP program is
// attached to the network link, possible values include
// unix.XDP_FLAGS_DRV_MODE, unix.XDP_FLAGS_HW_MODE, unix.XDP_FLAGS_SKB_MODE,
// unix.XDP_FLAGS_UPDATE_IF_NOEXIST.
var DefaultXdpFlags uint32
func init() {
DefaultSocketFlags = 0
DefaultXdpFlags = 0
}
// NewSocket returns a new XDP socket attached to the network interface which
// has the given interface, and attached to the given queue on that network
// interface.
func NewSocket(Ifindex int, QueueID int, options *SocketOptions) (xsk *Socket, err error) {
if options == nil {
options = &DefaultSocketOptions
}
xsk = &Socket{fd: -1, ifindex: Ifindex, options: *options}
xsk.fd, err = syscall.Socket(unix.AF_XDP, syscall.SOCK_RAW, 0)
if err != nil {
return nil, fmt.Errorf("syscall.Socket failed: %v", err)
}
xsk.umem, err = syscall.Mmap(-1, 0, options.NumFrames*options.FrameSize,
syscall.PROT_READ|syscall.PROT_WRITE,
syscall.MAP_PRIVATE|syscall.MAP_ANONYMOUS|syscall.MAP_POPULATE)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("syscall.Mmap failed: %v", err)
}
xdpUmemReg := unix.XDPUmemReg{
Addr: uint64(uintptr(unsafe.Pointer(&xsk.umem[0]))),
Len: uint64(len(xsk.umem)),
Size: uint32(options.FrameSize),
Headroom: 0,
}
var errno syscall.Errno
var rc uintptr
rc, _, errno = unix.Syscall6(syscall.SYS_SETSOCKOPT, uintptr(xsk.fd),
unix.SOL_XDP, unix.XDP_UMEM_REG,
uintptr(unsafe.Pointer(&xdpUmemReg)),
unsafe.Sizeof(xdpUmemReg), 0)
if rc != 0 {
xsk.Close()
return nil, fmt.Errorf("unix.SetsockoptUint64 XDP_UMEM_REG failed: %v", errno)
}
err = syscall.SetsockoptInt(xsk.fd, unix.SOL_XDP, unix.XDP_UMEM_FILL_RING,
options.FillRingNumDescs)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("unix.SetsockoptUint64 XDP_UMEM_FILL_RING failed: %v", err)
}
err = unix.SetsockoptInt(xsk.fd, unix.SOL_XDP, unix.XDP_UMEM_COMPLETION_RING,
options.CompletionRingNumDescs)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("unix.SetsockoptUint64 XDP_UMEM_COMPLETION_RING failed: %v", err)
}
var rxRing bool
if options.RxRingNumDescs > 0 {
err = unix.SetsockoptInt(xsk.fd, unix.SOL_XDP, unix.XDP_RX_RING,
options.RxRingNumDescs)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("unix.SetsockoptUint64 XDP_RX_RING failed: %v", err)
}
rxRing = true
}
var txRing bool
if options.TxRingNumDescs > 0 {
err = unix.SetsockoptInt(xsk.fd, unix.SOL_XDP, unix.XDP_TX_RING,
options.TxRingNumDescs)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("unix.SetsockoptUint64 XDP_TX_RING failed: %v", err)
}
txRing = true
}
if !(rxRing || txRing) {
return nil, fmt.Errorf("RxRingNumDescs and TxRingNumDescs cannot both be set to zero")
}
var offsets unix.XDPMmapOffsets
var vallen uint32
vallen = uint32(unsafe.Sizeof(offsets))
rc, _, errno = unix.Syscall6(syscall.SYS_GETSOCKOPT, uintptr(xsk.fd),
unix.SOL_XDP, unix.XDP_MMAP_OFFSETS,
uintptr(unsafe.Pointer(&offsets)),
uintptr(unsafe.Pointer(&vallen)), 0)
if rc != 0 {
xsk.Close()
return nil, fmt.Errorf("unix.Syscall6 getsockopt XDP_MMAP_OFFSETS failed: %v", errno)
}
fillRingSlice, err := syscall.Mmap(xsk.fd, unix.XDP_UMEM_PGOFF_FILL_RING,
int(offsets.Fr.Desc+uint64(options.FillRingNumDescs)*uint64(unsafe.Sizeof(uint64(0)))),
syscall.PROT_READ|syscall.PROT_WRITE,
syscall.MAP_SHARED|syscall.MAP_POPULATE)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("syscall.Mmap XDP_UMEM_PGOFF_FILL_RING failed: %v", err)
}
xsk.fillRing.Producer = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&fillRingSlice[0])) + uintptr(offsets.Fr.Producer)))
xsk.fillRing.Consumer = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&fillRingSlice[0])) + uintptr(offsets.Fr.Consumer)))
sh := (*reflect.SliceHeader)(unsafe.Pointer(&xsk.fillRing.Descs))
sh.Data = uintptr(unsafe.Pointer(&fillRingSlice[0])) + uintptr(offsets.Fr.Desc)
sh.Len = options.FillRingNumDescs
sh.Cap = options.FillRingNumDescs
completionRingSlice, err := syscall.Mmap(xsk.fd, unix.XDP_UMEM_PGOFF_COMPLETION_RING,
int(offsets.Cr.Desc+uint64(options.CompletionRingNumDescs)*uint64(unsafe.Sizeof(uint64(0)))),
syscall.PROT_READ|syscall.PROT_WRITE,
syscall.MAP_SHARED|syscall.MAP_POPULATE)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("syscall.Mmap XDP_UMEM_PGOFF_COMPLETION_RING failed: %v", err)
}
xsk.completionRing.Producer = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&completionRingSlice[0])) + uintptr(offsets.Cr.Producer)))
xsk.completionRing.Consumer = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&completionRingSlice[0])) + uintptr(offsets.Cr.Consumer)))
sh = (*reflect.SliceHeader)(unsafe.Pointer(&xsk.completionRing.Descs))
sh.Data = uintptr(unsafe.Pointer(&completionRingSlice[0])) + uintptr(offsets.Cr.Desc)
sh.Len = options.CompletionRingNumDescs
sh.Cap = options.CompletionRingNumDescs
if rxRing {
rxRingSlice, err := syscall.Mmap(xsk.fd, unix.XDP_PGOFF_RX_RING,
int(offsets.Rx.Desc+uint64(options.RxRingNumDescs)*uint64(unsafe.Sizeof(Desc{}))),
syscall.PROT_READ|syscall.PROT_WRITE,
syscall.MAP_SHARED|syscall.MAP_POPULATE)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("syscall.Mmap XDP_PGOFF_RX_RING failed: %v", err)
}
xsk.rxRing.Producer = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&rxRingSlice[0])) + uintptr(offsets.Rx.Producer)))
xsk.rxRing.Consumer = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&rxRingSlice[0])) + uintptr(offsets.Rx.Consumer)))
sh = (*reflect.SliceHeader)(unsafe.Pointer(&xsk.rxRing.Descs))
sh.Data = uintptr(unsafe.Pointer(&rxRingSlice[0])) + uintptr(offsets.Rx.Desc)
sh.Len = options.RxRingNumDescs
sh.Cap = options.RxRingNumDescs
xsk.rxDescs = make([]Desc, 0, options.RxRingNumDescs)
}
if txRing {
txRingSlice, err := syscall.Mmap(xsk.fd, unix.XDP_PGOFF_TX_RING,
int(offsets.Tx.Desc+uint64(options.TxRingNumDescs)*uint64(unsafe.Sizeof(Desc{}))),
syscall.PROT_READ|syscall.PROT_WRITE,
syscall.MAP_SHARED|syscall.MAP_POPULATE)
if err != nil {
xsk.Close()
return nil, fmt.Errorf("syscall.Mmap XDP_PGOFF_TX_RING failed: %v", err)
}
xsk.txRing.Producer = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&txRingSlice[0])) + uintptr(offsets.Tx.Producer)))
xsk.txRing.Consumer = (*uint32)(unsafe.Pointer(uintptr(unsafe.Pointer(&txRingSlice[0])) + uintptr(offsets.Tx.Consumer)))
sh = (*reflect.SliceHeader)(unsafe.Pointer(&xsk.txRing.Descs))
sh.Data = uintptr(unsafe.Pointer(&txRingSlice[0])) + uintptr(offsets.Tx.Desc)
sh.Len = options.TxRingNumDescs
sh.Cap = options.TxRingNumDescs
}
sa := unix.SockaddrXDP{
Flags: DefaultSocketFlags,
Ifindex: uint32(Ifindex),
QueueID: uint32(QueueID),
}
if err = unix.Bind(xsk.fd, &sa); err != nil {
xsk.Close()
return nil, fmt.Errorf("syscall.Bind SockaddrXDP failed: %v", err)
}
xsk.freeRXDescs = make([]bool, options.NumFrames)
xsk.freeTXDescs = make([]bool, options.NumFrames)
for i := range xsk.freeRXDescs {
xsk.freeRXDescs[i] = true
}
for i := range xsk.freeTXDescs {
xsk.freeTXDescs[i] = true
}
xsk.getTXDescs = make([]Desc, 0, options.CompletionRingNumDescs)
xsk.getRXDescs = make([]Desc, 0, options.FillRingNumDescs)
return xsk, nil
}
// Fill submits the given descriptors to be filled (i.e. to receive frames into)
// it returns how many descriptors where actually put onto Fill ring queue.
// The descriptors can be acquired either by calling the GetDescs() method or
// by calling Receive() method.
func (xsk *Socket) Fill(descs []Desc) int {
numFreeSlots := xsk.NumFreeFillSlots()
if numFreeSlots < len(descs) {
descs = descs[:numFreeSlots]
}
prod := *xsk.fillRing.Producer
for _, desc := range descs {
xsk.fillRing.Descs[prod&uint32(xsk.options.FillRingNumDescs-1)] = desc.Addr
prod++
xsk.freeRXDescs[desc.Addr/uint64(xsk.options.FrameSize)] = false
}
//fencer.SFence()
*xsk.fillRing.Producer = prod
xsk.numFilled += len(descs)
return len(descs)
}
// Receive returns the descriptors which were filled, i.e. into which frames
// were received into.
func (xsk *Socket) Receive(num int) []Desc {
numAvailable := xsk.NumReceived()
if num > int(numAvailable) {
num = int(numAvailable)
}
descs := xsk.rxDescs[:0]
cons := *xsk.rxRing.Consumer
//fencer.LFence()
for i := 0; i < num; i++ {
descs = append(descs, xsk.rxRing.Descs[cons&uint32(xsk.options.RxRingNumDescs-1)])
cons++
xsk.freeRXDescs[descs[i].Addr/uint64(xsk.options.FrameSize)] = true
}
//fencer.MFence()
*xsk.rxRing.Consumer = cons
xsk.numFilled -= len(descs)
return descs
}
// Transmit submits the given descriptors to be sent out, it returns how many
// descriptors were actually pushed onto the Tx ring queue.
// The descriptors can be acquired either by calling the GetDescs() method or
// by calling Receive() method.
func (xsk *Socket) Transmit(descs []Desc) (numSubmitted int) {
numFreeSlots := xsk.NumFreeTxSlots()
if len(descs) > numFreeSlots {
descs = descs[:numFreeSlots]
}
prod := *xsk.txRing.Producer
for _, desc := range descs {
xsk.txRing.Descs[prod&uint32(xsk.options.TxRingNumDescs-1)] = desc
prod++
xsk.freeTXDescs[desc.Addr/uint64(xsk.options.FrameSize)] = false
}
//fencer.SFence()
*xsk.txRing.Producer = prod
xsk.numTransmitted += len(descs)
numSubmitted = len(descs)
var rc uintptr
var errno syscall.Errno
for {
rc, _, errno = unix.Syscall6(syscall.SYS_SENDTO,
uintptr(xsk.fd),
0, 0,
uintptr(unix.MSG_DONTWAIT),
0, 0)
if rc != 0 {
switch errno {
case unix.EINTR:
// try again
case unix.EAGAIN:
return
case unix.EBUSY: // "completed but not sent"
return
default:
panic(fmt.Errorf("sendto failed with rc=%d and errno=%d", rc, errno))
}
} else {
break
}
}
return
}
// FD returns the file descriptor associated with this xdp.Socket which can be
// used e.g. to do polling.
func (xsk *Socket) FD() int {
return xsk.fd
}
// Poll blocks until kernel informs us that it has either received
// or completed (i.e. actually sent) some frames that were previously submitted
// using Fill() or Transmit() methods.
// The numReceived return value can be used as the argument for subsequent
// Receive() method call.
func (xsk *Socket) Poll(timeout int) (numReceived int, numCompleted int, err error) {
var events int16
if xsk.numFilled > 0 {
events |= unix.POLLIN
}
if xsk.numTransmitted > 0 {
events |= unix.POLLOUT
}
if events == 0 {
return
}
var pfds [1]unix.PollFd
pfds[0].Fd = int32(xsk.fd)
pfds[0].Events = events
for err = unix.EINTR; err == unix.EINTR; {
_, err = unix.Poll(pfds[:], timeout)
}
if err != nil {
return 0, 0, err
}
numReceived = xsk.NumReceived()
if numCompleted = xsk.NumCompleted(); numCompleted > 0 {
xsk.Complete(numCompleted)
}
return
}
// GetDescs returns up to n descriptors which are not currently in use.
// if rx is true, return desc in first half of umem, 2nd half otherwise
func (xsk *Socket) GetDescs(n int, rx bool) []Desc {
if n > cap(xsk.getRXDescs) {
n = cap(xsk.getRXDescs)
}
if !rx {
if n > cap(xsk.getTXDescs) {
n = cap(xsk.getTXDescs)
}
}
// numOfUMEMChunks := len(xsk.freeRXDescs) / 2
// if n > numOfUMEMChunks {
// n = numOfUMEMChunks
// }
descs := xsk.getRXDescs[:0]
j := 0
start := 0
end := cap(xsk.getRXDescs)
freeList := xsk.freeRXDescs
if !rx {
start = cap(xsk.getRXDescs)
end = len(xsk.freeTXDescs)
freeList = xsk.freeTXDescs
descs = xsk.getTXDescs[:0]
}
for i := start; i < end && j < n; i++ {
if freeList[i] == true {
descs = append(descs, Desc{
Addr: uint64(i) * uint64(xsk.options.FrameSize),
Len: uint32(xsk.options.FrameSize),
})
j++
}
}
return descs
}
// GetFrame returns the buffer containing the frame corresponding to the given
// descriptor. The returned byte slice points to the actual buffer of the
// corresponding frame, so modiyfing this slice modifies the frame contents.
func (xsk *Socket) GetFrame(d Desc) []byte {
return xsk.umem[d.Addr : d.Addr+uint64(d.Len)]
}
// Close closes and frees the resources allocated by the Socket.
func (xsk *Socket) Close() error {
allErrors := []error{}
var err error
if xsk.fd != -1 {
if err = unix.Close(xsk.fd); err != nil {
allErrors = append(allErrors, fmt.Errorf("failed to close XDP socket: %v", err))
}
xsk.fd = -1
var sh *reflect.SliceHeader
sh = (*reflect.SliceHeader)(unsafe.Pointer(&xsk.completionRing.Descs))
sh.Data = uintptr(0)
sh.Len = 0
sh.Cap = 0
sh = (*reflect.SliceHeader)(unsafe.Pointer(&xsk.txRing.Descs))
sh.Data = uintptr(0)
sh.Len = 0
sh.Cap = 0
sh = (*reflect.SliceHeader)(unsafe.Pointer(&xsk.rxRing.Descs))
sh.Data = uintptr(0)
sh.Len = 0
sh.Cap = 0
sh = (*reflect.SliceHeader)(unsafe.Pointer(&xsk.fillRing.Descs))
sh.Data = uintptr(0)
sh.Len = 0
sh.Cap = 0
}
if xsk.umem != nil {
if err := syscall.Munmap(xsk.umem); err != nil {
allErrors = append(allErrors, fmt.Errorf("failed to unmap the UMEM: %v", err))
}
xsk.umem = nil
}
if len(allErrors) > 0 {
return allErrors[0]
}
return nil
}
// Complete consumes up to n descriptors from the Completion ring queue to
// which the kernel produces when it has actually transmitted a descriptor it
// got from Tx ring queue.
// You should use this method if you are doing polling on the xdp.Socket file
// descriptor yourself, rather than using the Poll() method.
func (xsk *Socket) Complete(n int) {
cons := *xsk.completionRing.Consumer
//fencer.LFence()
for i := 0; i < n; i++ {
addr := xsk.completionRing.Descs[cons&uint32(xsk.options.CompletionRingNumDescs-1)]
cons++
xsk.freeTXDescs[addr/uint64(xsk.options.FrameSize)] = true
}
//fencer.MFence()
*xsk.completionRing.Consumer = cons
xsk.numTransmitted -= n
}
// NumFreeFillSlots returns how many free slots are available on the Fill ring
// queue, i.e. the queue to which we produce descriptors which should be filled
// by the kernel with incoming frames.
func (xsk *Socket) NumFreeFillSlots() int {
prod := *xsk.fillRing.Producer
cons := *xsk.fillRing.Consumer
max := uint32(xsk.options.FillRingNumDescs)
n := max - (prod - cons)
if n > max {
n = max
}
return int(n)
}
// NumFreeTxSlots returns how many free slots are available on the Tx ring
// queue, i.e. the queue to which we produce descriptors which should be
// transmitted by the kernel to the wire.
func (xsk *Socket) NumFreeTxSlots() int {
prod := *xsk.txRing.Producer
cons := *xsk.txRing.Consumer
max := uint32(xsk.options.TxRingNumDescs)
n := max - (prod - cons)
if n > max {
n = max
}
return int(n)
}
// NumReceived returns how many descriptors are there on the Rx ring queue
// which were produced by the kernel and which we have not yet consumed.
func (xsk *Socket) NumReceived() int {
prod := *xsk.rxRing.Producer
cons := *xsk.rxRing.Consumer
max := uint32(xsk.options.RxRingNumDescs)
n := prod - cons
if n > max {
n = max
}
return int(n)
}
// NumCompleted returns how many descriptors are there on the Completion ring
// queue which were produced by the kernel and which we have not yet consumed.
func (xsk *Socket) NumCompleted() int {
prod := *xsk.completionRing.Producer
cons := *xsk.completionRing.Consumer
max := uint32(xsk.options.CompletionRingNumDescs)
n := prod - cons
if n > max {
n = max
}
return int(n)
}
// NumFilled returns how many descriptors are there on the Fill ring
// queue which have not yet been consumed by the kernel.
// This method is useful if you're polling the xdp.Socket file descriptor
// yourself, rather than using the Poll() method - if it returns a number
// greater than zero it means you should set the unix.POLLIN flag.
func (xsk *Socket) NumFilled() int {
return xsk.numFilled
}
// NumTransmitted returns how many descriptors are there on the Tx ring queue
// which have not yet been consumed by the kernel.
// Note that even after the descriptors are consumed by the kernel from the Tx
// ring queue, it doesn't mean that they have actually been sent out on the
// wire, that can be assumed only after the descriptors have been produced by
// the kernel to the Completion ring queue.
// This method is useful if you're polling the xdp.Socket file descriptor
// yourself, rather than using the Poll() method - if it returns a number
// greater than zero it means you should set the unix.POLLOUT flag.
func (xsk *Socket) NumTransmitted() int {
return xsk.numTransmitted
}
// Stats returns various statistics for this XDP socket.
func (xsk *Socket) Stats() (Stats, error) {
var stats Stats
var size uint64
stats.Filled = uint64(*xsk.fillRing.Consumer)
stats.Received = uint64(*xsk.rxRing.Consumer)
if xsk.txRing.Consumer != nil {
stats.Transmitted = uint64(*xsk.txRing.Consumer)
}
if xsk.completionRing.Consumer != nil {
stats.Completed = uint64(*xsk.completionRing.Consumer)
}
size = uint64(unsafe.Sizeof(stats.KernelStats))
rc, _, errno := unix.Syscall6(syscall.SYS_GETSOCKOPT,
uintptr(xsk.fd),
unix.SOL_XDP, unix.XDP_STATISTICS,
uintptr(unsafe.Pointer(&stats.KernelStats)),
uintptr(unsafe.Pointer(&size)), 0)
if rc != 0 {
return stats, fmt.Errorf("getsockopt XDP_STATISTICS failed with errno %d", errno)
}
return stats, nil
}
// Program represents the necessary data structures for a simple XDP program that can filter traffic
// based on the attached rx queue.
type Program struct {
Program *ebpf.Program
Queues *ebpf.Map
Sockets *ebpf.Map
}
// Attach the XDP Program to an interface.
func (p *Program) Attach(Ifindex int) error {
if err := removeProgram(Ifindex); err != nil {
return err
}
return attachProgram(Ifindex, p.Program)
}
// Detach the XDP Program from an interface.
func (p *Program) Detach(Ifindex int) error {
return removeProgram(Ifindex)
}
// Register adds the socket file descriptor as the recipient for packets from the given queueID.
func (p *Program) Register(queueID int, fd int) error {
if err := p.Sockets.Put(uint32(queueID), uint32(fd)); err != nil {
return fmt.Errorf("failed to update xsksMap: %v", err)
}
if err := p.Queues.Put(uint32(queueID), uint32(1)); err != nil {
return fmt.Errorf("failed to update qidconfMap: %v", err)
}
return nil
}
// Unregister removes any associated mapping to sockets for the given queueID.
func (p *Program) Unregister(queueID int) error {
if err := p.Queues.Delete(uint32(queueID)); err != nil {
return err
}
if err := p.Sockets.Delete(uint32(queueID)); err != nil {
return err
}
return nil
}
// Close closes and frees the resources allocated for the Program.
func (p *Program) Close() error {
allErrors := []error{}
if p.Sockets != nil {
if err := p.Sockets.Close(); err != nil {
allErrors = append(allErrors, fmt.Errorf("failed to close xsksMap: %v", err))
}
p.Sockets = nil
}
if p.Queues != nil {
if err := p.Queues.Close(); err != nil {
allErrors = append(allErrors, fmt.Errorf("failed to close qidconfMap: %v", err))
}
p.Queues = nil
}
if p.Program != nil {
if err := p.Program.Close(); err != nil {
allErrors = append(allErrors, fmt.Errorf("failed to close XDP program: %v", err))
}
p.Program = nil
}
if len(allErrors) > 0 {
return allErrors[0]
}
return nil
}
// NewProgram returns a translation of the default eBPF XDP program found in the
// xsk_load_xdp_prog() function in <linux>/tools/lib/bpf/xsk.c:
// https://github.com/torvalds/linux/blob/master/tools/lib/bpf/xsk.c#L259
func NewProgram(maxQueueEntries int) (*Program, error) {
qidconfMap, err := ebpf.NewMap(&ebpf.MapSpec{
Name: "qidconf_map",
Type: ebpf.Array,
KeySize: uint32(unsafe.Sizeof(int32(0))),
ValueSize: uint32(unsafe.Sizeof(int32(0))),
MaxEntries: uint32(maxQueueEntries),
Flags: 0,
InnerMap: nil,
})
if err != nil {
return nil, fmt.Errorf("ebpf.NewMap qidconf_map failed (try increasing RLIMIT_MEMLOCK): %v", err)
}
xsksMap, err := ebpf.NewMap(&ebpf.MapSpec{
Name: "xsks_map",
Type: ebpf.XSKMap,
KeySize: uint32(unsafe.Sizeof(int32(0))),
ValueSize: uint32(unsafe.Sizeof(int32(0))),
MaxEntries: uint32(maxQueueEntries),
Flags: 0,
InnerMap: nil,
})
if err != nil {
return nil, fmt.Errorf("ebpf.NewMap xsks_map failed (try increasing RLIMIT_MEMLOCK): %v", err)
}
/*
This is a translation of the default eBPF XDP program found in the
xsk_load_xdp_prog() function in <linux>/tools/lib/bpf/xsk.c:
https://github.com/torvalds/linux/blob/master/tools/lib/bpf/xsk.c#L259
// This is the C-program:
// SEC("xdp_sock") int xdp_sock_prog(struct xdp_md *ctx)
// {
// int *qidconf, index = ctx->rx_queue_index;
//
// // A set entry here means that the correspnding queue_id
// // has an active AF_XDP socket bound to it.
// qidconf = bpf_map_lookup_elem(&qidconf_map, &index);
// if (!qidconf)
// return XDP_ABORTED;
//
// if (*qidconf)
// return bpf_redirect_map(&xsks_map, index, 0);
//
// return XDP_PASS;
// }
//
struct bpf_insn prog[] = {
// r1 = *(u32 *)(r1 + 16)
BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, 16), // 0
// *(u32 *)(r10 - 4) = r1
BPF_STX_MEM(BPF_W, BPF_REG_10, BPF_REG_1, -4), // 1
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), // 2
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4), // 3
BPF_LD_MAP_FD(BPF_REG_1, xsk->qidconf_map_fd), // 4 (2 instructions)
BPF_EMIT_CALL(BPF_FUNC_map_lookup_elem), // 5
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0), // 6
BPF_MOV32_IMM(BPF_REG_0, 0), // 7
// if r1 == 0 goto +8
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 8), // 8
BPF_MOV32_IMM(BPF_REG_0, 2), // 9
// r1 = *(u32 *)(r1 + 0)
BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1, 0), // 10
// if r1 == 0 goto +5
BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 5), // 11
// r2 = *(u32 *)(r10 - 4)
BPF_LD_MAP_FD(BPF_REG_1, xsk->xsks_map_fd), // 12 (2 instructions)
BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_10, -4), // 13
BPF_MOV32_IMM(BPF_REG_3, 0), // 14
BPF_EMIT_CALL(BPF_FUNC_redirect_map), // 15
// The jumps are to this instruction
BPF_EXIT_INSN(), // 16
};
eBPF instructions:
0: code: 97 dst_reg: 1 src_reg: 1 off: 16 imm: 0 // 0
1: code: 99 dst_reg: 10 src_reg: 1 off: -4 imm: 0 // 1
2: code: 191 dst_reg: 2 src_reg: 10 off: 0 imm: 0 // 2
3: code: 7 dst_reg: 2 src_reg: 0 off: 0 imm: -4 // 3
4: code: 24 dst_reg: 1 src_reg: 1 off: 0 imm: 4 // 4 XXX use qidconfMap.FD as IMM
5: code: 0 dst_reg: 0 src_reg: 0 off: 0 imm: 0 // part of the same instruction
6: code: 133 dst_reg: 0 src_reg: 0 off: 0 imm: 1 // 5
7: code: 191 dst_reg: 1 src_reg: 0 off: 0 imm: 0 // 6
8: code: 180 dst_reg: 0 src_reg: 0 off: 0 imm: 0 // 7
9: code: 21 dst_reg: 1 src_reg: 0 off: 8 imm: 0 // 8
10: code: 180 dst_reg: 0 src_reg: 0 off: 0 imm: 2 // 9
11: code: 97 dst_reg: 1 src_reg: 1 off: 0 imm: 0 // 10
12: code: 21 dst_reg: 1 src_reg: 0 off: 5 imm: 0 // 11
13: code: 24 dst_reg: 1 src_reg: 1 off: 0 imm: 5 // 12 XXX use xsksMap.FD as IMM
14: code: 0 dst_reg: 0 src_reg: 0 off: 0 imm: 0 // part of the same instruction
15: code: 97 dst_reg: 2 src_reg: 10 off: -4 imm: 0 // 13
16: code: 180 dst_reg: 3 src_reg: 0 off: 0 imm: 0 // 14
17: code: 133 dst_reg: 0 src_reg: 0 off: 0 imm: 51 // 15
18: code: 149 dst_reg: 0 src_reg: 0 off: 0 imm: 0 // 16
*/
program, err := ebpf.NewProgram(&ebpf.ProgramSpec{
Name: "xsk_ebpf",
Type: ebpf.XDP,
Instructions: asm.Instructions{
{OpCode: 97, Dst: 1, Src: 1, Offset: 16}, // 0: code: 97 dst_reg: 1 src_reg: 1 off: 16 imm: 0 // 0
{OpCode: 99, Dst: 10, Src: 1, Offset: -4}, // 1: code: 99 dst_reg: 10 src_reg: 1 off: -4 imm: 0 // 1
{OpCode: 191, Dst: 2, Src: 10}, // 2: code: 191 dst_reg: 2 src_reg: 10 off: 0 imm: 0 // 2
{OpCode: 7, Dst: 2, Src: 0, Offset: 0, Constant: -4}, // 3: code: 7 dst_reg: 2 src_reg: 0 off: 0 imm: -4 // 3
{OpCode: 24, Dst: 1, Src: 1, Offset: 0, Constant: int64(qidconfMap.FD())}, // 4: code: 24 dst_reg: 1 src_reg: 1 off: 0 imm: 4 // 4 XXX use qidconfMap.FD as IMM
//{ OpCode: 0 }, // 5: code: 0 dst_reg: 0 src_reg: 0 off: 0 imm: 0 // part of the same instruction
{OpCode: 133, Dst: 0, Src: 0, Constant: 1}, // 6: code: 133 dst_reg: 0 src_reg: 0 off: 0 imm: 1 // 5
{OpCode: 191, Dst: 1, Src: 0}, // 7: code: 191 dst_reg: 1 src_reg: 0 off: 0 imm: 0 // 6
{OpCode: 180, Dst: 0, Src: 0}, // 8: code: 180 dst_reg: 0 src_reg: 0 off: 0 imm: 0 // 7
{OpCode: 21, Dst: 1, Src: 0, Offset: 8}, // 9: code: 21 dst_reg: 1 src_reg: 0 off: 8 imm: 0 // 8
{OpCode: 180, Dst: 0, Src: 0, Constant: 2}, // 10: code: 180 dst_reg: 0 src_reg: 0 off: 0 imm: 2 // 9
{OpCode: 97, Dst: 1, Src: 1}, // 11: code: 97 dst_reg: 1 src_reg: 1 off: 0 imm: 0 // 10
{OpCode: 21, Dst: 1, Offset: 5}, // 12: code: 21 dst_reg: 1 src_reg: 0 off: 5 imm: 0 // 11
{OpCode: 24, Dst: 1, Src: 1, Constant: int64(xsksMap.FD())}, // 13: code: 24 dst_reg: 1 src_reg: 1 off: 0 imm: 5 // 12 XXX use xsksMap.FD as IMM
//{ OpCode: 0 }, // 14: code: 0 dst_reg: 0 src_reg: 0 off: 0 imm: 0 // part of the same instruction
{OpCode: 97, Dst: 2, Src: 10, Offset: -4}, // 15: code: 97 dst_reg: 2 src_reg: 10 off: -4 imm: 0 // 13
{OpCode: 180, Dst: 3}, // 16: code: 180 dst_reg: 3 src_reg: 0 off: 0 imm: 0 // 14
{OpCode: 133, Constant: 51}, // 17: code: 133 dst_reg: 0 src_reg: 0 off: 0 imm: 51 // 15
{OpCode: 149}, // 18: code: 149 dst_reg: 0 src_reg: 0 off: 0 imm: 0 // 16
},
License: "LGPL-2.1 or BSD-2-Clause",
KernelVersion: 0,
})
if err != nil {
return nil, fmt.Errorf("error: ebpf.NewProgram failed: %v", err)
}
return &Program{Program: program, Queues: qidconfMap, Sockets: xsksMap}, nil
}
// LoadProgram load a external XDP program, along with queue and socket map;
// fname is the BPF kernel program file (.o);
// funcname is the function name in the program file;
// qidmapname is the Queues map name;
// xskmapname is the Sockets map name;
func LoadProgram(fname, funcname, qidmapname, xskmapname string) (*Program, error) {
prog := new(Program)
col, err := ebpf.LoadCollection(fname)
if err != nil {
return nil, err
}
var ok bool
if prog.Program, ok = col.Programs[funcname]; !ok {
return nil, fmt.Errorf("%v doesn't contain a function named %v", fname, funcname)
}
if prog.Queues, ok = col.Maps[qidmapname]; !ok {
return nil, fmt.Errorf("%v doesn't contain a queue map named %v", fname, qidmapname)
}
if prog.Sockets, ok = col.Maps[xskmapname]; !ok {
return nil, fmt.Errorf("%v doesn't contain a socket map named %v", fname, xskmapname)
}
return prog, nil
}
// removeProgram removes an existing XDP program from the given network interface.
func removeProgram(Ifindex int) error {
var link netlink.Link
var err error
link, err = netlink.LinkByIndex(Ifindex)
if err != nil {
return err
}
if !isXdpAttached(link) {
return nil
}
if err = netlink.LinkSetXdpFd(link, -1); err != nil {
return fmt.Errorf("netlink.LinkSetXdpFd(link, -1) failed: %v", err)
}
for {
link, err = netlink.LinkByIndex(Ifindex)
if err != nil {
return err
}
if !isXdpAttached(link) {
break
}
time.Sleep(time.Second)
}
return nil
}
func isXdpAttached(link netlink.Link) bool {
if link.Attrs() != nil && link.Attrs().Xdp != nil && link.Attrs().Xdp.Attached {
return true
}
return false
}
// attachProgram attaches the given XDP program to the network interface.
func attachProgram(Ifindex int, program *ebpf.Program) error {
link, err := netlink.LinkByIndex(Ifindex)
if err != nil {
return err
}
return netlink.LinkSetXdpFdWithFlags(link, program.FD(), int(DefaultXdpFlags))
}

View File

@@ -96,6 +96,9 @@ func (cfg *ConsolePlugin) connect() (conn quic.Connection, err error) {
} }
func (cfg *ConsolePlugin) OnInit() error { func (cfg *ConsolePlugin) OnInit() error {
if cfg.Secret == "" {
return nil
}
conn, err := cfg.connect() conn, err := cfg.connect()
if err != nil { if err != nil {
return err return err

View File

@@ -59,8 +59,8 @@ func (p *HDLPlugin) WriteFlvHeader(sub *m7s.Subscriber) (flv net.Buffers) {
flags |= 1 flags |= 1
metaData["videocodecid"] = int(rtmp.ParseVideoCodec(vt.FourCC())) metaData["videocodecid"] = int(rtmp.ParseVideoCodec(vt.FourCC()))
ctx := vt.ICodecCtx.(IVideoCodecCtx) ctx := vt.ICodecCtx.(IVideoCodecCtx)
metaData["width"] = ctx.GetWidth() metaData["width"] = ctx.Width()
metaData["height"] = ctx.GetHeight() metaData["height"] = ctx.Height()
} }
var data = amf.Marshal(metaData) var data = amf.Marshal(metaData)
var b [15]byte var b [15]byte

View File

@@ -27,6 +27,10 @@ func NewHDLPuller() *HDLPuller {
} }
} }
func NewPullHandler() m7s.PullHandler {
return NewHDLPuller()
}
func (puller *HDLPuller) Connect(p *m7s.Client) (err error) { func (puller *HDLPuller) Connect(p *m7s.Client) (err error) {
if strings.HasPrefix(p.RemoteURL, "http") { if strings.HasPrefix(p.RemoteURL, "http") {
var res *http.Response var res *http.Response
@@ -108,7 +112,7 @@ func (puller *HDLPuller) Pull(p *m7s.Puller) (err error) {
var frame rtmp.RTMPData var frame rtmp.RTMPData
switch ds := int(dataSize); t { switch ds := int(dataSize); t {
case FLV_TAG_TYPE_AUDIO, FLV_TAG_TYPE_VIDEO: case FLV_TAG_TYPE_AUDIO, FLV_TAG_TYPE_VIDEO:
frame.ScalableMemoryAllocator = puller.ScalableMemoryAllocator frame.SetAllocator(puller.ScalableMemoryAllocator)
err = puller.ReadNto(ds, frame.NextN(ds)) err = puller.ReadNto(ds, frame.NextN(ds))
default: default:
err = puller.Skip(ds) err = puller.Skip(ds)

View File

@@ -271,7 +271,6 @@ func (amf *AMF) Marshal(v any) []byte {
for i := 0; i < size; i++ { for i := 0; i < size; i++ {
amf.Marshal(v.Index(i).Interface()) amf.Marshal(v.Index(i).Interface())
} }
amf.Write(END_OBJ)
case reflect.Ptr: case reflect.Ptr:
vv := reflect.Indirect(v) vv := reflect.Indirect(v)
if vv.Kind() == reflect.Struct { if vv.Kind() == reflect.Struct {

View File

@@ -1,6 +1,7 @@
package rtmp package rtmp
import ( import (
"github.com/deepch/vdk/codec/aacparser"
. "m7s.live/m7s/v5/pkg" . "m7s.live/m7s/v5/pkg"
"m7s.live/m7s/v5/pkg/codec" "m7s.live/m7s/v5/pkg/codec"
"m7s.live/m7s/v5/pkg/util" "m7s.live/m7s/v5/pkg/util"
@@ -13,7 +14,7 @@ type RTMPAudio struct {
func (avcc *RTMPAudio) Parse(t *AVTrack) (err error) { func (avcc *RTMPAudio) Parse(t *AVTrack) (err error) {
reader := avcc.NewReader() reader := avcc.NewReader()
var b, b0, b1 byte var b byte
b, err = reader.ReadByte() b, err = reader.ReadByte()
if err != nil { if err != nil {
return return
@@ -41,27 +42,10 @@ func (avcc *RTMPAudio) Parse(t *AVTrack) (err error) {
return return
} }
if b == 0 { if b == 0 {
var ctx AACCtx var ctx codec.AACCtx
b0, err = reader.ReadByte()
if err != nil {
return
}
b1, err = reader.ReadByte()
if err != nil {
return
}
var cloneFrame RTMPAudio var cloneFrame RTMPAudio
cloneFrame.CopyFrom(&avcc.Memory) cloneFrame.CopyFrom(&avcc.Memory)
ctx.Asc = []byte{b0, b1} ctx.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(cloneFrame.Buffers[0][2:])
ctx.AudioObjectType = b0 >> 3
ctx.SamplingFrequencyIndex = (b0 & 0x07 << 1) | (b1 >> 7)
ctx.ChannelConfiguration = (b1 >> 3) & 0x0F
ctx.FrameLengthFlag = (b1 >> 2) & 0x01
ctx.DependsOnCoreCoder = (b1 >> 1) & 0x01
ctx.ExtensionFlag = b1 & 0x01
ctx.Channels = int(ctx.ChannelConfiguration)
ctx.SampleRate = SamplingFrequencies[ctx.SamplingFrequencyIndex]
ctx.SampleSize = 16
t.SequenceFrame = &cloneFrame t.SequenceFrame = &cloneFrame
t.ICodecCtx = &ctx t.ICodecCtx = &ctx
} }
@@ -69,21 +53,13 @@ func (avcc *RTMPAudio) Parse(t *AVTrack) (err error) {
return return
} }
func (avcc *RTMPAudio) ConvertCtx(from codec.ICodecCtx, t *AVTrack) (err error) { func (avcc *RTMPAudio) ConvertCtx(from codec.ICodecCtx) (to codec.ICodecCtx, seq IAVFrame, err error) {
switch fourCC := from.FourCC(); fourCC { to = from.GetBase()
case codec.FourCC_MP4A: switch v := to.(type) {
var ctx AACCtx case *codec.AACCtx:
ctx.AACCtx = *from.GetBase().(*codec.AACCtx) var seqFrame RTMPAudio
b0, b1 := ctx.Asc[0], ctx.Asc[1] seqFrame.AppendOne(append([]byte{0xAF, 0x00}, v.ConfigBytes...))
ctx.AudioObjectType = b0 >> 3 seq = &seqFrame
ctx.SamplingFrequencyIndex = (b0 & 0x07 << 1) | (b1 >> 7)
ctx.ChannelConfiguration = (b1 >> 3) & 0x0F
ctx.FrameLengthFlag = (b1 >> 2) & 0x01
ctx.DependsOnCoreCoder = (b1 >> 1) & 0x01
ctx.ExtensionFlag = b1 & 0x01
t.ICodecCtx = &ctx
default:
t.ICodecCtx = from.GetBase()
} }
return return
} }
@@ -91,7 +67,7 @@ func (avcc *RTMPAudio) ConvertCtx(from codec.ICodecCtx, t *AVTrack) (err error)
func (avcc *RTMPAudio) Demux(codecCtx codec.ICodecCtx) (raw any, err error) { func (avcc *RTMPAudio) Demux(codecCtx codec.ICodecCtx) (raw any, err error) {
reader := avcc.NewReader() reader := avcc.NewReader()
var result util.Memory var result util.Memory
if _, ok := codecCtx.(*AACCtx); ok { if _, ok := codecCtx.(*codec.AACCtx); ok {
err = reader.Skip(2) err = reader.Skip(2)
reader.Range(result.AppendOne) reader.Range(result.AppendOne)
return result, err return result, err

View File

@@ -16,6 +16,14 @@ type Client struct {
ServerInfo map[string]any ServerInfo map[string]any
} }
func NewPushHandler() m7s.PushHandler {
return &Client{}
}
func NewPullHandler() m7s.PullHandler {
return &Client{}
}
func (client *Client) Connect(p *m7s.Client) (err error) { func (client *Client) Connect(p *m7s.Client) (err error) {
chunkSize := 4096 chunkSize := 4096
addr := p.RemoteURL addr := p.RemoteURL

View File

@@ -1,14 +1,10 @@
package rtmp package rtmp
import ( import (
"bytes"
"encoding/binary"
"errors" "errors"
"fmt" "fmt"
"io" "io"
"github.com/cnotch/ipchub/av/codec/hevc"
"github.com/q191201771/naza/pkg/nazabits"
"m7s.live/m7s/v5/pkg/codec" "m7s.live/m7s/v5/pkg/codec"
"m7s.live/m7s/v5/pkg/util" "m7s.live/m7s/v5/pkg/util"
) )
@@ -17,19 +13,9 @@ type (
AudioCodecID byte AudioCodecID byte
VideoCodecID byte VideoCodecID byte
H264Ctx struct {
codec.H264Ctx
ConfigurationVersion byte // 8 bits Version
AVCProfileIndication byte // 8 bits
ProfileCompatibility byte // 8 bits
AVCLevelIndication byte // 8 bits
LengthSizeMinusOne byte
NalulenSize int
}
H265Ctx struct { H265Ctx struct {
codec.H265Ctx codec.H265Ctx
NalulenSize int Enhanced bool
} }
AV1Ctx struct { AV1Ctx struct {
@@ -47,63 +33,6 @@ type (
InitialPresentationDelayPresent byte InitialPresentationDelayPresent byte
InitialPresentationDelayMinusOne byte InitialPresentationDelayMinusOne byte
} }
AACCtx struct {
codec.AACCtx
AudioSpecificConfig
}
GASpecificConfig struct {
FrameLengthFlag byte // 1 bit
DependsOnCoreCoder byte // 1 bit
ExtensionFlag byte // 1 bit
}
AudioSpecificConfig struct {
AudioObjectType byte // 5 bits
SamplingFrequencyIndex byte // 4 bits
ChannelConfiguration byte // 4 bits
GASpecificConfig
}
AVCDecoderConfigurationRecord struct {
ConfigurationVersion byte // 8 bits Version
AVCProfileIndication byte // 8 bits
ProfileCompatibility byte // 8 bits
AVCLevelIndication byte // 8 bits
Reserved1 byte // 6 bits
LengthSizeMinusOne byte // 2 bits 非常重要,每个NALU包前面都(lengthSizeMinusOne & 3)+1个字节的NAL包长度描述
Reserved2 byte // 3 bits
NumOfSequenceParameterSets byte // 5 bits SPS 的个数,计算方法是 numOfSequenceParameterSets & 0x1F
NumOfPictureParameterSets byte // 8 bits PPS 的个数
SequenceParameterSetLength uint16 // 16 byte SPS Length
SequenceParameterSetNALUnit []byte // n byte SPS
PictureParameterSetLength uint16 // 16 byte PPS Length
PictureParameterSetNALUnit []byte // n byte PPS
}
HVCDecoderConfigurationRecord struct {
PicWidthInLumaSamples uint32 // sps
PicHeightInLumaSamples uint32 // sps
configurationVersion uint8
generalProfileSpace uint8
generalTierFlag uint8
generalProfileIdc uint8
generalProfileCompatibilityFlags uint32
generalConstraintIndicatorFlags uint64
generalLevelIdc uint8
lengthSizeMinusOne uint8
numTemporalLayers uint8
temporalIdNested uint8
parallelismType uint8
chromaFormat uint8
bitDepthLumaMinus8 uint8
bitDepthChromaMinus8 uint8
avgFrameRate uint16
}
) )
const ( const (
@@ -169,525 +98,13 @@ func ParseVideoCodec(name codec.FourCC) VideoCodecID {
return 0 return 0
} }
func (p *AVCDecoderConfigurationRecord) Marshal(b []byte) (n int) {
b[0] = 1
b[1] = p.AVCProfileIndication
b[2] = p.ProfileCompatibility
b[3] = p.AVCLevelIndication
b[4] = p.LengthSizeMinusOne | 0xfc
b[5] = uint8(1) | 0xe0
n += 6
binary.BigEndian.PutUint16(b[n:], p.SequenceParameterSetLength)
n += 2
copy(b[n:], p.SequenceParameterSetNALUnit)
n += len(p.SequenceParameterSetNALUnit)
b[n] = uint8(1)
n++
binary.BigEndian.PutUint16(b[n:], p.PictureParameterSetLength)
n += 2
copy(b[n:], p.PictureParameterSetNALUnit)
n += len(p.PictureParameterSetNALUnit)
return
}
var ErrDecconfInvalid = errors.New("decode error") var ErrDecconfInvalid = errors.New("decode error")
func (ctx *H264Ctx) Unmarshal(b *util.MemoryReader) (err error) {
if b.Length < 7 {
err = errors.New("not enough len")
return
}
b.ReadByteTo(&ctx.ConfigurationVersion, &ctx.AVCProfileIndication, &ctx.ProfileCompatibility, &ctx.AVCLevelIndication, &ctx.LengthSizeMinusOne)
ctx.LengthSizeMinusOne = ctx.LengthSizeMinusOne & 0x03
ctx.NalulenSize = int(ctx.LengthSizeMinusOne) + 1
var numOfSequenceParameterSets byte
numOfSequenceParameterSets, err = b.ReadByteMask(0x1f)
if err != nil {
return
}
for range numOfSequenceParameterSets {
spslen, err1 := b.ReadBE(2)
if err1 != nil {
return err1
}
spsbytes, err2 := b.ReadBytes(int(spslen))
if err2 != nil {
return err2
}
ctx.SPS = append(ctx.SPS, spsbytes)
}
if b.Length < 1 {
err = ErrDecconfInvalid
return
}
if err = ctx.SPSInfo.Unmarshal(ctx.SPS[0]); err != nil {
return
}
ppscount, err1 := b.ReadByte()
if err1 != nil {
return err1
}
for range ppscount {
ppslen, err1 := b.ReadBE(2)
if err1 != nil {
return err1
}
ppsbytes, err2 := b.ReadBytes(int(ppslen))
if err2 != nil {
return err2
}
ctx.PPS = append(ctx.PPS, ppsbytes)
}
return
}
func ParseHevcSPS(data []byte) (self codec.SPSInfo, err error) {
var rawsps hevc.H265RawSPS
if err = rawsps.Decode(data); err == nil {
self.CropLeft, self.CropRight, self.CropTop, self.CropBottom = uint(rawsps.Conf_win_left_offset), uint(rawsps.Conf_win_right_offset), uint(rawsps.Conf_win_top_offset), uint(rawsps.Conf_win_bottom_offset)
self.Width = uint(rawsps.Pic_width_in_luma_samples)
self.Height = uint(rawsps.Pic_height_in_luma_samples)
}
return
}
var SamplingFrequencies = [...]int{96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0} var SamplingFrequencies = [...]int{96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0}
var RTMP_AVC_HEAD = []byte{0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0x00, 0x1E, 0xFF} var RTMP_AVC_HEAD = []byte{0x17, 0x00, 0x00, 0x00, 0x00, 0x01, 0x42, 0x00, 0x1E, 0xFF}
var ErrHevc = errors.New("hevc parse config error") var ErrHevc = errors.New("hevc parse config error")
func (ctx *H265Ctx) Unmarshal(b *util.MemoryReader) (err error) {
if b.Length < 23 {
err = errors.New("not enough len")
return
}
b.Skip(21)
var x byte
x, err = b.ReadByte()
if err != nil {
return ErrHevc
}
ctx.NalulenSize = int(x&0x03) + 1
x, err = b.ReadByte() // number of arrays
if err != nil {
return ErrHevc
}
x, err = b.ReadByte()
if err != nil || x&0x7f != byte(codec.NAL_UNIT_VPS) {
return ErrHevc
}
numNalus, err := b.ReadBE(2)
if err != nil {
return ErrHevc
}
for range numNalus {
vpslen, err := b.ReadBE(2)
if err != nil {
return ErrHevc
}
vps, err := b.ReadBytes(int(vpslen))
if err != nil {
return ErrHevc
}
ctx.VPS = append(ctx.VPS, vps)
}
x, err = b.ReadByte()
if err != nil || x&0x7f != byte(codec.NAL_UNIT_SPS) {
return ErrHevc
}
numNalus, err = b.ReadBE(2)
if err != nil {
return ErrHevc
}
for range numNalus {
spslen, err := b.ReadBE(2)
if err != nil {
return ErrHevc
}
sps, err := b.ReadBytes(int(spslen))
if err != nil {
return ErrHevc
}
ctx.SPS = append(ctx.SPS, sps)
}
ctx.SPSInfo, err = ParseHevcSPS(ctx.SPS[0])
if err != nil {
return ErrHevc
}
x, err = b.ReadByte()
if err != nil || x&0x7f != byte(codec.NAL_UNIT_PPS) {
return ErrHevc
}
numNalus, err = b.ReadBE(2)
if err != nil {
return ErrHevc
}
for range numNalus {
ppslen, err := b.ReadBE(2)
if err != nil {
return ErrHevc
}
pps, err := b.ReadBytes(int(ppslen))
if err != nil {
return ErrHevc
}
ctx.PPS = append(ctx.PPS, pps)
}
return
}
func BuildH265SeqHeaderFromVpsSpsPps(vps, sps, pps []byte) ([]byte, error) {
sh := make([]byte, 43+len(vps)+len(sps)+len(pps))
sh[0] = 0b1001_0000 | byte(PacketTypeSequenceStart)
copy(sh[1:], codec.FourCC_H265[:])
// unsigned int(8) configurationVersion = 1;
sh[5] = 0x1
ctx := HVCDecoderConfigurationRecord{
configurationVersion: 1,
lengthSizeMinusOne: 3, // 4 bytes
generalProfileCompatibilityFlags: 0xffffffff,
generalConstraintIndicatorFlags: 0xffffffffffff,
}
if err := ctx.ParseVps(vps); err != nil {
return nil, err
}
if err := ctx.ParseSps(sps); err != nil {
return nil, err
}
// unsigned int(2) general_profile_space;
// unsigned int(1) general_tier_flag;
// unsigned int(5) general_profile_idc;
sh[6] = ctx.generalProfileSpace<<6 | ctx.generalTierFlag<<5 | ctx.generalProfileIdc
// unsigned int(32) general_profile_compatibility_flags
util.PutBE(sh[7:7+4], ctx.generalProfileCompatibilityFlags)
// unsigned int(48) general_constraint_indicator_flags
util.PutBE(sh[11:11+4], uint32(ctx.generalConstraintIndicatorFlags>>16))
util.PutBE(sh[15:15+2], uint16(ctx.generalConstraintIndicatorFlags))
// unsigned int(8) general_level_idc;
sh[17] = ctx.generalLevelIdc
// bit(4) reserved = 1111b;
// unsigned int(12) min_spatial_segmentation_idc;
// bit(6) reserved = 111111b;
// unsigned int(2) parallelismType;
// TODO chef: 这两个字段没有解析
util.PutBE(sh[18:20], 0xf000)
sh[20] = ctx.parallelismType | 0xfc
// bit(6) reserved = 111111b;
// unsigned int(2) chromaFormat;
sh[21] = ctx.chromaFormat | 0xfc
// bit(5) reserved = 11111b;
// unsigned int(3) bitDepthLumaMinus8;
sh[22] = ctx.bitDepthLumaMinus8 | 0xf8
// bit(5) reserved = 11111b;
// unsigned int(3) bitDepthChromaMinus8;
sh[23] = ctx.bitDepthChromaMinus8 | 0xf8
// bit(16) avgFrameRate;
util.PutBE(sh[24:26], ctx.avgFrameRate)
// bit(2) constantFrameRate;
// bit(3) numTemporalLayers;
// bit(1) temporalIdNested;
// unsigned int(2) lengthSizeMinusOne;
sh[26] = 0<<6 | ctx.numTemporalLayers<<3 | ctx.temporalIdNested<<2 | ctx.lengthSizeMinusOne
// num of vps sps pps
sh[27] = 0x03
i := 28
sh[i] = byte(codec.NAL_UNIT_VPS)
// num of vps
util.PutBE(sh[i+1:i+3], 1)
// length
util.PutBE(sh[i+3:i+5], len(vps))
copy(sh[i+5:], vps)
i = i + 5 + len(vps)
sh[i] = byte(codec.NAL_UNIT_SPS)
util.PutBE(sh[i+1:i+3], 1)
util.PutBE(sh[i+3:i+5], len(sps))
copy(sh[i+5:], sps)
i = i + 5 + len(sps)
sh[i] = byte(codec.NAL_UNIT_PPS)
util.PutBE(sh[i+1:i+3], 1)
util.PutBE(sh[i+3:i+5], len(pps))
copy(sh[i+5:], pps)
return sh, nil
}
func (ctx *HVCDecoderConfigurationRecord) ParseVps(vps []byte) error {
if len(vps) < 2 {
return ErrHevc
}
rbsp := nal2rbsp(vps[2:])
br := nazabits.NewBitReader(rbsp)
// skip
// vps_video_parameter_set_id u(4)
// vps_reserved_three_2bits u(2)
// vps_max_layers_minus1 u(6)
if _, err := br.ReadBits16(12); err != nil {
return ErrHevc
}
vpsMaxSubLayersMinus1, err := br.ReadBits8(3)
if err != nil {
return ErrHevc
}
if vpsMaxSubLayersMinus1+1 > ctx.numTemporalLayers {
ctx.numTemporalLayers = vpsMaxSubLayersMinus1 + 1
}
// skip
// vps_temporal_id_nesting_flag u(1)
// vps_reserved_0xffff_16bits u(16)
if _, err := br.ReadBits32(17); err != nil {
return ErrHevc
}
return ctx.parsePtl(&br, vpsMaxSubLayersMinus1)
}
func (ctx *HVCDecoderConfigurationRecord) ParseSps(sps []byte) error {
var err error
if len(sps) < 2 {
return ErrHevc
}
rbsp := nal2rbsp(sps[2:])
br := nazabits.NewBitReader(rbsp)
// sps_video_parameter_set_id
if _, err = br.ReadBits8(4); err != nil {
return err
}
spsMaxSubLayersMinus1, err := br.ReadBits8(3)
if err != nil {
return err
}
if spsMaxSubLayersMinus1+1 > ctx.numTemporalLayers {
ctx.numTemporalLayers = spsMaxSubLayersMinus1 + 1
}
// sps_temporal_id_nesting_flag
if ctx.temporalIdNested, err = br.ReadBit(); err != nil {
return err
}
if err = ctx.parsePtl(&br, spsMaxSubLayersMinus1); err != nil {
return err
}
// sps_seq_parameter_set_id
if _, err = br.ReadGolomb(); err != nil {
return err
}
var cf uint32
if cf, err = br.ReadGolomb(); err != nil {
return err
}
ctx.chromaFormat = uint8(cf)
if ctx.chromaFormat == 3 {
if _, err = br.ReadBit(); err != nil {
return err
}
}
if ctx.PicWidthInLumaSamples, err = br.ReadGolomb(); err != nil {
return err
}
if ctx.PicHeightInLumaSamples, err = br.ReadGolomb(); err != nil {
return err
}
conformanceWindowFlag, err := br.ReadBit()
if err != nil {
return err
}
if conformanceWindowFlag != 0 {
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
}
var bdlm8 uint32
if bdlm8, err = br.ReadGolomb(); err != nil {
return err
}
ctx.bitDepthLumaMinus8 = uint8(bdlm8)
var bdcm8 uint32
if bdcm8, err = br.ReadGolomb(); err != nil {
return err
}
ctx.bitDepthChromaMinus8 = uint8(bdcm8)
_, err = br.ReadGolomb()
if err != nil {
return err
}
spsSubLayerOrderingInfoPresentFlag, err := br.ReadBit()
if err != nil {
return err
}
var i uint8
if spsSubLayerOrderingInfoPresentFlag != 0 {
i = 0
} else {
i = spsMaxSubLayersMinus1
}
for ; i <= spsMaxSubLayersMinus1; i++ {
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
return nil
}
func (ctx *HVCDecoderConfigurationRecord) parsePtl(br *nazabits.BitReader, maxSubLayersMinus1 uint8) error {
var err error
var ptl HVCDecoderConfigurationRecord
if ptl.generalProfileSpace, err = br.ReadBits8(2); err != nil {
return err
}
if ptl.generalTierFlag, err = br.ReadBit(); err != nil {
return err
}
if ptl.generalProfileIdc, err = br.ReadBits8(5); err != nil {
return err
}
if ptl.generalProfileCompatibilityFlags, err = br.ReadBits32(32); err != nil {
return err
}
if ptl.generalConstraintIndicatorFlags, err = br.ReadBits64(48); err != nil {
return err
}
if ptl.generalLevelIdc, err = br.ReadBits8(8); err != nil {
return err
}
ctx.updatePtl(&ptl)
if maxSubLayersMinus1 == 0 {
return nil
}
subLayerProfilePresentFlag := make([]uint8, maxSubLayersMinus1)
subLayerLevelPresentFlag := make([]uint8, maxSubLayersMinus1)
for i := uint8(0); i < maxSubLayersMinus1; i++ {
if subLayerProfilePresentFlag[i], err = br.ReadBit(); err != nil {
return err
}
if subLayerLevelPresentFlag[i], err = br.ReadBit(); err != nil {
return err
}
}
if maxSubLayersMinus1 > 0 {
for i := maxSubLayersMinus1; i < 8; i++ {
if _, err = br.ReadBits8(2); err != nil {
return err
}
}
}
for i := uint8(0); i < maxSubLayersMinus1; i++ {
if subLayerProfilePresentFlag[i] != 0 {
if _, err = br.ReadBits32(32); err != nil {
return err
}
if _, err = br.ReadBits32(32); err != nil {
return err
}
if _, err = br.ReadBits32(24); err != nil {
return err
}
}
if subLayerLevelPresentFlag[i] != 0 {
if _, err = br.ReadBits8(8); err != nil {
return err
}
}
}
return nil
}
func (ctx *HVCDecoderConfigurationRecord) updatePtl(ptl *HVCDecoderConfigurationRecord) {
ctx.generalProfileSpace = ptl.generalProfileSpace
if ptl.generalTierFlag > ctx.generalTierFlag {
ctx.generalLevelIdc = ptl.generalLevelIdc
ctx.generalTierFlag = ptl.generalTierFlag
} else {
if ptl.generalLevelIdc > ctx.generalLevelIdc {
ctx.generalLevelIdc = ptl.generalLevelIdc
}
}
if ptl.generalProfileIdc > ctx.generalProfileIdc {
ctx.generalProfileIdc = ptl.generalProfileIdc
}
ctx.generalProfileCompatibilityFlags &= ptl.generalProfileCompatibilityFlags
ctx.generalConstraintIndicatorFlags &= ptl.generalConstraintIndicatorFlags
}
func nal2rbsp(nal []byte) []byte {
// TODO chef:
// 1. 输出应该可由外部申请
// 2. 替换性能
// 3. 该函数应该放入avc中
return bytes.Replace(nal, []byte{0x0, 0x0, 0x3}, []byte{0x0, 0x0}, -1)
}
var ( var (
ErrInvalidMarker = errors.New("invalid marker value found in AV1CodecConfigurationRecord") ErrInvalidMarker = errors.New("invalid marker value found in AV1CodecConfigurationRecord")
ErrInvalidVersion = errors.New("unsupported AV1CodecConfigurationRecord version") ErrInvalidVersion = errors.New("unsupported AV1CodecConfigurationRecord version")
@@ -759,7 +176,3 @@ func (p *AV1Ctx) Unmarshal(data *util.MemoryReader) (err error) {
} }
return nil return nil
} }
func (ctx *AACCtx) GetInfo() string {
return fmt.Sprintf("AudioObjectType: %d, SamplingFrequencyIndex: %d, ChannelConfiguration: %d, FrameLengthFlag: %d, DependsOnCoreCoder: %d, ExtensionFlag: %d", ctx.AudioObjectType, ctx.SamplingFrequencyIndex, ctx.ChannelConfiguration, ctx.FrameLengthFlag, ctx.DependsOnCoreCoder, ctx.ExtensionFlag)
}

View File

@@ -24,7 +24,7 @@ type RTMPData struct {
} }
func (avcc *RTMPData) Dump(t byte, w io.Writer) { func (avcc *RTMPData) Dump(t byte, w io.Writer) {
m := avcc.Borrow(9 + avcc.Size) m := avcc.GetAllocator().Borrow(9 + avcc.Size)
m[0] = t m[0] = t
binary.BigEndian.PutUint32(m[1:], uint32(4+avcc.Size)) binary.BigEndian.PutUint32(m[1:], uint32(4+avcc.Size))
binary.BigEndian.PutUint32(m[5:], avcc.Timestamp) binary.BigEndian.PutUint32(m[5:], avcc.Timestamp)

View File

@@ -74,7 +74,7 @@ func NewNetConnection(conn net.Conn, logger *slog.Logger) (ret *NetConnection) {
tmpBuf: make(util.Buffer, 4), tmpBuf: make(util.Buffer, 4),
chunkHeaderBuf: make(util.Buffer, 0, 20), chunkHeaderBuf: make(util.Buffer, 0, 20),
} }
ret.mediaDataPool.ScalableMemoryAllocator = util.NewScalableMemoryAllocator(1 << util.MinPowerOf2) ret.mediaDataPool.SetAllocator(util.NewScalableMemoryAllocator(1 << util.MinPowerOf2))
ret.Info("new connection") ret.Info("new connection")
return return
} }
@@ -154,9 +154,8 @@ func (conn *NetConnection) readChunk() (msg *Chunk, err error) {
} }
conn.readSeqNum += uint32(bufSize) conn.readSeqNum += uint32(bufSize)
if chunk.bufLen == 0 { if chunk.bufLen == 0 {
chunk.AVData.RecyclableMemory = util.RecyclableMemory{ chunk.AVData.RecyclableMemory = util.RecyclableMemory{}
ScalableMemoryAllocator: conn.mediaDataPool.ScalableMemoryAllocator, chunk.AVData.SetAllocator(conn.mediaDataPool.GetAllocator())
}
chunk.AVData.NextN(msgLen) chunk.AVData.NextN(msgLen)
} }
buffer := chunk.AVData.Buffers[0] buffer := chunk.AVData.Buffers[0]

View File

@@ -1,8 +1,8 @@
package rtmp package rtmp
import ( import (
"context"
"encoding/binary" "encoding/binary"
"github.com/deepch/vdk/codec/h265parser"
"io" "io"
"time" "time"
@@ -43,14 +43,15 @@ func (avcc *RTMPVideo) Parse(t *AVTrack) (err error) {
cloneFrame.CopyFrom(&avcc.Memory) cloneFrame.CopyFrom(&avcc.Memory)
switch fourCC { switch fourCC {
case codec.FourCC_H264: case codec.FourCC_H264:
var ctx H264Ctx var ctx codec.H264Ctx
if err = ctx.Unmarshal(reader); err == nil { if _, err = ctx.RecordInfo.Unmarshal(cloneFrame.Buffers[0][reader.Offset():]); err == nil {
t.SequenceFrame = &cloneFrame t.SequenceFrame = &cloneFrame
t.ICodecCtx = &ctx t.ICodecCtx = &ctx
} }
case codec.FourCC_H265: case codec.FourCC_H265:
var ctx H265Ctx var ctx H265Ctx
if err = ctx.Unmarshal(reader); err == nil { if _, err = ctx.RecordInfo.Unmarshal(cloneFrame.Buffers[0][reader.Offset():]); err == nil {
ctx.RecordInfo.LengthSizeMinusOne = 3 // Unmarshal wrong LengthSizeMinusOne
t.SequenceFrame = &cloneFrame t.SequenceFrame = &cloneFrame
t.ICodecCtx = &ctx t.ICodecCtx = &ctx
} }
@@ -110,65 +111,56 @@ func (avcc *RTMPVideo) Parse(t *AVTrack) (err error) {
return return
} }
func (avcc *RTMPVideo) ConvertCtx(from codec.ICodecCtx, t *AVTrack) (err error) { func (avcc *RTMPVideo) ConvertCtx(from codec.ICodecCtx) (to codec.ICodecCtx, seq IAVFrame, err error) {
var b util.Buffer
var enhanced = true //TODO
switch fourCC := from.FourCC(); fourCC { switch fourCC := from.FourCC(); fourCC {
case codec.FourCC_H264: case codec.FourCC_H264:
h264ctx := from.GetBase().(*codec.H264Ctx) h264ctx := from.GetBase().(*codec.H264Ctx)
var ctx H264Ctx b = make(util.Buffer, h264ctx.RecordInfo.Len()+4)
ctx.H264Ctx = *h264ctx b[0] = 0x17
lenSPS := len(h264ctx.SPS[0]) h264ctx.RecordInfo.Marshal(b[4:])
lenPPS := len(h264ctx.PPS[0])
var b util.Buffer
if lenSPS > 3 {
b.Write(RTMP_AVC_HEAD[:6])
b.Write(h264ctx.SPS[0][1:4])
b.Write(RTMP_AVC_HEAD[9:10])
} else {
b.Write(RTMP_AVC_HEAD)
}
b.WriteByte(0xE1)
b.WriteUint16(uint16(lenSPS))
b.Write(h264ctx.SPS[0])
b.WriteByte(0x01)
b.WriteUint16(uint16(lenPPS))
b.Write(h264ctx.PPS[0])
t.ICodecCtx = &ctx
var seqFrame RTMPData var seqFrame RTMPData
seqFrame.AppendOne(b) seqFrame.AppendOne(b)
t.SequenceFrame = seqFrame.WrapVideo() //if t.Enabled(context.TODO(), TraceLevel) {
if t.Enabled(context.TODO(), TraceLevel) { // c := t.FourCC().String()
c := t.FourCC().String() // size := seqFrame.GetSize()
size := seqFrame.GetSize() // data := seqFrame.String()
data := seqFrame.String() // t.Trace("decConfig", "codec", c, "size", size, "data", data)
t.Trace("decConfig", "codec", c, "size", size, "data", data) //}
} return h264ctx, seqFrame.WrapVideo(), err
case codec.FourCC_H265: case codec.FourCC_H265:
// TODO: H265 h265ctx := from.GetBase().(*codec.H265Ctx)
b = make(util.Buffer, h265ctx.RecordInfo.Len()+5)
if enhanced {
b[0] = 0b1001_0000 | byte(PacketTypeSequenceStart)
copy(b[1:], codec.FourCC_H265[:])
} else {
b[0], b[1], b[2], b[3], b[4] = 0x1C, 0, 0, 0, 0
}
h265ctx.RecordInfo.Marshal(b[5:], h265parser.SPSInfo{})
var ctx H265Ctx
ctx.Enhanced = enhanced
ctx.H265Ctx = *h265ctx
var seqFrame RTMPData
seqFrame.AppendOne(b)
return &ctx, seqFrame.WrapVideo(), err
case codec.FourCC_AV1:
} }
return return
} }
func (avcc *RTMPVideo) parseH264(ctx *H264Ctx, reader *util.MemoryReader) (any, error) { func (avcc *RTMPVideo) parseH264(ctx *codec.H264Ctx, reader *util.MemoryReader) (any, error) {
cts, err := reader.ReadBE(3)
if err != nil {
return nil, err
}
avcc.CTS = cts
var nalus Nalus var nalus Nalus
if err := nalus.ParseAVCC(reader, ctx.NalulenSize); err != nil { if err := nalus.ParseAVCC(reader, int(ctx.RecordInfo.LengthSizeMinusOne)+1); err != nil {
return nalus, err return nalus, err
} }
return nalus, nil return nalus, nil
} }
func (avcc *RTMPVideo) parseH265(ctx *H265Ctx, reader *util.MemoryReader) (any, error) { func (avcc *RTMPVideo) parseH265(ctx *H265Ctx, reader *util.MemoryReader) (any, error) {
cts, err := reader.ReadBE(3)
if err != nil {
return nil, err
}
avcc.CTS = cts
var nalus Nalus var nalus Nalus
if err := nalus.ParseAVCC(reader, ctx.NalulenSize); err != nil { if err := nalus.ParseAVCC(reader, int(ctx.RecordInfo.LengthSizeMinusOne)+1); err != nil {
return nalus, err return nalus, err
} }
return nalus, nil return nalus, nil
@@ -200,60 +192,58 @@ func (avcc *RTMPVideo) Demux(codecCtx codec.ICodecCtx) (any, error) {
} }
switch packetType { switch packetType {
case PacketTypeSequenceStart: case PacketTypeSequenceStart:
// if _, err = avcc.DecodeConfig(nil); err != nil { // see Parse()
// return nil, err
// }
return nil, nil return nil, nil
case PacketTypeCodedFrames: case PacketTypeCodedFrames:
if codecCtx.FourCC() == codec.FourCC_H265 { switch ctx := codecCtx.(type) {
return avcc.parseH265(codecCtx.(*H265Ctx), reader) case *H265Ctx:
} else { if avcc.CTS, err = reader.ReadBE(3); err != nil {
return nil, err
}
return avcc.parseH265(ctx, reader)
case *AV1Ctx:
return avcc.parseAV1(reader) return avcc.parseAV1(reader)
} }
case PacketTypeCodedFramesX: case PacketTypeCodedFramesX: // no cts
return avcc.parseH265(codecCtx.(*H265Ctx), reader)
} }
} else { } else {
b0, err = reader.ReadByte() //sequence frame flag b0, err = reader.ReadByte() //sequence frame flag
if err != nil { if err != nil {
return nil, err return nil, err
} }
if b0 == 0 { if avcc.CTS, err = reader.ReadBE(3); err != nil {
if err = reader.Skip(3); err != nil {
return nil, err return nil, err
} }
var nalus Nalus var nalus Nalus
if codecCtx.FourCC() == codec.FourCC_H265 { switch ctx := codecCtx.(type) {
var ctx = codecCtx.(*H265Ctx) case *H265Ctx:
nalus.Append(ctx.SPS[0]) if b0 == 0 {
nalus.Append(ctx.PPS[0]) nalus.Append(ctx.VPS())
nalus.Append(ctx.VPS[0]) nalus.Append(ctx.SPS())
nalus.Append(ctx.PPS())
} else { } else {
var ctx = codecCtx.(*H264Ctx) return avcc.parseH265(ctx, reader)
nalus.Append(ctx.SPS[0]) }
nalus.Append(ctx.PPS[0])
case *codec.H264Ctx:
if b0 == 0 {
nalus.Append(ctx.SPS())
nalus.Append(ctx.PPS())
} else {
return avcc.parseH264(ctx, reader)
}
} }
return nalus, nil return nalus, nil
} else {
if codecCtx.FourCC() == codec.FourCC_H265 {
return avcc.parseH265(codecCtx.(*H265Ctx), reader)
} else {
return avcc.parseH264(codecCtx.(*H264Ctx), reader)
}
}
} }
return nil, nil return nil, nil
} }
func (avcc *RTMPVideo) Mux(codecCtx codec.ICodecCtx, from *AVFrame) { func (avcc *RTMPVideo) muxOld26x(codecID VideoCodecID, from *AVFrame) {
avcc.Timestamp = uint32(from.Timestamp / time.Millisecond)
switch ctx := codecCtx.(type) {
case *AV1Ctx:
panic(ctx)
default:
nalus := from.Raw.(Nalus) nalus := from.Raw.(Nalus)
avcc.RecycleIndexes = make([]int, 0, len(nalus)) // Recycle partial data avcc.InitRecycleIndexes(len(nalus)) // Recycle partial data
head := avcc.NextN(5) head := avcc.NextN(5)
head[0] = util.Conditoinal[byte](from.IDR, 0x10, 0x20) | byte(ParseVideoCodec(codecCtx.FourCC())) head[0] = util.Conditoinal[byte](from.IDR, 0x10, 0x20) | byte(codecID)
head[1] = 1 head[1] = 1
util.PutBE(head[2:5], from.CTS/time.Millisecond) // cts util.PutBE(head[2:5], from.CTS/time.Millisecond) // cts
for _, nalu := range nalus { for _, nalu := range nalus {
@@ -263,4 +253,34 @@ func (avcc *RTMPVideo) Mux(codecCtx codec.ICodecCtx, from *AVFrame) {
avcc.Append(nalu.Buffers...) avcc.Append(nalu.Buffers...)
} }
} }
func (avcc *RTMPVideo) Mux(codecCtx codec.ICodecCtx, from *AVFrame) {
avcc.Timestamp = uint32(from.Timestamp / time.Millisecond)
switch ctx := codecCtx.(type) {
case *AV1Ctx:
panic(ctx)
case *codec.H264Ctx:
avcc.muxOld26x(CodecID_H264, from)
case *H265Ctx:
if ctx.Enhanced {
nalus := from.Raw.(Nalus)
avcc.InitRecycleIndexes(len(nalus)) // Recycle partial data
head := avcc.NextN(8)
if from.IDR {
head[0] = 0b1001_0000 | byte(PacketTypeCodedFrames)
} else {
head[0] = 0b1010_0000 | byte(PacketTypeCodedFrames)
}
copy(head[1:], codec.FourCC_H265[:])
util.PutBE(head[5:8], from.CTS/time.Millisecond) // cts
for _, nalu := range nalus {
naluLenM := avcc.NextN(4)
naluLen := uint32(nalu.Size)
binary.BigEndian.PutUint32(naluLenM, naluLen)
avcc.Append(nalu.Buffers...)
}
} else {
avcc.muxOld26x(CodecID_H265, from)
}
}
} }

View File

@@ -6,6 +6,7 @@ import (
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"github.com/bluenviron/mediacommon/pkg/bits" "github.com/bluenviron/mediacommon/pkg/bits"
"github.com/deepch/vdk/codec/aacparser"
"io" "io"
"regexp" "regexp"
"strings" "strings"
@@ -28,7 +29,7 @@ type RTPData struct {
} }
func (r *RTPData) Dump(t byte, w io.Writer) { func (r *RTPData) Dump(t byte, w io.Writer) {
m := r.Borrow(3 + len(r.Packets)*2 + r.GetSize()) m := r.GetAllocator().Borrow(3 + len(r.Packets)*2 + r.GetSize())
m[0] = t m[0] = t
binary.BigEndian.PutUint16(m[1:], uint16(len(r.Packets))) binary.BigEndian.PutUint16(m[1:], uint16(len(r.Packets)))
offset := 3 offset := 3
@@ -112,15 +113,16 @@ func (r *RTPCtx) parseFmtpLine(cp *webrtc.RTPCodecParameters) {
func (r *RTPCtx) GetInfo() string { func (r *RTPCtx) GetInfo() string {
return r.GetRTPCodecParameter().SDPFmtpLine return r.GetRTPCodecParameter().SDPFmtpLine
} }
func (r *RTPAACCtx) GetInfo() string {
return r.AACCtx.GetInfo()
}
func (r *RTPOPUSCtx) GetInfo() string {
return r.OPUSCtx.GetInfo()
}
func (r *RTPCtx) GetRTPCodecParameter() webrtc.RTPCodecParameters { func (r *RTPCtx) GetRTPCodecParameter() webrtc.RTPCodecParameters {
return r.RTPCodecParameters return r.RTPCodecParameters
} }
func (r *RTPCtx) GetSequenceFrame() IAVFrame {
return nil
}
func (r *RTPData) Append(ctx *RTPCtx, ts uint32, payload []byte) (lastPacket *rtp.Packet) { func (r *RTPData) Append(ctx *RTPCtx, ts uint32, payload []byte) (lastPacket *rtp.Packet) {
ctx.SequenceNumber++ ctx.SequenceNumber++
lastPacket = &rtp.Packet{ lastPacket = &rtp.Packet{
@@ -137,7 +139,7 @@ func (r *RTPData) Append(ctx *RTPCtx, ts uint32, payload []byte) (lastPacket *rt
return return
} }
func (r *RTPData) ConvertCtx(from codec.ICodecCtx, t *AVTrack) (err error) { func (r *RTPData) ConvertCtx(from codec.ICodecCtx) (to codec.ICodecCtx, seq IAVFrame, err error) {
switch from.FourCC() { switch from.FourCC() {
case codec.FourCC_H264: case codec.FourCC_H264:
var ctx RTPH264Ctx var ctx RTPH264Ctx
@@ -146,31 +148,31 @@ func (r *RTPData) ConvertCtx(from codec.ICodecCtx, t *AVTrack) (err error) {
ctx.MimeType = webrtc.MimeTypeH264 ctx.MimeType = webrtc.MimeTypeH264
ctx.ClockRate = 90000 ctx.ClockRate = 90000
spsInfo := ctx.SPSInfo spsInfo := ctx.SPSInfo
ctx.SDPFmtpLine = fmt.Sprintf("sprop-parameter-sets=%s,%s;profile-level-id=%02x%02x%02x;level-asymmetry-allowed=1;packetization-mode=1", base64.StdEncoding.EncodeToString(ctx.SPS[0]), base64.StdEncoding.EncodeToString(ctx.PPS[0]), spsInfo.ProfileIdc, spsInfo.ConstraintSetFlag, spsInfo.LevelIdc) ctx.SDPFmtpLine = fmt.Sprintf("sprop-parameter-sets=%s,%s;profile-level-id=%02x%02x%02x;level-asymmetry-allowed=1;packetization-mode=1", base64.StdEncoding.EncodeToString(ctx.SPS()), base64.StdEncoding.EncodeToString(ctx.PPS()), spsInfo.ProfileIdc, spsInfo.ConstraintSetFlag, spsInfo.LevelIdc)
ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx))) ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx)))
t.ICodecCtx = &ctx to = &ctx
case codec.FourCC_H265: case codec.FourCC_H265:
var ctx RTPH265Ctx var ctx RTPH265Ctx
ctx.H265Ctx = *from.GetBase().(*codec.H265Ctx) ctx.H265Ctx = *from.GetBase().(*codec.H265Ctx)
ctx.PayloadType = 98 ctx.PayloadType = 98
ctx.MimeType = webrtc.MimeTypeH265 ctx.MimeType = webrtc.MimeTypeH265
ctx.SDPFmtpLine = fmt.Sprintf("profile-id=1;sprop-sps=%s;sprop-pps=%s;sprop-vps=%s", base64.StdEncoding.EncodeToString(ctx.SPS[0]), base64.StdEncoding.EncodeToString(ctx.PPS[0]), base64.StdEncoding.EncodeToString(ctx.VPS[0])) ctx.SDPFmtpLine = fmt.Sprintf("profile-id=1;sprop-sps=%s;sprop-pps=%s;sprop-vps=%s", base64.StdEncoding.EncodeToString(ctx.SPS()), base64.StdEncoding.EncodeToString(ctx.PPS()), base64.StdEncoding.EncodeToString(ctx.VPS()))
ctx.ClockRate = 90000 ctx.ClockRate = 90000
ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx))) ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx)))
t.ICodecCtx = &ctx to = &ctx
case codec.FourCC_MP4A: case codec.FourCC_MP4A:
var ctx RTPAACCtx var ctx RTPAACCtx
ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx))) ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx)))
ctx.AACCtx = *from.GetBase().(*codec.AACCtx) ctx.AACCtx = *from.GetBase().(*codec.AACCtx)
ctx.MimeType = "audio/MPEG4-GENERIC" ctx.MimeType = "audio/MPEG4-GENERIC"
ctx.SDPFmtpLine = fmt.Sprintf("profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=%s", hex.EncodeToString(ctx.AACCtx.Asc)) ctx.SDPFmtpLine = fmt.Sprintf("profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=%s", hex.EncodeToString(ctx.AACCtx.ConfigBytes))
ctx.IndexLength = 3 ctx.IndexLength = 3
ctx.IndexDeltaLength = 3 ctx.IndexDeltaLength = 3
ctx.SizeLength = 13 ctx.SizeLength = 13
ctx.RTPCtx.Channels = uint16(ctx.AACCtx.Channels) ctx.RTPCtx.Channels = uint16(ctx.AACCtx.GetChannels())
ctx.PayloadType = 97 ctx.PayloadType = 97
ctx.ClockRate = uint32(ctx.SampleRate) ctx.ClockRate = uint32(ctx.CodecData.SampleRate())
t.ICodecCtx = &ctx to = &ctx
case codec.FourCC_ALAW: case codec.FourCC_ALAW:
var ctx RTPPCMACtx var ctx RTPPCMACtx
ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx))) ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx)))
@@ -178,7 +180,7 @@ func (r *RTPData) ConvertCtx(from codec.ICodecCtx, t *AVTrack) (err error) {
ctx.MimeType = webrtc.MimeTypePCMA ctx.MimeType = webrtc.MimeTypePCMA
ctx.PayloadType = 8 ctx.PayloadType = 8
ctx.ClockRate = uint32(ctx.SampleRate) ctx.ClockRate = uint32(ctx.SampleRate)
t.ICodecCtx = &ctx to = &ctx
case codec.FourCC_ULAW: case codec.FourCC_ULAW:
var ctx RTPPCMUCtx var ctx RTPPCMUCtx
ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx))) ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx)))
@@ -186,15 +188,15 @@ func (r *RTPData) ConvertCtx(from codec.ICodecCtx, t *AVTrack) (err error) {
ctx.MimeType = webrtc.MimeTypePCMU ctx.MimeType = webrtc.MimeTypePCMU
ctx.PayloadType = 0 ctx.PayloadType = 0
ctx.ClockRate = uint32(ctx.SampleRate) ctx.ClockRate = uint32(ctx.SampleRate)
t.ICodecCtx = &ctx to = &ctx
case codec.FourCC_OPUS: case codec.FourCC_OPUS:
var ctx RTPOPUSCtx var ctx RTPOPUSCtx
ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx))) ctx.SSRC = uint32(uintptr(unsafe.Pointer(&ctx)))
ctx.OPUSCtx = *from.GetBase().(*codec.OPUSCtx) ctx.OPUSCtx = *from.GetBase().(*codec.OPUSCtx)
ctx.MimeType = webrtc.MimeTypeOpus ctx.MimeType = webrtc.MimeTypeOpus
ctx.PayloadType = 111 ctx.PayloadType = 111
ctx.ClockRate = uint32(ctx.SampleRate) ctx.ClockRate = uint32(ctx.CodecData.SampleRate())
t.ICodecCtx = &ctx to = &ctx
} }
return return
} }
@@ -228,10 +230,10 @@ func (r *RTPAudio) Parse(t *AVTrack) (err error) {
ctx.IndexDeltaLength = 3 ctx.IndexDeltaLength = 3
ctx.SizeLength = 13 ctx.SizeLength = 13
if conf, ok := ctx.Fmtp["config"]; ok { if conf, ok := ctx.Fmtp["config"]; ok {
if ctx.AACCtx.Asc, err = hex.DecodeString(conf); err == nil { if ctx.AACCtx.ConfigBytes, err = hex.DecodeString(conf); err == nil {
ctx.SampleRate = int(r.ClockRate) if ctx.CodecData, err = aacparser.NewCodecDataFromMPEG4AudioConfigBytes(ctx.AACCtx.ConfigBytes); err != nil {
ctx.Channels = int(r.Channels) return
ctx.SampleSize = 16 }
} }
} }
t.ICodecCtx = ctx t.ICodecCtx = ctx

View File

@@ -4,6 +4,9 @@ import (
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"github.com/deepch/vdk/codec/h264parser"
"github.com/deepch/vdk/codec/h265parser"
"io"
"strings" "strings"
"time" "time"
@@ -22,6 +25,7 @@ type (
RTPH265Ctx struct { RTPH265Ctx struct {
RTPCtx RTPCtx
codec.H265Ctx codec.H265Ctx
DONL bool
} }
RTPAV1Ctx struct { RTPAV1Ctx struct {
RTPCtx RTPCtx
@@ -43,6 +47,8 @@ var (
) )
const ( const (
H265_NALU_AP = h265parser.NAL_UNIT_UNSPECIFIED_48
H265_NALU_FU = h265parser.NAL_UNIT_UNSPECIFIED_49
startBit = 1 << 7 startBit = 1 << 7
endBit = 1 << 6 endBit = 1 << 6
MTUSize = 1460 MTUSize = 1460
@@ -57,16 +63,20 @@ func (r *RTPVideo) Parse(t *AVTrack) (err error) {
} else { } else {
ctx = &RTPH264Ctx{} ctx = &RTPH264Ctx{}
ctx.parseFmtpLine(r.RTPCodecParameters) ctx.parseFmtpLine(r.RTPCodecParameters)
var sps, pps []byte
//packetization-mode=1; sprop-parameter-sets=J2QAKaxWgHgCJ+WagICAgQ==,KO48sA==; profile-level-id=640029 //packetization-mode=1; sprop-parameter-sets=J2QAKaxWgHgCJ+WagICAgQ==,KO48sA==; profile-level-id=640029
if sprop, ok := ctx.Fmtp["sprop-parameter-sets"]; ok { if sprop, ok := ctx.Fmtp["sprop-parameter-sets"]; ok {
if sprops := strings.Split(sprop, ","); len(sprops) == 2 { if sprops := strings.Split(sprop, ","); len(sprops) == 2 {
if sps, err := base64.StdEncoding.DecodeString(sprops[0]); err == nil { if sps, err = base64.StdEncoding.DecodeString(sprops[0]); err != nil {
ctx.SPS = [][]byte{sps} return
} }
if pps, err := base64.StdEncoding.DecodeString(sprops[1]); err == nil { if pps, err = base64.StdEncoding.DecodeString(sprops[1]); err == nil {
ctx.PPS = [][]byte{pps} return
} }
} }
if ctx.CodecData, err = h264parser.NewCodecDataFromSPSAndPPS(sps, pps); err != nil {
return
}
} }
t.ICodecCtx = ctx t.ICodecCtx = ctx
} }
@@ -75,13 +85,13 @@ func (r *RTPVideo) Parse(t *AVTrack) (err error) {
} }
for _, nalu := range t.Value.Raw.(Nalus) { for _, nalu := range t.Value.Raw.(Nalus) {
switch codec.ParseH264NALUType(nalu.Buffers[0][0]) { switch codec.ParseH264NALUType(nalu.Buffers[0][0]) {
case codec.NALU_SPS: case h264parser.NALU_SPS:
ctx.SPS = [][]byte{nalu.ToBytes()} ctx.RecordInfo.SPS = [][]byte{nalu.ToBytes()}
if err = ctx.SPSInfo.Unmarshal(ctx.SPS[0]); err != nil { if ctx.SPSInfo, err = h264parser.ParseSPS(ctx.SPS()); err != nil {
return return
} }
case codec.NALU_PPS: case h264parser.NALU_PPS:
ctx.PPS = [][]byte{nalu.ToBytes()} ctx.RecordInfo.PPS = [][]byte{nalu.ToBytes()}
case codec.NALU_IDR_Picture: case codec.NALU_IDR_Picture:
t.Value.IDR = true t.Value.IDR = true
} }
@@ -101,6 +111,30 @@ func (r *RTPVideo) Parse(t *AVTrack) (err error) {
} else { } else {
ctx = &RTPH265Ctx{} ctx = &RTPH265Ctx{}
ctx.parseFmtpLine(r.RTPCodecParameters) ctx.parseFmtpLine(r.RTPCodecParameters)
var vps, sps, pps []byte
if sprop_sps, ok := ctx.Fmtp["sprop-sps"]; ok {
if sps, err = base64.StdEncoding.DecodeString(sprop_sps); err != nil {
return
}
}
if sprop_pps, ok := ctx.Fmtp["sprop-pps"]; ok {
if pps, err = base64.StdEncoding.DecodeString(sprop_pps); err != nil {
return
}
}
if sprop_vps, ok := ctx.Fmtp["sprop-vps"]; ok {
if vps, err = base64.StdEncoding.DecodeString(sprop_vps); err != nil {
return
}
}
if ctx.CodecData, err = h265parser.NewCodecDataFromVPSAndSPSAndPPS(vps, sps, pps); err != nil {
return
}
if sprop_donl, ok := ctx.Fmtp["sprop-max-don-diff"]; ok {
if sprop_donl != "0" {
ctx.DONL = true
}
}
t.ICodecCtx = ctx t.ICodecCtx = ctx
} }
if t.Value.Raw, err = r.Demux(ctx); err != nil { if t.Value.Raw, err = r.Demux(ctx); err != nil {
@@ -108,22 +142,24 @@ func (r *RTPVideo) Parse(t *AVTrack) (err error) {
} }
for _, nalu := range t.Value.Raw.(Nalus) { for _, nalu := range t.Value.Raw.(Nalus) {
switch codec.ParseH265NALUType(nalu.Buffers[0][0]) { switch codec.ParseH265NALUType(nalu.Buffers[0][0]) {
case codec.NAL_UNIT_SPS: case h265parser.NAL_UNIT_VPS:
ctx = &RTPH265Ctx{} ctx = &RTPH265Ctx{}
ctx.SPS = [][]byte{nalu.ToBytes()} ctx.RecordInfo.VPS = [][]byte{nalu.ToBytes()}
ctx.SPSInfo.Unmarshal(ctx.SPS[0])
ctx.RTPCodecParameters = *r.RTPCodecParameters ctx.RTPCodecParameters = *r.RTPCodecParameters
t.ICodecCtx = ctx t.ICodecCtx = ctx
case codec.NAL_UNIT_PPS: case h265parser.NAL_UNIT_SPS:
ctx.PPS = [][]byte{nalu.ToBytes()} ctx.RecordInfo.SPS = [][]byte{nalu.ToBytes()}
case codec.NAL_UNIT_VPS: if ctx.SPSInfo, err = h265parser.ParseSPS(ctx.SPS()); err != nil {
ctx.VPS = [][]byte{nalu.ToBytes()} return
case codec.NAL_UNIT_CODED_SLICE_BLA, }
codec.NAL_UNIT_CODED_SLICE_BLANT, case h265parser.NAL_UNIT_PPS:
codec.NAL_UNIT_CODED_SLICE_BLA_N_LP, ctx.RecordInfo.PPS = [][]byte{nalu.ToBytes()}
codec.NAL_UNIT_CODED_SLICE_IDR, case h265parser.NAL_UNIT_CODED_SLICE_BLA_W_LP,
codec.NAL_UNIT_CODED_SLICE_IDR_N_LP, h265parser.NAL_UNIT_CODED_SLICE_BLA_W_RADL,
codec.NAL_UNIT_CODED_SLICE_CRA: h265parser.NAL_UNIT_CODED_SLICE_BLA_N_LP,
h265parser.NAL_UNIT_CODED_SLICE_IDR_W_RADL,
h265parser.NAL_UNIT_CODED_SLICE_IDR_N_LP,
h265parser.NAL_UNIT_CODED_SLICE_CRA:
t.Value.IDR = true t.Value.IDR = true
} }
} }
@@ -156,9 +192,9 @@ func (r *RTPVideo) Mux(codecCtx codec.ICodecCtx, from *AVFrame) {
ctx := &c.RTPCtx ctx := &c.RTPCtx
r.RTPCodecParameters = &ctx.RTPCodecParameters r.RTPCodecParameters = &ctx.RTPCodecParameters
var lastPacket *rtp.Packet var lastPacket *rtp.Packet
if from.IDR && len(c.SPS) > 0 && len(c.PPS) > 0 { if from.IDR && len(c.RecordInfo.SPS) > 0 && len(c.RecordInfo.PPS) > 0 {
r.Append(ctx, pts, c.SPS[0]) r.Append(ctx, pts, c.SPS())
r.Append(ctx, pts, c.PPS[0]) r.Append(ctx, pts, c.PPS())
} }
for _, nalu := range from.Raw.(Nalus) { for _, nalu := range from.Raw.(Nalus) {
if reader := nalu.NewReader(); reader.Length > MTUSize { if reader := nalu.NewReader(); reader.Length > MTUSize {
@@ -188,11 +224,53 @@ func (r *RTPVideo) Mux(codecCtx codec.ICodecCtx, from *AVFrame) {
} }
} }
lastPacket.Header.Marker = true lastPacket.Header.Marker = true
case *RTPH265Ctx:
ctx := &c.RTPCtx
r.RTPCodecParameters = &ctx.RTPCodecParameters
var lastPacket *rtp.Packet
if from.IDR && len(c.RecordInfo.SPS) > 0 && len(c.RecordInfo.PPS) > 0 && len(c.RecordInfo.VPS) > 0 {
r.Append(ctx, pts, c.VPS())
r.Append(ctx, pts, c.SPS())
r.Append(ctx, pts, c.PPS())
}
for _, nalu := range from.Raw.(Nalus) {
if reader := nalu.NewReader(); reader.Length > MTUSize {
var b0, b1 byte
_ = reader.ReadByteTo(&b0, &b1)
//fu
naluType := byte(codec.ParseH265NALUType(b0))
b0 = (byte(H265_NALU_FU) << 1) | (b0 & 0b10000001)
payloadLen := MTUSize
if reader.Length+3 < payloadLen {
payloadLen = reader.Length + 3
}
mem := r.NextN(payloadLen)
reader.ReadBytesTo(mem[3:])
mem[0], mem[1], mem[2] = b0, b1, naluType|startBit
lastPacket = r.Append(ctx, pts, mem)
for payloadLen = MTUSize; reader.Length > 0; lastPacket = r.Append(ctx, pts, mem) {
if reader.Length+3 < payloadLen {
payloadLen = reader.Length + 3
}
mem = r.NextN(payloadLen)
reader.ReadBytesTo(mem[3:])
mem[0], mem[1], mem[2] = b0, b1, naluType
}
lastPacket.Payload[2] |= endBit
} else {
mem := r.NextN(reader.Length)
reader.ReadBytesTo(mem)
lastPacket = r.Append(ctx, pts, mem)
}
}
lastPacket.Header.Marker = true
} }
} }
func (r *RTPVideo) Demux(ictx codec.ICodecCtx) (any, error) { func (r *RTPVideo) Demux(ictx codec.ICodecCtx) (any, error) {
switch ictx.(type) { switch c := ictx.(type) {
case *RTPH264Ctx: case *RTPH264Ctx:
var nalus Nalus var nalus Nalus
var nalu util.Memory var nalu util.Memory
@@ -243,6 +321,57 @@ func (r *RTPVideo) Demux(ictx codec.ICodecCtx) (any, error) {
} }
} }
return nalus, nil return nalus, nil
case *RTPH265Ctx:
var nalus Nalus
var nalu util.Memory
gotNalu := func() {
if nalu.Size > 0 {
nalus = append(nalus, nalu)
nalu = util.Memory{}
}
}
for _, packet := range r.Packets {
b0 := packet.Payload[0]
if t := codec.ParseH265NALUType(b0); t < H265_NALU_AP {
nalu.AppendOne(packet.Payload)
gotNalu()
} else {
var buffer = util.Buffer(packet.Payload)
switch t {
case H265_NALU_AP:
buffer.ReadUint16()
if c.DONL {
buffer.ReadUint16()
}
for buffer.CanRead() {
nalu.AppendOne(buffer.ReadN(int(buffer.ReadUint16())))
gotNalu()
}
if c.DONL {
buffer.ReadByte()
}
case H265_NALU_FU:
if buffer.Len() < 3 {
return nil, io.ErrShortBuffer
}
first3 := buffer.ReadN(3)
fuHeader := first3[2]
if c.DONL {
buffer.ReadUint16()
}
if naluType := fuHeader & 0b00111111; util.Bit1(fuHeader, 0) {
nalu.AppendOne([]byte{first3[0]&0b10000001 | (naluType << 1), first3[1]})
}
nalu.AppendOne(buffer)
if util.Bit1(fuHeader, 1) {
gotNalu()
}
default:
return nil, fmt.Errorf("unsupported nalu type %d", t)
}
}
}
return nalus, nil
} }
return nil, nil return nil, nil
} }

View File

@@ -3,7 +3,6 @@ package plugin_rtsp
import ( import (
"errors" "errors"
"fmt" "fmt"
"github.com/AlexxIT/go2rtc/pkg/core"
"m7s.live/m7s/v5" "m7s.live/m7s/v5"
"m7s.live/m7s/v5/pkg/util" "m7s.live/m7s/v5/pkg/util"
. "m7s.live/m7s/v5/plugin/rtsp/pkg" . "m7s.live/m7s/v5/plugin/rtsp/pkg"
@@ -104,7 +103,7 @@ func (p *RTSPPlugin) OnTCPConnect(conn *net.TCPConn) {
} }
nc.SDP = string(req.Body) // for info nc.SDP = string(req.Body) // for info
var medias []*core.Media var medias []*Media
if medias, err = UnmarshalSDP(req.Body); err != nil { if medias, err = UnmarshalSDP(req.Body); err != nil {
return return
} }
@@ -151,11 +150,11 @@ func (p *RTSPPlugin) OnTCPConnect(conn *net.TCPConn) {
} }
sender.NetConnection = nc sender.NetConnection = nc
// convert tracks to real output medias // convert tracks to real output medias
var medias []*core.Media var medias []*Media
if medias, err = sender.GetMedia(); err != nil { if medias, err = sender.GetMedia(); err != nil {
return return
} }
if res.Body, err = core.MarshalSDP(nc.SessionName, medias); err != nil { if res.Body, err = MarshalSDP(nc.SessionName, medias); err != nil {
return return
} }
@@ -175,7 +174,8 @@ func (p *RTSPPlugin) OnTCPConnect(conn *net.TCPConn) {
const transport = "RTP/AVP/TCP;unicast;interleaved=" const transport = "RTP/AVP/TCP;unicast;interleaved="
if strings.HasPrefix(tr, transport) { if strings.HasPrefix(tr, transport) {
nc.Session = core.RandString(8, 10)
nc.Session = util.RandomString(10)
if sendMode { if sendMode {
if i := reqTrackID(req); i >= 0 { if i := reqTrackID(req); i >= 0 {

View File

@@ -4,8 +4,6 @@ import (
"crypto/tls" "crypto/tls"
"errors" "errors"
"fmt" "fmt"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/AlexxIT/go2rtc/pkg/tcp"
"m7s.live/m7s/v5" "m7s.live/m7s/v5"
"m7s.live/m7s/v5/pkg/util" "m7s.live/m7s/v5/pkg/util"
"net" "net"
@@ -19,6 +17,14 @@ type Client struct {
Stream Stream
} }
func NewPushHandler() m7s.PushHandler {
return &Client{}
}
func NewPullHandler() m7s.PullHandler {
return &Client{}
}
func (c *Client) Connect(p *m7s.Client) (err error) { func (c *Client) Connect(p *m7s.Client) (err error) {
addr := p.RemoteURL addr := p.RemoteURL
var rtspURL *url.URL var rtspURL *url.URL
@@ -69,7 +75,7 @@ func (c *Client) Pull(p *m7s.Puller) (err error) {
} }
p.Dispose(err) p.Dispose(err)
}() }()
var media []*core.Media var media []*Media
if media, err = c.Describe(); err != nil { if media, err = c.Describe(); err != nil {
return return
} }
@@ -86,7 +92,7 @@ func (c *Client) Pull(p *m7s.Puller) (err error) {
func (c *Client) Push(p *m7s.Pusher) (err error) { func (c *Client) Push(p *m7s.Pusher) (err error) {
defer c.Close() defer c.Close()
sender := &Sender{Subscriber: &p.Subscriber, Stream: c.Stream} sender := &Sender{Subscriber: &p.Subscriber, Stream: c.Stream}
var medias []*core.Media var medias []*Media
medias, err = sender.GetMedia() medias, err = sender.GetMedia()
err = c.Announce(medias) err = c.Announce(medias)
if err != nil { if err != nil {
@@ -122,12 +128,12 @@ func (c *Client) Do(req *util.Request) (*util.Response, error) {
if res.StatusCode == http.StatusUnauthorized { if res.StatusCode == http.StatusUnauthorized {
switch c.auth.Method { switch c.auth.Method {
case tcp.AuthNone: case util.AuthNone:
if c.auth.ReadNone(res) { if c.auth.ReadNone(res) {
return c.Do(req) return c.Do(req)
} }
return nil, errors.New("user/pass not provided") return nil, errors.New("user/pass not provided")
case tcp.AuthUnknown: case util.AuthUnknown:
if c.auth.Read(res) { if c.auth.Read(res) {
return c.Do(req) return c.Do(req)
} }
@@ -161,7 +167,7 @@ func (c *Client) Options() error {
return nil return nil
} }
func (c *Client) Describe() (medias []*core.Media, err error) { func (c *Client) Describe() (medias []*Media, err error) {
// 5.3 Back channel connection // 5.3 Back channel connection
// https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec.pdf // https://www.onvif.org/specs/stream/ONVIF-Streaming-Spec.pdf
req := &util.Request{ req := &util.Request{
@@ -201,7 +207,7 @@ func (c *Client) Describe() (medias []*core.Media, err error) {
return return
} }
if c.Media != "" { if c.Media != "" {
clone := make([]*core.Media, 0, len(medias)) clone := make([]*Media, 0, len(medias))
for _, media := range medias { for _, media := range medias {
if strings.Contains(c.Media, media.Kind) { if strings.Contains(c.Media, media.Kind) {
clone = append(clone, media) clone = append(clone, media)
@@ -213,7 +219,7 @@ func (c *Client) Describe() (medias []*core.Media, err error) {
return return
} }
func (c *Client) Announce(medias []*core.Media) (err error) { func (c *Client) Announce(medias []*Media) (err error) {
req := &util.Request{ req := &util.Request{
Method: MethodAnnounce, Method: MethodAnnounce,
URL: c.URL, URL: c.URL,
@@ -222,7 +228,7 @@ func (c *Client) Announce(medias []*core.Media) (err error) {
}, },
} }
req.Body, err = core.MarshalSDP(c.SessionName, medias) req.Body, err = MarshalSDP(c.SessionName, medias)
if err != nil { if err != nil {
return err return err
} }
@@ -232,7 +238,7 @@ func (c *Client) Announce(medias []*core.Media) (err error) {
return return
} }
func (c *Client) SetupMedia(media *core.Media, index int) (byte, error) { func (c *Client) SetupMedia(media *Media, index int) (byte, error) {
var transport string var transport string
transport = fmt.Sprintf( transport = fmt.Sprintf(
// i - RTP (data channel) // i - RTP (data channel)
@@ -308,7 +314,7 @@ func (c *Client) SetupMedia(media *core.Media, index int) (byte, error) {
} }
} }
channel := core.Between(transport, "interleaved=", "-") channel := Between(transport, "interleaved=", "-")
i, err := strconv.Atoi(channel) i, err := strconv.Atoi(channel)
if err != nil { if err != nil {
return 0, err return 0, err

302
plugin/rtsp/pkg/codec.go Normal file
View File

@@ -0,0 +1,302 @@
package rtsp
import (
"encoding/base64"
"encoding/json"
"fmt"
"github.com/pion/sdp/v3"
"strconv"
"strings"
"unicode"
)
const (
DirectionRecvonly = "recvonly"
DirectionSendonly = "sendonly"
DirectionSendRecv = "sendrecv"
KindVideo = "video"
KindAudio = "audio"
CodecH264 = "H264" // payloadType: 96
CodecH265 = "H265"
CodecVP8 = "VP8"
CodecVP9 = "VP9"
CodecAV1 = "AV1"
CodecJPEG = "JPEG" // payloadType: 26
CodecRAW = "RAW"
CodecPCMU = "PCMU" // payloadType: 0
CodecPCMA = "PCMA" // payloadType: 8
CodecAAC = "MPEG4-GENERIC"
CodecOpus = "OPUS" // payloadType: 111
CodecG722 = "G722"
CodecMP3 = "MPA" // payload: 14, aka MPEG-1 Layer III
CodecPCM = "L16" // Linear PCM (big endian)
CodecPCML = "PCML" // Linear PCM (little endian)
CodecELD = "ELD" // AAC-ELD
CodecFLAC = "FLAC"
CodecAll = "ALL"
CodecAny = "ANY"
)
const PayloadTypeRAW byte = 255
func Between(s, sub1, sub2 string) string {
i := strings.Index(s, sub1)
if i < 0 {
return ""
}
s = s[i+len(sub1):]
if i = strings.Index(s, sub2); i >= 0 {
return s[:i]
}
return s
}
func Atoi(s string) (i int) {
if s != "" {
i, _ = strconv.Atoi(s)
}
return
}
type Codec struct {
Name string // H264, PCMU, PCMA, opus...
ClockRate uint32 // 90000, 8000, 16000...
Channels uint16 // 0, 1, 2
FmtpLine string
PayloadType uint8
}
// MarshalJSON - return FFprobe compatible output
func (c *Codec) MarshalJSON() ([]byte, error) {
info := map[string]any{}
if name := FFmpegCodecName(c.Name); name != "" {
info["codec_name"] = name
info["codec_type"] = c.Kind()
}
if c.Name == CodecH264 {
profile, level := DecodeH264(c.FmtpLine)
if profile != "" {
info["profile"] = profile
info["level"] = level
}
}
if c.ClockRate != 0 && c.ClockRate != 90000 {
info["sample_rate"] = c.ClockRate
}
if c.Channels > 0 {
info["channels"] = c.Channels
}
return json.Marshal(info)
}
func FFmpegCodecName(name string) string {
switch name {
case CodecH264:
return "h264"
case CodecH265:
return "hevc"
case CodecJPEG:
return "mjpeg"
case CodecRAW:
return "rawvideo"
case CodecPCMA:
return "pcm_alaw"
case CodecPCMU:
return "pcm_mulaw"
case CodecPCM:
return "pcm_s16be"
case CodecPCML:
return "pcm_s16le"
case CodecAAC:
return "aac"
case CodecOpus:
return "opus"
case CodecVP8:
return "vp8"
case CodecVP9:
return "vp9"
case CodecAV1:
return "av1"
case CodecELD:
return "aac/eld"
case CodecFLAC:
return "flac"
case CodecMP3:
return "mp3"
}
return name
}
func (c *Codec) String() (s string) {
s = c.Name
if c.ClockRate != 0 && c.ClockRate != 90000 {
s += fmt.Sprintf("/%d", c.ClockRate)
}
if c.Channels > 0 {
s += fmt.Sprintf("/%d", c.Channels)
}
return
}
func (c *Codec) IsRTP() bool {
return c.PayloadType != PayloadTypeRAW
}
func (c *Codec) IsVideo() bool {
return c.Kind() == KindVideo
}
func (c *Codec) IsAudio() bool {
return c.Kind() == KindAudio
}
func (c *Codec) Kind() string {
return GetKind(c.Name)
}
func (c *Codec) PrintName() string {
switch c.Name {
case CodecAAC:
return "AAC"
case CodecPCM:
return "S16B"
case CodecPCML:
return "S16L"
}
return c.Name
}
func (c *Codec) Clone() *Codec {
clone := *c
return &clone
}
func (c *Codec) Match(remote *Codec) bool {
switch remote.Name {
case CodecAll, CodecAny:
return true
}
return c.Name == remote.Name &&
(c.ClockRate == remote.ClockRate || remote.ClockRate == 0) &&
(c.Channels == remote.Channels || remote.Channels == 0)
}
func UnmarshalCodec(md *sdp.MediaDescription, payloadType string) *Codec {
c := &Codec{PayloadType: byte(Atoi(payloadType))}
for _, attr := range md.Attributes {
switch {
case c.Name == "" && attr.Key == "rtpmap" && strings.HasPrefix(attr.Value, payloadType):
i := strings.IndexByte(attr.Value, ' ')
ss := strings.Split(attr.Value[i+1:], "/")
c.Name = strings.ToUpper(ss[0])
// fix tailing space: `a=rtpmap:96 H264/90000 `
c.ClockRate = uint32(Atoi(strings.TrimRightFunc(ss[1], unicode.IsSpace)))
if len(ss) == 3 && ss[2] == "2" {
c.Channels = 2
}
case c.FmtpLine == "" && attr.Key == "fmtp" && strings.HasPrefix(attr.Value, payloadType):
if i := strings.IndexByte(attr.Value, ' '); i > 0 {
c.FmtpLine = attr.Value[i+1:]
}
}
}
if c.Name == "" {
// https://en.wikipedia.org/wiki/RTP_payload_formats
switch payloadType {
case "0":
c.Name = CodecPCMU
c.ClockRate = 8000
case "8":
c.Name = CodecPCMA
c.ClockRate = 8000
case "10":
c.Name = CodecPCM
c.ClockRate = 44100
c.Channels = 2
case "11":
c.Name = CodecPCM
c.ClockRate = 44100
case "14":
c.Name = CodecMP3
c.ClockRate = 90000 // it's not real sample rate
case "26":
c.Name = CodecJPEG
c.ClockRate = 90000
case "96", "97", "98":
if len(md.Bandwidth) == 0 {
c.Name = payloadType
break
}
// FFmpeg + RTSP + pcm_s16le = doesn't pass info about codec name and params
// so try to guess the codec based on bitrate
// https://github.com/AlexxIT/go2rtc/issues/523
switch md.Bandwidth[0].Bandwidth {
case 128:
c.ClockRate = 8000
case 256:
c.ClockRate = 16000
case 384:
c.ClockRate = 24000
case 512:
c.ClockRate = 32000
case 705:
c.ClockRate = 44100
case 768:
c.ClockRate = 48000
case 1411:
// default Windows DShow
c.ClockRate = 44100
c.Channels = 2
case 1536:
// default Linux ALSA
c.ClockRate = 48000
c.Channels = 2
default:
c.Name = payloadType
break
}
c.Name = CodecPCML
default:
c.Name = payloadType
}
}
return c
}
func DecodeH264(fmtp string) (profile string, level byte) {
if ps := Between(fmtp, "sprop-parameter-sets=", ","); ps != "" {
if sps, _ := base64.StdEncoding.DecodeString(ps); len(sps) >= 4 {
switch sps[1] {
case 0x42:
profile = "Baseline"
case 0x4D:
profile = "Main"
case 0x58:
profile = "Extended"
case 0x64:
profile = "High"
default:
profile = fmt.Sprintf("0x%02X", sps[1])
}
level = sps[3]
}
}
return
}

202
plugin/rtsp/pkg/media.go Normal file
View File

@@ -0,0 +1,202 @@
package rtsp
import (
"encoding/json"
"fmt"
"github.com/pion/sdp/v3"
"strings"
)
// Media take best from:
// - deepch/vdk/format/rtsp/sdp.Media
// - pion/sdp.MediaDescription
type Media struct {
Kind string `json:"kind,omitempty"` // video or audio
Direction string `json:"direction,omitempty"` // sendonly, recvonly
Codecs []*Codec `json:"codecs,omitempty"`
ID string `json:"id,omitempty"` // MID for WebRTC, Control for RTSP
}
func (m *Media) String() string {
s := fmt.Sprintf("%s, %s", m.Kind, m.Direction)
for _, codec := range m.Codecs {
name := codec.String()
if strings.Contains(s, name) {
continue
}
s += ", " + name
}
return s
}
func (m *Media) MarshalJSON() ([]byte, error) {
return json.Marshal(m.String())
}
func (m *Media) Clone() *Media {
clone := *m
clone.Codecs = make([]*Codec, len(m.Codecs))
for i, codec := range m.Codecs {
clone.Codecs[i] = codec.Clone()
}
return &clone
}
func (m *Media) MatchMedia(remote *Media) (codec, remoteCodec *Codec) {
// check same kind and opposite dirrection
if m.Kind != remote.Kind ||
m.Direction == DirectionSendonly && remote.Direction != DirectionRecvonly ||
m.Direction == DirectionRecvonly && remote.Direction != DirectionSendonly {
return nil, nil
}
for _, codec = range m.Codecs {
for _, remoteCodec = range remote.Codecs {
if codec.Match(remoteCodec) {
return
}
}
}
return nil, nil
}
func (m *Media) MatchCodec(remote *Codec) *Codec {
for _, codec := range m.Codecs {
if codec.Match(remote) {
return codec
}
}
return nil
}
func (m *Media) MatchAll() bool {
for _, codec := range m.Codecs {
if codec.Name == CodecAll {
return true
}
}
return false
}
func (m *Media) Equal(media *Media) bool {
if media.ID != "" {
return m.ID == media.ID
}
return m.String() == media.String()
}
func GetKind(name string) string {
switch name {
case CodecH264, CodecH265, CodecVP8, CodecVP9, CodecAV1, CodecJPEG, CodecRAW:
return KindVideo
case CodecPCMU, CodecPCMA, CodecAAC, CodecOpus, CodecG722, CodecMP3, CodecPCM, CodecPCML, CodecELD, CodecFLAC:
return KindAudio
}
return ""
}
func MarshalSDP(name string, medias []*Media) ([]byte, error) {
sd := &sdp.SessionDescription{
Origin: sdp.Origin{
Username: "-", SessionID: 1, SessionVersion: 1,
NetworkType: "IN", AddressType: "IP4", UnicastAddress: "0.0.0.0",
},
SessionName: sdp.SessionName(name),
ConnectionInformation: &sdp.ConnectionInformation{
NetworkType: "IN", AddressType: "IP4", Address: &sdp.Address{
Address: "0.0.0.0",
},
},
TimeDescriptions: []sdp.TimeDescription{
{Timing: sdp.Timing{}},
},
}
for _, media := range medias {
if media.Codecs == nil {
continue
}
codec := media.Codecs[0]
name := codec.Name
if name == CodecELD {
name = CodecAAC
}
md := &sdp.MediaDescription{
MediaName: sdp.MediaName{
Media: media.Kind,
Protos: []string{"RTP", "AVP"},
},
}
md.WithCodec(codec.PayloadType, name, codec.ClockRate, codec.Channels, codec.FmtpLine)
if media.ID != "" {
md.WithValueAttribute("control", media.ID)
}
sd.MediaDescriptions = append(sd.MediaDescriptions, md)
}
return sd.Marshal()
}
func UnmarshalMedia(md *sdp.MediaDescription) *Media {
m := &Media{
Kind: md.MediaName.Media,
}
for _, attr := range md.Attributes {
switch attr.Key {
case DirectionSendonly, DirectionRecvonly, DirectionSendRecv:
m.Direction = attr.Key
case "control", "mid":
m.ID = attr.Value
}
}
for _, format := range md.MediaName.Formats {
m.Codecs = append(m.Codecs, UnmarshalCodec(md, format))
}
return m
}
func ParseQuery(query map[string][]string) (medias []*Media) {
// set media candidates from query list
for key, values := range query {
switch key {
case KindVideo, KindAudio:
for _, value := range values {
media := &Media{Kind: key, Direction: DirectionSendonly}
for _, name := range strings.Split(value, ",") {
name = strings.ToUpper(name)
// check aliases
switch name {
case "", "COPY":
name = CodecAny
case "MJPEG":
name = CodecJPEG
case "AAC":
name = CodecAAC
case "MP3":
name = CodecMP3
}
media.Codecs = append(media.Codecs, &Codec{Name: name})
}
medias = append(medias, media)
}
}
}
return
}

View File

@@ -2,7 +2,6 @@ package rtsp
import ( import (
"bytes" "bytes"
"github.com/AlexxIT/go2rtc/pkg/core"
"io" "io"
"net/url" "net/url"
"regexp" "regexp"
@@ -24,7 +23,7 @@ o=- 0 0 IN IP4 0.0.0.0
s=- s=-
t=0 0` t=0 0`
func UnmarshalSDP(rawSDP []byte) ([]*core.Media, error) { func UnmarshalSDP(rawSDP []byte) ([]*Media, error) {
sd := &sdp.SessionDescription{} sd := &sdp.SessionDescription{}
if err := sd.Unmarshal(rawSDP); err != nil { if err := sd.Unmarshal(rawSDP); err != nil {
// fix multiple `s=` https://github.com/AlexxIT/WebRTC/issues/417 // fix multiple `s=` https://github.com/AlexxIT/WebRTC/issues/417
@@ -53,21 +52,21 @@ func UnmarshalSDP(rawSDP []byte) ([]*core.Media, error) {
} }
} }
var medias []*core.Media var medias []*Media
for _, md := range sd.MediaDescriptions { for _, md := range sd.MediaDescriptions {
media := core.UnmarshalMedia(md) media := UnmarshalMedia(md)
// Check buggy SDP with fmtp for H264 on another track // Check buggy SDP with fmtp for H264 on another track
// https://github.com/AlexxIT/WebRTC/issues/419 // https://github.com/AlexxIT/WebRTC/issues/419
for _, codec := range media.Codecs { for _, codec := range media.Codecs {
if codec.Name == core.CodecH264 && codec.FmtpLine == "" { if codec.Name == CodecH264 && codec.FmtpLine == "" {
codec.FmtpLine = findFmtpLine(codec.PayloadType, sd.MediaDescriptions) codec.FmtpLine = findFmtpLine(codec.PayloadType, sd.MediaDescriptions)
} }
} }
if media.Direction == "" { if media.Direction == "" {
media.Direction = core.DirectionRecvonly media.Direction = DirectionRecvonly
} }
medias = append(medias, media) medias = append(medias, media)
@@ -79,7 +78,7 @@ func UnmarshalSDP(rawSDP []byte) ([]*core.Media, error) {
func findFmtpLine(payloadType uint8, descriptions []*sdp.MediaDescription) string { func findFmtpLine(payloadType uint8, descriptions []*sdp.MediaDescription) string {
s := strconv.Itoa(int(payloadType)) s := strconv.Itoa(int(payloadType))
for _, md := range descriptions { for _, md := range descriptions {
codec := core.UnmarshalCodec(md, s) codec := UnmarshalCodec(md, s)
if codec.FmtpLine != "" { if codec.FmtpLine != "" {
return codec.FmtpLine return codec.FmtpLine
} }

View File

@@ -2,7 +2,6 @@ package rtsp
import ( import (
"fmt" "fmt"
"github.com/AlexxIT/go2rtc/pkg/core"
"github.com/pion/rtcp" "github.com/pion/rtcp"
"github.com/pion/rtp" "github.com/pion/rtp"
"github.com/pion/webrtc/v4" "github.com/pion/webrtc/v4"
@@ -35,17 +34,17 @@ func (ns *Stream) Close() error {
return nil return nil
} }
func (s *Sender) GetMedia() (medias []*core.Media, err error) { func (s *Sender) GetMedia() (medias []*Media, err error) {
if s.SubAudio && s.Publisher.PubAudio && s.Publisher.HasAudioTrack() { if s.SubAudio && s.Publisher.PubAudio && s.Publisher.HasAudioTrack() {
audioTrack := s.Publisher.GetAudioTrack(reflect.TypeOf((*mrtp.RTPAudio)(nil))) audioTrack := s.Publisher.GetAudioTrack(reflect.TypeOf((*mrtp.RTPAudio)(nil)))
if err = audioTrack.WaitReady(); err != nil { if err = audioTrack.WaitReady(); err != nil {
return return
} }
parameter := audioTrack.ICodecCtx.(mrtp.IRTPCtx).GetRTPCodecParameter() parameter := audioTrack.ICodecCtx.(mrtp.IRTPCtx).GetRTPCodecParameter()
media := &core.Media{ media := &Media{
Kind: "audio", Kind: "audio",
Direction: core.DirectionRecvonly, Direction: DirectionRecvonly,
Codecs: []*core.Codec{{ Codecs: []*Codec{{
Name: parameter.MimeType[6:], Name: parameter.MimeType[6:],
ClockRate: parameter.ClockRate, ClockRate: parameter.ClockRate,
Channels: parameter.Channels, Channels: parameter.Channels,
@@ -64,17 +63,17 @@ func (s *Sender) GetMedia() (medias []*core.Media, err error) {
return return
} }
parameter := videoTrack.ICodecCtx.(mrtp.IRTPCtx).GetRTPCodecParameter() parameter := videoTrack.ICodecCtx.(mrtp.IRTPCtx).GetRTPCodecParameter()
c := core.Codec{ c := Codec{
Name: parameter.MimeType[6:], Name: parameter.MimeType[6:],
ClockRate: parameter.ClockRate, ClockRate: parameter.ClockRate,
Channels: parameter.Channels, Channels: parameter.Channels,
FmtpLine: parameter.SDPFmtpLine, FmtpLine: parameter.SDPFmtpLine,
PayloadType: uint8(parameter.PayloadType), PayloadType: uint8(parameter.PayloadType),
} }
media := &core.Media{ media := &Media{
Kind: "video", Kind: "video",
Direction: core.DirectionRecvonly, Direction: DirectionRecvonly,
Codecs: []*core.Codec{&c}, Codecs: []*Codec{&c},
ID: fmt.Sprintf("trackID=%d", len(medias)), ID: fmt.Sprintf("trackID=%d", len(medias)),
} }
s.VideoChannelID = len(medias) << 1 s.VideoChannelID = len(medias) << 1
@@ -120,7 +119,7 @@ func (s *Sender) Send() (err error) {
return s.send() return s.send()
} }
func (r *Receiver) SetMedia(medias []*core.Media) (err error) { func (r *Receiver) SetMedia(medias []*Media) (err error) {
r.AudioChannelID = -1 r.AudioChannelID = -1
r.VideoChannelID = -1 r.VideoChannelID = -1
for i, media := range medias { for i, media := range medias {
@@ -156,9 +155,9 @@ func (r *Receiver) SetMedia(medias []*core.Media) (err error) {
} }
func (r *Receiver) Receive() (err error) { func (r *Receiver) Receive() (err error) {
audioFrame, videoFrame := &mrtp.RTPAudio{}, &mrtp.RTPVideo{} audioFrame, videoFrame := &mrtp.RTPAudio{}, &mrtp.RTPVideo{}
audioFrame.ScalableMemoryAllocator = r.MemoryAllocator audioFrame.SetAllocator(r.MemoryAllocator)
audioFrame.RTPCodecParameters = r.AudioCodecParameters audioFrame.RTPCodecParameters = r.AudioCodecParameters
videoFrame.ScalableMemoryAllocator = r.MemoryAllocator videoFrame.SetAllocator(r.MemoryAllocator)
videoFrame.RTPCodecParameters = r.VideoCodecParameters videoFrame.RTPCodecParameters = r.VideoCodecParameters
var channelID byte var channelID byte
var buf []byte var buf []byte
@@ -189,7 +188,7 @@ func (r *Receiver) Receive() (err error) {
audioFrame.AddRecycleBytes(buf) audioFrame.AddRecycleBytes(buf)
audioFrame.Packets = []*rtp.Packet{packet} audioFrame.Packets = []*rtp.Packet{packet}
audioFrame.RTPCodecParameters = r.VideoCodecParameters audioFrame.RTPCodecParameters = r.VideoCodecParameters
audioFrame.ScalableMemoryAllocator = r.MemoryAllocator audioFrame.SetAllocator(r.MemoryAllocator)
} }
case r.VideoChannelID: case r.VideoChannelID:
if !r.PubVideo { if !r.PubVideo {
@@ -210,7 +209,7 @@ func (r *Receiver) Receive() (err error) {
videoFrame.AddRecycleBytes(buf) videoFrame.AddRecycleBytes(buf)
videoFrame.Packets = []*rtp.Packet{packet} videoFrame.Packets = []*rtp.Packet{packet}
videoFrame.RTPCodecParameters = r.VideoCodecParameters videoFrame.RTPCodecParameters = r.VideoCodecParameters
videoFrame.ScalableMemoryAllocator = r.MemoryAllocator videoFrame.SetAllocator(r.MemoryAllocator)
} }
default: default:

View File

@@ -13,67 +13,60 @@ import (
"m7s.live/m7s/v5/plugin/stress/pb" "m7s.live/m7s/v5/plugin/stress/pb"
) )
func (r *StressPlugin) PushRTMP(ctx context.Context, req *pb.PushRequest) (res *gpb.SuccessResponse, err error) { func (r *StressPlugin) pull(count int, format, url string, newFunc func() m7s.PullHandler) error {
l := r.pushers.Length if i := r.pullers.Length; count > i {
for i := range req.PushCount { for j := i; j < count; j++ {
pusher, err := r.Push(req.StreamPath, fmt.Sprintf("rtmp://%s/stress/%d", req.RemoteHost, int(i)+l)) puller, err := r.Pull(fmt.Sprintf("stress/%d", j), fmt.Sprintf(format, url))
if err != nil { if err != nil {
return nil, err return err
} }
go r.startPush(pusher, &rtmp.Client{}) go r.startPull(puller, newFunc())
} }
return &gpb.SuccessResponse{}, nil } else if count < i {
for j := i; j > count; j-- {
r.pullers.Items[j-1].Stop(pkg.ErrStopFromAPI)
r.pullers.Remove(r.pullers.Items[j-1])
}
}
return nil
}
func (r *StressPlugin) push(count int, streamPath, format, remoteHost string, newFunc func() m7s.PushHandler) (err error) {
if i := r.pushers.Length; count > i {
for j := i; j < count; j++ {
pusher, err := r.Push(streamPath, fmt.Sprintf(format, remoteHost, j))
if err != nil {
return err
}
go r.startPush(pusher, newFunc())
}
} else if count < i {
for j := i; j > count; j-- {
r.pushers.Items[j-1].Stop(pkg.ErrStopFromAPI)
r.pushers.Remove(r.pushers.Items[j-1])
}
}
return nil
}
func (r *StressPlugin) PushRTMP(ctx context.Context, req *pb.PushRequest) (res *gpb.SuccessResponse, err error) {
return &gpb.SuccessResponse{}, r.push(int(req.PushCount), req.StreamPath, "rtmp://%s/stress/%d", req.RemoteHost, rtmp.NewPushHandler)
} }
func (r *StressPlugin) PushRTSP(ctx context.Context, req *pb.PushRequest) (res *gpb.SuccessResponse, err error) { func (r *StressPlugin) PushRTSP(ctx context.Context, req *pb.PushRequest) (res *gpb.SuccessResponse, err error) {
l := r.pushers.Length return &gpb.SuccessResponse{}, r.push(int(req.PushCount), req.StreamPath, "rtsp://%s/stress/%d", req.RemoteHost, rtsp.NewPushHandler)
for i := range req.PushCount {
pusher, err := r.Push(req.StreamPath, fmt.Sprintf("rtsp://%s/stress/%d", req.RemoteHost, int(i)+l))
if err != nil {
return nil, err
}
go r.startPush(pusher, &rtsp.Client{})
}
return &gpb.SuccessResponse{}, nil
} }
func (r *StressPlugin) PullRTMP(ctx context.Context, req *pb.PullRequest) (res *gpb.SuccessResponse, err error) { func (r *StressPlugin) PullRTMP(ctx context.Context, req *pb.PullRequest) (res *gpb.SuccessResponse, err error) {
i := r.pullers.Length return &gpb.SuccessResponse{}, r.pull(int(req.PullCount), "rtmp://%s", req.RemoteURL, rtmp.NewPullHandler)
for range req.PullCount {
puller, err := r.Pull(fmt.Sprintf("stress/%d", i), fmt.Sprintf("rtmp://%s", req.RemoteURL))
if err != nil {
return nil, err
}
go r.startPull(puller, &rtmp.Client{})
i++
}
return &gpb.SuccessResponse{}, nil
} }
func (r *StressPlugin) PullRTSP(ctx context.Context, req *pb.PullRequest) (res *gpb.SuccessResponse, err error) { func (r *StressPlugin) PullRTSP(ctx context.Context, req *pb.PullRequest) (res *gpb.SuccessResponse, err error) {
i := r.pullers.Length return &gpb.SuccessResponse{}, r.pull(int(req.PullCount), "rtsp://%s", req.RemoteURL, rtsp.NewPullHandler)
for range req.PullCount {
puller, err := r.Pull(fmt.Sprintf("stress/%d", i), fmt.Sprintf("rtsp://%s", req.RemoteURL))
if err != nil {
return nil, err
}
go r.startPull(puller, &rtsp.Client{})
i++
}
return &gpb.SuccessResponse{}, nil
} }
func (r *StressPlugin) PullHDL(ctx context.Context, req *pb.PullRequest) (res *gpb.SuccessResponse, err error) { func (r *StressPlugin) PullHDL(ctx context.Context, req *pb.PullRequest) (res *gpb.SuccessResponse, err error) {
i := r.pullers.Length return &gpb.SuccessResponse{}, r.pull(int(req.PullCount), "http://%s", req.RemoteURL, hdl.NewPullHandler)
for range req.PullCount {
puller, err := r.Pull(fmt.Sprintf("stress/%d", i), fmt.Sprintf("http://%s", req.RemoteURL))
if err != nil {
return nil, err
}
go r.startPull(puller, hdl.NewHDLPuller())
i++
}
return &gpb.SuccessResponse{}, nil
} }
func (r *StressPlugin) startPush(pusher *m7s.Pusher, handler m7s.PushHandler) { func (r *StressPlugin) startPush(pusher *m7s.Pusher, handler m7s.PushHandler) {

View File

@@ -0,0 +1,383 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.28.1
// protoc v3.19.1
// source: stress.proto
package pb
import (
_ "google.golang.org/genproto/googleapis/api/annotations"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
emptypb "google.golang.org/protobuf/types/known/emptypb"
pb "m7s.live/m7s/v5/pb"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
type CountResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
PushCount uint32 `protobuf:"varint,1,opt,name=pushCount,proto3" json:"pushCount,omitempty"`
PullCount uint32 `protobuf:"varint,2,opt,name=pullCount,proto3" json:"pullCount,omitempty"`
}
func (x *CountResponse) Reset() {
*x = CountResponse{}
if protoimpl.UnsafeEnabled {
mi := &file_stress_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *CountResponse) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*CountResponse) ProtoMessage() {}
func (x *CountResponse) ProtoReflect() protoreflect.Message {
mi := &file_stress_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use CountResponse.ProtoReflect.Descriptor instead.
func (*CountResponse) Descriptor() ([]byte, []int) {
return file_stress_proto_rawDescGZIP(), []int{0}
}
func (x *CountResponse) GetPushCount() uint32 {
if x != nil {
return x.PushCount
}
return 0
}
func (x *CountResponse) GetPullCount() uint32 {
if x != nil {
return x.PullCount
}
return 0
}
type PushRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
StreamPath string `protobuf:"bytes,1,opt,name=streamPath,proto3" json:"streamPath,omitempty"`
RemoteHost string `protobuf:"bytes,2,opt,name=remoteHost,proto3" json:"remoteHost,omitempty"`
PushCount int32 `protobuf:"varint,3,opt,name=pushCount,proto3" json:"pushCount,omitempty"`
}
func (x *PushRequest) Reset() {
*x = PushRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_stress_proto_msgTypes[1]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PushRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PushRequest) ProtoMessage() {}
func (x *PushRequest) ProtoReflect() protoreflect.Message {
mi := &file_stress_proto_msgTypes[1]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PushRequest.ProtoReflect.Descriptor instead.
func (*PushRequest) Descriptor() ([]byte, []int) {
return file_stress_proto_rawDescGZIP(), []int{1}
}
func (x *PushRequest) GetStreamPath() string {
if x != nil {
return x.StreamPath
}
return ""
}
func (x *PushRequest) GetRemoteHost() string {
if x != nil {
return x.RemoteHost
}
return ""
}
func (x *PushRequest) GetPushCount() int32 {
if x != nil {
return x.PushCount
}
return 0
}
type PullRequest struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
RemoteURL string `protobuf:"bytes,1,opt,name=remoteURL,proto3" json:"remoteURL,omitempty"`
PullCount int32 `protobuf:"varint,2,opt,name=pullCount,proto3" json:"pullCount,omitempty"`
}
func (x *PullRequest) Reset() {
*x = PullRequest{}
if protoimpl.UnsafeEnabled {
mi := &file_stress_proto_msgTypes[2]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *PullRequest) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*PullRequest) ProtoMessage() {}
func (x *PullRequest) ProtoReflect() protoreflect.Message {
mi := &file_stress_proto_msgTypes[2]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use PullRequest.ProtoReflect.Descriptor instead.
func (*PullRequest) Descriptor() ([]byte, []int) {
return file_stress_proto_rawDescGZIP(), []int{2}
}
func (x *PullRequest) GetRemoteURL() string {
if x != nil {
return x.RemoteURL
}
return ""
}
func (x *PullRequest) GetPullCount() int32 {
if x != nil {
return x.PullCount
}
return 0
}
var File_stress_proto protoreflect.FileDescriptor
var file_stress_proto_rawDesc = []byte{
0x0a, 0x0c, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06,
0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f,
0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x1a, 0x0c, 0x67, 0x6c, 0x6f, 0x62, 0x61, 0x6c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22,
0x4b, 0x0a, 0x0d, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65,
0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x01, 0x20,
0x01, 0x28, 0x0d, 0x52, 0x09, 0x70, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x1c,
0x0a, 0x09, 0x70, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28,
0x0d, 0x52, 0x09, 0x70, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x6b, 0x0a, 0x0b,
0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1e, 0x0a, 0x0a, 0x73,
0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x73, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x50, 0x61, 0x74, 0x68, 0x12, 0x1e, 0x0a, 0x0a, 0x72,
0x65, 0x6d, 0x6f, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
0x0a, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x48, 0x6f, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x70,
0x75, 0x73, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09,
0x70, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x22, 0x49, 0x0a, 0x0b, 0x50, 0x75, 0x6c,
0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x72, 0x65, 0x6d, 0x6f,
0x74, 0x65, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x72, 0x65, 0x6d,
0x6f, 0x74, 0x65, 0x55, 0x52, 0x4c, 0x12, 0x1c, 0x0a, 0x09, 0x70, 0x75, 0x6c, 0x6c, 0x43, 0x6f,
0x75, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x05, 0x52, 0x09, 0x70, 0x75, 0x6c, 0x6c, 0x43,
0x6f, 0x75, 0x6e, 0x74, 0x32, 0x84, 0x06, 0x0a, 0x03, 0x61, 0x70, 0x69, 0x12, 0x63, 0x0a, 0x08,
0x50, 0x75, 0x73, 0x68, 0x52, 0x54, 0x4d, 0x50, 0x12, 0x13, 0x2e, 0x73, 0x74, 0x72, 0x65, 0x73,
0x73, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e,
0x6d, 0x37, 0x73, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x22, 0x21, 0x2f, 0x73, 0x74,
0x72, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2f, 0x72, 0x74,
0x6d, 0x70, 0x2f, 0x7b, 0x70, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x7d, 0x3a, 0x01,
0x2a, 0x12, 0x63, 0x0a, 0x08, 0x50, 0x75, 0x73, 0x68, 0x52, 0x54, 0x53, 0x50, 0x12, 0x13, 0x2e,
0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x50, 0x75, 0x73, 0x68, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6d, 0x37, 0x73, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26,
0x22, 0x21, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x75,
0x73, 0x68, 0x2f, 0x72, 0x74, 0x73, 0x70, 0x2f, 0x7b, 0x70, 0x75, 0x73, 0x68, 0x43, 0x6f, 0x75,
0x6e, 0x74, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x63, 0x0a, 0x08, 0x50, 0x75, 0x6c, 0x6c, 0x52, 0x54,
0x4d, 0x50, 0x12, 0x13, 0x2e, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2e, 0x50, 0x75, 0x6c, 0x6c,
0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6d, 0x37, 0x73, 0x2e, 0x53, 0x75,
0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x26, 0x22, 0x21, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x70, 0x75, 0x6c, 0x6c, 0x2f, 0x72, 0x74, 0x6d, 0x70, 0x2f, 0x7b, 0x70, 0x75,
0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x7d, 0x3a, 0x01, 0x2a, 0x12, 0x63, 0x0a, 0x08, 0x50,
0x75, 0x6c, 0x6c, 0x52, 0x54, 0x53, 0x50, 0x12, 0x13, 0x2e, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73,
0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x14, 0x2e, 0x6d,
0x37, 0x73, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
0x73, 0x65, 0x22, 0x2c, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x26, 0x22, 0x21, 0x2f, 0x73, 0x74, 0x72,
0x65, 0x73, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x75, 0x6c, 0x6c, 0x2f, 0x72, 0x74, 0x73,
0x70, 0x2f, 0x7b, 0x70, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x7d, 0x3a, 0x01, 0x2a,
0x12, 0x61, 0x0a, 0x07, 0x50, 0x75, 0x6c, 0x6c, 0x48, 0x44, 0x4c, 0x12, 0x13, 0x2e, 0x73, 0x74,
0x72, 0x65, 0x73, 0x73, 0x2e, 0x50, 0x75, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x1a, 0x14, 0x2e, 0x6d, 0x37, 0x73, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65,
0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x22, 0x20,
0x2f, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x70, 0x75, 0x6c, 0x6c,
0x2f, 0x68, 0x64, 0x6c, 0x2f, 0x7b, 0x70, 0x75, 0x6c, 0x6c, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x7d,
0x3a, 0x01, 0x2a, 0x12, 0x54, 0x0a, 0x08, 0x47, 0x65, 0x74, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x12,
0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75,
0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73,
0x2e, 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x19,
0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x12, 0x11, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2f,
0x61, 0x70, 0x69, 0x2f, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x57, 0x0a, 0x08, 0x53, 0x74, 0x6f,
0x70, 0x50, 0x75, 0x73, 0x68, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e,
0x6d, 0x37, 0x73, 0x2e, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x17, 0x22, 0x15, 0x2f, 0x73, 0x74,
0x72, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x2f, 0x70, 0x75,
0x73, 0x68, 0x12, 0x57, 0x0a, 0x08, 0x53, 0x74, 0x6f, 0x70, 0x50, 0x75, 0x6c, 0x6c, 0x12, 0x16,
0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x6d, 0x37, 0x73, 0x2e, 0x53, 0x75, 0x63,
0x63, 0x65, 0x73, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x1d, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x17, 0x22, 0x15, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2f, 0x61, 0x70,
0x69, 0x2f, 0x73, 0x74, 0x6f, 0x70, 0x2f, 0x70, 0x75, 0x6c, 0x6c, 0x42, 0x22, 0x5a, 0x20, 0x6d,
0x37, 0x73, 0x2e, 0x6c, 0x69, 0x76, 0x65, 0x2f, 0x6d, 0x37, 0x73, 0x2f, 0x76, 0x35, 0x2f, 0x70,
0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2f, 0x73, 0x74, 0x72, 0x65, 0x73, 0x73, 0x2f, 0x70, 0x62, 0x62,
0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
file_stress_proto_rawDescOnce sync.Once
file_stress_proto_rawDescData = file_stress_proto_rawDesc
)
func file_stress_proto_rawDescGZIP() []byte {
file_stress_proto_rawDescOnce.Do(func() {
file_stress_proto_rawDescData = protoimpl.X.CompressGZIP(file_stress_proto_rawDescData)
})
return file_stress_proto_rawDescData
}
var file_stress_proto_msgTypes = make([]protoimpl.MessageInfo, 3)
var file_stress_proto_goTypes = []interface{}{
(*CountResponse)(nil), // 0: stress.CountResponse
(*PushRequest)(nil), // 1: stress.PushRequest
(*PullRequest)(nil), // 2: stress.PullRequest
(*emptypb.Empty)(nil), // 3: google.protobuf.Empty
(*pb.SuccessResponse)(nil), // 4: m7s.SuccessResponse
}
var file_stress_proto_depIdxs = []int32{
1, // 0: stress.api.PushRTMP:input_type -> stress.PushRequest
1, // 1: stress.api.PushRTSP:input_type -> stress.PushRequest
2, // 2: stress.api.PullRTMP:input_type -> stress.PullRequest
2, // 3: stress.api.PullRTSP:input_type -> stress.PullRequest
2, // 4: stress.api.PullHDL:input_type -> stress.PullRequest
3, // 5: stress.api.GetCount:input_type -> google.protobuf.Empty
3, // 6: stress.api.StopPush:input_type -> google.protobuf.Empty
3, // 7: stress.api.StopPull:input_type -> google.protobuf.Empty
4, // 8: stress.api.PushRTMP:output_type -> m7s.SuccessResponse
4, // 9: stress.api.PushRTSP:output_type -> m7s.SuccessResponse
4, // 10: stress.api.PullRTMP:output_type -> m7s.SuccessResponse
4, // 11: stress.api.PullRTSP:output_type -> m7s.SuccessResponse
4, // 12: stress.api.PullHDL:output_type -> m7s.SuccessResponse
0, // 13: stress.api.GetCount:output_type -> stress.CountResponse
4, // 14: stress.api.StopPush:output_type -> m7s.SuccessResponse
4, // 15: stress.api.StopPull:output_type -> m7s.SuccessResponse
8, // [8:16] is the sub-list for method output_type
0, // [0:8] is the sub-list for method input_type
0, // [0:0] is the sub-list for extension type_name
0, // [0:0] is the sub-list for extension extendee
0, // [0:0] is the sub-list for field type_name
}
func init() { file_stress_proto_init() }
func file_stress_proto_init() {
if File_stress_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_stress_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*CountResponse); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_stress_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PushRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
file_stress_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*PullRequest); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_stress_proto_rawDesc,
NumEnums: 0,
NumMessages: 3,
NumExtensions: 0,
NumServices: 1,
},
GoTypes: file_stress_proto_goTypes,
DependencyIndexes: file_stress_proto_depIdxs,
MessageInfos: file_stress_proto_msgTypes,
}.Build()
File_stress_proto = out.File
file_stress_proto_rawDesc = nil
file_stress_proto_goTypes = nil
file_stress_proto_depIdxs = nil
}

View File

@@ -0,0 +1,849 @@
// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: stress.proto
/*
Package pb is a reverse proxy.
It translates gRPC into RESTful JSON APIs.
*/
package pb
import (
"context"
"io"
"net/http"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/grpc-ecosystem/grpc-gateway/v2/utilities"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/emptypb"
)
// Suppress "imported and not used" errors
var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray
var _ = metadata.Join
func request_Api_PushRTMP_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PushRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pushCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pushCount")
}
protoReq.PushCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pushCount", err)
}
msg, err := client.PushRTMP(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Api_PushRTMP_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PushRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pushCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pushCount")
}
protoReq.PushCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pushCount", err)
}
msg, err := server.PushRTMP(ctx, &protoReq)
return msg, metadata, err
}
func request_Api_PushRTSP_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PushRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pushCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pushCount")
}
protoReq.PushCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pushCount", err)
}
msg, err := client.PushRTSP(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Api_PushRTSP_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PushRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pushCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pushCount")
}
protoReq.PushCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pushCount", err)
}
msg, err := server.PushRTSP(ctx, &protoReq)
return msg, metadata, err
}
func request_Api_PullRTMP_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PullRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pullCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pullCount")
}
protoReq.PullCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pullCount", err)
}
msg, err := client.PullRTMP(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Api_PullRTMP_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PullRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pullCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pullCount")
}
protoReq.PullCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pullCount", err)
}
msg, err := server.PullRTMP(ctx, &protoReq)
return msg, metadata, err
}
func request_Api_PullRTSP_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PullRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pullCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pullCount")
}
protoReq.PullCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pullCount", err)
}
msg, err := client.PullRTSP(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Api_PullRTSP_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PullRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pullCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pullCount")
}
protoReq.PullCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pullCount", err)
}
msg, err := server.PullRTSP(ctx, &protoReq)
return msg, metadata, err
}
func request_Api_PullHDL_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PullRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pullCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pullCount")
}
protoReq.PullCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pullCount", err)
}
msg, err := client.PullHDL(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Api_PullHDL_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq PullRequest
var metadata runtime.ServerMetadata
if err := marshaler.NewDecoder(req.Body).Decode(&protoReq); err != nil && err != io.EOF {
return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
}
var (
val string
ok bool
err error
_ = err
)
val, ok = pathParams["pullCount"]
if !ok {
return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "pullCount")
}
protoReq.PullCount, err = runtime.Int32(val)
if err != nil {
return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "pullCount", err)
}
msg, err := server.PullHDL(ctx, &protoReq)
return msg, metadata, err
}
func request_Api_GetCount_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq emptypb.Empty
var metadata runtime.ServerMetadata
msg, err := client.GetCount(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Api_GetCount_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq emptypb.Empty
var metadata runtime.ServerMetadata
msg, err := server.GetCount(ctx, &protoReq)
return msg, metadata, err
}
func request_Api_StopPush_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq emptypb.Empty
var metadata runtime.ServerMetadata
msg, err := client.StopPush(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Api_StopPush_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq emptypb.Empty
var metadata runtime.ServerMetadata
msg, err := server.StopPush(ctx, &protoReq)
return msg, metadata, err
}
func request_Api_StopPull_0(ctx context.Context, marshaler runtime.Marshaler, client ApiClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq emptypb.Empty
var metadata runtime.ServerMetadata
msg, err := client.StopPull(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
return msg, metadata, err
}
func local_request_Api_StopPull_0(ctx context.Context, marshaler runtime.Marshaler, server ApiServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
var protoReq emptypb.Empty
var metadata runtime.ServerMetadata
msg, err := server.StopPull(ctx, &protoReq)
return msg, metadata, err
}
// RegisterApiHandlerServer registers the http handlers for service Api to "mux".
// UnaryRPC :call ApiServer directly.
// StreamingRPC :currently unsupported pending https://github.com/grpc/grpc-go/issues/906.
// Note that using this registration option will cause many gRPC library features to stop working. Consider using RegisterApiHandlerFromEndpoint instead.
func RegisterApiHandlerServer(ctx context.Context, mux *runtime.ServeMux, server ApiServer) error {
mux.Handle("POST", pattern_Api_PushRTMP_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/stress.Api/PushRTMP", runtime.WithHTTPPathPattern("/stress/api/push/rtmp/{pushCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Api_PushRTMP_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PushRTMP_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_PushRTSP_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/stress.Api/PushRTSP", runtime.WithHTTPPathPattern("/stress/api/push/rtsp/{pushCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Api_PushRTSP_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PushRTSP_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_PullRTMP_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/stress.Api/PullRTMP", runtime.WithHTTPPathPattern("/stress/api/pull/rtmp/{pullCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Api_PullRTMP_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PullRTMP_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_PullRTSP_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/stress.Api/PullRTSP", runtime.WithHTTPPathPattern("/stress/api/pull/rtsp/{pullCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Api_PullRTSP_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PullRTSP_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_PullHDL_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/stress.Api/PullHDL", runtime.WithHTTPPathPattern("/stress/api/pull/hdl/{pullCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Api_PullHDL_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PullHDL_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Api_GetCount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/stress.Api/GetCount", runtime.WithHTTPPathPattern("/stress/api/count"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Api_GetCount_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_GetCount_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_StopPush_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/stress.Api/StopPush", runtime.WithHTTPPathPattern("/stress/api/stop/push"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Api_StopPush_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_StopPush_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_StopPull_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
var stream runtime.ServerTransportStream
ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateIncomingContext(ctx, mux, req, "/stress.Api/StopPull", runtime.WithHTTPPathPattern("/stress/api/stop/pull"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := local_request_Api_StopPull_0(annotatedContext, inboundMarshaler, server, req, pathParams)
md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_StopPull_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
// RegisterApiHandlerFromEndpoint is same as RegisterApiHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterApiHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
conn, err := grpc.DialContext(ctx, endpoint, opts...)
if err != nil {
return err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return RegisterApiHandler(ctx, mux, conn)
}
// RegisterApiHandler registers the http handlers for service Api to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterApiHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
return RegisterApiHandlerClient(ctx, mux, NewApiClient(conn))
}
// RegisterApiHandlerClient registers the http handlers for service Api
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "ApiClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "ApiClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "ApiClient" to call the correct interceptors.
func RegisterApiHandlerClient(ctx context.Context, mux *runtime.ServeMux, client ApiClient) error {
mux.Handle("POST", pattern_Api_PushRTMP_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/stress.Api/PushRTMP", runtime.WithHTTPPathPattern("/stress/api/push/rtmp/{pushCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Api_PushRTMP_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PushRTMP_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_PushRTSP_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/stress.Api/PushRTSP", runtime.WithHTTPPathPattern("/stress/api/push/rtsp/{pushCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Api_PushRTSP_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PushRTSP_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_PullRTMP_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/stress.Api/PullRTMP", runtime.WithHTTPPathPattern("/stress/api/pull/rtmp/{pullCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Api_PullRTMP_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PullRTMP_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_PullRTSP_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/stress.Api/PullRTSP", runtime.WithHTTPPathPattern("/stress/api/pull/rtsp/{pullCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Api_PullRTSP_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PullRTSP_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_PullHDL_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/stress.Api/PullHDL", runtime.WithHTTPPathPattern("/stress/api/pull/hdl/{pullCount}"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Api_PullHDL_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_PullHDL_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("GET", pattern_Api_GetCount_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/stress.Api/GetCount", runtime.WithHTTPPathPattern("/stress/api/count"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Api_GetCount_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_GetCount_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_StopPush_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/stress.Api/StopPush", runtime.WithHTTPPathPattern("/stress/api/stop/push"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Api_StopPush_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_StopPush_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
mux.Handle("POST", pattern_Api_StopPull_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
ctx, cancel := context.WithCancel(req.Context())
defer cancel()
inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
var err error
var annotatedContext context.Context
annotatedContext, err = runtime.AnnotateContext(ctx, mux, req, "/stress.Api/StopPull", runtime.WithHTTPPathPattern("/stress/api/stop/pull"))
if err != nil {
runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
return
}
resp, md, err := request_Api_StopPull_0(annotatedContext, inboundMarshaler, client, req, pathParams)
annotatedContext = runtime.NewServerMetadataContext(annotatedContext, md)
if err != nil {
runtime.HTTPError(annotatedContext, mux, outboundMarshaler, w, req, err)
return
}
forward_Api_StopPull_0(annotatedContext, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
})
return nil
}
var (
pattern_Api_PushRTMP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"stress", "api", "push", "rtmp", "pushCount"}, ""))
pattern_Api_PushRTSP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"stress", "api", "push", "rtsp", "pushCount"}, ""))
pattern_Api_PullRTMP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"stress", "api", "pull", "rtmp", "pullCount"}, ""))
pattern_Api_PullRTSP_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"stress", "api", "pull", "rtsp", "pullCount"}, ""))
pattern_Api_PullHDL_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3, 1, 0, 4, 1, 5, 4}, []string{"stress", "api", "pull", "hdl", "pullCount"}, ""))
pattern_Api_GetCount_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"stress", "api", "count"}, ""))
pattern_Api_StopPush_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"stress", "api", "stop", "push"}, ""))
pattern_Api_StopPull_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 2, 3}, []string{"stress", "api", "stop", "pull"}, ""))
)
var (
forward_Api_PushRTMP_0 = runtime.ForwardResponseMessage
forward_Api_PushRTSP_0 = runtime.ForwardResponseMessage
forward_Api_PullRTMP_0 = runtime.ForwardResponseMessage
forward_Api_PullRTSP_0 = runtime.ForwardResponseMessage
forward_Api_PullHDL_0 = runtime.ForwardResponseMessage
forward_Api_GetCount_0 = runtime.ForwardResponseMessage
forward_Api_StopPush_0 = runtime.ForwardResponseMessage
forward_Api_StopPull_0 = runtime.ForwardResponseMessage
)

View File

@@ -0,0 +1,359 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
// - protoc-gen-go-grpc v1.2.0
// - protoc v3.19.1
// source: stress.proto
package pb
import (
context "context"
grpc "google.golang.org/grpc"
codes "google.golang.org/grpc/codes"
status "google.golang.org/grpc/status"
emptypb "google.golang.org/protobuf/types/known/emptypb"
pb "m7s.live/m7s/v5/pb"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the grpc package it is being compiled against.
// Requires gRPC-Go v1.32.0 or later.
const _ = grpc.SupportPackageIsVersion7
// ApiClient is the client API for Api service.
//
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
type ApiClient interface {
PushRTMP(ctx context.Context, in *PushRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error)
PushRTSP(ctx context.Context, in *PushRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error)
PullRTMP(ctx context.Context, in *PullRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error)
PullRTSP(ctx context.Context, in *PullRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error)
PullHDL(ctx context.Context, in *PullRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error)
GetCount(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CountResponse, error)
StopPush(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*pb.SuccessResponse, error)
StopPull(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*pb.SuccessResponse, error)
}
type apiClient struct {
cc grpc.ClientConnInterface
}
func NewApiClient(cc grpc.ClientConnInterface) ApiClient {
return &apiClient{cc}
}
func (c *apiClient) PushRTMP(ctx context.Context, in *PushRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error) {
out := new(pb.SuccessResponse)
err := c.cc.Invoke(ctx, "/stress.api/PushRTMP", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) PushRTSP(ctx context.Context, in *PushRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error) {
out := new(pb.SuccessResponse)
err := c.cc.Invoke(ctx, "/stress.api/PushRTSP", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) PullRTMP(ctx context.Context, in *PullRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error) {
out := new(pb.SuccessResponse)
err := c.cc.Invoke(ctx, "/stress.api/PullRTMP", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) PullRTSP(ctx context.Context, in *PullRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error) {
out := new(pb.SuccessResponse)
err := c.cc.Invoke(ctx, "/stress.api/PullRTSP", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) PullHDL(ctx context.Context, in *PullRequest, opts ...grpc.CallOption) (*pb.SuccessResponse, error) {
out := new(pb.SuccessResponse)
err := c.cc.Invoke(ctx, "/stress.api/PullHDL", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) GetCount(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*CountResponse, error) {
out := new(CountResponse)
err := c.cc.Invoke(ctx, "/stress.api/GetCount", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) StopPush(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*pb.SuccessResponse, error) {
out := new(pb.SuccessResponse)
err := c.cc.Invoke(ctx, "/stress.api/StopPush", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
func (c *apiClient) StopPull(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (*pb.SuccessResponse, error) {
out := new(pb.SuccessResponse)
err := c.cc.Invoke(ctx, "/stress.api/StopPull", in, out, opts...)
if err != nil {
return nil, err
}
return out, nil
}
// ApiServer is the server API for Api service.
// All implementations must embed UnimplementedApiServer
// for forward compatibility
type ApiServer interface {
PushRTMP(context.Context, *PushRequest) (*pb.SuccessResponse, error)
PushRTSP(context.Context, *PushRequest) (*pb.SuccessResponse, error)
PullRTMP(context.Context, *PullRequest) (*pb.SuccessResponse, error)
PullRTSP(context.Context, *PullRequest) (*pb.SuccessResponse, error)
PullHDL(context.Context, *PullRequest) (*pb.SuccessResponse, error)
GetCount(context.Context, *emptypb.Empty) (*CountResponse, error)
StopPush(context.Context, *emptypb.Empty) (*pb.SuccessResponse, error)
StopPull(context.Context, *emptypb.Empty) (*pb.SuccessResponse, error)
mustEmbedUnimplementedApiServer()
}
// UnimplementedApiServer must be embedded to have forward compatible implementations.
type UnimplementedApiServer struct {
}
func (UnimplementedApiServer) PushRTMP(context.Context, *PushRequest) (*pb.SuccessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PushRTMP not implemented")
}
func (UnimplementedApiServer) PushRTSP(context.Context, *PushRequest) (*pb.SuccessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PushRTSP not implemented")
}
func (UnimplementedApiServer) PullRTMP(context.Context, *PullRequest) (*pb.SuccessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PullRTMP not implemented")
}
func (UnimplementedApiServer) PullRTSP(context.Context, *PullRequest) (*pb.SuccessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PullRTSP not implemented")
}
func (UnimplementedApiServer) PullHDL(context.Context, *PullRequest) (*pb.SuccessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method PullHDL not implemented")
}
func (UnimplementedApiServer) GetCount(context.Context, *emptypb.Empty) (*CountResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method GetCount not implemented")
}
func (UnimplementedApiServer) StopPush(context.Context, *emptypb.Empty) (*pb.SuccessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method StopPush not implemented")
}
func (UnimplementedApiServer) StopPull(context.Context, *emptypb.Empty) (*pb.SuccessResponse, error) {
return nil, status.Errorf(codes.Unimplemented, "method StopPull not implemented")
}
func (UnimplementedApiServer) mustEmbedUnimplementedApiServer() {}
// UnsafeApiServer may be embedded to opt out of forward compatibility for this service.
// Use of this interface is not recommended, as added methods to ApiServer will
// result in compilation errors.
type UnsafeApiServer interface {
mustEmbedUnimplementedApiServer()
}
func RegisterApiServer(s grpc.ServiceRegistrar, srv ApiServer) {
s.RegisterService(&Api_ServiceDesc, srv)
}
func _Api_PushRTMP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PushRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).PushRTMP(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/stress.api/PushRTMP",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).PushRTMP(ctx, req.(*PushRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Api_PushRTSP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PushRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).PushRTSP(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/stress.api/PushRTSP",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).PushRTSP(ctx, req.(*PushRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Api_PullRTMP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PullRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).PullRTMP(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/stress.api/PullRTMP",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).PullRTMP(ctx, req.(*PullRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Api_PullRTSP_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PullRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).PullRTSP(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/stress.api/PullRTSP",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).PullRTSP(ctx, req.(*PullRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Api_PullHDL_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(PullRequest)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).PullHDL(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/stress.api/PullHDL",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).PullHDL(ctx, req.(*PullRequest))
}
return interceptor(ctx, in, info, handler)
}
func _Api_GetCount_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(emptypb.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).GetCount(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/stress.api/GetCount",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).GetCount(ctx, req.(*emptypb.Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Api_StopPush_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(emptypb.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).StopPush(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/stress.api/StopPush",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).StopPush(ctx, req.(*emptypb.Empty))
}
return interceptor(ctx, in, info, handler)
}
func _Api_StopPull_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(emptypb.Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(ApiServer).StopPull(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/stress.api/StopPull",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(ApiServer).StopPull(ctx, req.(*emptypb.Empty))
}
return interceptor(ctx, in, info, handler)
}
// Api_ServiceDesc is the grpc.ServiceDesc for Api service.
// It's only intended for direct use with grpc.RegisterService,
// and not to be introspected or modified (even as a copy)
var Api_ServiceDesc = grpc.ServiceDesc{
ServiceName: "stress.api",
HandlerType: (*ApiServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "PushRTMP",
Handler: _Api_PushRTMP_Handler,
},
{
MethodName: "PushRTSP",
Handler: _Api_PushRTSP_Handler,
},
{
MethodName: "PullRTMP",
Handler: _Api_PullRTMP_Handler,
},
{
MethodName: "PullRTSP",
Handler: _Api_PullRTSP_Handler,
},
{
MethodName: "PullHDL",
Handler: _Api_PullHDL_Handler,
},
{
MethodName: "GetCount",
Handler: _Api_GetCount_Handler,
},
{
MethodName: "StopPush",
Handler: _Api_StopPush_Handler,
},
{
MethodName: "StopPull",
Handler: _Api_StopPull_Handler,
},
},
Streams: []grpc.StreamDesc{},
Metadata: "stress.proto",
}

View File

@@ -186,19 +186,19 @@ func (conf *WebRTCPlugin) Push_(w http.ResponseWriter, r *http.Request) {
defer mem.Recycle() defer mem.Recycle()
frame := &mrtp.RTPAudio{} frame := &mrtp.RTPAudio{}
frame.RTPCodecParameters = &codecP frame.RTPCodecParameters = &codecP
frame.ScalableMemoryAllocator = mem frame.SetAllocator(mem)
for { for {
var packet rtp.Packet var packet rtp.Packet
buf := frame.Malloc(mrtp.MTUSize) buf := mem.Malloc(mrtp.MTUSize)
if n, _, err = track.Read(buf); err == nil { if n, _, err = track.Read(buf); err == nil {
frame.FreeRest(&buf, n) mem.FreeRest(&buf, n)
err = packet.Unmarshal(buf) err = packet.Unmarshal(buf)
} }
if err != nil { if err != nil {
return return
} }
if len(packet.Payload) == 0 { if len(packet.Payload) == 0 {
frame.Free(buf) mem.Free(buf)
continue continue
} }
if len(frame.Packets) == 0 || packet.Timestamp == frame.Packets[0].Timestamp { if len(frame.Packets) == 0 || packet.Timestamp == frame.Packets[0].Timestamp {
@@ -210,7 +210,7 @@ func (conf *WebRTCPlugin) Push_(w http.ResponseWriter, r *http.Request) {
frame.AddRecycleBytes(buf) frame.AddRecycleBytes(buf)
frame.Packets = []*rtp.Packet{&packet} frame.Packets = []*rtp.Packet{&packet}
frame.RTPCodecParameters = &codecP frame.RTPCodecParameters = &codecP
frame.ScalableMemoryAllocator = mem frame.SetAllocator(mem)
} }
} }
} else { } else {
@@ -222,7 +222,7 @@ func (conf *WebRTCPlugin) Push_(w http.ResponseWriter, r *http.Request) {
defer mem.Recycle() defer mem.Recycle()
frame := &mrtp.RTPVideo{} frame := &mrtp.RTPVideo{}
frame.RTPCodecParameters = &codecP frame.RTPCodecParameters = &codecP
frame.ScalableMemoryAllocator = mem frame.SetAllocator(mem)
for { for {
if time.Since(lastPLISent) > conf.PLI { if time.Since(lastPLISent) > conf.PLI {
if rtcpErr := conn.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}); rtcpErr != nil { if rtcpErr := conn.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}); rtcpErr != nil {
@@ -232,16 +232,16 @@ func (conf *WebRTCPlugin) Push_(w http.ResponseWriter, r *http.Request) {
lastPLISent = time.Now() lastPLISent = time.Now()
} }
var packet rtp.Packet var packet rtp.Packet
buf := frame.Malloc(mrtp.MTUSize) buf := mem.Malloc(mrtp.MTUSize)
if n, _, err = track.Read(buf); err == nil { if n, _, err = track.Read(buf); err == nil {
frame.FreeRest(&buf, n) mem.FreeRest(&buf, n)
err = packet.Unmarshal(buf) err = packet.Unmarshal(buf)
} }
if err != nil { if err != nil {
return return
} }
if len(packet.Payload) == 0 { if len(packet.Payload) == 0 {
frame.Free(buf) mem.Free(buf)
continue continue
} }
if len(frame.Packets) == 0 || packet.Timestamp == frame.Packets[0].Timestamp { if len(frame.Packets) == 0 || packet.Timestamp == frame.Packets[0].Timestamp {
@@ -255,7 +255,7 @@ func (conf *WebRTCPlugin) Push_(w http.ResponseWriter, r *http.Request) {
frame.AddRecycleBytes(buf) frame.AddRecycleBytes(buf)
frame.Packets = []*rtp.Packet{&packet} frame.Packets = []*rtp.Packet{&packet}
frame.RTPCodecParameters = &codecP frame.RTPCodecParameters = &codecP
frame.ScalableMemoryAllocator = mem frame.SetAllocator(mem)
} }
} }
} }
@@ -349,7 +349,7 @@ func (conf *WebRTCPlugin) Play_(w http.ResponseWriter, r *http.Request) {
} else { } else {
var rtpCtx mrtp.RTPData var rtpCtx mrtp.RTPData
var tmpAVTrack AVTrack var tmpAVTrack AVTrack
err = rtpCtx.ConvertCtx(vt.ICodecCtx, &tmpAVTrack) tmpAVTrack.ICodecCtx, tmpAVTrack.SequenceFrame, err = rtpCtx.ConvertCtx(vt.ICodecCtx)
if err == nil { if err == nil {
rcc = tmpAVTrack.ICodecCtx.(mrtp.IRTPCtx).GetRTPCodecParameter() rcc = tmpAVTrack.ICodecCtx.(mrtp.IRTPCtx).GetRTPCodecParameter()
} else { } else {

View File

@@ -248,7 +248,6 @@ func (p *Publisher) WriteVideo(data IAVFrame) (err error) {
} }
if t.Value.IDR { if t.Value.IDR {
if !t.IsReady() { if !t.IsReady() {
p.Info("ready")
t.Ready(nil) t.Ready(nil)
} else if idr != nil { } else if idr != nil {
p.GOP = int(t.Value.Sequence - idr.Value.Sequence) p.GOP = int(t.Value.Sequence - idr.Value.Sequence)
@@ -271,8 +270,7 @@ func (p *Publisher) WriteVideo(data IAVFrame) (err error) {
toType := track.FrameType.Elem() toType := track.FrameType.Elem()
toFrame := reflect.New(toType).Interface().(IAVFrame) toFrame := reflect.New(toType).Interface().(IAVFrame)
if track.ICodecCtx == nil { if track.ICodecCtx == nil {
err = toFrame.ConvertCtx(t.ICodecCtx, track) if track.ICodecCtx, track.SequenceFrame, err = toFrame.ConvertCtx(t.ICodecCtx); err != nil {
if err != nil {
track.Error("DecodeConfig", "err", err) track.Error("DecodeConfig", "err", err)
return return
} }
@@ -294,7 +292,7 @@ func (p *Publisher) WriteVideo(data IAVFrame) (err error) {
toFrame.SetAllocator(data.GetAllocator()) toFrame.SetAllocator(data.GetAllocator())
toFrame.Mux(track.ICodecCtx, &t.Value) toFrame.Mux(track.ICodecCtx, &t.Value)
if codecCtxChanged { if codecCtxChanged {
err = toFrame.ConvertCtx(t.ICodecCtx, track) track.ICodecCtx, track.SequenceFrame, err = toFrame.ConvertCtx(t.ICodecCtx)
} }
t.Value.Wraps = append(t.Value.Wraps, toFrame) t.Value.Wraps = append(t.Value.Wraps, toFrame)
if track.ICodecCtx != nil { if track.ICodecCtx != nil {
@@ -351,8 +349,7 @@ func (p *Publisher) WriteAudio(data IAVFrame) (err error) {
toType := track.FrameType.Elem() toType := track.FrameType.Elem()
toFrame := reflect.New(toType).Interface().(IAVFrame) toFrame := reflect.New(toType).Interface().(IAVFrame)
if track.ICodecCtx == nil { if track.ICodecCtx == nil {
err = toFrame.ConvertCtx(t.ICodecCtx, track) if track.ICodecCtx, track.SequenceFrame, err = toFrame.ConvertCtx(t.ICodecCtx); err != nil {
if err != nil {
track.Error("DecodeConfig", "err", err) track.Error("DecodeConfig", "err", err)
return return
} }
@@ -374,7 +371,7 @@ func (p *Publisher) WriteAudio(data IAVFrame) (err error) {
toFrame.SetAllocator(data.GetAllocator()) toFrame.SetAllocator(data.GetAllocator())
toFrame.Mux(track.ICodecCtx, &t.Value) toFrame.Mux(track.ICodecCtx, &t.Value)
if codecCtxChanged { if codecCtxChanged {
err = toFrame.ConvertCtx(t.ICodecCtx, track) track.ICodecCtx, track.SequenceFrame, err = toFrame.ConvertCtx(t.ICodecCtx)
} }
t.Value.Wraps = append(t.Value.Wraps, toFrame) t.Value.Wraps = append(t.Value.Wraps, toFrame)
if track.ICodecCtx != nil { if track.ICodecCtx != nil {

View File

@@ -102,6 +102,7 @@ func (s *Server) run(ctx context.Context, conf any) (err error) {
s.handler = s s.handler = s
s.config.HTTP.ListenAddrTLS = ":8443" s.config.HTTP.ListenAddrTLS = ":8443"
s.config.HTTP.ListenAddr = ":8080" s.config.HTTP.ListenAddr = ":8080"
s.config.TCP.ListenAddr = ":50051"
s.LogHandler.SetLevel(slog.LevelInfo) s.LogHandler.SetLevel(slog.LevelInfo)
s.LogHandler.Add(defaultLogHandler) s.LogHandler.Add(defaultLogHandler)
s.Logger = slog.New(&s.LogHandler).With("server", s.ID) s.Logger = slog.New(&s.LogHandler).With("server", s.ID)

View File

@@ -144,17 +144,31 @@ func (s *Subscriber) createVideoReader(dataType reflect.Type, startVideoTs time.
return return
} }
type SubscribeHandler[A any, V any] struct {
OnAudio func(A) error
OnVideo func(V) error
ProcessAudio chan func(*AVFrame)
ProcessVideo chan func(*AVFrame)
}
func PlayBlock[A any, V any](s *Subscriber, onAudio func(A) error, onVideo func(V) error) (err error) { func PlayBlock[A any, V any](s *Subscriber, onAudio func(A) error, onVideo func(V) error) (err error) {
var handler SubscribeHandler[A, V]
handler.OnAudio = onAudio
handler.OnVideo = onVideo
return PlayBlock0(s, handler)
}
func PlayBlock0[A any, V any](s *Subscriber, handler SubscribeHandler[A, V]) (err error) {
var a1, v1 reflect.Type var a1, v1 reflect.Type
var startAudioTs, startVideoTs time.Duration var startAudioTs, startVideoTs time.Duration
var initState = 0 var initState = 0
prePublisher := s.Publisher prePublisher := s.Publisher
var audioFrame, videoFrame *AVFrame var audioFrame, videoFrame *AVFrame
if s.SubAudio { if s.SubAudio {
a1 = reflect.TypeOf(onAudio).In(0) a1 = reflect.TypeOf(handler.OnAudio).In(0)
} }
if s.SubVideo { if s.SubVideo {
v1 = reflect.TypeOf(onVideo).In(0) v1 = reflect.TypeOf(handler.OnVideo).In(0)
} }
awi := s.createAudioReader(a1, startAudioTs) awi := s.createAudioReader(a1, startAudioTs)
vwi := s.createVideoReader(v1, startVideoTs) vwi := s.createVideoReader(v1, startVideoTs)
@@ -172,16 +186,21 @@ func PlayBlock[A any, V any](s *Subscriber, onAudio func(A) error, onVideo func(
if s.Enabled(s, TraceLevel) { if s.Enabled(s, TraceLevel) {
s.Trace("send audio frame", "seq", audioFrame.Sequence) s.Trace("send audio frame", "seq", audioFrame.Sequence)
} }
err = onAudio(audioFrame.Wraps[awi].(A)) err = handler.OnAudio(audioFrame.Wraps[awi].(A))
} else { } else {
s.AudioReader.StopRead() s.AudioReader.StopRead()
} }
} else { } else {
err = onAudio(any(audioFrame).(A)) err = handler.OnAudio(any(audioFrame).(A))
} }
if err != nil && !errors.Is(err, ErrInterrupt) { if err != nil && !errors.Is(err, ErrInterrupt) {
s.Stop(err) s.Stop(err)
} }
if handler.ProcessAudio != nil {
if f, ok := <-handler.ProcessAudio; ok {
f(audioFrame)
}
}
audioFrame = nil audioFrame = nil
return return
} }
@@ -191,16 +210,21 @@ func PlayBlock[A any, V any](s *Subscriber, onAudio func(A) error, onVideo func(
if s.Enabled(s, TraceLevel) { if s.Enabled(s, TraceLevel) {
s.Trace("send video frame", "seq", videoFrame.Sequence, "data", videoFrame.Wraps[vwi].String(), "size", videoFrame.Wraps[vwi].GetSize()) s.Trace("send video frame", "seq", videoFrame.Sequence, "data", videoFrame.Wraps[vwi].String(), "size", videoFrame.Wraps[vwi].GetSize())
} }
err = onVideo(videoFrame.Wraps[vwi].(V)) err = handler.OnVideo(videoFrame.Wraps[vwi].(V))
} else { } else {
s.VideoReader.StopRead() s.VideoReader.StopRead()
} }
} else { } else {
err = onVideo(any(videoFrame).(V)) err = handler.OnVideo(any(videoFrame).(V))
} }
if err != nil && !errors.Is(err, ErrInterrupt) { if err != nil && !errors.Is(err, ErrInterrupt) {
s.Stop(err) s.Stop(err)
} }
if handler.ProcessVideo != nil {
if f, ok := <-handler.ProcessVideo; ok {
f(videoFrame)
}
}
videoFrame = nil videoFrame = nil
return return
} }
@@ -242,7 +266,7 @@ func PlayBlock[A any, V any](s *Subscriber, onAudio func(A) error, onVideo func(
vr.LastCodecCtx = vr.Track.ICodecCtx vr.LastCodecCtx = vr.Track.ICodecCtx
if seqFrame := vr.Track.SequenceFrame; seqFrame != nil { if seqFrame := vr.Track.SequenceFrame; seqFrame != nil {
s.Debug("video codec changed", "data", seqFrame.String()) s.Debug("video codec changed", "data", seqFrame.String())
err = onVideo(seqFrame.(V)) err = handler.OnVideo(seqFrame.(V))
} }
} }
if ar != nil { if ar != nil {
@@ -294,7 +318,7 @@ func PlayBlock[A any, V any](s *Subscriber, onAudio func(A) error, onVideo func(
if ar.DecConfChanged() { if ar.DecConfChanged() {
ar.LastCodecCtx = ar.Track.ICodecCtx ar.LastCodecCtx = ar.Track.ICodecCtx
if seqFrame := ar.Track.SequenceFrame; seqFrame != nil { if seqFrame := ar.Track.SequenceFrame; seqFrame != nil {
err = onAudio(seqFrame.(A)) err = handler.OnAudio(seqFrame.(A))
} }
} }
if vr != nil && videoFrame != nil { if vr != nil && videoFrame != nil {

View File

@@ -1,6 +1,8 @@
package m7s package m7s
import "m7s.live/m7s/v5/pkg" import (
"m7s.live/m7s/v5/pkg"
)
type Transformer struct { type Transformer struct {
*Publisher *Publisher