add media to capture config.

This commit is contained in:
Miroslav Šedivý 2021-12-09 23:22:24 +01:00
parent 21140d3dd3
commit 6b14e01415
5 changed files with 72 additions and 28 deletions

View File

@ -122,7 +122,7 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
videoIDs: config.VideoIDs, videoIDs: config.VideoIDs,
// sources // sources
webcam: streamSrcNew(map[string]string{ webcam: streamSrcNew(config.WebcamEnabled, map[string]string{
codec.VP8().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " + codec.VP8().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " +
fmt.Sprintf("! application/x-rtp, payload=%d, encoding-name=VP8-DRAFT-IETF-01 ", codec.VP8().PayloadType) + fmt.Sprintf("! application/x-rtp, payload=%d, encoding-name=VP8-DRAFT-IETF-01 ", codec.VP8().PayloadType) +
"! rtpvp8depay " + "! rtpvp8depay " +
@ -130,7 +130,8 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
"! videoconvert " + "! videoconvert " +
"! videorate " + "! videorate " +
"! identity drop-allocation=true " + "! identity drop-allocation=true " +
"! v4l2sink sync=false device=/dev/video0", fmt.Sprintf("! v4l2sink sync=false device=%s", config.WebcamDevice),
// TODO: Test this pipeline.
codec.VP9().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " + codec.VP9().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " +
"! application/x-rtp " + "! application/x-rtp " +
"! rtpvp9depay " + "! rtpvp9depay " +
@ -138,7 +139,8 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
"! videoconvert " + "! videoconvert " +
"! videorate " + "! videorate " +
"! identity drop-allocation=true " + "! identity drop-allocation=true " +
"! v4l2sink sync=false device=/dev/video0", fmt.Sprintf("! v4l2sink sync=false device=%s", config.WebcamDevice),
// TODO: Test this pipeline.
codec.H264().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " + codec.H264().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " +
"! application/x-rtp " + "! application/x-rtp " +
"! rtph264depay " + "! rtph264depay " +
@ -146,19 +148,20 @@ func New(desktop types.DesktopManager, config *config.Capture) *CaptureManagerCt
"! videoconvert " + "! videoconvert " +
"! videorate " + "! videorate " +
"! identity drop-allocation=true " + "! identity drop-allocation=true " +
"! v4l2sink sync=false device=/dev/video0", fmt.Sprintf("! v4l2sink sync=false device=%s", config.WebcamDevice),
}, "webcam"), }, "webcam"),
microphone: streamSrcNew(map[string]string{ microphone: streamSrcNew(config.MicrophoneEnabled, map[string]string{
codec.Opus().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " + codec.Opus().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " +
fmt.Sprintf("! application/x-rtp, payload=%d, encoding-name=OPUS ", codec.Opus().PayloadType) + fmt.Sprintf("! application/x-rtp, payload=%d, encoding-name=OPUS ", codec.Opus().PayloadType) +
"! rtpopusdepay " + "! rtpopusdepay " +
"! decodebin " + "! decodebin " +
"! pulsesink device=audio_input", fmt.Sprintf("! pulsesink device=%s", config.MicrophoneDevice),
// TODO: Test this pipeline.
codec.G722().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " + codec.G722().Name: "appsrc format=time is-live=true do-timestamp=true name=appsrc " +
"! application/x-rtp clock-rate=8000 " + "! application/x-rtp clock-rate=8000 " +
"! rtpg722depay " + "! rtpg722depay " +
"! decodebin " + "! decodebin " +
"! pulsesink device=audio_input", fmt.Sprintf("! pulsesink device=%s", config.MicrophoneDevice),
}, "microphone"), }, "microphone"),
} }
} }

View File

