@@ -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:
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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=
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"`
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package defs
|
||||||
|
|
||||||
|
// APIPathTrack is a track.
|
||||||
|
type APIPathTrack struct {
|
||||||
|
Codec APIPathTrackCodec `json:"codec"`
|
||||||
|
CodecProps APIPathTrackCodecProps `json:"codecProps"`
|
||||||
|
}
|
||||||
@@ -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() {}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
|||||||
@@ -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{},
|
||||||
|
|||||||
Reference in New Issue
Block a user