diff --git a/cmd/ffmpeg/ffmpeg.go b/cmd/ffmpeg/ffmpeg.go index 947d55a2..4f6ff51d 100644 --- a/cmd/ffmpeg/ffmpeg.go +++ b/cmd/ffmpeg/ffmpeg.go @@ -142,6 +142,8 @@ func parseArgs(s string) *Args { s += "?video" case args.audio > 0 && args.video == 0: s += "?audio" + default: + s += "?video&audio" } args.input = strings.Replace(defaults["rtsp"], "{input}", s, 1) } else if strings.HasPrefix(s, "device?") { diff --git a/cmd/rtsp/rtsp.go b/cmd/rtsp/rtsp.go index 6b8f8a77..aa910a98 100644 --- a/cmd/rtsp/rtsp.go +++ b/cmd/rtsp/rtsp.go @@ -3,6 +3,7 @@ package rtsp import ( "github.com/AlexxIT/go2rtc/cmd/app" "github.com/AlexxIT/go2rtc/cmd/streams" + "github.com/AlexxIT/go2rtc/pkg/mp4" "github.com/AlexxIT/go2rtc/pkg/rtsp" "github.com/AlexxIT/go2rtc/pkg/streamer" "github.com/AlexxIT/go2rtc/pkg/tcp" @@ -164,13 +165,7 @@ func tcpHandler(conn *rtsp.Conn) { conn.SessionName = app.UserAgent - conn.Medias = streamer.ParseQuery(conn.URL.Query()) - if conn.Medias == nil { - conn.Medias = []*streamer.Media{ - {Kind: streamer.KindVideo, Direction: streamer.DirectionRecvonly}, - {Kind: streamer.KindAudio, Direction: streamer.DirectionRecvonly}, - } - } + conn.Medias = ParseQuery(conn.URL.Query()) if err := stream.AddConsumer(conn); err != nil { log.Warn().Err(err).Str("stream", name).Msg("[rtsp]") @@ -235,36 +230,11 @@ func tcpHandler(conn *rtsp.Conn) { _ = conn.Close() } -func initMedias(conn *rtsp.Conn) { - // set media candidates from query list - for key, value := range conn.URL.Query() { - switch key { - case streamer.KindVideo, streamer.KindAudio: - for _, name := range value { - name = strings.ToUpper(name) - - // check aliases - switch name { - case "COPY": - name = "" // pass empty codecs list - case "MJPEG": - name = streamer.CodecJPEG - case "AAC": - name = streamer.CodecAAC - } - - media := &streamer.Media{ - Kind: key, Direction: streamer.DirectionRecvonly, - } - - // empty codecs match all codecs - if name != "" { - // empty clock rate and channels match any values - media.Codecs = []*streamer.Codec{{Name: name}} - } - - conn.Medias = append(conn.Medias, media) - } - } +func ParseQuery(query map[string][]string) []*streamer.Media { + if query["mp4"] != nil { + cons := mp4.Consumer{} + return cons.GetMedias() } + + return streamer.ParseQuery(query) } diff --git a/cmd/streams/stream.go b/cmd/streams/stream.go index 82cbfaf7..cc707819 100644 --- a/cmd/streams/stream.go +++ b/cmd/streams/stream.go @@ -91,7 +91,9 @@ func (s *Stream) AddConsumer(cons streamer.Consumer) (err error) { consumer.tracks = append(consumer.tracks, consTrack) producers = append(producers, prod) - break producers + if !consMedia.MatchAll() { + break producers + } } } } diff --git a/pkg/rtsp/streamer.go b/pkg/rtsp/streamer.go index 442be3ce..2af46629 100644 --- a/pkg/rtsp/streamer.go +++ b/pkg/rtsp/streamer.go @@ -9,7 +9,26 @@ import ( // Element Producer func (c *Conn) GetMedias() []*streamer.Media { - return c.Medias + if c.Medias != nil { + return c.Medias + } + + return []*streamer.Media{ + { + Kind: streamer.KindVideo, + Direction: streamer.DirectionRecvonly, + Codecs: []*streamer.Codec{ + {Name: streamer.CodecAll}, + }, + }, + { + Kind: streamer.KindAudio, + Direction: streamer.DirectionRecvonly, + Codecs: []*streamer.Codec{ + {Name: streamer.CodecAll}, + }, + }, + } } func (c *Conn) GetTrack(media *streamer.Media, codec *streamer.Codec) *streamer.Track { @@ -63,11 +82,20 @@ func (c *Conn) AddTrack(media *streamer.Media, track *streamer.Track) *streamer. codec := track.Codec.Clone() codec.PayloadType = uint8(96 + i) - for i, m := range c.Medias { - if m == media { - media.Codecs = []*streamer.Codec{codec} - c.Medias[i] = media - break + if media.MatchAll() { + // fill consumer medias list + c.Medias = append(c.Medias, &streamer.Media{ + Kind: media.Kind, Direction: media.Direction, + Codecs: []*streamer.Codec{codec}, + }) + } else { + // find consumer media and replace codec with right one + for i, m := range c.Medias { + if m == media { + media.Codecs = []*streamer.Codec{codec} + c.Medias[i] = media + break + } } } diff --git a/pkg/streamer/media.go b/pkg/streamer/media.go index bb74660a..5765c75d 100644 --- a/pkg/streamer/media.go +++ b/pkg/streamer/media.go @@ -36,6 +36,9 @@ const ( CodecMP3 = "MPA" // payload: 14, aka MPEG-1 Layer III CodecELD = "ELD" // AAC-ELD + + CodecAll = "ALL" + CodecAny = "ANY" ) const PayloadTypeRAW byte = 255 @@ -111,10 +114,6 @@ func (m *Media) MatchMedia(media *Media) *Codec { } for _, localCodec := range m.Codecs { - if media.Codecs == nil { - return localCodec - } - for _, remoteCodec := range media.Codecs { if localCodec.Match(remoteCodec) { return localCodec @@ -124,6 +123,10 @@ func (m *Media) MatchMedia(media *Media) *Codec { return nil } +func (m *Media) MatchAll() bool { + return len(m.Codecs) > 0 && m.Codecs[0].Name == CodecAll +} + // Codec take best from: // - deepch/vdk/av.CodecData // - pion/webrtc.RTPCodecCapability @@ -153,6 +156,11 @@ func (c *Codec) Clone() *Codec { } func (c *Codec) Match(codec *Codec) bool { + switch codec.Name { + case CodecAll, CodecAny: + return true + } + return c.Name == codec.Name && (c.ClockRate == codec.ClockRate || codec.ClockRate == 0) && (c.Channels == codec.Channels || codec.Channels == 0) @@ -307,16 +315,12 @@ func ParseQuery(query map[string][]string) (medias []*Media) { media := &Media{Kind: key, Direction: DirectionRecvonly} for _, name := range strings.Split(value, ",") { - if name == "" { - continue - } - name = strings.ToUpper(name) // check aliases switch name { - case "COPY": - name = "" // pass empty codecs list + case "", "COPY": + name = CodecAny case "MJPEG": name = CodecJPEG case "AAC": diff --git a/pkg/streamer/media_test.go b/pkg/streamer/media_test.go index e6b01be7..c098f414 100644 --- a/pkg/streamer/media_test.go +++ b/pkg/streamer/media_test.go @@ -3,6 +3,7 @@ package streamer import ( "github.com/pion/sdp/v3" "github.com/stretchr/testify/assert" + "net/url" "testing" ) @@ -21,3 +22,21 @@ func TestSDP(t *testing.T) { err = sd.Unmarshal(data) assert.Empty(t, err) } + +func TestParseQuery(t *testing.T) { + u, _ := url.Parse("rtsp://localhost:8554/camera1") + medias := ParseQuery(u.Query()) + assert.Nil(t, medias) + + for _, rawULR := range []string{ + "rtsp://localhost:8554/camera1?video", + "rtsp://localhost:8554/camera1?video=copy", + "rtsp://localhost:8554/camera1?video=any", + } { + u, _ = url.Parse(rawULR) + medias = ParseQuery(u.Query()) + assert.Equal(t, []*Media{ + {Kind: KindVideo, Direction: DirectionRecvonly, Codecs: []*Codec{{Name: CodecAny}}}, + }, medias) + } +}