@ -131,7 +131,7 @@ func (manager *ScreencastManagerCtx) start() error {
defer manager.mu.Unlock() defer manager.mu.Unlock()
if !manager.enabled { if !manager.enabled {
return errors.New("screenshot pipeline not enabled") return errors.New("screencast not enabled")
} }
err := manager.createPipeline() err := manager.createPipeline()

View File

@ -14,6 +14,7 @@ import (
type StreamSrcManagerCtx struct { type StreamSrcManagerCtx struct {
logger zerolog.Logger logger zerolog.Logger
enabled bool
codecPipeline map[string]string // codec -> pipeline codecPipeline map[string]string // codec -> pipeline
codec codec.RTPCodec codec codec.RTPCodec
@ -22,7 +23,7 @@ type StreamSrcManagerCtx struct {
pipelineStr string pipelineStr string
} }
func streamSrcNew(codecPipeline map[string]string, video_id string) *StreamSrcManagerCtx { func streamSrcNew(enabled bool, codecPipeline map[string]string, video_id string) *StreamSrcManagerCtx {
logger := log.With(). logger := log.With().
Str("module", "capture"). Str("module", "capture").
Str("submodule", "stream-src"). Str("submodule", "stream-src").
@ -30,6 +31,7 @@ func streamSrcNew(codecPipeline map[string]string, video_id string) *StreamSrcMa
return &StreamSrcManagerCtx{ return &StreamSrcManagerCtx{
logger: logger, logger: logger,
enabled: enabled,
codecPipeline: codecPipeline, codecPipeline: codecPipeline,
} }
} }
@ -52,6 +54,10 @@ func (manager *StreamSrcManagerCtx) Start(codec codec.RTPCodec) error {
return types.ErrCapturePipelineAlreadyExists return types.ErrCapturePipelineAlreadyExists
} }
if !manager.enabled {
return errors.New("stream-src not enabled")
}
found := false found := false
for codecName, pipeline := range manager.codecPipeline { for codecName, pipeline := range manager.codecPipeline {
if codecName == codec.Name { if codecName == codec.Name {

View File

@ -33,11 +33,17 @@ type Capture struct {
ScreencastRate string ScreencastRate string
ScreencastQuality string ScreencastQuality string
ScreencastPipeline string ScreencastPipeline string
WebcamEnabled bool
WebcamDevice string
MicrophoneEnabled bool
MicrophoneDevice string
} }
func (Capture) Init(cmd *cobra.Command) error { func (Capture) Init(cmd *cobra.Command) error {
// audio // audio
cmd.PersistentFlags().String("capture.audio.device", "audio_output.monitor", "audio device to capture") cmd.PersistentFlags().String("capture.audio.device", "audio_output.monitor", "pulseaudio device to capture")
if err := viper.BindPFlag("capture.audio.device", cmd.PersistentFlags().Lookup("capture.audio.device")); err != nil { if err := viper.BindPFlag("capture.audio.device", cmd.PersistentFlags().Lookup("capture.audio.device")); err != nil {
return err return err
} }
@ -110,6 +116,28 @@ func (Capture) Init(cmd *cobra.Command) error {
return err return err
} }
// webcam
cmd.PersistentFlags().Bool("capture.webcam.enabled", false, "enable webcam stream")
if err := viper.BindPFlag("capture.webcam.enabled", cmd.PersistentFlags().Lookup("capture.webcam.enabled")); err != nil {
return err
}
cmd.PersistentFlags().String("capture.webcam.device", "/dev/video0", "v4l2sink device used for webcam")
if err := viper.BindPFlag("capture.webcam.device", cmd.PersistentFlags().Lookup("capture.webcam.device")); err != nil {
return err
}
// microphone
cmd.PersistentFlags().Bool("capture.microphone.enabled", true, "enable microphone stream")
if err := viper.BindPFlag("capture.microphone.enabled", cmd.PersistentFlags().Lookup("capture.microphone.enabled")); err != nil {
return err
}
cmd.PersistentFlags().String("capture.microphone.device", "audio_input", "pulseaudio device used for microphone")
if err := viper.BindPFlag("capture.microphone.device", cmd.PersistentFlags().Lookup("capture.microphone.device")); err != nil {
return err
}
return nil return nil
} }
@ -174,4 +202,12 @@ func (s *Capture) Set() {
s.ScreencastRate = viper.GetString("capture.screencast.rate") s.ScreencastRate = viper.GetString("capture.screencast.rate")
s.ScreencastQuality = viper.GetString("capture.screencast.quality") s.ScreencastQuality = viper.GetString("capture.screencast.quality")
s.ScreencastPipeline = viper.GetString("capture.screencast.pipeline") s.ScreencastPipeline = viper.GetString("capture.screencast.pipeline")
// webcam
s.WebcamEnabled = viper.GetBool("capture.webcam.enabled")
s.WebcamDevice = viper.GetString("capture.webcam.device")
// microphone
s.MicrophoneEnabled = viper.GetBool("capture.microphone.enabled")
s.MicrophoneDevice = viper.GetString("capture.microphone.device")
} }

View File

@ -157,24 +157,24 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
} }
connection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) { connection.OnTrack(func(track *webrtc.TrackRemote, receiver *webrtc.RTPReceiver) {
if !session.Profile().CanShareMedia { defer receiver.Stop()
logger.Warn().
Str("kind", track.Kind().String()).
Msgf("got track but share media is disabled for this session")
receiver.Stop() logger := logger.With().
return
}
logger.Info().
Str("kind", track.Kind().String()). Str("kind", track.Kind().String()).
Str("mime", track.Codec().RTPCodecCapability.MimeType). Str("mime", track.Codec().RTPCodecCapability.MimeType).
Msgf("received new track") Logger()
logger.Info().Msgf("received new track")
if !session.Profile().CanShareMedia {
logger.Warn().Msg("share media is disabled for this session")
return
}
// parse codec // parse codec
codec, ok := codec.ParseRTC(track.Codec()) codec, ok := codec.ParseRTC(track.Codec())
if !ok { if !ok {
logger.Warn().Str("mime", track.Codec().RTPCodecCapability.MimeType).Msg("unknown codec") logger.Warn().Msg("unknown codec")
return return
} }
@ -192,6 +192,7 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
logger.Err(err).Msg("failed to start pipeline") logger.Err(err).Msg("failed to start pipeline")
return return
} }
defer srcManager.Stop()
// Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval // Send a PLI on an interval so that the publisher is pushing a keyframe every rtcpPLIInterval
ticker := time.NewTicker(time.Second * 3) ticker := time.NewTicker(time.Second * 3)
@ -199,9 +200,9 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
go func() { go func() {
for range ticker.C { for range ticker.C {
rtcpSendErr := connection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}}) err := connection.WriteRTCP([]rtcp.Packet{&rtcp.PictureLossIndication{MediaSSRC: uint32(track.SSRC())}})
if rtcpSendErr != nil { if err != nil {
fmt.Println(rtcpSendErr) logger.Err(err).Msg("rtcp send err")
} }
} }
}() }()
@ -210,16 +211,14 @@ func (manager *WebRTCManagerCtx) CreatePeer(session types.Session, videoID strin
for { for {
i, _, err := track.Read(buf) i, _, err := track.Read(buf)
if err != nil { if err != nil {
logger.Warn().Err(err).Msg("failed read from pipeline") logger.Warn().Err(err).Msg("failed read from track")
break break
} }
srcManager.Push(buf[:i]) srcManager.Push(buf[:i])
} }
logger.Warn().Msg("src manager stream connection died, stopping") logger.Info().Msg("track data finished")
srcManager.Stop()
logger.Warn().Msg("src manager stream stopped!!!!!!!!!!!!!!!")
}) })
connection.OnDataChannel(func(dc *webrtc.DataChannel) { connection.OnDataChannel(func(dc *webrtc.DataChannel) {