diff --git a/apidocs/openapi.yaml b/apidocs/openapi.yaml index 96e498b4..c3e4ede4 100644 --- a/apidocs/openapi.yaml +++ b/apidocs/openapi.yaml @@ -201,6 +201,23 @@ components: - $ref: '#/components/schemas/PathSourceHLSSource' sourceReady: type: boolean + tracks: + type: array + items: + type: string + enum: + - Generic + - H264 + - H265 + - JPEG + - MPEG2Audio + - MPEG2Video + - MPEG4Audio + - Opus + - PCMA + - PCMU + - VP8 + - VP9 readers: type: array items: diff --git a/internal/core/api_test.go b/internal/core/api_test.go index cf1e6998..264ecf8a 100644 --- a/internal/core/api_test.go +++ b/internal/core/api_test.go @@ -12,6 +12,7 @@ import ( "time" "github.com/aler9/gortsplib" + "github.com/aler9/gortsplib/pkg/mpeg4audio" "github.com/stretchr/testify/require" ) @@ -161,8 +162,9 @@ func TestAPIPathsList(t *testing.T) { } type path struct { - SourceReady bool Source pathSource `json:"source"` + SourceReady bool `json:"sourceReady"` + Tracks []string `json:"tracks"` } type pathList struct { @@ -176,15 +178,27 @@ func TestAPIPathsList(t *testing.T) { require.Equal(t, true, ok) defer p.close() - track := &gortsplib.TrackH264{ - PayloadType: 96, - SPS: []byte{0x01, 0x02, 0x03, 0x04}, - PPS: []byte{0x01, 0x02, 0x03, 0x04}, + tracks := gortsplib.Tracks{ + &gortsplib.TrackH264{ + PayloadType: 96, + SPS: []byte{0x01, 0x02, 0x03, 0x04}, + PPS: []byte{0x01, 0x02, 0x03, 0x04}, + }, + &gortsplib.TrackMPEG4Audio{ + PayloadType: 97, + Config: &mpeg4audio.Config{ + Type: 2, + SampleRate: 44100, + ChannelCount: 2, + }, + SizeLength: 13, + IndexLength: 3, + IndexDeltaLength: 3, + }, } source := gortsplib.Client{} - err := source.StartPublishing("rtsp://localhost:8554/mypath", - gortsplib.Tracks{track}) + err := source.StartPublishing("rtsp://localhost:8554/mypath", tracks) require.NoError(t, err) defer source.Close() @@ -194,10 +208,11 @@ func TestAPIPathsList(t *testing.T) { require.Equal(t, pathList{ Items: map[string]path{ "mypath": { - SourceReady: true, Source: pathSource{ Type: "rtspSession", }, + SourceReady: true, + Tracks: []string{"H264", "MPEG4Audio"}, }, }, }, out) @@ -221,15 +236,27 @@ func TestAPIPathsList(t *testing.T) { require.Equal(t, true, ok) defer p.close() - track := &gortsplib.TrackH264{ - PayloadType: 96, - SPS: []byte{0x01, 0x02, 0x03, 0x04}, - PPS: []byte{0x01, 0x02, 0x03, 0x04}, + tracks := gortsplib.Tracks{ + &gortsplib.TrackH264{ + PayloadType: 96, + SPS: []byte{0x01, 0x02, 0x03, 0x04}, + PPS: []byte{0x01, 0x02, 0x03, 0x04}, + }, + &gortsplib.TrackMPEG4Audio{ + PayloadType: 97, + Config: &mpeg4audio.Config{ + Type: 2, + SampleRate: 44100, + ChannelCount: 2, + }, + SizeLength: 13, + IndexLength: 3, + IndexDeltaLength: 3, + }, } source := gortsplib.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}} - err = source.StartPublishing("rtsps://localhost:8322/mypath", - gortsplib.Tracks{track}) + err = source.StartPublishing("rtsps://localhost:8322/mypath", tracks) require.NoError(t, err) defer source.Close() @@ -239,10 +266,11 @@ func TestAPIPathsList(t *testing.T) { require.Equal(t, pathList{ Items: map[string]path{ "mypath": { - SourceReady: true, Source: pathSource{ Type: "rtspsSession", }, + SourceReady: true, + Tracks: []string{"H264", "MPEG4Audio"}, }, }, }, out) @@ -263,10 +291,11 @@ func TestAPIPathsList(t *testing.T) { require.Equal(t, pathList{ Items: map[string]path{ "mypath": { - SourceReady: false, Source: pathSource{ Type: "rtspSource", }, + SourceReady: false, + Tracks: []string{}, }, }, }, out) @@ -287,10 +316,11 @@ func TestAPIPathsList(t *testing.T) { require.Equal(t, pathList{ Items: map[string]path{ "mypath": { - SourceReady: false, Source: pathSource{ Type: "rtmpSource", }, + SourceReady: false, + Tracks: []string{}, }, }, }, out) @@ -311,10 +341,11 @@ func TestAPIPathsList(t *testing.T) { require.Equal(t, pathList{ Items: map[string]path{ "mypath": { - SourceReady: false, Source: pathSource{ Type: "hlsSource", }, + SourceReady: false, + Tracks: []string{}, }, }, }, out) diff --git a/internal/core/path.go b/internal/core/path.go index ad26a0b6..b574240f 100644 --- a/internal/core/path.go +++ b/internal/core/path.go @@ -182,6 +182,7 @@ type pathAPIPathsListItem struct { Conf *conf.PathConf `json:"conf"` Source interface{} `json:"source"` SourceReady bool `json:"sourceReady"` + Tracks []string `json:"tracks"` Readers []interface{} `json:"readers"` } @@ -220,7 +221,6 @@ type path struct { ctx context.Context ctxCancel func() source source - sourceReady bool stream *stream readers map[reader]pathReaderState describeRequestsOnHold []pathDescribeReq @@ -544,7 +544,7 @@ func (pa *path) run() { req.res <- pathReaderSetupPlayRes{err: fmt.Errorf("terminated")} } - if pa.sourceReady { + if pa.stream != nil { pa.sourceSetNotReady() } @@ -669,7 +669,6 @@ func (pa *path) sourceSetReady(tracks gortsplib.Tracks, generateRTPPackets bool) } pa.stream = stream - pa.sourceReady = true if pa.conf.RunOnReady != "" { pa.log(logger.Info, "runOnReady command started") @@ -702,8 +701,6 @@ func (pa *path) sourceSetNotReady() { pa.log(logger.Info, "runOnReady command stopped") } - pa.sourceReady = false - if pa.stream != nil { pa.stream.close() pa.stream = nil @@ -721,7 +718,7 @@ func (pa *path) doReaderRemove(r reader) { } func (pa *path) doPublisherRemove() { - if pa.sourceReady { + if pa.stream != nil { if pa.hasOnDemandPublisher() && pa.onDemandPublisherState != pathOnDemandStateInitial { pa.onDemandPublisherStop() } else { @@ -740,7 +737,7 @@ func (pa *path) handleDescribe(req pathDescribeReq) { return } - if pa.sourceReady { + if pa.stream != nil { req.res <- pathDescribeRes{ stream: pa.stream, } @@ -849,7 +846,7 @@ func (pa *path) handlePublisherRecord(req pathPublisherRecordReq) { } func (pa *path) handlePublisherPause(req pathPublisherPauseReq) { - if req.author == pa.source && pa.sourceReady { + if req.author == pa.source && pa.stream != nil { if pa.hasOnDemandPublisher() && pa.onDemandPublisherState != pathOnDemandStateInitial { pa.onDemandPublisherStop() } else { @@ -879,7 +876,7 @@ func (pa *path) handleReaderRemove(req pathReaderRemoveReq) { } func (pa *path) handleReaderSetupPlay(req pathReaderSetupPlayReq) { - if pa.sourceReady { + if pa.stream != nil { pa.handleReaderSetupPlayPost(req) return } @@ -952,7 +949,13 @@ func (pa *path) handleAPIPathsList(req pathAPIPathsListSubReq) { } return pa.source.apiSourceDescribe() }(), - SourceReady: pa.sourceReady, + SourceReady: pa.stream != nil, + Tracks: func() []string { + if pa.stream == nil { + return []string{} + } + return sourceTrackNames(pa.stream.tracks()) + }(), Readers: func() []interface{} { ret := []interface{}{} for r := range pa.readers { diff --git a/internal/core/source.go b/internal/core/source.go index 2135db8a..3dc2e133 100644 --- a/internal/core/source.go +++ b/internal/core/source.go @@ -17,14 +17,17 @@ type source interface { apiSourceDescribe() interface{} } -func sourceTrackInfo(tracks gortsplib.Tracks) string { - trackCodecs := make([]string, len(tracks)) +func sourceTrackNames(tracks gortsplib.Tracks) []string { + ret := make([]string, len(tracks)) for i, t := range tracks { n := reflect.TypeOf(t).Elem().Name() n = n[len("Track"):] - trackCodecs[i] = n + ret[i] = n } + return ret +} +func sourceTrackInfo(tracks gortsplib.Tracks) string { return fmt.Sprintf("%d %s (%s)", len(tracks), func() string { @@ -33,5 +36,5 @@ func sourceTrackInfo(tracks gortsplib.Tracks) string { } return "tracks" }(), - strings.Join(trackCodecs, ", ")) + strings.Join(sourceTrackNames(tracks), ", ")) }