install go2rtc on bob
This commit is contained in:
@@ -0,0 +1,142 @@
|
||||
//go:build linux
|
||||
|
||||
package v4l2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/AlexxIT/go2rtc/pkg/core"
|
||||
"github.com/AlexxIT/go2rtc/pkg/h264/annexb"
|
||||
"github.com/AlexxIT/go2rtc/pkg/v4l2/device"
|
||||
"github.com/pion/rtp"
|
||||
)
|
||||
|
||||
type Producer struct {
|
||||
core.Connection
|
||||
dev *device.Device
|
||||
}
|
||||
|
||||
func Open(rawURL string) (*Producer, error) {
|
||||
// Example (ffmpeg source compatible):
|
||||
// v4l2:device?video=/dev/video0&input_format=mjpeg&video_size=1280x720
|
||||
u, err := url.Parse(rawURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
query := u.Query()
|
||||
|
||||
dev, err := device.Open(query.Get("video"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
codec := &core.Codec{
|
||||
ClockRate: 90000,
|
||||
PayloadType: core.PayloadTypeRAW,
|
||||
}
|
||||
|
||||
var width, height, pixFmt uint32
|
||||
|
||||
if wh := strings.Split(query.Get("video_size"), "x"); len(wh) == 2 {
|
||||
codec.FmtpLine = "width=" + wh[0] + ";height=" + wh[1]
|
||||
width = uint32(core.Atoi(wh[0]))
|
||||
height = uint32(core.Atoi(wh[1]))
|
||||
}
|
||||
|
||||
switch query.Get("input_format") {
|
||||
case "yuyv422":
|
||||
if codec.FmtpLine == "" {
|
||||
return nil, errors.New("v4l2: invalid video_size")
|
||||
}
|
||||
codec.Name = core.CodecRAW
|
||||
codec.FmtpLine += ";colorspace=422"
|
||||
pixFmt = device.V4L2_PIX_FMT_YUYV
|
||||
case "nv12":
|
||||
if codec.FmtpLine == "" {
|
||||
return nil, errors.New("v4l2: invalid video_size")
|
||||
}
|
||||
codec.Name = core.CodecRAW
|
||||
codec.FmtpLine += ";colorspace=420mpeg2" // maybe 420jpeg
|
||||
pixFmt = device.V4L2_PIX_FMT_NV12
|
||||
case "mjpeg":
|
||||
codec.Name = core.CodecJPEG
|
||||
pixFmt = device.V4L2_PIX_FMT_MJPEG
|
||||
case "h264":
|
||||
codec.Name = core.CodecH264
|
||||
pixFmt = device.V4L2_PIX_FMT_H264
|
||||
case "hevc":
|
||||
codec.Name = core.CodecH265
|
||||
pixFmt = device.V4L2_PIX_FMT_HEVC
|
||||
default:
|
||||
return nil, errors.New("v4l2: invalid input_format")
|
||||
}
|
||||
|
||||
if err = dev.SetFormat(width, height, pixFmt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if fps := core.Atoi(query.Get("framerate")); fps > 0 {
|
||||
if err = dev.SetParam(uint32(fps)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
medias := []*core.Media{
|
||||
{
|
||||
Kind: core.KindVideo,
|
||||
Direction: core.DirectionRecvonly,
|
||||
Codecs: []*core.Codec{codec},
|
||||
},
|
||||
}
|
||||
return &Producer{
|
||||
Connection: core.Connection{
|
||||
ID: core.NewID(),
|
||||
FormatName: "v4l2",
|
||||
Medias: medias,
|
||||
},
|
||||
dev: dev,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Producer) Start() error {
|
||||
if err := c.dev.StreamOn(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var bitstream bool
|
||||
switch c.Medias[0].Codecs[0].Name {
|
||||
case core.CodecH264, core.CodecH265:
|
||||
bitstream = true
|
||||
}
|
||||
|
||||
for {
|
||||
buf, err := c.dev.Capture()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.Recv += len(buf)
|
||||
|
||||
if len(c.Receivers) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
if bitstream {
|
||||
buf = annexb.EncodeToAVCC(buf)
|
||||
}
|
||||
|
||||
pkt := &rtp.Packet{
|
||||
Header: rtp.Header{Timestamp: core.Now90000()},
|
||||
Payload: buf,
|
||||
}
|
||||
c.Receivers[0].WriteRTP(pkt)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Producer) Stop() error {
|
||||
_ = c.Connection.Stop()
|
||||
return errors.Join(c.dev.StreamOff(), c.dev.Close())
|
||||
}
|
||||
Reference in New Issue
Block a user