api: provide track details (#5307) (#5333) (#1726) (#5585)

This commit is contained in:
Alessandro Ros
2026-03-18 00:01:43 +01:00
committed by GitHub
parent 95e5b55dae
commit 2b302e7940
10 changed files with 646 additions and 61 deletions
+133
View File
@@ -973,8 +973,13 @@ components:
nullable: true nullable: true
tracks: tracks:
type: array type: array
deprecated: true
items: items:
$ref: '#/components/schemas/PathTrackCodec' $ref: '#/components/schemas/PathTrackCodec'
tracks2:
type: array
items:
$ref: '#/components/schemas/PathTrack'
inboundBytes: inboundBytes:
type: integer type: integer
format: uint64 format: uint64
@@ -1027,6 +1032,134 @@ components:
id: id:
type: string type: string
PathTrack:
type: object
properties:
codec:
$ref: '#/components/schemas/PathTrackCodec'
codecProps:
type: object
nullable: true
allOf:
- $ref: '#/components/schemas/PathTrackCodecProps'
PathTrackCodecProps:
oneOf:
- $ref: '#/components/schemas/PathTrackCodecPropsAV1'
- $ref: '#/components/schemas/PathTrackCodecPropsVP9'
- $ref: '#/components/schemas/PathTrackCodecPropsH265'
- $ref: '#/components/schemas/PathTrackCodecPropsH264'
- $ref: '#/components/schemas/PathTrackCodecPropsOpus'
- $ref: '#/components/schemas/PathTrackCodecPropsMPEG4Audio'
- $ref: '#/components/schemas/PathTrackCodecPropsAC3'
- $ref: '#/components/schemas/PathTrackCodecPropsG711'
- $ref: '#/components/schemas/PathTrackCodecPropsLPCM'
PathTrackCodecPropsAV1:
type: object
properties:
width:
type: integer
format: int64
height:
type: integer
format: int64
profile:
type: integer
format: int64
level:
type: integer
format: int64
tier:
type: integer
format: int64
PathTrackCodecPropsVP9:
type: object
properties:
profile:
type: integer
format: int64
PathTrackCodecPropsH265:
type: object
properties:
width:
type: integer
format: int64
height:
type: integer
format: int64
profile:
type: string
level:
type: string
PathTrackCodecPropsH264:
type: object
properties:
width:
type: integer
format: int64
height:
type: integer
format: int64
profile:
type: string
level:
type: string
PathTrackCodecPropsOpus:
type: object
properties:
channelCount:
type: integer
format: int64
PathTrackCodecPropsMPEG4Audio:
type: object
properties:
sampleRate:
type: integer
format: int64
channelCount:
type: integer
format: int64
PathTrackCodecPropsAC3:
type: object
properties:
sampleRate:
type: integer
format: int64
channelCount:
type: integer
format: int64
PathTrackCodecPropsG711:
type: object
properties:
muLaw:
type: boolean
sampleRate:
type: integer
format: int64
channelCount:
type: integer
format: int64
PathTrackCodecPropsLPCM:
type: object
properties:
bitDepth:
type: integer
format: int64
sampleRate:
type: integer
format: int64
channelCount:
type: integer
format: int64
HLSMuxer: HLSMuxer:
type: object type: object
properties: properties:
+3 -3
View File
@@ -10,10 +10,10 @@ require (
github.com/abema/go-mp4 v1.5.0 github.com/abema/go-mp4 v1.5.0
github.com/alecthomas/kong v1.14.0 github.com/alecthomas/kong v1.14.0
github.com/asticode/go-astits v1.15.0 github.com/asticode/go-astits v1.15.0
github.com/bluenviron/gohlslib/v2 v2.2.8 github.com/bluenviron/gohlslib/v2 v2.2.9
github.com/bluenviron/gortmplib v0.3.0 github.com/bluenviron/gortmplib v0.3.0
github.com/bluenviron/gortsplib/v5 v5.4.1-0.20260316202619-e63876e33be9 github.com/bluenviron/gortsplib/v5 v5.5.0
github.com/bluenviron/mediacommon/v2 v2.8.2 github.com/bluenviron/mediacommon/v2 v2.8.3
github.com/datarhei/gosrt v0.10.0 github.com/datarhei/gosrt v0.10.0
github.com/fsnotify/fsnotify v1.9.0 github.com/fsnotify/fsnotify v1.9.0
github.com/gin-contrib/pprof v1.5.3 github.com/gin-contrib/pprof v1.5.3
+6 -6
View File
@@ -33,14 +33,14 @@ github.com/asticode/go-astits v1.15.0 h1:yRyCiUc8Jj4F7clt2GDxHghMpWuFL5rkaLuGUd2
github.com/asticode/go-astits v1.15.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI= github.com/asticode/go-astits v1.15.0/go.mod h1:QSHmknZ51pf6KJdHKZHJTLlMegIrhega3LPWz3ND/iI=
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYhJeJ2aZxADI2tGADS15AzIF8MQ8XAhT4= github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYhJeJ2aZxADI2tGADS15AzIF8MQ8XAhT4=
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI= github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
github.com/bluenviron/gohlslib/v2 v2.2.8 h1:+KueZeydRDciIq2t3fwT4pshqN+Vreu4c2IMPLVx2Kk= github.com/bluenviron/gohlslib/v2 v2.2.9 h1:ahRbuBzw+FvdVDwli731RgJsErunyBDWhghpS5fT2qQ=
github.com/bluenviron/gohlslib/v2 v2.2.8/go.mod h1:c4b9vKhyksO6fHhWdCpuuczxUNFeZtBahlFiwrvW2oA= github.com/bluenviron/gohlslib/v2 v2.2.9/go.mod h1:kA0hTg96hmTZjeZ/Vxwpu/Njy0emAoidEoDGQ/KlMH0=
github.com/bluenviron/gortmplib v0.3.0 h1:jBNl7bYtXSq+USx70gQ4kzRsffM1h0XRjGwlHQHZVq8= github.com/bluenviron/gortmplib v0.3.0 h1:jBNl7bYtXSq+USx70gQ4kzRsffM1h0XRjGwlHQHZVq8=
github.com/bluenviron/gortmplib v0.3.0/go.mod h1:3tUsWceMOrs8Ylt5UyQEHNHCVl52V1gi9tz+IaLTafI= github.com/bluenviron/gortmplib v0.3.0/go.mod h1:3tUsWceMOrs8Ylt5UyQEHNHCVl52V1gi9tz+IaLTafI=
github.com/bluenviron/gortsplib/v5 v5.4.1-0.20260316202619-e63876e33be9 h1:BSz3Mt+1AqqFtB0zaScPCf3lrHr9L8aQ4XtBFWgrcxU= github.com/bluenviron/gortsplib/v5 v5.5.0 h1:2wlUKhlSw1ptKEVnkEuZMpQuN8Xt311Fdd/SB124JPA=
github.com/bluenviron/gortsplib/v5 v5.4.1-0.20260316202619-e63876e33be9/go.mod h1:4K3mXlLQ3vN4AyQKtxQE8T5I0Fr1KTNanBCzxDBJKug= github.com/bluenviron/gortsplib/v5 v5.5.0/go.mod h1:otcPqR836QZej/EYx7njn8vl4TLj8Ya3QAf+GBwh2cQ=
github.com/bluenviron/mediacommon/v2 v2.8.2 h1:ZHuxthfO8o4/fhlG6JvoK5lT27tMWw4k5efpJlpu0yE= github.com/bluenviron/mediacommon/v2 v2.8.3 h1:T6xb7ZK3eBixi/HynzhtGRCEIrazwcmGIeu0WDTVISY=
github.com/bluenviron/mediacommon/v2 v2.8.2/go.mod h1:4AsE74EnTxkHeUs1VMER31fivU0jufZUAepaKFRV1lM= github.com/bluenviron/mediacommon/v2 v2.8.3/go.mod h1:CsYjGgzIz8RbloQf4BHR4uReogZsB4PEKWfePVIzJv8=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE= github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
+6
View File
@@ -661,6 +661,12 @@ func (pa *path) doAPIPathsGet(req pathAPIPathsGetReq) {
} }
return defs.MediasToCodecs(pa.stream.Desc.Medias) return defs.MediasToCodecs(pa.stream.Desc.Medias)
}(), }(),
Tracks2: func() []defs.APIPathTrack {
if !pa.isAvailable() {
return []defs.APIPathTrack{}
}
return defs.MediasToTracks(pa.stream.Desc.Medias)
}(),
InboundBytes: func() uint64 { InboundBytes: func() uint64 {
if !pa.isAvailable() { if !pa.isAvailable() {
return 0 return 0
+2 -1
View File
@@ -72,7 +72,8 @@ type APIPath struct {
Online bool `json:"online"` Online bool `json:"online"`
OnlineTime *time.Time `json:"onlineTime"` OnlineTime *time.Time `json:"onlineTime"`
Source *APIPathSource `json:"source"` Source *APIPathSource `json:"source"`
Tracks []APIPathTrackCodec `json:"tracks"` Tracks []APIPathTrackCodec `json:"tracks" deprecated:"true"`
Tracks2 []APIPathTrack `json:"tracks2"`
Readers []APIPathReader `json:"readers"` Readers []APIPathReader `json:"readers"`
InboundBytes uint64 `json:"inboundBytes"` InboundBytes uint64 `json:"inboundBytes"`
OutboundBytes uint64 `json:"outboundBytes"` OutboundBytes uint64 `json:"outboundBytes"`
+7
View File
@@ -0,0 +1,7 @@
package defs
// APIPathTrack is a track.
type APIPathTrack struct {
Codec APIPathTrackCodec `json:"codec"`
CodecProps APIPathTrackCodecProps `json:"codecProps"`
}
+246 -51
View File
@@ -1,8 +1,13 @@
package defs package defs
import ( import (
"fmt"
"strconv"
"github.com/bluenviron/gortsplib/v5/pkg/description" "github.com/bluenviron/gortsplib/v5/pkg/description"
"github.com/bluenviron/gortsplib/v5/pkg/format" "github.com/bluenviron/gortsplib/v5/pkg/format"
codecsh264 "github.com/bluenviron/mediacommon/v2/pkg/codecs/h264"
codecsh265 "github.com/bluenviron/mediacommon/v2/pkg/codecs/h265"
) )
// APIPathTrackCodec is a path track codec. // APIPathTrackCodec is a path track codec.
@@ -37,65 +42,239 @@ const (
APIPathTrackCodecGeneric APIPathTrackCodec = "Generic" APIPathTrackCodecGeneric APIPathTrackCodec = "Generic"
) )
func formatToCodec(forma format.Format) APIPathTrackCodec {
switch forma.(type) {
// video
case *format.AV1:
return APIPathTrackCodecAV1
case *format.VP9:
return APIPathTrackCodecVP9
case *format.VP8:
return APIPathTrackCodecVP8
case *format.H265:
return APIPathTrackCodecH265
case *format.H264:
return APIPathTrackCodecH264
case *format.MPEG4Video:
return APIPathTrackCodecMPEG4Video
case *format.MPEG1Video:
return APIPathTrackCodecMPEG1Video
case *format.MJPEG:
return APIPathTrackCodecMJPEG
// audio
case *format.Opus:
return APIPathTrackCodecOpus
case *format.Vorbis:
return APIPathTrackCodecVorbis
case *format.MPEG4Audio:
return APIPathTrackCodecMPEG4Audio
case *format.MPEG4AudioLATM:
return APIPathTrackCodecMPEG4AudioLATM
case *format.MPEG1Audio:
return APIPathTrackCodecMPEG1Audio
case *format.AC3:
return APIPathTrackCodecAC3
case *format.Speex:
return APIPathTrackCodecSpeex
case *format.G726:
return APIPathTrackCodecG726
case *format.G722:
return APIPathTrackCodecG722
case *format.G711:
return APIPathTrackCodecG711
case *format.LPCM:
return APIPathTrackCodecLPCM
// other
case *format.MPEGTS:
return APIPathTrackCodecMPEGTS
case *format.KLV:
return APIPathTrackCodecKLV
default:
return APIPathTrackCodecGeneric
}
}
func h264ProfileString(profileIdc uint8) string {
switch profileIdc {
case 66:
return "Baseline"
case 77:
return "Main"
case 88:
return "Extended"
case 100:
return "High"
case 110:
return "High 10"
case 122:
return "High 4:2:2"
case 244:
return "High 4:4:4 Predictive"
default:
return strconv.Itoa(int(profileIdc))
}
}
func h264LevelString(levelIdc uint8) string {
major := levelIdc / 10
minor := levelIdc % 10
if minor == 0 {
return fmt.Sprintf("%d", major)
}
return fmt.Sprintf("%d.%d", major, minor)
}
func h265ProfileString(profileIdc uint8) string {
switch profileIdc {
case 1:
return "Main"
case 2:
return "Main 10"
case 3:
return "Main Still Picture"
case 4:
return "Range Extensions"
case 5:
return "High Throughput"
case 6:
return "Multiview Main"
case 7:
return "Scalable Main"
case 8:
return "3D Main"
case 9:
return "Screen Content Coding Extensions"
case 10:
return "Scalable Range Extensions"
default:
return strconv.Itoa(int(profileIdc))
}
}
func h265LevelString(levelIdc uint8) string {
// H.265 level_idc = 30 * level, so 3.0 = 90, 4.0 = 120, 4.1 = 123
level := float64(levelIdc) / 30.0
// Check if it's a clean level like 3.0, 4.0
if levelIdc%30 == 0 {
return fmt.Sprintf("%.0f", level)
}
return fmt.Sprintf("%.1f", level)
}
// FormatsToCodecs returns codecs of given formats. // FormatsToCodecs returns codecs of given formats.
func FormatsToCodecs(formats []format.Format) []APIPathTrackCodec { func FormatsToCodecs(formats []format.Format) []APIPathTrackCodec {
ret := make([]APIPathTrackCodec, len(formats)) ret := make([]APIPathTrackCodec, len(formats))
for i, forma := range formats { for i, forma := range formats {
switch forma.(type) { ret[i] = formatToCodec(forma)
// video
case *format.AV1:
ret[i] = APIPathTrackCodecAV1
case *format.VP9:
ret[i] = APIPathTrackCodecVP9
case *format.VP8:
ret[i] = APIPathTrackCodecVP8
case *format.H265:
ret[i] = APIPathTrackCodecH265
case *format.H264:
ret[i] = APIPathTrackCodecH264
case *format.MPEG4Video:
ret[i] = APIPathTrackCodecMPEG4Video
case *format.MPEG1Video:
ret[i] = APIPathTrackCodecMPEG1Video
case *format.MJPEG:
ret[i] = APIPathTrackCodecMJPEG
// audio
case *format.Opus:
ret[i] = APIPathTrackCodecOpus
case *format.Vorbis:
ret[i] = APIPathTrackCodecVorbis
case *format.MPEG4Audio:
ret[i] = APIPathTrackCodecMPEG4Audio
case *format.MPEG4AudioLATM:
ret[i] = APIPathTrackCodecMPEG4AudioLATM
case *format.MPEG1Audio:
ret[i] = APIPathTrackCodecMPEG1Audio
case *format.AC3:
ret[i] = APIPathTrackCodecAC3
case *format.Speex:
ret[i] = APIPathTrackCodecSpeex
case *format.G726:
ret[i] = APIPathTrackCodecG726
case *format.G722:
ret[i] = APIPathTrackCodecG722
case *format.G711:
ret[i] = APIPathTrackCodecG711
case *format.LPCM:
ret[i] = APIPathTrackCodecLPCM
// other
case *format.MPEGTS:
ret[i] = APIPathTrackCodecMPEGTS
case *format.KLV:
ret[i] = APIPathTrackCodecKLV
default:
ret[i] = APIPathTrackCodecGeneric
}
} }
return ret return ret
} }
func formatToTrack(forma format.Format) APIPathTrack {
return APIPathTrack{
Codec: formatToCodec(forma),
CodecProps: formatToTrackCodecProps(forma),
}
}
func formatToTrackCodecProps(forma format.Format) APIPathTrackCodecProps {
switch forma := forma.(type) {
case *format.AV1:
props := &APIPathTrackCodecPropsAV1{}
if forma.Profile != nil {
props.Profile = *forma.Profile
}
if forma.LevelIdx != nil {
props.Level = *forma.LevelIdx
}
if forma.Tier != nil {
props.Tier = *forma.Tier
}
return props
case *format.VP9:
props := &APIPathTrackCodecPropsVP9{}
if forma.ProfileID != nil {
props.Profile = *forma.ProfileID
}
return props
case *format.H265:
props := &APIPathTrackCodecPropsH265{}
_, sps, _ := forma.SafeParams()
if sps != nil {
var s codecsh265.SPS
if err := s.Unmarshal(sps); err == nil {
props.Width = s.Width()
props.Height = s.Height()
props.Profile = h265ProfileString(s.ProfileTierLevel.GeneralProfileIdc)
props.Level = h265LevelString(s.ProfileTierLevel.GeneralLevelIdc)
}
}
return props
case *format.H264:
props := &APIPathTrackCodecPropsH264{}
sps, _ := forma.SafeParams()
if sps != nil {
var s codecsh264.SPS
if err := s.Unmarshal(sps); err == nil {
props.Width = s.Width()
props.Height = s.Height()
props.Profile = h264ProfileString(s.ProfileIdc)
props.Level = h264LevelString(s.LevelIdc)
}
}
return props
case *format.Opus:
return &APIPathTrackCodecPropsOpus{
ChannelCount: forma.ChannelCount,
}
case *format.MPEG4Audio:
props := &APIPathTrackCodecPropsMPEG4Audio{}
if forma.Config != nil {
props.SampleRate = forma.Config.SampleRate
props.ChannelCount = int(forma.Config.ChannelConfig)
}
return props
case *format.AC3:
return &APIPathTrackCodecPropsAC3{
SampleRate: forma.SampleRate,
ChannelCount: forma.ChannelCount,
}
case *format.G711:
return &APIPathTrackCodecPropsG711{
MULaw: forma.MULaw,
SampleRate: forma.SampleRate,
ChannelCount: forma.ChannelCount,
}
case *format.LPCM:
return &APIPathTrackCodecPropsLPCM{
BitDepth: forma.BitDepth,
SampleRate: forma.SampleRate,
ChannelCount: forma.ChannelCount,
}
default:
return nil
}
}
func gatherFormats(medias []*description.Media) []format.Format { func gatherFormats(medias []*description.Media) []format.Format {
n := 0 n := 0
for _, media := range medias { for _, media := range medias {
@@ -120,3 +299,19 @@ func gatherFormats(medias []*description.Media) []format.Format {
func MediasToCodecs(medias []*description.Media) []APIPathTrackCodec { func MediasToCodecs(medias []*description.Media) []APIPathTrackCodec {
return FormatsToCodecs(gatherFormats(medias)) return FormatsToCodecs(gatherFormats(medias))
} }
// FormatsToTracks returns tracks of given formats.
func FormatsToTracks(formats []format.Format) []APIPathTrack {
ret := make([]APIPathTrack, len(formats))
for i, forma := range formats {
ret[i] = formatToTrack(forma)
}
return ret
}
// MediasToTracks returns tracks of given medias.
func MediasToTracks(medias []*description.Media) []APIPathTrack {
return FormatsToTracks(gatherFormats(medias))
}
@@ -0,0 +1,85 @@
package defs
// APIPathTrackCodecProps contains codec-specific properties.
type APIPathTrackCodecProps interface {
apiPathTrackCodecProps()
}
// APIPathTrackCodecPropsAV1 contains codec-specific properties of AV1.
type APIPathTrackCodecPropsAV1 struct {
Width int `json:"width"`
Height int `json:"height"`
Profile int `json:"profile"`
Level int `json:"level"`
Tier int `json:"tier"`
}
func (*APIPathTrackCodecPropsAV1) apiPathTrackCodecProps() {}
// APIPathTrackCodecPropsVP9 contains codec-specific properties of VP9.
type APIPathTrackCodecPropsVP9 struct {
Profile int `json:"profile"`
}
func (*APIPathTrackCodecPropsVP9) apiPathTrackCodecProps() {}
// APIPathTrackCodecPropsH265 contains codec-specific properties of H265.
type APIPathTrackCodecPropsH265 struct {
Width int `json:"width"`
Height int `json:"height"`
Profile string `json:"profile"`
Level string `json:"level"`
}
func (*APIPathTrackCodecPropsH265) apiPathTrackCodecProps() {}
// APIPathTrackCodecPropsH264 contains codec-specific properties of H264.
type APIPathTrackCodecPropsH264 struct {
Width int `json:"width"`
Height int `json:"height"`
Profile string `json:"profile"`
Level string `json:"level"`
}
func (*APIPathTrackCodecPropsH264) apiPathTrackCodecProps() {}
// APIPathTrackCodecPropsOpus contains codec-specific properties of Opus.
type APIPathTrackCodecPropsOpus struct {
ChannelCount int `json:"channelCount"`
}
func (*APIPathTrackCodecPropsOpus) apiPathTrackCodecProps() {}
// APIPathTrackCodecPropsMPEG4Audio contains codec-specific properties of MPEG4Audio.
type APIPathTrackCodecPropsMPEG4Audio struct {
SampleRate int `json:"sampleRate"`
ChannelCount int `json:"channelCount"`
}
func (*APIPathTrackCodecPropsMPEG4Audio) apiPathTrackCodecProps() {}
// APIPathTrackCodecPropsAC3 contains codec-specific properties of AC3.
type APIPathTrackCodecPropsAC3 struct {
SampleRate int `json:"sampleRate"`
ChannelCount int `json:"channelCount"`
}
func (*APIPathTrackCodecPropsAC3) apiPathTrackCodecProps() {}
// APIPathTrackCodecPropsG711 contains codec-specific properties of G711.
type APIPathTrackCodecPropsG711 struct {
MULaw bool `json:"muLaw"`
SampleRate int `json:"sampleRate"`
ChannelCount int `json:"channelCount"`
}
func (*APIPathTrackCodecPropsG711) apiPathTrackCodecProps() {}
// APIPathTrackCodecPropsLPCM contains codec-specific properties of LPCM.
type APIPathTrackCodecPropsLPCM struct {
BitDepth int `json:"bitDepth"`
SampleRate int `json:"sampleRate"`
ChannelCount int `json:"channelCount"`
}
func (*APIPathTrackCodecPropsLPCM) apiPathTrackCodecProps() {}
+146
View File
@@ -3,10 +3,68 @@ package defs
import ( import (
"testing" "testing"
"github.com/bluenviron/gortsplib/v5/pkg/description"
"github.com/bluenviron/gortsplib/v5/pkg/format" "github.com/bluenviron/gortsplib/v5/pkg/format"
"github.com/bluenviron/mediacommon/v2/pkg/codecs/mpeg4audio"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func ptr[T any](v T) *T {
return &v
}
func testFormatH264() *format.H264 {
return &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
0x27, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04,
0x00, 0x00, 0x03, 0x00, 0xf0, 0x3c, 0x60, 0xc9, 0x20,
},
PPS: []byte{0x08, 0x06, 0x07, 0x08},
PacketizationMode: 1,
}
}
func testFormatMPEG4Audio() *format.MPEG4Audio {
return &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.AudioSpecificConfig{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
ChannelConfig: 2,
},
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
}
}
func testFormatH265() *format.H265 {
return &format.H265{
PayloadTyp: 96,
VPS: []byte{
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x02, 0x20,
0x00, 0x00, 0x03, 0x00, 0xb0, 0x00, 0x00, 0x03,
0x00, 0x00, 0x03, 0x00, 0x7b, 0x18, 0xb0, 0x24,
},
SPS: []byte{
0x42, 0x01, 0x01, 0x02, 0x20, 0x00, 0x00, 0x03,
0x00, 0xb0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
0x00, 0x7b, 0xa0, 0x07, 0x82, 0x00, 0x88, 0x7d,
0xb6, 0x71, 0x8b, 0x92, 0x44, 0x80, 0x53, 0x88,
0x88, 0x92, 0xcf, 0x24, 0xa6, 0x92, 0x72, 0xc9,
0x12, 0x49, 0x22, 0xdc, 0x91, 0xaa, 0x48, 0xfc,
0xa2, 0x23, 0xff, 0x00, 0x01, 0x00, 0x01, 0x6a,
0x02, 0x02, 0x02, 0x01,
},
PPS: []byte{
0x44, 0x01, 0xc0, 0x25, 0x2f, 0x05, 0x32, 0x40,
},
}
}
func TestFormatsToCodecs(t *testing.T) { func TestFormatsToCodecs(t *testing.T) {
codecs := FormatsToCodecs([]format.Format{ codecs := FormatsToCodecs([]format.Format{
&format.AV1{}, &format.AV1{},
@@ -58,3 +116,91 @@ func TestFormatsToCodecs(t *testing.T) {
APIPathTrackCodecGeneric, APIPathTrackCodecGeneric,
}, codecs) }, codecs)
} }
func TestMediasToTracks(t *testing.T) {
tracks := MediasToTracks([]*description.Media{
{Formats: []format.Format{testFormatH264()}},
{Formats: []format.Format{testFormatH265()}},
{
Formats: []format.Format{
&format.AV1{Profile: ptr(1), LevelIdx: ptr(9), Tier: ptr(0)},
&format.VP9{ProfileID: ptr(2)},
&format.MJPEG{},
&format.Opus{ChannelCount: 2},
&format.G711{MULaw: true, SampleRate: 8000, ChannelCount: 1},
&format.LPCM{BitDepth: 16, SampleRate: 48000, ChannelCount: 2},
},
},
{
Formats: []format.Format{testFormatMPEG4Audio()},
},
})
require.Equal(t, []APIPathTrack{
{
Codec: APIPathTrackCodecH264,
CodecProps: &APIPathTrackCodecPropsH264{
Width: 1920,
Height: 1080,
Profile: "Baseline",
Level: "4",
},
},
{
Codec: APIPathTrackCodecH265,
CodecProps: &APIPathTrackCodecPropsH265{
Width: 960,
Height: 540,
Profile: "Main 10",
Level: "4.1",
},
},
{
Codec: APIPathTrackCodecAV1,
CodecProps: &APIPathTrackCodecPropsAV1{
Profile: 1,
Level: 9,
Tier: 0,
},
},
{
Codec: APIPathTrackCodecVP9,
CodecProps: &APIPathTrackCodecPropsVP9{
Profile: 2,
},
},
{
Codec: APIPathTrackCodecMJPEG,
CodecProps: nil,
},
{
Codec: APIPathTrackCodecOpus,
CodecProps: &APIPathTrackCodecPropsOpus{
ChannelCount: 2,
},
},
{
Codec: APIPathTrackCodecG711,
CodecProps: &APIPathTrackCodecPropsG711{
MULaw: true,
SampleRate: 8000,
ChannelCount: 1,
},
},
{
Codec: APIPathTrackCodecLPCM,
CodecProps: &APIPathTrackCodecPropsLPCM{
BitDepth: 16,
SampleRate: 48000,
ChannelCount: 2,
},
},
{
Codec: APIPathTrackCodecMPEG4Audio,
CodecProps: &APIPathTrackCodecPropsMPEG4Audio{
SampleRate: 44100,
ChannelCount: 2,
},
},
}, tracks)
}
+12
View File
@@ -77,6 +77,14 @@ func goStructToApi(t *testing.T, rt reflect.Type) openAPIProperty {
return openAPIProperty{Ref: "#/components/schemas/" + schemaName(rt)} return openAPIProperty{Ref: "#/components/schemas/" + schemaName(rt)}
} }
if rt == reflect.TypeOf((*defs.APIPathTrackCodecProps)(nil)).Elem() {
return openAPIProperty{
Type: "object",
AllOf: []openAPIProperty{{Ref: "#/components/schemas/" + schemaName(rt)}},
Nullable: true,
}
}
switch { switch {
case rt == reflect.TypeOf(""): case rt == reflect.TypeOf(""):
return openAPIProperty{Type: "string"} return openAPIProperty{Type: "string"}
@@ -367,6 +375,10 @@ func TestGo2API(t *testing.T) {
"PathSource", "PathSource",
defs.APIPathSource{}, defs.APIPathSource{},
}, },
{
"PathTrack",
defs.APIPathTrack{},
},
{ {
"Recording", "Recording",
defs.APIRecording{}, defs.APIRecording{},