use launch

This commit is contained in:
kaedwen committed 2024-04-01 18:01:50 +02:00
1 parent ac2e689131
commit 146e656b8c
6 files changed
+153 -93

No files matched your search

+37
View File
@@ -0,0 +1,37 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Launch Package",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/main.go",
"env": {
"HTTP_STATIC": "${workspaceFolder}/static/dist",
"VIDEO_SRC_DEVICE": "/dev/v4l/by-id/usb-Microsoft_Microsoft®_LifeCam_HD-3000-video-index0",
//"AUDIO_SRC_DEVICE": "plughw:4,0,0",
"AUDIO_SRC": "pulsesrc",
"AUDIO_SINK": "pulsesink",
"INPUT_DEVICE": "/dev/input/event5",
"JINGLE_PATH": "${workspaceFolder}/audio",
"SONOS_TARGET": "Living Room",
"VIDEO_SRC_CODEC": "H264",
"VIDEO_SRC_HEIGHT": "600",
"VIDEO_SRC_WIDTH": "800",
"VIDEO_SRC_FPS": "15",
"VIDEO_SRC_BPS": "8000",
}
},
{
"name": "Launch Package Test",
"type": "go",
"request": "launch",
"mode": "auto",
"program": "${workspaceFolder}/test/main.go"
}
]
}
+8 -6
View File
@@ -49,12 +49,14 @@ type ConfigHTTP struct {
} }
type ConfigVideoSourceStream struct { type ConfigVideoSourceStream struct {
Source string `arg:"--video-src,env:VIDEO_SRC" default:"v4l2src"` Source string `arg:"--video-src,env:VIDEO_SRC" default:"v4l2src"`
Device string `arg:"--video-src-device,env:VIDEO_SRC_DEVICE" default:"/dev/video0"` Device string `arg:"--video-src-device,env:VIDEO_SRC_DEVICE" default:"/dev/video0"`
Codec StreamCodec `arg:"--video-src-codec,env:VIDEO_SRC_CODEC" default:"vp8"` Codec StreamCodec `arg:"--video-src-codec,env:VIDEO_SRC_CODEC" default:"vp8"`
Height uint `arg:"--video-src-height,env:VIDEO_SRC_HEIGHT" default:"480"` Height uint `arg:"--video-src-height,env:VIDEO_SRC_HEIGHT" default:"480"`
Width uint `arg:"--video-src-width,env:VIDEO_SRC_WIDTH" default:"640"` Width uint `arg:"--video-src-width,env:VIDEO_SRC_WIDTH" default:"640"`
Queue bool `arg:"--video-src-queue,env:VIDEO_SRC_QUEUE" default:"false"` Framerate uint `arg:"--video-src-fps,env:VIDEO_SRC_FPS" default:"30"`
Bitrate uint `arg:"--video-src-bps,env:VIDEO_SRC_BPS" default:"300"`
Queue bool `arg:"--video-src-queue,env:VIDEO_SRC_QUEUE" default:"false"`
} }
type ConfigAudioSourceStream struct { type ConfigAudioSourceStream struct {
+16 -3
View File
@@ -1,6 +1,9 @@
package streamer package streamer
import "fmt" import (
"fmt"
"strings"
)
type PipelineBuilder struct { type PipelineBuilder struct {
parts []string parts []string
@@ -18,9 +21,19 @@ func (pb *PipelineBuilder) Add(element string) *PipelineBuilder {
func (pb *PipelineBuilder) AddWithProperties(element string, properties map[string]any) *PipelineBuilder { func (pb *PipelineBuilder) AddWithProperties(element string, properties map[string]any) *PipelineBuilder {
pl := make([]string, 0, len(properties)) pl := make([]string, 0, len(properties))
for k, v := range properties { for k, v := range properties {
pl = append(pl, fmt.Sprintf("%s=%v", k, v)) pl = append(pl, fmt.Sprint(k, "=", v))
} }
pb.parts = append(pb.parts, fmt.Sprint(element, pl)) pb.parts = append(pb.parts, fmt.Sprint(element, " ", strings.Join(pl, " ")))
return pb return pb
} }
func (pb *PipelineBuilder) AddFilter(c *Caps) {
if c != nil {
pb.parts = append(pb.parts, c.Build())
}
}
func (pb *PipelineBuilder) Build() string {
return strings.Join(pb.parts, " ! ")
}
+55 -10
View File
@@ -38,11 +38,56 @@ func setCallback(sink *app.Sink, ch chan media.Sample) {
}) })
} }
func CreateVideoPipelineSinkWithLaunch(_ *zap.Logger, s StreamElement) (*gst.Pipeline, <-chan media.Sample, error) { func CreateVideoPipelineSinkWithLaunch(lg *zap.Logger, s StreamElement) (*gst.Pipeline, <-chan media.Sample, error) {
pb := NewPipelineBuilder() pb := NewPipelineBuilder()
pb.AddWithProperties("v4l2src", map[string]any{"device", s.}) pb.AddWithProperties("v4l2src", s.Properties)
pipeline, err := gst.NewPipelineFromString(`v4l2src device=/dev/video4 ! capsfilter caps="video/x-raw,width=(int)320,height=(int)240" ! videoconvert ! queue ! capsfilter caps="video/x-raw,format=(string)I420" ! x264enc speed-preset=ultrafast tune=zerolatency key-int-max=20 ! capsfilter caps="video/x-h264,stream-format=(string)byte-stream" ! appsink name=appsink`) pb.AddFilter(s.SrcCaps)
pb.Add("videoconvert")
if s.Queue {
pb.Add("queue")
}
switch s.Codec {
case common.VP8:
pb.AddWithProperties("vp8enc", map[string]any{
"bitrate": s.Bitrate,
"error-resilient": "partitions",
"keyframe-max-dist": int(10),
"cpu-used": int(5),
"deadline": int(1),
"auto-alt-ref": true,
})
case common.VP9:
pb.AddWithProperties("vp9enc", map[string]any{
"bitrate": s.Bitrate,
})
case common.H264:
pb.AddFilter(NewCaps("video/x-raw", map[string]any{
"format": "I420",
}))
pb.AddWithProperties("x264enc", map[string]any{
"bitrate": s.Bitrate,
"speed-preset": "ultrafast",
"tune": "zerolatency",
"key-int-max": int(20),
})
pb.AddFilter(NewCaps("video/x-h264", map[string]any{
"stream-format": "byte-stream",
}))
default:
return nil, nil, fmt.Errorf("unsupported video codec given - %s", s.Codec)
}
pb.AddWithProperties("appsink", map[string]any{
"name": "appsink",
})
ps := pb.Build()
lg.Info("launch pipeline", zap.String("definition", ps))
pipeline, err := gst.NewPipelineFromString(ps)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
@@ -168,13 +213,13 @@ func CreateAudioPipelineSink(s StreamElement, lg *zap.Logger) (*gst.Pipeline, <-
src.Set(name, value) src.Set(name, value)
} }
if s.Caps != nil { // if s.Caps != nil {
c := s.Caps.Build() // c := s.Caps.Build()
lg.Info("capsfilter", zap.String("caps", c.String())) // lg.Info("capsfilter", zap.String("caps", c.String()))
elems = append(elems, Element{src, c}) // elems = append(elems, Element{src, c})
} else { // } else {
elems = append(elems, Element{src, nil}) // elems = append(elems, Element{src, nil})
} // }
// just to be on the save side // just to be on the save side
conv, err := gst.NewElement("audioconvert") conv, err := gst.NewElement("audioconvert")
+14 -61
View File
@@ -1,83 +1,36 @@
package streamer package streamer
import ( import (
"github.com/go-gst/go-gst/gst" "fmt"
"strings"
"github.com/kaedwen/webrtc/pkg/common" "github.com/kaedwen/webrtc/pkg/common"
) )
type StreamElement struct { type StreamElement struct {
Kind string Kind string
Codec common.StreamCodec Codec common.StreamCodec
Bitrate uint
Properties map[string]interface{} Properties map[string]interface{}
Caps *CapsBuilder SrcCaps *Caps
EnvCaps *Caps
Queue bool Queue bool
} }
type CapsBuilder struct {
caps Caps
}
type Caps struct { type Caps struct {
mime string mime string
values []CapsValue filter map[string]any
} }
type CapsValue struct { func NewCaps(mime string, filter map[string]any) *Caps {
name string return &Caps{mime, filter}
value any
} }
func NewCapsBuilder(mime string) *CapsBuilder { func (c *Caps) Build() string {
return &CapsBuilder{ fv := make([]string, 0, len(c.filter))
caps: Caps{mime, nil}, for k, v := range c.filter {
} fv = append(fv, fmt.Sprint(k, "=", v))
}
func (b *CapsBuilder) Height(v int) *CapsBuilder {
b.caps.values = append(b.caps.values, CapsValue{
name: "height",
value: v,
})
return b
}
func (b *CapsBuilder) Width(v int) *CapsBuilder {
b.caps.values = append(b.caps.values, CapsValue{
name: "width",
value: v,
})
return b
}
func (b *CapsBuilder) Channels(v int) *CapsBuilder {
b.caps.values = append(b.caps.values, CapsValue{
name: "channels",
value: v,
})
return b
}
func (b *CapsBuilder) Format(v string) *CapsBuilder {
b.caps.values = append(b.caps.values, CapsValue{
name: "format",
value: v,
})
return b
}
func (b *CapsBuilder) Rate(v int) *CapsBuilder {
b.caps.values = append(b.caps.values, CapsValue{
name: "rate",
value: v,
})
return b
}
func (b *CapsBuilder) Build() *gst.Caps {
caps := gst.NewEmptySimpleCaps(b.caps.mime)
for _, v := range b.caps.values {
caps.SetValue(v.name, v.value)
} }
return caps return fmt.Sprint(c.mime, ",", strings.Join(fv, ","))
} }
+23 -13
View File
@@ -3,6 +3,7 @@ package webrtc
import ( import (
"context" "context"
"errors" "errors"
"fmt"
"io" "io"
"sync" "sync"
"time" "time"
@@ -120,9 +121,12 @@ func (wh *WebrtcHandler) handleAudioSamples(ctx context.Context, cfg *common.Con
src := streamer.StreamElement{ src := streamer.StreamElement{
Kind: cfg.Source, Kind: cfg.Source,
Properties: properties, Properties: properties,
Caps: streamer.NewCapsBuilder("audio/x-raw").Channels(int(cfg.Channels)).Rate(48000), SrcCaps: streamer.NewCaps("audio/x-raw", map[string]any{
Queue: cfg.Queue, "channels": cfg.Channels,
Codec: cfg.Codec, "rate": 48000,
}),
Queue: cfg.Queue,
Codec: cfg.Codec,
} }
var err error var err error
@@ -155,19 +159,25 @@ func (wh *WebrtcHandler) handleAudioSamples(ctx context.Context, cfg *common.Con
} }
func (wh *WebrtcHandler) handleVideoSamples(ctx context.Context, cfg *common.ConfigVideoSourceStream) error { func (wh *WebrtcHandler) handleVideoSamples(ctx context.Context, cfg *common.ConfigVideoSourceStream) error {
// src := streamer.StreamElement{ src := streamer.StreamElement{
// Kind: cfg.Source, Kind: cfg.Source,
// Properties: map[string]interface{}{ Properties: map[string]interface{}{
// "device": cfg.Device, "device": cfg.Device,
// }, },
// Caps: streamer.NewCapsBuilder("video/x-raw").Format("YUY2").Width(int(cfg.Width)).Height(int(cfg.Height)), SrcCaps: streamer.NewCaps("video/x-raw", map[string]any{
// Queue: cfg.Queue, "height": cfg.Height,
// Codec: cfg.Codec, "width": cfg.Width,
// } "framerate": fmt.Sprintf("%d/1", cfg.Framerate),
"format": "YUY2",
}),
Bitrate: cfg.Bitrate,
Queue: cfg.Queue,
Codec: cfg.Codec,
}
var err error var err error
var videoCh <-chan media.Sample var videoCh <-chan media.Sample
wh.videoPipeline, videoCh, err = streamer.CreateVideoPipelineSinkWithLaunch(wh.lg) wh.videoPipeline, videoCh, err = streamer.CreateVideoPipelineSinkWithLaunch(wh.lg, src)
if err != nil { if err != nil {
return err return err
} }