mirror of
https://github.com/langhuihui/monibuca.git
synced 2025-10-12 06:40:04 +08:00
feat: h265
This commit is contained in:
6
api.go
6
api.go
@@ -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
|
||||||
|
@@ -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
126
example/xdp/main.go
Normal 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
13
go.mod
@@ -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
39
go.sum
@@ -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=
|
||||||
|
@@ -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) {
|
||||||
|
@@ -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
|
||||||
|
@@ -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())
|
||||||
|
}
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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 {
|
||||||
|
21
pkg/track.go
21
pkg/track.go
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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")
|
||||||
}
|
}
|
||||||
|
@@ -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
30
pkg/util/rm_disable.go
Normal 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
50
pkg/util/rm_enable.go
Normal 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
933
pkg/util/xdp_linux.go
Normal 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))
|
||||||
|
}
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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)
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
|
@@ -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 = ‘1111’b;
|
|
||||||
// unsigned int(12) min_spatial_segmentation_idc;
|
|
||||||
// bit(6) reserved = ‘111111’b;
|
|
||||||
// unsigned int(2) parallelismType;
|
|
||||||
// TODO chef: 这两个字段没有解析
|
|
||||||
util.PutBE(sh[18:20], 0xf000)
|
|
||||||
sh[20] = ctx.parallelismType | 0xfc
|
|
||||||
|
|
||||||
// bit(6) reserved = ‘111111’b;
|
|
||||||
// unsigned int(2) chromaFormat;
|
|
||||||
sh[21] = ctx.chromaFormat | 0xfc
|
|
||||||
|
|
||||||
// bit(5) reserved = ‘11111’b;
|
|
||||||
// unsigned int(3) bitDepthLumaMinus8;
|
|
||||||
sh[22] = ctx.bitDepthLumaMinus8 | 0xf8
|
|
||||||
|
|
||||||
// bit(5) reserved = ‘11111’b;
|
|
||||||
// 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)
|
|
||||||
}
|
|
||||||
|
@@ -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)
|
||||||
|
@@ -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]
|
||||||
|
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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
|
||||||
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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
302
plugin/rtsp/pkg/codec.go
Normal 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
202
plugin/rtsp/pkg/media.go
Normal 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
|
||||||
|
}
|
@@ -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
|
||||||
}
|
}
|
||||||
|
@@ -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:
|
||||||
|
|
||||||
|
@@ -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) {
|
||||||
|
383
plugin/stress/pb/stress.pb.go
Normal file
383
plugin/stress/pb/stress.pb.go
Normal 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
|
||||||
|
}
|
849
plugin/stress/pb/stress.pb.gw.go
Normal file
849
plugin/stress/pb/stress.pb.gw.go
Normal 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
|
||||||
|
)
|
359
plugin/stress/pb/stress_grpc.pb.go
Normal file
359
plugin/stress/pb/stress_grpc.pb.go
Normal 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",
|
||||||
|
}
|
@@ -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 {
|
||||||
|
11
publisher.go
11
publisher.go
@@ -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 {
|
||||||
|
@@ -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)
|
||||||
|
@@ -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 {
|
||||||
|
@@ -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
|
||||||
|
Reference in New Issue
Block a user