mirror of
https://github.com/bluenviron/mediamtx.git
synced 2026-01-19 18:09:49 -08:00
build(deps): bump github.com/bluenviron/gortmplib from 0.1.2 to 0.2.0 (#5300)
Some checks failed
code_lint / go (push) Has been cancelled
code_lint / go_mod (push) Has been cancelled
code_lint / docs (push) Has been cancelled
code_lint / api_docs (push) Has been cancelled
code_test / test_64 (push) Has been cancelled
code_test / test_32 (push) Has been cancelled
code_test / test_e2e (push) Has been cancelled
Some checks failed
code_lint / go (push) Has been cancelled
code_lint / go_mod (push) Has been cancelled
code_lint / docs (push) Has been cancelled
code_lint / api_docs (push) Has been cancelled
code_test / test_64 (push) Has been cancelled
code_test / test_32 (push) Has been cancelled
code_test / test_e2e (push) Has been cancelled
Bumps [github.com/bluenviron/gortmplib](https://github.com/bluenviron/gortmplib) from 0.1.2 to 0.2.0. - [Commits](https://github.com/bluenviron/gortmplib/compare/v0.1.2...v0.2.0) --- updated-dependencies: - dependency-name: github.com/bluenviron/gortmplib dependency-version: 0.2.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
This commit is contained in:
parent
174618f3f3
commit
62effa79ef
10 changed files with 646 additions and 532 deletions
2
go.mod
2
go.mod
|
|
@ -11,7 +11,7 @@ require (
|
|||
github.com/alecthomas/kong v1.13.0
|
||||
github.com/asticode/go-astits v1.14.0
|
||||
github.com/bluenviron/gohlslib/v2 v2.2.4
|
||||
github.com/bluenviron/gortmplib v0.1.2
|
||||
github.com/bluenviron/gortmplib v0.2.0
|
||||
github.com/bluenviron/gortsplib/v5 v5.2.2
|
||||
github.com/bluenviron/mediacommon/v2 v2.6.0
|
||||
github.com/datarhei/gosrt v0.9.0
|
||||
|
|
|
|||
4
go.sum
4
go.sum
|
|
@ -37,8 +37,8 @@ github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c h1:8XZeJrs4+ZYh
|
|||
github.com/benburkert/openpgp v0.0.0-20160410205803-c2471f86866c/go.mod h1:x1vxHcL/9AVzuk5HOloOEPrtJY0MaalYr78afXZ+pWI=
|
||||
github.com/bluenviron/gohlslib/v2 v2.2.4 h1:5F/Ud2VuJrrLYCDV0Ham947UIIrd801/GyoCsPYMqiw=
|
||||
github.com/bluenviron/gohlslib/v2 v2.2.4/go.mod h1:5J8Ry4xraLs6W0B2gVS0IqhCzcq+CDDJIisfkpoHyxM=
|
||||
github.com/bluenviron/gortmplib v0.1.2 h1:bJ+5pUxKnHjEoKPbNs7lDLyYy5R9Z1lISJ1n54DsjWE=
|
||||
github.com/bluenviron/gortmplib v0.1.2/go.mod h1:oVOWgfs7wsZNoKYLttyqLar7a71ZTohO1JOVXkNxtHg=
|
||||
github.com/bluenviron/gortmplib v0.2.0 h1:j15eeHrgVh6Avg9oAx+r4w0HugTqrIqLBsYnhs3D1dE=
|
||||
github.com/bluenviron/gortmplib v0.2.0/go.mod h1:yzobxBF8zusF2nKbEOF69zIIL429j0kaCWc/euNdvO4=
|
||||
github.com/bluenviron/gortsplib/v5 v5.2.2 h1:5q2viB8PGxWOSXNhVvj8buyr1wighLbHqRZ0U7MLM3o=
|
||||
github.com/bluenviron/gortsplib/v5 v5.2.2/go.mod h1:xkVBOAnR4fzaerPN650CBb7N+zUUsj7PI2HiY1TP7Co=
|
||||
github.com/bluenviron/mediacommon/v2 v2.6.0 h1:wZAPXwv7V78Cx2x7cToYIHOLToHl6APcvHbdQT+gOkg=
|
||||
|
|
|
|||
|
|
@ -15,9 +15,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
rtmpcodecs "github.com/bluenviron/gortmplib/pkg/codecs"
|
||||
"github.com/bluenviron/gortsplib/v5"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts"
|
||||
tscodecs "github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts/codecs"
|
||||
srt "github.com/datarhei/gosrt"
|
||||
|
|
@ -441,14 +441,21 @@ func TestAPIProtocolListGet(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
|
||||
track := &gortmplib.Track{
|
||||
Codec: &rtmpcodecs.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
},
|
||||
}
|
||||
|
||||
w := &gortmplib.Writer{
|
||||
Conn: conn,
|
||||
Tracks: []format.Format{test.FormatH264},
|
||||
Tracks: []*gortmplib.Track{track},
|
||||
}
|
||||
err = w.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = w.WriteH264(test.FormatH264, 2*time.Second, 2*time.Second, [][]byte{{5, 2, 3, 4}})
|
||||
err = w.WriteH264(track, 2*time.Second, 2*time.Second, [][]byte{{5, 2, 3, 4}})
|
||||
require.NoError(t, err)
|
||||
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
|
|
@ -1038,14 +1045,21 @@ func TestAPIProtocolKick(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
defer conn.Close()
|
||||
|
||||
track := &gortmplib.Track{
|
||||
Codec: &rtmpcodecs.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
},
|
||||
}
|
||||
|
||||
w := &gortmplib.Writer{
|
||||
Conn: conn,
|
||||
Tracks: []format.Format{test.FormatH264},
|
||||
Tracks: []*gortmplib.Track{track},
|
||||
}
|
||||
err = w.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = w.WriteH264(test.FormatH264, 2*time.Second, 2*time.Second, [][]byte{{5, 2, 3, 4}})
|
||||
err = w.WriteH264(track, 2*time.Second, 2*time.Second, [][]byte{{5, 2, 3, 4}})
|
||||
require.NoError(t, err)
|
||||
|
||||
case "webrtc":
|
||||
|
|
|
|||
|
|
@ -13,9 +13,9 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
rtmpcodecs "github.com/bluenviron/gortmplib/pkg/codecs"
|
||||
"github.com/bluenviron/gortsplib/v5"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
"github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts"
|
||||
tscodecs "github.com/bluenviron/mediacommon/v2/pkg/formats/mpegts/codecs"
|
||||
srt "github.com/datarhei/gosrt"
|
||||
|
|
@ -218,14 +218,21 @@ webrtc_sessions_rtcp_packets_sent 0
|
|||
require.NoError(t, err2)
|
||||
defer conn.Close()
|
||||
|
||||
track := &gortmplib.Track{
|
||||
Codec: &rtmpcodecs.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
},
|
||||
}
|
||||
|
||||
w := &gortmplib.Writer{
|
||||
Conn: conn,
|
||||
Tracks: []format.Format{test.FormatH264},
|
||||
Tracks: []*gortmplib.Track{track},
|
||||
}
|
||||
err2 = w.Initialize()
|
||||
require.NoError(t, err2)
|
||||
|
||||
err2 = w.WriteH264(test.FormatH264, 2*time.Second, 2*time.Second, [][]byte{{5, 2, 3, 4}})
|
||||
err2 = w.WriteH264(track, 2*time.Second, 2*time.Second, [][]byte{{5, 2, 3, 4}})
|
||||
require.NoError(t, err2)
|
||||
|
||||
<-terminate
|
||||
|
|
@ -246,14 +253,21 @@ webrtc_sessions_rtcp_packets_sent 0
|
|||
require.NoError(t, err2)
|
||||
defer conn.Close()
|
||||
|
||||
track := &gortmplib.Track{
|
||||
Codec: &rtmpcodecs.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
},
|
||||
}
|
||||
|
||||
w := &gortmplib.Writer{
|
||||
Conn: conn,
|
||||
Tracks: []format.Format{test.FormatH264},
|
||||
Tracks: []*gortmplib.Track{track},
|
||||
}
|
||||
err2 = w.Initialize()
|
||||
require.NoError(t, err2)
|
||||
|
||||
err2 = w.WriteH264(test.FormatH264, 2*time.Second, 2*time.Second, [][]byte{{5, 2, 3, 4}})
|
||||
err2 = w.WriteH264(track, 2*time.Second, 2*time.Second, [][]byte{{5, 2, 3, 4}})
|
||||
require.NoError(t, err2)
|
||||
|
||||
<-terminate
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
"github.com/bluenviron/gortmplib/pkg/codecs"
|
||||
"github.com/bluenviron/gortmplib/pkg/message"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
|
|
@ -44,7 +45,7 @@ func FromStream(
|
|||
nconn net.Conn,
|
||||
writeTimeout time.Duration,
|
||||
) error {
|
||||
var tracks []format.Format
|
||||
var tracks []*gortmplib.Track
|
||||
var w *gortmplib.Writer
|
||||
|
||||
for _, media := range desc.Medias {
|
||||
|
|
@ -52,6 +53,11 @@ func FromStream(
|
|||
switch forma := forma.(type) {
|
||||
case *format.AV1:
|
||||
if slices.Contains(conn.FourCcList, any(fourCCToString(message.FourCCAV1))) {
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.AV1{},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -62,16 +68,19 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
return (*w).WriteAV1(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(u.PTS, forma.ClockRate()),
|
||||
u.Payload.(unit.PayloadAV1))
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
}
|
||||
|
||||
case *format.VP9:
|
||||
if slices.Contains(conn.FourCcList, any(fourCCToString(message.FourCCVP9))) {
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.VP9{},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -82,16 +91,24 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
return (*w).WriteVP9(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(u.PTS, forma.ClockRate()),
|
||||
u.Payload.(unit.PayloadVP9))
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
}
|
||||
|
||||
case *format.H265:
|
||||
if slices.Contains(conn.FourCcList, any(fourCCToString(message.FourCCHEVC))) {
|
||||
vps, sps, pps := forma.SafeParams()
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.H265{
|
||||
VPS: vps,
|
||||
SPS: sps,
|
||||
PPS: pps,
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
var videoDTSExtractor *h265.DTSExtractor
|
||||
|
||||
r.OnData(
|
||||
|
|
@ -117,16 +134,23 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
return (*w).WriteH265(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(u.PTS, forma.ClockRate()),
|
||||
timestampToDuration(dts, forma.ClockRate()),
|
||||
u.Payload.(unit.PayloadH265))
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
}
|
||||
|
||||
case *format.H264:
|
||||
sps, pps := forma.SafeParams()
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.H264{
|
||||
SPS: sps,
|
||||
PPS: pps,
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
var videoDTSExtractor *h264.DTSExtractor
|
||||
|
||||
r.OnData(
|
||||
|
|
@ -170,16 +194,21 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
return (*w).WriteH264(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(u.PTS, forma.ClockRate()),
|
||||
timestampToDuration(dts, forma.ClockRate()),
|
||||
u.Payload.(unit.PayloadH264))
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
|
||||
case *format.Opus:
|
||||
if slices.Contains(conn.FourCcList, any(fourCCToString(message.FourCCOpus))) {
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.Opus{
|
||||
ChannelCount: forma.ChannelCount,
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -193,7 +222,7 @@ func FromStream(
|
|||
for _, pkt := range u.Payload.(unit.PayloadOpus) {
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
err := (*w).WriteOpus(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(pts, forma.ClockRate()),
|
||||
pkt,
|
||||
)
|
||||
|
|
@ -206,11 +235,16 @@ func FromStream(
|
|||
|
||||
return nil
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
}
|
||||
|
||||
case *format.MPEG4Audio:
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.MPEG4Audio{
|
||||
Config: forma.Config,
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -224,7 +258,7 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
err := (*w).WriteMPEG4Audio(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(pts, forma.ClockRate()),
|
||||
au,
|
||||
)
|
||||
|
|
@ -236,10 +270,15 @@ func FromStream(
|
|||
return nil
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
|
||||
case *format.MPEG4AudioLATM:
|
||||
if !forma.CPresent {
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.MPEG4Audio{
|
||||
Config: forma.StreamMuxConfig.Programs[0].Layers[0].AudioSpecificConfig,
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -257,16 +296,19 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
return (*w).WriteMPEG4Audio(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(u.PTS, forma.ClockRate()),
|
||||
ame.Payloads[0][0][0],
|
||||
)
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
}
|
||||
|
||||
case *format.MPEG1Audio:
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.MPEG1Audio{},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -286,7 +328,7 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
err = (*w).WriteMPEG1Audio(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(pts, forma.ClockRate()),
|
||||
frame)
|
||||
if err != nil {
|
||||
|
|
@ -300,10 +342,16 @@ func FromStream(
|
|||
return nil
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
|
||||
case *format.AC3:
|
||||
if slices.Contains(conn.FourCcList, any(fourCCToString(message.FourCCAC3))) {
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.AC3{
|
||||
SampleRate: forma.SampleRate,
|
||||
ChannelCount: forma.ChannelCount,
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -317,7 +365,7 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
err := (*w).WriteAC3(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(pts, forma.ClockRate()),
|
||||
frame)
|
||||
if err != nil {
|
||||
|
|
@ -327,12 +375,19 @@ func FromStream(
|
|||
|
||||
return nil
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
}
|
||||
|
||||
case *format.G711:
|
||||
if forma.SampleRate == 8000 {
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.G711{
|
||||
MULaw: forma.MULaw,
|
||||
SampleRate: forma.SampleRate,
|
||||
ChannelCount: forma.ChannelCount,
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -343,13 +398,11 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
return (*w).WriteG711(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(u.PTS, forma.ClockRate()),
|
||||
u.Payload.(unit.PayloadG711),
|
||||
)
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
}
|
||||
|
||||
case *format.LPCM:
|
||||
|
|
@ -358,6 +411,15 @@ func FromStream(
|
|||
forma.SampleRate == 11025 ||
|
||||
forma.SampleRate == 22050 ||
|
||||
forma.SampleRate == 44100) {
|
||||
track := &gortmplib.Track{
|
||||
Codec: &codecs.LPCM{
|
||||
BitDepth: forma.BitDepth,
|
||||
SampleRate: forma.SampleRate,
|
||||
ChannelCount: forma.ChannelCount,
|
||||
},
|
||||
}
|
||||
tracks = append(tracks, track)
|
||||
|
||||
r.OnData(
|
||||
media,
|
||||
forma,
|
||||
|
|
@ -368,13 +430,11 @@ func FromStream(
|
|||
|
||||
nconn.SetWriteDeadline(time.Now().Add(writeTimeout))
|
||||
return (*w).WriteLPCM(
|
||||
forma,
|
||||
track,
|
||||
timestampToDuration(u.PTS, forma.ClockRate()),
|
||||
u.Payload.(unit.PayloadLPCM),
|
||||
)
|
||||
})
|
||||
|
||||
tracks = append(tracks, forma)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -393,10 +453,12 @@ func FromStream(
|
|||
return err
|
||||
}
|
||||
|
||||
setuppedFormats := r.Formats()
|
||||
|
||||
n := 1
|
||||
for _, media := range desc.Medias {
|
||||
for _, forma := range media.Formats {
|
||||
if !slices.Contains(tracks, forma) {
|
||||
if !slices.Contains(setuppedFormats, forma) {
|
||||
r.Parent.Log(logger.Warn, "skipping track %d (%s)", n, forma.Codec())
|
||||
}
|
||||
n++
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
"github.com/bluenviron/gortmplib/pkg/codecs"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
"github.com/bluenviron/mediamtx/internal/codecprocessor"
|
||||
|
|
@ -20,405 +21,49 @@ import (
|
|||
)
|
||||
|
||||
func TestFromStream(t *testing.T) {
|
||||
for _, ca := range []string{
|
||||
"h264 + aac",
|
||||
"av1",
|
||||
"vp9",
|
||||
"h265",
|
||||
"h264",
|
||||
"opus",
|
||||
"aac",
|
||||
"mp3",
|
||||
"ac-3",
|
||||
"pcma",
|
||||
"pcmu",
|
||||
"lpcm",
|
||||
"h265 + h264 + vp9 + av1 + opus + aac",
|
||||
} {
|
||||
t.Run(ca, func(t *testing.T) {
|
||||
var medias []*description.Media
|
||||
h265VPS := []byte{
|
||||
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60,
|
||||
0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x03, 0x00, 0x78, 0xba, 0x02, 0x40,
|
||||
}
|
||||
h265SPS := []byte{
|
||||
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03,
|
||||
0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||
0x00, 0x78, 0xa0, 0x03, 0xc0, 0x80, 0x11, 0x07,
|
||||
0xcb, 0x96, 0xe9, 0x29, 0x30, 0xbc, 0x05, 0xa0,
|
||||
0x20, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00,
|
||||
0x03, 0x03, 0xc1,
|
||||
}
|
||||
h265PPS := []byte{
|
||||
0x44, 0x01, 0xc0, 0x73, 0xc1, 0x89,
|
||||
}
|
||||
|
||||
switch ca {
|
||||
case "h264 + aac":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{test.FormatH264},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{test.FormatMPEG4Audio},
|
||||
},
|
||||
}
|
||||
|
||||
case "av1":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.AV1{
|
||||
PayloadTyp: 96,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
case "vp9":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.VP9{
|
||||
PayloadTyp: 96,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
case "h265":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{
|
||||
&format.H265{
|
||||
PayloadTyp: 96,
|
||||
VPS: []byte{
|
||||
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60,
|
||||
0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x03, 0x00, 0x78, 0xba, 0x02, 0x40,
|
||||
},
|
||||
SPS: []byte{
|
||||
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03,
|
||||
0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||
0x00, 0x78, 0xa0, 0x03, 0xc0, 0x80, 0x11, 0x07,
|
||||
0xcb, 0x96, 0xe9, 0x29, 0x30, 0xbc, 0x05, 0xa0,
|
||||
0x20, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00,
|
||||
0x03, 0x03, 0xc1,
|
||||
},
|
||||
PPS: []byte{
|
||||
0x44, 0x01, 0xc0, 0x73, 0xc1, 0x89,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
case "h264":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{test.FormatH264},
|
||||
},
|
||||
}
|
||||
|
||||
case "opus":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.Opus{
|
||||
PayloadTyp: 96,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
case "aac":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{test.FormatMPEG4Audio},
|
||||
},
|
||||
}
|
||||
|
||||
case "mp3":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.MPEG1Audio{}},
|
||||
},
|
||||
}
|
||||
|
||||
case "ac-3":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.AC3{
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
case "pcma":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.G711{
|
||||
MULaw: false,
|
||||
SampleRate: 8000,
|
||||
ChannelCount: 1,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
case "pcmu":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.G711{
|
||||
MULaw: true,
|
||||
SampleRate: 8000,
|
||||
ChannelCount: 1,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
case "lpcm":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.LPCM{
|
||||
BitDepth: 16,
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
case "h265 + h264 + vp9 + av1 + opus + aac":
|
||||
medias = []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.H265{}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.H264{}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.VP9{}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.AV1{}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.Opus{
|
||||
PayloadTyp: 96,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.MPEG4Audio{
|
||||
PayloadTyp: 96,
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
SizeLength: 13,
|
||||
IndexLength: 3,
|
||||
IndexDeltaLength: 3,
|
||||
}},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
strm := &stream.Stream{
|
||||
WriteQueueSize: 512,
|
||||
RTPMaxPayloadSize: 1450,
|
||||
Desc: &description.Session{Medias: medias},
|
||||
GenerateRTPPackets: true,
|
||||
Parent: test.NilLogger,
|
||||
}
|
||||
err := strm.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:9121")
|
||||
require.NoError(t, err)
|
||||
defer ln.Close()
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
u, err2 := url.Parse("rtmp://127.0.0.1:9121/stream")
|
||||
require.NoError(t, err2)
|
||||
|
||||
c := &gortmplib.Client{
|
||||
URL: u,
|
||||
}
|
||||
err2 = c.Initialize(context.Background())
|
||||
require.NoError(t, err2)
|
||||
|
||||
r := &gortmplib.Reader{
|
||||
Conn: c,
|
||||
}
|
||||
err2 = r.Initialize()
|
||||
require.NoError(t, err2)
|
||||
|
||||
switch ca {
|
||||
case "h264 + aac":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
PacketizationMode: 1,
|
||||
PayloadTyp: 96,
|
||||
},
|
||||
&format.MPEG4Audio{
|
||||
PayloadTyp: 96,
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
SizeLength: 13,
|
||||
IndexLength: 3,
|
||||
IndexDeltaLength: 3,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "av1":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.AV1{
|
||||
PayloadTyp: 96,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "vp9":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.VP9{
|
||||
PayloadTyp: 96,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "h265":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.H265{
|
||||
VPS: []byte{
|
||||
0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60,
|
||||
0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03,
|
||||
0x00, 0x00, 0x03, 0x00, 0x78, 0xba, 0x02, 0x40,
|
||||
},
|
||||
SPS: []byte{
|
||||
0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03,
|
||||
0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03,
|
||||
0x00, 0x78, 0xa0, 0x03, 0xc0, 0x80, 0x11, 0x07,
|
||||
0xcb, 0x96, 0xe9, 0x29, 0x30, 0xbc, 0x05, 0xa0,
|
||||
0x20, 0x00, 0x00, 0x03, 0x00, 0x20, 0x00, 0x00,
|
||||
0x03, 0x03, 0xc1,
|
||||
},
|
||||
PPS: []byte{
|
||||
0x44, 0x01, 0xc0, 0x73, 0xc1, 0x89,
|
||||
},
|
||||
PayloadTyp: 96,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "h264":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
PacketizationMode: 1,
|
||||
PayloadTyp: 96,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "opus":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.Opus{
|
||||
PayloadTyp: 96,
|
||||
ChannelCount: 2,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "aac":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.MPEG4Audio{
|
||||
PayloadTyp: 96,
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
SizeLength: 13,
|
||||
IndexLength: 3,
|
||||
IndexDeltaLength: 3,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "mp3":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.MPEG1Audio{},
|
||||
}, r.Tracks())
|
||||
|
||||
case "ac-3":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.AC3{
|
||||
PayloadTyp: 96,
|
||||
SampleRate: 48000,
|
||||
ChannelCount: 1,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "pcma":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.G711{
|
||||
PayloadTyp: 8,
|
||||
MULaw: false,
|
||||
ChannelCount: 1,
|
||||
SampleRate: 8000,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "pcmu":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.G711{
|
||||
MULaw: true,
|
||||
ChannelCount: 1,
|
||||
SampleRate: 8000,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "lpcm":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.LPCM{
|
||||
PayloadTyp: 96,
|
||||
BitDepth: 16,
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
},
|
||||
}, r.Tracks())
|
||||
|
||||
case "h265 + h264 + vp9 + av1 + opus + aac":
|
||||
require.Equal(t, []format.Format{
|
||||
&format.H265{
|
||||
PayloadTyp: 96,
|
||||
VPS: codecprocessor.H265DefaultVPS,
|
||||
SPS: codecprocessor.H265DefaultSPS,
|
||||
PPS: codecprocessor.H265DefaultPPS,
|
||||
},
|
||||
&format.H264{
|
||||
PayloadTyp: 96,
|
||||
PacketizationMode: 1,
|
||||
SPS: codecprocessor.H264DefaultSPS,
|
||||
PPS: codecprocessor.H264DefaultPPS,
|
||||
},
|
||||
&format.VP9{
|
||||
PayloadTyp: 96,
|
||||
},
|
||||
&format.AV1{
|
||||
PayloadTyp: 96,
|
||||
},
|
||||
&format.Opus{
|
||||
PayloadTyp: 96,
|
||||
ChannelCount: 2,
|
||||
},
|
||||
&format.MPEG4Audio{
|
||||
PayloadTyp: 96,
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
SizeLength: 13,
|
||||
IndexLength: 3,
|
||||
IndexDeltaLength: 3,
|
||||
},
|
||||
}, r.Tracks())
|
||||
}
|
||||
|
||||
close(done)
|
||||
}()
|
||||
|
||||
nconn, err := ln.Accept()
|
||||
require.NoError(t, err)
|
||||
defer nconn.Close()
|
||||
|
||||
conn := &gortmplib.ServerConn{
|
||||
RW: nconn,
|
||||
}
|
||||
err = conn.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = conn.Accept()
|
||||
require.NoError(t, err)
|
||||
|
||||
r := &stream.Reader{Parent: test.NilLogger}
|
||||
|
||||
err = FromStream(strm.Desc, r, conn, nconn, 10*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
strm.AddReader(r)
|
||||
defer strm.RemoveReader(r)
|
||||
|
||||
switch ca {
|
||||
case "h264 + aac":
|
||||
cases := []struct {
|
||||
name string
|
||||
medias []*description.Media
|
||||
expectedTracks []*gortmplib.Track
|
||||
writeUnits func([]*description.Media, *stream.Stream)
|
||||
}{
|
||||
{
|
||||
name: "h264 + aac",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{test.FormatH264},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{test.FormatMPEG4Audio},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
}},
|
||||
{Codec: &codecs.MPEG4Audio{
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 0,
|
||||
Payload: unit.PayloadH264{
|
||||
|
|
@ -432,8 +77,21 @@ func TestFromStream(t *testing.T) {
|
|||
{3, 4},
|
||||
},
|
||||
})
|
||||
|
||||
case "av1":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "av1",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.AV1{
|
||||
PayloadTyp: 96,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.AV1{}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 2 * int64(i),
|
||||
|
|
@ -443,16 +101,51 @@ func TestFromStream(t *testing.T) {
|
|||
}},
|
||||
})
|
||||
}
|
||||
|
||||
case "vp9":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "vp9",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.VP9{
|
||||
PayloadTyp: 96,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.VP9{}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 2 * int64(i),
|
||||
Payload: unit.PayloadVP9{1, 2},
|
||||
})
|
||||
}
|
||||
|
||||
case "h265":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "h265",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{
|
||||
&format.H265{
|
||||
PayloadTyp: 96,
|
||||
VPS: h265VPS,
|
||||
SPS: h265SPS,
|
||||
PPS: h265PPS,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.H265{
|
||||
VPS: h265VPS,
|
||||
SPS: h265SPS,
|
||||
PPS: h265PPS,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 2 * int64(i),
|
||||
|
|
@ -462,8 +155,22 @@ func TestFromStream(t *testing.T) {
|
|||
}},
|
||||
})
|
||||
}
|
||||
|
||||
case "h264":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "h264",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{test.FormatH264},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 2 * int64(i),
|
||||
|
|
@ -472,8 +179,24 @@ func TestFromStream(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "opus":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "opus",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.Opus{
|
||||
PayloadTyp: 96,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.Opus{
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 5 * int64(i),
|
||||
|
|
@ -482,8 +205,21 @@ func TestFromStream(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "aac":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "aac",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{test.FormatMPEG4Audio},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.MPEG4Audio{
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 5 * int64(i),
|
||||
|
|
@ -492,8 +228,19 @@ func TestFromStream(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "mp3":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mp3",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.MPEG1Audio{}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.MPEG1Audio{}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 5 * int64(i),
|
||||
|
|
@ -504,8 +251,25 @@ func TestFromStream(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "ac-3":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ac-3",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.AC3{
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.AC3{
|
||||
SampleRate: 48000,
|
||||
ChannelCount: 1,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 5 * int64(i),
|
||||
|
|
@ -563,8 +327,27 @@ func TestFromStream(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "pcma", "pcmu":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pcma",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.G711{
|
||||
MULaw: false,
|
||||
SampleRate: 8000,
|
||||
ChannelCount: 1,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.G711{
|
||||
MULaw: false,
|
||||
ChannelCount: 1,
|
||||
SampleRate: 8000,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 5 * int64(i),
|
||||
|
|
@ -573,8 +356,56 @@ func TestFromStream(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "lpcm":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pcmu",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.G711{
|
||||
MULaw: true,
|
||||
SampleRate: 8000,
|
||||
ChannelCount: 1,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.G711{
|
||||
MULaw: true,
|
||||
ChannelCount: 1,
|
||||
SampleRate: 8000,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 5 * int64(i),
|
||||
Payload: unit.PayloadG711{
|
||||
3, 4,
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "lpcm",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.LPCM{
|
||||
BitDepth: 16,
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.LPCM{
|
||||
BitDepth: 16,
|
||||
SampleRate: 44100,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
for i := range 2 {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
PTS: 90000 * 5 * int64(i),
|
||||
|
|
@ -583,8 +414,59 @@ func TestFromStream(t *testing.T) {
|
|||
},
|
||||
})
|
||||
}
|
||||
|
||||
case "h265 + h264 + vp9 + av1 + opus + aac":
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "h265 + h264 + vp9 + av1 + opus + aac",
|
||||
medias: []*description.Media{
|
||||
{
|
||||
Formats: []format.Format{&format.H265{}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.H264{}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.VP9{}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.AV1{}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.Opus{
|
||||
PayloadTyp: 96,
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
},
|
||||
{
|
||||
Formats: []format.Format{&format.MPEG4Audio{
|
||||
PayloadTyp: 96,
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
SizeLength: 13,
|
||||
IndexLength: 3,
|
||||
IndexDeltaLength: 3,
|
||||
}},
|
||||
},
|
||||
},
|
||||
expectedTracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.H265{
|
||||
VPS: codecprocessor.H265DefaultVPS,
|
||||
SPS: codecprocessor.H265DefaultSPS,
|
||||
PPS: codecprocessor.H265DefaultPPS,
|
||||
}},
|
||||
{Codec: &codecs.H264{
|
||||
SPS: codecprocessor.H264DefaultSPS,
|
||||
PPS: codecprocessor.H264DefaultPPS,
|
||||
}},
|
||||
{Codec: &codecs.VP9{}},
|
||||
{Codec: &codecs.AV1{}},
|
||||
{Codec: &codecs.Opus{
|
||||
ChannelCount: 2,
|
||||
}},
|
||||
{Codec: &codecs.MPEG4Audio{
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
}},
|
||||
},
|
||||
writeUnits: func(medias []*description.Media, strm *stream.Stream) {
|
||||
strm.WriteUnit(medias[0], medias[0].Formats[0], &unit.Unit{
|
||||
Payload: unit.PayloadH265{
|
||||
{
|
||||
|
|
@ -641,7 +523,73 @@ func TestFromStream(t *testing.T) {
|
|||
{3, 4},
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
medias := tc.medias
|
||||
|
||||
strm := &stream.Stream{
|
||||
WriteQueueSize: 512,
|
||||
RTPMaxPayloadSize: 1450,
|
||||
Desc: &description.Session{Medias: medias},
|
||||
GenerateRTPPackets: true,
|
||||
Parent: test.NilLogger,
|
||||
}
|
||||
err := strm.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
ln, err := net.Listen("tcp", "127.0.0.1:9121")
|
||||
require.NoError(t, err)
|
||||
defer ln.Close()
|
||||
|
||||
done := make(chan struct{})
|
||||
|
||||
go func() {
|
||||
u, err2 := url.Parse("rtmp://127.0.0.1:9121/stream")
|
||||
require.NoError(t, err2)
|
||||
|
||||
c := &gortmplib.Client{
|
||||
URL: u,
|
||||
}
|
||||
err2 = c.Initialize(context.Background())
|
||||
require.NoError(t, err2)
|
||||
|
||||
r := &gortmplib.Reader{
|
||||
Conn: c,
|
||||
}
|
||||
err2 = r.Initialize()
|
||||
require.NoError(t, err2)
|
||||
|
||||
require.Equal(t, tc.expectedTracks, r.Tracks())
|
||||
|
||||
close(done)
|
||||
}()
|
||||
|
||||
nconn, err := ln.Accept()
|
||||
require.NoError(t, err)
|
||||
defer nconn.Close()
|
||||
|
||||
conn := &gortmplib.ServerConn{
|
||||
RW: nconn,
|
||||
}
|
||||
err = conn.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = conn.Accept()
|
||||
require.NoError(t, err)
|
||||
|
||||
r := &stream.Reader{Parent: test.NilLogger}
|
||||
|
||||
err = FromStream(strm.Desc, r, conn, nconn, 10*time.Second)
|
||||
require.NoError(t, err)
|
||||
|
||||
strm.AddReader(r)
|
||||
defer strm.RemoveReader(r)
|
||||
|
||||
tc.writeUnits(medias, strm)
|
||||
|
||||
<-done
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
"github.com/bluenviron/gortmplib/pkg/codecs"
|
||||
"github.com/bluenviron/gortmplib/pkg/message"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
|
|
@ -38,145 +39,199 @@ func ToStream(
|
|||
var medias []*description.Media
|
||||
|
||||
for _, track := range r.Tracks() {
|
||||
ctrack := track
|
||||
|
||||
switch ttrack := track.(type) {
|
||||
case *format.AV1:
|
||||
switch codec := track.Codec.(type) {
|
||||
case *codecs.AV1:
|
||||
forma := &format.AV1{
|
||||
PayloadTyp: 96,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeVideo,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataAV1(ttrack, func(pts time.Duration, tu [][]byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataAV1(track, func(pts time.Duration, tu [][]byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadAV1(tu),
|
||||
})
|
||||
})
|
||||
|
||||
case *format.VP9:
|
||||
case *codecs.VP9:
|
||||
forma := &format.VP9{
|
||||
PayloadTyp: 96,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeVideo,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataVP9(ttrack, func(pts time.Duration, frame []byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataVP9(track, func(pts time.Duration, frame []byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadVP9(frame),
|
||||
})
|
||||
})
|
||||
|
||||
case *format.H265:
|
||||
case *codecs.H265:
|
||||
forma := &format.H265{
|
||||
PayloadTyp: 96,
|
||||
VPS: codec.VPS,
|
||||
SPS: codec.SPS,
|
||||
PPS: codec.PPS,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeVideo,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataH265(ttrack, func(pts time.Duration, _ time.Duration, au [][]byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataH265(track, func(pts time.Duration, _ time.Duration, au [][]byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadH265(au),
|
||||
})
|
||||
})
|
||||
|
||||
case *format.H264:
|
||||
case *codecs.H264:
|
||||
forma := &format.H264{
|
||||
PayloadTyp: 96,
|
||||
SPS: codec.SPS,
|
||||
PPS: codec.PPS,
|
||||
PacketizationMode: 1,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeVideo,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataH264(ttrack, func(pts time.Duration, _ time.Duration, au [][]byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataH264(track, func(pts time.Duration, _ time.Duration, au [][]byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadH264(au),
|
||||
})
|
||||
})
|
||||
|
||||
case *format.Opus:
|
||||
case *codecs.Opus:
|
||||
forma := &format.Opus{
|
||||
PayloadTyp: 96,
|
||||
ChannelCount: codec.ChannelCount,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeAudio,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataOpus(ttrack, func(pts time.Duration, packet []byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataOpus(track, func(pts time.Duration, packet []byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadOpus{packet},
|
||||
})
|
||||
})
|
||||
|
||||
case *format.MPEG4Audio:
|
||||
case *codecs.MPEG4Audio:
|
||||
forma := &format.MPEG4Audio{
|
||||
PayloadTyp: 96,
|
||||
Config: codec.Config,
|
||||
SizeLength: 13,
|
||||
IndexLength: 3,
|
||||
IndexDeltaLength: 3,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeAudio,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataMPEG4Audio(ttrack, func(pts time.Duration, au []byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataMPEG4Audio(track, func(pts time.Duration, au []byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadMPEG4Audio{au},
|
||||
})
|
||||
})
|
||||
|
||||
case *format.MPEG1Audio:
|
||||
case *codecs.MPEG1Audio:
|
||||
forma := &format.MPEG1Audio{}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeAudio,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataMPEG1Audio(ttrack, func(pts time.Duration, frame []byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataMPEG1Audio(track, func(pts time.Duration, frame []byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadMPEG1Audio{frame},
|
||||
})
|
||||
})
|
||||
|
||||
case *format.AC3:
|
||||
case *codecs.AC3:
|
||||
forma := &format.AC3{
|
||||
PayloadTyp: 96,
|
||||
SampleRate: codec.SampleRate,
|
||||
ChannelCount: codec.ChannelCount,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeAudio,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataAC3(ttrack, func(pts time.Duration, frame []byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataAC3(track, func(pts time.Duration, frame []byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadAC3{frame},
|
||||
})
|
||||
})
|
||||
|
||||
case *format.G711:
|
||||
case *codecs.G711:
|
||||
forma := &format.G711{
|
||||
PayloadTyp: func() uint8 {
|
||||
switch {
|
||||
case codec.ChannelCount == 1 && codec.MULaw:
|
||||
return 0
|
||||
case codec.ChannelCount == 1 && !codec.MULaw:
|
||||
return 8
|
||||
default:
|
||||
return 96
|
||||
}
|
||||
}(),
|
||||
MULaw: codec.MULaw,
|
||||
SampleRate: codec.SampleRate,
|
||||
ChannelCount: codec.ChannelCount,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeAudio,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataG711(ttrack, func(pts time.Duration, samples []byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataG711(track, func(pts time.Duration, samples []byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadG711(samples),
|
||||
})
|
||||
})
|
||||
|
||||
case *format.LPCM:
|
||||
case *codecs.LPCM:
|
||||
forma := &format.LPCM{
|
||||
PayloadTyp: 96,
|
||||
BitDepth: codec.BitDepth,
|
||||
SampleRate: codec.SampleRate,
|
||||
ChannelCount: codec.ChannelCount,
|
||||
}
|
||||
medi := &description.Media{
|
||||
Type: description.MediaTypeAudio,
|
||||
Formats: []format.Format{ctrack},
|
||||
Formats: []format.Format{forma},
|
||||
}
|
||||
medias = append(medias, medi)
|
||||
|
||||
r.OnDataLPCM(ttrack, func(pts time.Duration, samples []byte) {
|
||||
(*strm).WriteUnit(medi, ctrack, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, ctrack.ClockRate()),
|
||||
r.OnDataLPCM(track, func(pts time.Duration, samples []byte) {
|
||||
(*strm).WriteUnit(medi, forma, &unit.Unit{
|
||||
PTS: durationToTimestamp(pts, forma.ClockRate()),
|
||||
Payload: unit.PayloadLPCM(samples),
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"crypto/rand"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
|
|
@ -666,10 +667,12 @@ func FromStream(
|
|||
return errNoSupportedCodecsFrom
|
||||
}
|
||||
|
||||
setuppedFormats := r.Formats()
|
||||
|
||||
n := 1
|
||||
for _, media := range desc.Medias {
|
||||
for _, forma := range media.Formats {
|
||||
if forma != videoFormat && forma != audioFormat {
|
||||
if !slices.Contains(setuppedFormats, forma) {
|
||||
r.Parent.Log(logger.Warn, "skipping track %d (%s)", n, forma.Codec())
|
||||
}
|
||||
n++
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
"github.com/bluenviron/gortmplib/pkg/codecs"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/description"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
"github.com/bluenviron/mediamtx/internal/conf"
|
||||
"github.com/bluenviron/mediamtx/internal/defs"
|
||||
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
||||
|
|
@ -158,14 +158,22 @@ func TestServerPublish(t *testing.T) {
|
|||
defer conn.Close()
|
||||
|
||||
w := &gortmplib.Writer{
|
||||
Conn: conn,
|
||||
Tracks: []format.Format{test.FormatH264, test.FormatMPEG4Audio},
|
||||
Conn: conn,
|
||||
Tracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
}},
|
||||
{Codec: &codecs.MPEG4Audio{
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
}},
|
||||
},
|
||||
}
|
||||
err = w.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = w.WriteH264(
|
||||
test.FormatH264,
|
||||
w.Tracks[0],
|
||||
2*time.Second, 2*time.Second, [][]byte{
|
||||
{5, 2, 3, 4},
|
||||
})
|
||||
|
|
@ -292,9 +300,11 @@ func TestServerRead(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
tracks := r.Tracks()
|
||||
require.Equal(t, []format.Format{test.FormatH264}, tracks)
|
||||
require.Len(t, tracks, 1)
|
||||
_, ok := tracks[0].Codec.(*codecs.H264)
|
||||
require.True(t, ok)
|
||||
|
||||
r.OnDataH264(tracks[0].(*format.H264), func(_ time.Duration, _ time.Duration, au [][]byte) {
|
||||
r.OnDataH264(tracks[0], func(_ time.Duration, _ time.Duration, au [][]byte) {
|
||||
require.Equal(t, [][]byte{
|
||||
test.FormatH264.SPS,
|
||||
test.FormatH264.PPS,
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import (
|
|||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/bluenviron/gortmplib"
|
||||
"github.com/bluenviron/gortsplib/v5/pkg/format"
|
||||
"github.com/bluenviron/gortmplib/pkg/codecs"
|
||||
"github.com/bluenviron/mediamtx/internal/conf"
|
||||
"github.com/bluenviron/mediamtx/internal/defs"
|
||||
"github.com/bluenviron/mediamtx/internal/test"
|
||||
|
|
@ -118,13 +118,21 @@ func TestSource(t *testing.T) {
|
|||
require.NoError(t, err)
|
||||
|
||||
w := &gortmplib.Writer{
|
||||
Conn: conn,
|
||||
Tracks: []format.Format{test.FormatH264, test.FormatMPEG4Audio},
|
||||
Conn: conn,
|
||||
Tracks: []*gortmplib.Track{
|
||||
{Codec: &codecs.H264{
|
||||
SPS: test.FormatH264.SPS,
|
||||
PPS: test.FormatH264.PPS,
|
||||
}},
|
||||
{Codec: &codecs.MPEG4Audio{
|
||||
Config: test.FormatMPEG4Audio.Config,
|
||||
}},
|
||||
},
|
||||
}
|
||||
err = w.Initialize()
|
||||
require.NoError(t, err)
|
||||
|
||||
err = w.WriteMPEG4Audio(test.FormatMPEG4Audio, 2*time.Second, []byte{5, 2, 3, 4})
|
||||
err = w.WriteMPEG4Audio(w.Tracks[1], 2*time.Second, []byte{5, 2, 3, 4})
|
||||
require.NoError(t, err)
|
||||
|
||||
break
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue