mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-10-04 23:32:44 +08:00
update gortsplib
This commit is contained in:
4
go.mod
4
go.mod
@@ -3,7 +3,7 @@ module github.com/aler9/rtsp-simple-server
|
|||||||
go 1.17
|
go 1.17
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/aler9/gortsplib v0.0.0-20220130132715-cd0db96a5d41
|
github.com/aler9/gortsplib v0.0.0-20220130155047-8c02b12955f8
|
||||||
github.com/asticode/go-astits v1.10.0
|
github.com/asticode/go-astits v1.10.0
|
||||||
github.com/fsnotify/fsnotify v1.4.9
|
github.com/fsnotify/fsnotify v1.4.9
|
||||||
github.com/gin-gonic/gin v1.7.2
|
github.com/gin-gonic/gin v1.7.2
|
||||||
@@ -12,7 +12,6 @@ require (
|
|||||||
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
|
||||||
github.com/notedit/rtmp v0.0.2
|
github.com/notedit/rtmp v0.0.2
|
||||||
github.com/pion/rtp v1.6.2
|
github.com/pion/rtp v1.6.2
|
||||||
github.com/pion/sdp/v3 v3.0.2
|
|
||||||
github.com/stretchr/testify v1.6.1
|
github.com/stretchr/testify v1.6.1
|
||||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
gopkg.in/alecthomas/kingpin.v2 v2.2.6
|
||||||
@@ -37,6 +36,7 @@ require (
|
|||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
|
||||||
github.com/pion/randutil v0.1.0 // indirect
|
github.com/pion/randutil v0.1.0 // indirect
|
||||||
github.com/pion/rtcp v1.2.4 // indirect
|
github.com/pion/rtcp v1.2.4 // indirect
|
||||||
|
github.com/pion/sdp/v3 v3.0.2 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/ugorji/go/codec v1.1.7 // indirect
|
github.com/ugorji/go/codec v1.1.7 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
|
||||||
|
4
go.sum
4
go.sum
@@ -2,8 +2,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo
|
|||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d h1:UQZhZ2O0vMHr2cI+DC1Mbh0TJxzA3RcLoMsFw+aXw7E=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||||
github.com/aler9/gortsplib v0.0.0-20220130132715-cd0db96a5d41 h1:Ib0hUXSsNYBWeSYAFz4n76qJesJ/i3IpEThnmH6uY38=
|
github.com/aler9/gortsplib v0.0.0-20220130155047-8c02b12955f8 h1:kGq65HZMjRo2ay0eJO/MxkEhD2DcDTkcX4qCgsjUkSs=
|
||||||
github.com/aler9/gortsplib v0.0.0-20220130132715-cd0db96a5d41/go.mod h1:fyQrQyHo8QvdR/h357tkv1g36VesZlzEPsdAu2VrHHc=
|
github.com/aler9/gortsplib v0.0.0-20220130155047-8c02b12955f8/go.mod h1:fyQrQyHo8QvdR/h357tkv1g36VesZlzEPsdAu2VrHHc=
|
||||||
github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927 h1:95mXJ5fUCYpBRdSOnLAQAdJHHKxxxJrVCiaqDi965YQ=
|
github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927 h1:95mXJ5fUCYpBRdSOnLAQAdJHHKxxxJrVCiaqDi965YQ=
|
||||||
github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927/go.mod h1:vzuE21rowz+lT1NGsWbreIvYulgBpCGnQyeTyFblUHc=
|
github.com/aler9/rtmp v0.0.0-20210403095203-3be4a5535927/go.mod h1:vzuE21rowz+lT1NGsWbreIvYulgBpCGnQyeTyFblUHc=
|
||||||
github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8=
|
github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8=
|
||||||
|
@@ -186,7 +186,7 @@ func TestAPIPathsList(t *testing.T) {
|
|||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
@@ -251,7 +251,7 @@ func TestAPIList(t *testing.T) {
|
|||||||
defer p.close()
|
defer p.close()
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
switch ca {
|
switch ca {
|
||||||
@@ -382,7 +382,7 @@ func TestAPIKick(t *testing.T) {
|
|||||||
defer p.close()
|
defer p.close()
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
switch ca {
|
switch ca {
|
||||||
|
@@ -1,8 +1,11 @@
|
|||||||
package core
|
package core
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -12,7 +15,7 @@ import (
|
|||||||
|
|
||||||
"github.com/aler9/gortsplib"
|
"github.com/aler9/gortsplib"
|
||||||
"github.com/aler9/gortsplib/pkg/base"
|
"github.com/aler9/gortsplib/pkg/base"
|
||||||
psdp "github.com/pion/sdp/v3"
|
"github.com/aler9/gortsplib/pkg/headers"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -160,33 +163,61 @@ func TestCorePathAutoDeletion(t *testing.T) {
|
|||||||
defer p.close()
|
defer p.close()
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
c := gortsplib.Client{}
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
|
|
||||||
err := c.Start("rtsp", "localhost:8554")
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer c.Close()
|
defer conn.Close()
|
||||||
|
br := bufio.NewReader(conn)
|
||||||
|
|
||||||
if ca == "describe" {
|
if ca == "describe" {
|
||||||
ur, err := base.ParseURL("rtsp://localhost:8554/mypath")
|
u, err := base.ParseURL("rtsp://localhost:8554/mypath")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, _, _, err = c.Describe(ur)
|
var bb bytes.Buffer
|
||||||
require.EqualError(t, err, "bad status code: 404 (Not Found)")
|
base.Request{
|
||||||
|
Method: base.Describe,
|
||||||
|
URL: u,
|
||||||
|
Header: base.Header{
|
||||||
|
"CSeq": base.HeaderValue{"1"},
|
||||||
|
},
|
||||||
|
}.Write(&bb)
|
||||||
|
_, err = conn.Write(bb.Bytes())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var res base.Response
|
||||||
|
err = res.Read(br)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, base.StatusNotFound, res.StatusCode)
|
||||||
} else {
|
} else {
|
||||||
baseURL, err := base.ParseURL("rtsp://localhost:8554/mypath/")
|
u, err := base.ParseURL("rtsp://localhost:8554/mypath/trackID=0")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
var bb bytes.Buffer
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
base.Request{
|
||||||
|
Method: base.Setup,
|
||||||
|
URL: u,
|
||||||
|
Header: base.Header{
|
||||||
|
"CSeq": base.HeaderValue{"1"},
|
||||||
|
"Transport": headers.Transport{
|
||||||
|
Mode: func() *headers.TransportMode {
|
||||||
|
v := headers.TransportModePlay
|
||||||
|
return &v
|
||||||
|
}(),
|
||||||
|
Delivery: func() *headers.TransportDelivery {
|
||||||
|
v := headers.TransportDeliveryUnicast
|
||||||
|
return &v
|
||||||
|
}(),
|
||||||
|
Protocol: headers.TransportProtocolUDP,
|
||||||
|
ClientPorts: &[2]int{35466, 35467},
|
||||||
|
}.Write(),
|
||||||
|
},
|
||||||
|
}.Write(&bb)
|
||||||
|
_, err = conn.Write(bb.Bytes())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
|
var res base.Response
|
||||||
Key: "control",
|
err = res.Read(br)
|
||||||
Value: "trackID=0",
|
require.NoError(t, err)
|
||||||
})
|
require.Equal(t, base.StatusNotFound, res.StatusCode)
|
||||||
|
|
||||||
_, err = c.Setup(true, track, baseURL, 0, 0)
|
|
||||||
require.EqualError(t, err, "bad status code: 404 (Not Found)")
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -219,7 +250,7 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -270,35 +301,59 @@ func main() {
|
|||||||
defer p1.close()
|
defer p1.close()
|
||||||
|
|
||||||
func() {
|
func() {
|
||||||
c := gortsplib.Client{}
|
conn, err := net.Dial("tcp", "localhost:8554")
|
||||||
|
|
||||||
err := c.Start("rtsp", "localhost:8554")
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer c.Close()
|
defer conn.Close()
|
||||||
|
br := bufio.NewReader(conn)
|
||||||
|
|
||||||
if ca == "describe" || ca == "describe and setup" {
|
if ca == "describe" || ca == "describe and setup" {
|
||||||
ur, err := base.ParseURL("rtsp://localhost:8554/ondemand")
|
u, err := base.ParseURL("rtsp://localhost:8554/ondemand")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
_, _, _, err = c.Describe(ur)
|
var bb bytes.Buffer
|
||||||
|
base.Request{
|
||||||
|
Method: base.Describe,
|
||||||
|
URL: u,
|
||||||
|
Header: base.Header{
|
||||||
|
"CSeq": base.HeaderValue{"1"},
|
||||||
|
},
|
||||||
|
}.Write(&bb)
|
||||||
|
_, err = conn.Write(bb.Bytes())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var res base.Response
|
||||||
|
err = res.Read(br)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, base.StatusOK, res.StatusCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
if ca == "setup" || ca == "describe and setup" {
|
if ca == "setup" || ca == "describe and setup" {
|
||||||
baseURL, err := base.ParseURL("rtsp://localhost:8554/ondemand/")
|
u, err := base.ParseURL("rtsp://localhost:8554/ondemand/trackID=0")
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
var bb bytes.Buffer
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
base.Request{
|
||||||
|
Method: base.Setup,
|
||||||
|
URL: u,
|
||||||
|
Header: base.Header{
|
||||||
|
"CSeq": base.HeaderValue{"2"},
|
||||||
|
"Transport": headers.Transport{
|
||||||
|
Mode: func() *headers.TransportMode {
|
||||||
|
v := headers.TransportModePlay
|
||||||
|
return &v
|
||||||
|
}(),
|
||||||
|
Protocol: headers.TransportProtocolTCP,
|
||||||
|
InterleavedIDs: &[2]int{0, 1},
|
||||||
|
}.Write(),
|
||||||
|
},
|
||||||
|
}.Write(&bb)
|
||||||
|
_, err = conn.Write(bb.Bytes())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
|
var res base.Response
|
||||||
Key: "control",
|
err = res.Read(br)
|
||||||
Value: "trackID=0",
|
|
||||||
})
|
|
||||||
|
|
||||||
_, err = c.Setup(true, track, baseURL, 0, 0)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, base.StatusOK, res.StatusCode)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -326,7 +381,7 @@ func TestCorePathRunOnReady(t *testing.T) {
|
|||||||
require.Equal(t, true, ok)
|
require.Equal(t, true, ok)
|
||||||
defer p.close()
|
defer p.close()
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
@@ -360,7 +415,7 @@ func TestCoreHotReloading(t *testing.T) {
|
|||||||
|
|
||||||
func() {
|
func() {
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
@@ -380,7 +435,7 @@ func TestCoreHotReloading(t *testing.T) {
|
|||||||
|
|
||||||
func() {
|
func() {
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
conn := gortsplib.Client{}
|
conn := gortsplib.Client{}
|
||||||
|
@@ -276,37 +276,32 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
|
|||||||
m.path.onReaderRemove(pathReaderRemoveReq{author: m})
|
m.path.onReaderRemove(pathReaderRemoveReq{author: m})
|
||||||
}()
|
}()
|
||||||
|
|
||||||
var videoTrack *gortsplib.Track
|
var videoTrack *gortsplib.TrackH264
|
||||||
videoTrackID := -1
|
videoTrackID := -1
|
||||||
var h264Decoder *rtph264.Decoder
|
var h264Decoder *rtph264.Decoder
|
||||||
var audioTrack *gortsplib.Track
|
var audioTrack *gortsplib.TrackAAC
|
||||||
audioTrackID := -1
|
audioTrackID := -1
|
||||||
var aacDecoder *rtpaac.Decoder
|
var aacDecoder *rtpaac.Decoder
|
||||||
|
|
||||||
for i, t := range res.stream.tracks() {
|
for i, track := range res.stream.tracks() {
|
||||||
if t.IsH264() {
|
switch tt := track.(type) {
|
||||||
|
case *gortsplib.TrackH264:
|
||||||
if videoTrack != nil {
|
if videoTrack != nil {
|
||||||
return fmt.Errorf("can't read track %d with HLS: too many tracks", i+1)
|
return fmt.Errorf("can't encode track %d with HLS: too many tracks", i+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
videoTrack = t
|
videoTrack = tt
|
||||||
videoTrackID = i
|
videoTrackID = i
|
||||||
|
|
||||||
h264Decoder = rtph264.NewDecoder()
|
h264Decoder = rtph264.NewDecoder()
|
||||||
} else if t.IsAAC() {
|
|
||||||
|
case *gortsplib.TrackAAC:
|
||||||
if audioTrack != nil {
|
if audioTrack != nil {
|
||||||
return fmt.Errorf("can't read track %d with HLS: too many tracks", i+1)
|
return fmt.Errorf("can't encode track %d with HLS: too many tracks", i+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
audioTrack = t
|
audioTrack = tt
|
||||||
audioTrackID = i
|
audioTrackID = i
|
||||||
|
aacDecoder = rtpaac.NewDecoder(track.ClockRate())
|
||||||
conf, err := t.ExtractConfigAAC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
aacDecoder = rtpaac.NewDecoder(conf.SampleRate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -99,7 +99,7 @@ func (s *hlsSource) runInner() bool {
|
|||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
onTracks := func(videoTrack *gortsplib.Track, audioTrack *gortsplib.Track) error {
|
onTracks := func(videoTrack gortsplib.Track, audioTrack gortsplib.Track) error {
|
||||||
var tracks gortsplib.Tracks
|
var tracks gortsplib.Tracks
|
||||||
|
|
||||||
if videoTrack != nil {
|
if videoTrack != nil {
|
||||||
|
@@ -30,7 +30,7 @@ func TestMetrics(t *testing.T) {
|
|||||||
defer p.close()
|
defer p.close()
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
source := gortsplib.Client{}
|
source := gortsplib.Client{}
|
||||||
|
@@ -249,32 +249,33 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|||||||
c.state = gortsplib.ServerSessionStateRead
|
c.state = gortsplib.ServerSessionStateRead
|
||||||
c.stateMutex.Unlock()
|
c.stateMutex.Unlock()
|
||||||
|
|
||||||
var videoTrack *gortsplib.Track
|
var videoTrack *gortsplib.TrackH264
|
||||||
videoTrackID := -1
|
videoTrackID := -1
|
||||||
var h264Decoder *rtph264.Decoder
|
var h264Decoder *rtph264.Decoder
|
||||||
var audioTrack *gortsplib.Track
|
var audioTrack *gortsplib.TrackAAC
|
||||||
audioTrackID := -1
|
audioTrackID := -1
|
||||||
var audioClockRate int
|
var audioClockRate int
|
||||||
var aacDecoder *rtpaac.Decoder
|
var aacDecoder *rtpaac.Decoder
|
||||||
|
|
||||||
for i, t := range res.stream.tracks() {
|
for i, track := range res.stream.tracks() {
|
||||||
if t.IsH264() {
|
switch tt := track.(type) {
|
||||||
|
case *gortsplib.TrackH264:
|
||||||
if videoTrack != nil {
|
if videoTrack != nil {
|
||||||
return fmt.Errorf("can't read track %d with RTMP: too many tracks", i+1)
|
return fmt.Errorf("can't read track %d with RTMP: too many tracks", i+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
videoTrack = t
|
videoTrack = tt
|
||||||
videoTrackID = i
|
videoTrackID = i
|
||||||
h264Decoder = rtph264.NewDecoder()
|
h264Decoder = rtph264.NewDecoder()
|
||||||
} else if t.IsAAC() {
|
|
||||||
|
case *gortsplib.TrackAAC:
|
||||||
if audioTrack != nil {
|
if audioTrack != nil {
|
||||||
return fmt.Errorf("can't read track %d with RTMP: too many tracks", i+1)
|
return fmt.Errorf("can't read track %d with RTMP: too many tracks", i+1)
|
||||||
}
|
}
|
||||||
|
|
||||||
audioTrack = t
|
audioTrack = tt
|
||||||
audioTrackID = i
|
audioTrackID = i
|
||||||
audioClockRate, _ = audioTrack.ClockRate()
|
aacDecoder = rtpaac.NewDecoder(track.ClockRate())
|
||||||
aacDecoder = rtpaac.NewDecoder(audioClockRate)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,7 +284,10 @@ func (c *rtmpConn) runRead(ctx context.Context) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.conn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
|
c.conn.SetWriteDeadline(time.Now().Add(time.Duration(c.writeTimeout)))
|
||||||
c.conn.WriteMetadata(videoTrack, audioTrack)
|
err := c.conn.WriteMetadata(videoTrack, audioTrack)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
c.ringBuffer = ringbuffer.New(uint64(c.readBufferCount))
|
c.ringBuffer = ringbuffer.New(uint64(c.readBufferCount))
|
||||||
|
|
||||||
@@ -456,8 +460,7 @@ func (c *rtmpConn) runPublish(ctx context.Context) error {
|
|||||||
|
|
||||||
var aacEncoder *rtpaac.Encoder
|
var aacEncoder *rtpaac.Encoder
|
||||||
if audioTrack != nil {
|
if audioTrack != nil {
|
||||||
clockRate, _ := audioTrack.ClockRate()
|
aacEncoder = rtpaac.NewEncoder(96, audioTrack.ClockRate(), nil, nil, nil)
|
||||||
aacEncoder = rtpaac.NewEncoder(96, clockRate, nil, nil, nil)
|
|
||||||
audioTrackID = len(tracks)
|
audioTrackID = len(tracks)
|
||||||
tracks = append(tracks, audioTrack)
|
tracks = append(tracks, audioTrack)
|
||||||
}
|
}
|
||||||
|
@@ -142,8 +142,7 @@ func (s *rtmpSource) runInner() bool {
|
|||||||
|
|
||||||
var aacEncoder *rtpaac.Encoder
|
var aacEncoder *rtpaac.Encoder
|
||||||
if audioTrack != nil {
|
if audioTrack != nil {
|
||||||
clockRate, _ := audioTrack.ClockRate()
|
aacEncoder = rtpaac.NewEncoder(96, audioTrack.ClockRate(), nil, nil, nil)
|
||||||
aacEncoder = rtpaac.NewEncoder(96, clockRate, nil, nil, nil)
|
|
||||||
audioTrackID = len(tracks)
|
audioTrackID = len(tracks)
|
||||||
tracks = append(tracks, audioTrack)
|
tracks = append(tracks, audioTrack)
|
||||||
}
|
}
|
||||||
|
@@ -230,7 +230,7 @@ func TestRTSPServerAuth(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
source := gortsplib.Client{}
|
source := gortsplib.Client{}
|
||||||
@@ -268,7 +268,7 @@ func TestRTSPServerAuth(t *testing.T) {
|
|||||||
defer p.close()
|
defer p.close()
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
source := gortsplib.Client{}
|
source := gortsplib.Client{}
|
||||||
@@ -314,7 +314,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
|||||||
defer p.close()
|
defer p.close()
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
@@ -377,7 +377,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
|||||||
defer p.close()
|
defer p.close()
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
@@ -401,7 +401,7 @@ func TestRTSPServerAuthFail(t *testing.T) {
|
|||||||
defer a.close()
|
defer a.close()
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
c := gortsplib.Client{}
|
c := gortsplib.Client{}
|
||||||
@@ -434,7 +434,7 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
|
|||||||
defer p.close()
|
defer p.close()
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96,
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x01, 0x02, 0x03, 0x04}})
|
[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}, nil)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
s1 := gortsplib.Client{}
|
s1 := gortsplib.Client{}
|
||||||
|
@@ -41,10 +41,10 @@ type rtspSession struct {
|
|||||||
path *path
|
path *path
|
||||||
state gortsplib.ServerSessionState
|
state gortsplib.ServerSessionState
|
||||||
stateMutex sync.Mutex
|
stateMutex sync.Mutex
|
||||||
setuppedTracks map[int]*gortsplib.Track // read
|
setuppedTracks map[int]gortsplib.Track // read
|
||||||
onReadCmd *externalcmd.Cmd // read
|
onReadCmd *externalcmd.Cmd // read
|
||||||
announcedTracks gortsplib.Tracks // publish
|
announcedTracks gortsplib.Tracks // publish
|
||||||
stream *stream // publish
|
stream *stream // publish
|
||||||
}
|
}
|
||||||
|
|
||||||
func newRTSPSession(
|
func newRTSPSession(
|
||||||
@@ -126,30 +126,11 @@ func (s *rtspSession) onClose(err error) {
|
|||||||
// onAnnounce is called by rtspServer.
|
// onAnnounce is called by rtspServer.
|
||||||
func (s *rtspSession) onAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
func (s *rtspSession) onAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnnounceCtx) (*base.Response, error) {
|
||||||
for i, track := range ctx.Tracks {
|
for i, track := range ctx.Tracks {
|
||||||
if track.IsH264() {
|
if th264, ok := track.(*gortsplib.TrackH264); ok {
|
||||||
_, err := track.ExtractConfigH264()
|
if th264.SPS() == nil || th264.PPS() == nil {
|
||||||
if err != nil {
|
|
||||||
return &base.Response{
|
return &base.Response{
|
||||||
StatusCode: base.StatusBadRequest,
|
StatusCode: base.StatusBadRequest,
|
||||||
}, fmt.Errorf("H264 track %d is not valid: %v", i+1, err)
|
}, fmt.Errorf("track %d can't be used: H264 SPS or PPS not provided into the SDP", i)
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if track.IsAAC() {
|
|
||||||
_, err := track.ExtractConfigAAC()
|
|
||||||
if err != nil {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("AAC track %d is not valid: %v", i+1, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if track.IsOpus() {
|
|
||||||
_, err := track.ExtractConfigOpus()
|
|
||||||
if err != nil {
|
|
||||||
return &base.Response{
|
|
||||||
StatusCode: base.StatusBadRequest,
|
|
||||||
}, fmt.Errorf("Opus track %d is not valid: %v", i+1, err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -257,7 +238,7 @@ func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt
|
|||||||
}
|
}
|
||||||
|
|
||||||
if s.setuppedTracks == nil {
|
if s.setuppedTracks == nil {
|
||||||
s.setuppedTracks = make(map[int]*gortsplib.Track)
|
s.setuppedTracks = make(map[int]gortsplib.Track)
|
||||||
}
|
}
|
||||||
s.setuppedTracks[ctx.TrackID] = res.stream.tracks()[ctx.TrackID]
|
s.setuppedTracks[ctx.TrackID] = res.stream.tracks()[ctx.TrackID]
|
||||||
|
|
||||||
|
@@ -239,20 +239,21 @@ func (s *rtspSource) runInner() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *rtspSource) handleMissingH264Params(c *gortsplib.Client, tracks gortsplib.Tracks) error {
|
func (s *rtspSource) handleMissingH264Params(c *gortsplib.Client, tracks gortsplib.Tracks) error {
|
||||||
h264TrackID := func() int {
|
h264Track, h264TrackID := func() (*gortsplib.TrackH264, int) {
|
||||||
for i, t := range tracks {
|
for i, t := range tracks {
|
||||||
if t.IsH264() {
|
if th264, ok := t.(*gortsplib.TrackH264); ok {
|
||||||
return i
|
if th264.SPS() == nil {
|
||||||
|
return th264, i
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -1
|
return nil, -1
|
||||||
}()
|
}()
|
||||||
if h264TrackID < 0 {
|
if h264TrackID < 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err := tracks[h264TrackID].ExtractConfigH264()
|
if h264Track.SPS() != nil && h264Track.PPS() != nil {
|
||||||
if err == nil {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -261,7 +262,6 @@ func (s *rtspSource) handleMissingH264Params(c *gortsplib.Client, tracks gortspl
|
|||||||
|
|
||||||
var streamMutex sync.RWMutex
|
var streamMutex sync.RWMutex
|
||||||
var stream *stream
|
var stream *stream
|
||||||
var payloadType uint8
|
|
||||||
decoder := rtph264.NewDecoder()
|
decoder := rtph264.NewDecoder()
|
||||||
var sps []byte
|
var sps []byte
|
||||||
var pps []byte
|
var pps []byte
|
||||||
@@ -293,8 +293,6 @@ func (s *rtspSource) handleMissingH264Params(c *gortsplib.Client, tracks gortspl
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
payloadType = pkt.Header.PayloadType
|
|
||||||
|
|
||||||
for _, nalu := range nalus {
|
for _, nalu := range nalus {
|
||||||
typ := h264.NALUType(nalu[0] & 0x1F)
|
typ := h264.NALUType(nalu[0] & 0x1F)
|
||||||
switch typ {
|
switch typ {
|
||||||
@@ -325,7 +323,7 @@ func (s *rtspSource) handleMissingH264Params(c *gortsplib.Client, tracks gortspl
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = c.Play(nil)
|
_, err := c.Play(nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -348,15 +346,8 @@ func (s *rtspSource) handleMissingH264Params(c *gortsplib.Client, tracks gortspl
|
|||||||
case <-paramsReceived:
|
case <-paramsReceived:
|
||||||
s.log(logger.Info, "H264 parameters extracted")
|
s.log(logger.Info, "H264 parameters extracted")
|
||||||
|
|
||||||
track, err := gortsplib.NewTrackH264(payloadType, &gortsplib.TrackConfigH264{
|
h264Track.SetSPS(sps)
|
||||||
SPS: sps,
|
h264Track.SetPPS(pps)
|
||||||
PPS: pps,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
tracks[h264TrackID] = track
|
|
||||||
|
|
||||||
res := s.parent.onSourceStaticSetReady(pathSourceStaticSetReadyReq{
|
res := s.parent.onSourceStaticSetReady(pathSourceStaticSetReadyReq{
|
||||||
source: s,
|
source: s,
|
||||||
|
@@ -11,7 +11,6 @@ import (
|
|||||||
"github.com/aler9/gortsplib/pkg/base"
|
"github.com/aler9/gortsplib/pkg/base"
|
||||||
"github.com/aler9/gortsplib/pkg/rtph264"
|
"github.com/aler9/gortsplib/pkg/rtph264"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
psdp "github.com/pion/sdp/v3"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -41,8 +40,7 @@ func TestRTSPSource(t *testing.T) {
|
|||||||
"tls",
|
"tls",
|
||||||
} {
|
} {
|
||||||
t.Run(source, func(t *testing.T) {
|
t.Run(source, func(t *testing.T) {
|
||||||
track, _ := gortsplib.NewTrackH264(96,
|
track, _ := gortsplib.NewTrackH264(96, []byte{0x01, 0x02, 0x03, 0x04}, []byte{0x05, 0x06}, nil)
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x05, 0x06}})
|
|
||||||
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
|
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
|
||||||
var authValidator *auth.Validator
|
var authValidator *auth.Validator
|
||||||
|
|
||||||
@@ -151,8 +149,7 @@ func TestRTSPSource(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRTSPSourceNoPassword(t *testing.T) {
|
func TestRTSPSourceNoPassword(t *testing.T) {
|
||||||
track, _ := gortsplib.NewTrackH264(96,
|
track, _ := gortsplib.NewTrackH264(96, []byte{0x01, 0x02, 0x03, 0x04}, []byte{0x05, 0x06}, nil)
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x05, 0x06}})
|
|
||||||
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
|
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
|
||||||
var authValidator *auth.Validator
|
var authValidator *auth.Validator
|
||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
@@ -210,15 +207,8 @@ func TestRTSPSourceNoPassword(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestRTSPSourceMissingH264Params(t *testing.T) {
|
func TestRTSPSourceMissingH264Params(t *testing.T) {
|
||||||
track, _ := gortsplib.NewTrackH264(96,
|
track, err := gortsplib.NewTrackH264(96, nil, nil, nil)
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x01, 0x02, 0x03, 0x04}, PPS: []byte{0x05, 0x06}})
|
require.NoError(t, err)
|
||||||
var newattrs []psdp.Attribute
|
|
||||||
for _, attr := range track.Media.Attributes {
|
|
||||||
if attr.Key != "fmtp" {
|
|
||||||
newattrs = append(newattrs, attr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
track.Media.Attributes = newattrs
|
|
||||||
|
|
||||||
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
|
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
|
||||||
|
|
||||||
@@ -275,7 +265,7 @@ func TestRTSPSourceMissingH264Params(t *testing.T) {
|
|||||||
},
|
},
|
||||||
RTSPAddress: "127.0.0.1:8555",
|
RTSPAddress: "127.0.0.1:8555",
|
||||||
}
|
}
|
||||||
err := s.Start()
|
err = s.Start()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Wait()
|
defer s.Wait()
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
@@ -314,10 +304,10 @@ func TestRTSPSourceMissingH264Params(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer c.Close()
|
defer c.Close()
|
||||||
|
|
||||||
conf, err := c.Tracks()[0].ExtractConfigH264()
|
h264Track, ok := c.Tracks()[0].(*gortsplib.TrackH264)
|
||||||
require.NoError(t, err)
|
require.Equal(t, true, ok)
|
||||||
require.Equal(t, []byte{7, 1, 2, 3}, conf.SPS)
|
require.Equal(t, []byte{7, 1, 2, 3}, h264Track.SPS())
|
||||||
require.Equal(t, []byte{8}, conf.PPS)
|
require.Equal(t, []byte{8}, h264Track.PPS())
|
||||||
|
|
||||||
<-received
|
<-received
|
||||||
}
|
}
|
||||||
|
@@ -134,7 +134,7 @@ type clientVideoProcessorData struct {
|
|||||||
|
|
||||||
type clientVideoProcessor struct {
|
type clientVideoProcessor struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
onTrack func(*gortsplib.Track) error
|
onTrack func(gortsplib.Track) error
|
||||||
onPacket func([]byte)
|
onPacket func([]byte)
|
||||||
|
|
||||||
queue chan clientVideoProcessorData
|
queue chan clientVideoProcessorData
|
||||||
@@ -146,7 +146,7 @@ type clientVideoProcessor struct {
|
|||||||
|
|
||||||
func newClientVideoProcessor(
|
func newClientVideoProcessor(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
onTrack func(*gortsplib.Track) error,
|
onTrack func(gortsplib.Track) error,
|
||||||
onPacket func([]byte),
|
onPacket func([]byte),
|
||||||
) *clientVideoProcessor {
|
) *clientVideoProcessor {
|
||||||
p := &clientVideoProcessor{
|
p := &clientVideoProcessor{
|
||||||
@@ -203,7 +203,7 @@ func (p *clientVideoProcessor) doProcess(
|
|||||||
p.sps = append([]byte(nil), nalu...)
|
p.sps = append([]byte(nil), nalu...)
|
||||||
|
|
||||||
if p.encoder == nil && p.pps != nil {
|
if p.encoder == nil && p.pps != nil {
|
||||||
err := p.initializeTrack()
|
err := p.initializeEncoder()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -218,7 +218,7 @@ func (p *clientVideoProcessor) doProcess(
|
|||||||
p.pps = append([]byte(nil), nalu...)
|
p.pps = append([]byte(nil), nalu...)
|
||||||
|
|
||||||
if p.encoder == nil && p.sps != nil {
|
if p.encoder == nil && p.sps != nil {
|
||||||
err := p.initializeTrack()
|
err := p.initializeEncoder()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -272,8 +272,8 @@ func (p *clientVideoProcessor) process(
|
|||||||
p.queue <- clientVideoProcessorData{data, pts, dts}
|
p.queue <- clientVideoProcessorData{data, pts, dts}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *clientVideoProcessor) initializeTrack() error {
|
func (p *clientVideoProcessor) initializeEncoder() error {
|
||||||
track, err := gortsplib.NewTrackH264(96, &gortsplib.TrackConfigH264{SPS: p.sps, PPS: p.pps})
|
track, err := gortsplib.NewTrackH264(96, p.sps, p.pps, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -290,18 +290,17 @@ type clientAudioProcessorData struct {
|
|||||||
|
|
||||||
type clientAudioProcessor struct {
|
type clientAudioProcessor struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
onTrack func(*gortsplib.Track) error
|
onTrack func(gortsplib.Track) error
|
||||||
onPacket func([]byte)
|
onPacket func([]byte)
|
||||||
|
|
||||||
queue chan clientAudioProcessorData
|
queue chan clientAudioProcessorData
|
||||||
conf *gortsplib.TrackConfigAAC
|
|
||||||
encoder *rtpaac.Encoder
|
encoder *rtpaac.Encoder
|
||||||
clockStartRTC time.Time
|
clockStartRTC time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
func newClientAudioProcessor(
|
func newClientAudioProcessor(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
onTrack func(*gortsplib.Track) error,
|
onTrack func(gortsplib.Track) error,
|
||||||
onPacket func([]byte),
|
onPacket func([]byte),
|
||||||
) *clientAudioProcessor {
|
) *clientAudioProcessor {
|
||||||
p := &clientAudioProcessor{
|
p := &clientAudioProcessor{
|
||||||
@@ -354,18 +353,17 @@ func (p *clientAudioProcessor) doProcess(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.conf == nil {
|
if p.encoder == nil {
|
||||||
p.conf = &gortsplib.TrackConfigAAC{
|
track, err := gortsplib.NewTrackAAC(97, pkt.Type, pkt.SampleRate, pkt.ChannelCount, nil)
|
||||||
Type: pkt.Type,
|
if err != nil {
|
||||||
SampleRate: pkt.SampleRate,
|
return err
|
||||||
ChannelCount: pkt.ChannelCount,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.encoder == nil {
|
p.encoder = rtpaac.NewEncoder(97, track.ClockRate(), nil, nil, nil)
|
||||||
err := p.initializeTrack()
|
|
||||||
if err != nil {
|
err = p.onTrack(track)
|
||||||
return err
|
if err != nil {
|
||||||
}
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -373,10 +371,6 @@ func (p *clientAudioProcessor) doProcess(
|
|||||||
pktPts += 1000 * time.Second / time.Duration(pkt.SampleRate)
|
pktPts += 1000 * time.Second / time.Duration(pkt.SampleRate)
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.encoder == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
pkts, err := p.encoder.Encode(aus, pts)
|
pkts, err := p.encoder.Encode(aus, pts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("error while encoding AAC: %v", err)
|
return fmt.Errorf("error while encoding AAC: %v", err)
|
||||||
@@ -407,17 +401,6 @@ func (p *clientAudioProcessor) process(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *clientAudioProcessor) initializeTrack() error {
|
|
||||||
track, err := gortsplib.NewTrackAAC(97, p.conf)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
p.encoder = rtpaac.NewEncoder(97, p.conf.SampleRate, nil, nil, nil)
|
|
||||||
|
|
||||||
return p.onTrack(track)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ClientParent is the parent of a Client.
|
// ClientParent is the parent of a Client.
|
||||||
type ClientParent interface {
|
type ClientParent interface {
|
||||||
Log(level logger.Level, format string, args ...interface{})
|
Log(level logger.Level, format string, args ...interface{})
|
||||||
@@ -425,7 +408,7 @@ type ClientParent interface {
|
|||||||
|
|
||||||
// Client is a HLS client.
|
// Client is a HLS client.
|
||||||
type Client struct {
|
type Client struct {
|
||||||
onTracks func(*gortsplib.Track, *gortsplib.Track) error
|
onTracks func(gortsplib.Track, gortsplib.Track) error
|
||||||
onPacket func(bool, []byte)
|
onPacket func(bool, []byte)
|
||||||
parent ClientParent
|
parent ClientParent
|
||||||
|
|
||||||
@@ -447,8 +430,8 @@ type Client struct {
|
|||||||
audioProc *clientAudioProcessor
|
audioProc *clientAudioProcessor
|
||||||
|
|
||||||
tracksMutex sync.RWMutex
|
tracksMutex sync.RWMutex
|
||||||
videoTrack *gortsplib.Track
|
videoTrack gortsplib.Track
|
||||||
audioTrack *gortsplib.Track
|
audioTrack gortsplib.Track
|
||||||
|
|
||||||
// in
|
// in
|
||||||
allocateProcs chan clientAllocateProcsReq
|
allocateProcs chan clientAllocateProcsReq
|
||||||
@@ -461,7 +444,7 @@ type Client struct {
|
|||||||
func NewClient(
|
func NewClient(
|
||||||
primaryPlaylistURLStr string,
|
primaryPlaylistURLStr string,
|
||||||
fingerprint string,
|
fingerprint string,
|
||||||
onTracks func(*gortsplib.Track, *gortsplib.Track) error,
|
onTracks func(gortsplib.Track, gortsplib.Track) error,
|
||||||
onPacket func(bool, []byte),
|
onPacket func(bool, []byte),
|
||||||
parent ClientParent,
|
parent ClientParent,
|
||||||
) (*Client, error) {
|
) (*Client, error) {
|
||||||
@@ -896,33 +879,33 @@ func (c *Client) processSegment(innerCtx context.Context, byts []byte) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) onVideoTrack(track *gortsplib.Track) error {
|
func (c *Client) onVideoTrack(track gortsplib.Track) error {
|
||||||
c.tracksMutex.Lock()
|
c.tracksMutex.Lock()
|
||||||
defer c.tracksMutex.Unlock()
|
defer c.tracksMutex.Unlock()
|
||||||
|
|
||||||
c.videoTrack = track
|
c.videoTrack = track
|
||||||
|
|
||||||
if c.audioPID == nil || c.audioTrack != nil {
|
if c.audioPID == nil || c.audioTrack != nil {
|
||||||
return c.initializeTracks()
|
return c.initializeEncoders()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) onAudioTrack(track *gortsplib.Track) error {
|
func (c *Client) onAudioTrack(track gortsplib.Track) error {
|
||||||
c.tracksMutex.Lock()
|
c.tracksMutex.Lock()
|
||||||
defer c.tracksMutex.Unlock()
|
defer c.tracksMutex.Unlock()
|
||||||
|
|
||||||
c.audioTrack = track
|
c.audioTrack = track
|
||||||
|
|
||||||
if c.videoPID == nil || c.videoTrack != nil {
|
if c.videoPID == nil || c.videoTrack != nil {
|
||||||
return c.initializeTracks()
|
return c.initializeEncoders()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) initializeTracks() error {
|
func (c *Client) initializeEncoders() error {
|
||||||
return c.onTracks(c.videoTrack, c.audioTrack)
|
return c.onTracks(c.videoTrack, c.audioTrack)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -200,7 +200,7 @@ func TestClient(t *testing.T) {
|
|||||||
c, err := NewClient(
|
c, err := NewClient(
|
||||||
prefix+"://localhost:5780/stream.m3u8",
|
prefix+"://localhost:5780/stream.m3u8",
|
||||||
"33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739",
|
"33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739",
|
||||||
func(*gortsplib.Track, *gortsplib.Track) error {
|
func(gortsplib.Track, gortsplib.Track) error {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
func(isVideo bool, byts []byte) {
|
func(isVideo bool, byts []byte) {
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package hls
|
package hls
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -18,27 +19,15 @@ type Muxer struct {
|
|||||||
func NewMuxer(
|
func NewMuxer(
|
||||||
hlsSegmentCount int,
|
hlsSegmentCount int,
|
||||||
hlsSegmentDuration time.Duration,
|
hlsSegmentDuration time.Duration,
|
||||||
videoTrack *gortsplib.Track,
|
videoTrack *gortsplib.TrackH264,
|
||||||
audioTrack *gortsplib.Track) (*Muxer, error) {
|
audioTrack *gortsplib.TrackAAC) (*Muxer, error) {
|
||||||
var h264Conf *gortsplib.TrackConfigH264
|
|
||||||
if videoTrack != nil {
|
if videoTrack != nil {
|
||||||
var err error
|
if videoTrack.SPS() == nil || videoTrack.PPS() == nil {
|
||||||
h264Conf, err = videoTrack.ExtractConfigH264()
|
return nil, fmt.Errorf("invalid H264 track: SPS or PPS not provided into the SDP")
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var aacConf *gortsplib.TrackConfigAAC
|
primaryPlaylist := newMuxerPrimaryPlaylist(videoTrack, audioTrack)
|
||||||
if audioTrack != nil {
|
|
||||||
var err error
|
|
||||||
aacConf, err = audioTrack.ExtractConfigAAC()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
primaryPlaylist := newMuxerPrimaryPlaylist(videoTrack, audioTrack, h264Conf, aacConf)
|
|
||||||
|
|
||||||
streamPlaylist := newMuxerStreamPlaylist(hlsSegmentCount)
|
streamPlaylist := newMuxerStreamPlaylist(hlsSegmentCount)
|
||||||
|
|
||||||
@@ -47,8 +36,6 @@ func NewMuxer(
|
|||||||
hlsSegmentDuration,
|
hlsSegmentDuration,
|
||||||
videoTrack,
|
videoTrack,
|
||||||
audioTrack,
|
audioTrack,
|
||||||
h264Conf,
|
|
||||||
aacConf,
|
|
||||||
streamPlaylist)
|
streamPlaylist)
|
||||||
|
|
||||||
m := &Muxer{
|
m := &Muxer{
|
||||||
|
@@ -11,34 +11,30 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type muxerPrimaryPlaylist struct {
|
type muxerPrimaryPlaylist struct {
|
||||||
videoTrack *gortsplib.Track
|
videoTrack *gortsplib.TrackH264
|
||||||
audioTrack *gortsplib.Track
|
audioTrack *gortsplib.TrackAAC
|
||||||
h264Conf *gortsplib.TrackConfigH264
|
|
||||||
|
|
||||||
cnt []byte
|
cnt []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
func newMuxerPrimaryPlaylist(
|
func newMuxerPrimaryPlaylist(
|
||||||
videoTrack *gortsplib.Track,
|
videoTrack *gortsplib.TrackH264,
|
||||||
audioTrack *gortsplib.Track,
|
audioTrack *gortsplib.TrackAAC,
|
||||||
h264Conf *gortsplib.TrackConfigH264,
|
|
||||||
aacConf *gortsplib.TrackConfigAAC,
|
|
||||||
) *muxerPrimaryPlaylist {
|
) *muxerPrimaryPlaylist {
|
||||||
p := &muxerPrimaryPlaylist{
|
p := &muxerPrimaryPlaylist{
|
||||||
videoTrack: videoTrack,
|
videoTrack: videoTrack,
|
||||||
audioTrack: audioTrack,
|
audioTrack: audioTrack,
|
||||||
h264Conf: h264Conf,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var codecs []string
|
var codecs []string
|
||||||
|
|
||||||
if p.videoTrack != nil {
|
if p.videoTrack != nil {
|
||||||
codecs = append(codecs, "avc1."+hex.EncodeToString(p.h264Conf.SPS[1:4]))
|
codecs = append(codecs, "avc1."+hex.EncodeToString(p.videoTrack.SPS()[1:4]))
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter
|
// https://developer.mozilla.org/en-US/docs/Web/Media/Formats/codecs_parameter
|
||||||
if p.audioTrack != nil {
|
if p.audioTrack != nil {
|
||||||
codecs = append(codecs, "mp4a.40."+strconv.FormatInt(int64(aacConf.Type), 10))
|
codecs = append(codecs, "mp4a.40."+strconv.FormatInt(int64(p.audioTrack.Type()), 10))
|
||||||
}
|
}
|
||||||
|
|
||||||
p.cnt = []byte("#EXTM3U\n" +
|
p.cnt = []byte("#EXTM3U\n" +
|
||||||
|
@@ -18,12 +18,10 @@ func checkTSPacket(t *testing.T, byts []byte, pid int, afc int) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxerVideoAudio(t *testing.T) {
|
func TestMuxerVideoAudio(t *testing.T) {
|
||||||
videoTrack, err := gortsplib.NewTrackH264(96,
|
videoTrack, err := gortsplib.NewTrackH264(96, []byte{0x07, 0x01, 0x02, 0x03}, []byte{0x08}, nil)
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x07, 0x01, 0x02, 0x03}, PPS: []byte{0x08}})
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
audioTrack, err := gortsplib.NewTrackAAC(97,
|
audioTrack, err := gortsplib.NewTrackAAC(97, 2, 44100, 2, nil)
|
||||||
&gortsplib.TrackConfigAAC{Type: 2, SampleRate: 44100, ChannelCount: 2})
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
m, err := NewMuxer(3, 1*time.Second, videoTrack, audioTrack)
|
m, err := NewMuxer(3, 1*time.Second, videoTrack, audioTrack)
|
||||||
@@ -129,8 +127,7 @@ func TestMuxerVideoAudio(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxerAudio(t *testing.T) {
|
func TestMuxerAudio(t *testing.T) {
|
||||||
audioTrack, err := gortsplib.NewTrackAAC(97,
|
audioTrack, err := gortsplib.NewTrackAAC(97, 2, 44100, 2, nil)
|
||||||
&gortsplib.TrackConfigAAC{Type: 2, SampleRate: 44100, ChannelCount: 2})
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
m, err := NewMuxer(3, 1*time.Second, nil, audioTrack)
|
m, err := NewMuxer(3, 1*time.Second, nil, audioTrack)
|
||||||
@@ -178,15 +175,10 @@ func TestMuxerAudio(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestMuxerCloseBeforeFirstSegment(t *testing.T) {
|
func TestMuxerCloseBeforeFirstSegment(t *testing.T) {
|
||||||
videoTrack, err := gortsplib.NewTrackH264(96,
|
videoTrack, err := gortsplib.NewTrackH264(96, []byte{0x07, 0x01, 0x02, 0x03}, []byte{0x08}, nil)
|
||||||
&gortsplib.TrackConfigH264{SPS: []byte{0x07, 0x01, 0x02, 0x03}, PPS: []byte{0x08}})
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
audioTrack, err := gortsplib.NewTrackAAC(97,
|
m, err := NewMuxer(3, 1*time.Second, videoTrack, nil)
|
||||||
&gortsplib.TrackConfigAAC{Type: 2, SampleRate: 44100, ChannelCount: 2})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
m, err := NewMuxer(3, 1*time.Second, videoTrack, audioTrack)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// group with IDR
|
// group with IDR
|
||||||
|
@@ -28,10 +28,8 @@ func idrPresent(nalus [][]byte) bool {
|
|||||||
type muxerTSGenerator struct {
|
type muxerTSGenerator struct {
|
||||||
hlsSegmentCount int
|
hlsSegmentCount int
|
||||||
hlsSegmentDuration time.Duration
|
hlsSegmentDuration time.Duration
|
||||||
videoTrack *gortsplib.Track
|
videoTrack *gortsplib.TrackH264
|
||||||
audioTrack *gortsplib.Track
|
audioTrack *gortsplib.TrackAAC
|
||||||
h264Conf *gortsplib.TrackConfigH264
|
|
||||||
aacConf *gortsplib.TrackConfigAAC
|
|
||||||
streamPlaylist *muxerStreamPlaylist
|
streamPlaylist *muxerStreamPlaylist
|
||||||
|
|
||||||
writer *muxerTSWriter
|
writer *muxerTSWriter
|
||||||
@@ -45,10 +43,8 @@ type muxerTSGenerator struct {
|
|||||||
func newMuxerTSGenerator(
|
func newMuxerTSGenerator(
|
||||||
hlsSegmentCount int,
|
hlsSegmentCount int,
|
||||||
hlsSegmentDuration time.Duration,
|
hlsSegmentDuration time.Duration,
|
||||||
videoTrack *gortsplib.Track,
|
videoTrack *gortsplib.TrackH264,
|
||||||
audioTrack *gortsplib.Track,
|
audioTrack *gortsplib.TrackAAC,
|
||||||
h264Conf *gortsplib.TrackConfigH264,
|
|
||||||
aacConf *gortsplib.TrackConfigAAC,
|
|
||||||
streamPlaylist *muxerStreamPlaylist,
|
streamPlaylist *muxerStreamPlaylist,
|
||||||
) *muxerTSGenerator {
|
) *muxerTSGenerator {
|
||||||
m := &muxerTSGenerator{
|
m := &muxerTSGenerator{
|
||||||
@@ -56,8 +52,6 @@ func newMuxerTSGenerator(
|
|||||||
hlsSegmentDuration: hlsSegmentDuration,
|
hlsSegmentDuration: hlsSegmentDuration,
|
||||||
videoTrack: videoTrack,
|
videoTrack: videoTrack,
|
||||||
audioTrack: audioTrack,
|
audioTrack: audioTrack,
|
||||||
h264Conf: h264Conf,
|
|
||||||
aacConf: aacConf,
|
|
||||||
streamPlaylist: streamPlaylist,
|
streamPlaylist: streamPlaylist,
|
||||||
writer: newMuxerTSWriter(videoTrack, audioTrack),
|
writer: newMuxerTSWriter(videoTrack, audioTrack),
|
||||||
}
|
}
|
||||||
@@ -109,7 +103,7 @@ func (m *muxerTSGenerator) writeH264(pts time.Duration, nalus [][]byte) error {
|
|||||||
|
|
||||||
case h264.NALUTypeIDR:
|
case h264.NALUTypeIDR:
|
||||||
// add SPS and PPS before every IDR
|
// add SPS and PPS before every IDR
|
||||||
filteredNALUs = append(filteredNALUs, m.h264Conf.SPS, m.h264Conf.PPS)
|
filteredNALUs = append(filteredNALUs, m.videoTrack.SPS(), m.videoTrack.PPS())
|
||||||
}
|
}
|
||||||
|
|
||||||
filteredNALUs = append(filteredNALUs, nalu)
|
filteredNALUs = append(filteredNALUs, nalu)
|
||||||
@@ -157,9 +151,9 @@ func (m *muxerTSGenerator) writeAAC(pts time.Duration, aus [][]byte) error {
|
|||||||
|
|
||||||
for i, au := range aus {
|
for i, au := range aus {
|
||||||
pkts[i] = &aac.ADTSPacket{
|
pkts[i] = &aac.ADTSPacket{
|
||||||
Type: m.aacConf.Type,
|
Type: m.audioTrack.Type(),
|
||||||
SampleRate: m.aacConf.SampleRate,
|
SampleRate: m.audioTrack.ClockRate(),
|
||||||
ChannelCount: m.aacConf.ChannelCount,
|
ChannelCount: m.audioTrack.ChannelCount(),
|
||||||
AU: au,
|
AU: au,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -11,7 +11,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type muxerTSSegment struct {
|
type muxerTSSegment struct {
|
||||||
videoTrack *gortsplib.Track
|
videoTrack gortsplib.Track
|
||||||
writer *muxerTSWriter
|
writer *muxerTSWriter
|
||||||
|
|
||||||
name string
|
name string
|
||||||
@@ -22,7 +22,7 @@ type muxerTSSegment struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newMuxerTSSegment(
|
func newMuxerTSSegment(
|
||||||
videoTrack *gortsplib.Track,
|
videoTrack gortsplib.Track,
|
||||||
writer *muxerTSWriter,
|
writer *muxerTSWriter,
|
||||||
) *muxerTSSegment {
|
) *muxerTSSegment {
|
||||||
t := &muxerTSSegment{
|
t := &muxerTSSegment{
|
||||||
|
@@ -13,8 +13,8 @@ type muxerTSWriter struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newMuxerTSWriter(
|
func newMuxerTSWriter(
|
||||||
videoTrack *gortsplib.Track,
|
videoTrack gortsplib.Track,
|
||||||
audioTrack *gortsplib.Track) *muxerTSWriter {
|
audioTrack gortsplib.Track) *muxerTSWriter {
|
||||||
w := &muxerTSWriter{}
|
w := &muxerTSWriter{}
|
||||||
|
|
||||||
w.innerMuxer = astits.NewMuxer(context.Background(), w)
|
w.innerMuxer = astits.NewMuxer(context.Background(), w)
|
||||||
|
@@ -31,9 +31,8 @@ func New(
|
|||||||
}
|
}
|
||||||
|
|
||||||
s.senders = make([]*rtcpsender.RTCPSender, len(tracks))
|
s.senders = make([]*rtcpsender.RTCPSender, len(tracks))
|
||||||
for i, t := range tracks {
|
for i, track := range tracks {
|
||||||
clockRate, _ := t.ClockRate()
|
s.senders[i] = rtcpsender.New(track.ClockRate())
|
||||||
s.senders[i] = rtcpsender.New(clockRate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
go s.run()
|
go s.run()
|
||||||
|
@@ -82,9 +82,9 @@ func (c *Conn) WritePacket(pkt av.Packet) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ReadMetadata reads track informations.
|
// ReadMetadata reads track informations.
|
||||||
func (c *Conn) ReadMetadata() (*gortsplib.Track, *gortsplib.Track, error) {
|
func (c *Conn) ReadMetadata() (*gortsplib.TrackH264, *gortsplib.TrackAAC, error) {
|
||||||
var videoTrack *gortsplib.Track
|
var videoTrack *gortsplib.TrackH264
|
||||||
var audioTrack *gortsplib.Track
|
var audioTrack *gortsplib.TrackAAC
|
||||||
|
|
||||||
md, err := func() (flvio.AMFMap, error) {
|
md, err := func() (flvio.AMFMap, error) {
|
||||||
pkt, err := c.ReadPacket()
|
pkt, err := c.ReadPacket()
|
||||||
@@ -198,7 +198,7 @@ func (c *Conn) ReadMetadata() (*gortsplib.Track, *gortsplib.Track, error) {
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
videoTrack, err = gortsplib.NewTrackH264(96, &gortsplib.TrackConfigH264{SPS: codec.SPS[0], PPS: codec.PPS[0]})
|
videoTrack, err = gortsplib.NewTrackH264(96, codec.SPS[0], codec.PPS[0], nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -218,12 +218,8 @@ func (c *Conn) ReadMetadata() (*gortsplib.Track, *gortsplib.Track, error) {
|
|||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
audioTrack, err = gortsplib.NewTrackAAC(96, &gortsplib.TrackConfigAAC{
|
audioTrack, err = gortsplib.NewTrackAAC(96, int(mpegConf.Type), mpegConf.SampleRate,
|
||||||
Type: int(mpegConf.Type),
|
mpegConf.ChannelCount, mpegConf.AOTSpecificConfig)
|
||||||
SampleRate: mpegConf.SampleRate,
|
|
||||||
ChannelCount: mpegConf.ChannelCount,
|
|
||||||
AOTSpecificConfig: mpegConf.AOTSpecificConfig,
|
|
||||||
})
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
@@ -237,7 +233,7 @@ func (c *Conn) ReadMetadata() (*gortsplib.Track, *gortsplib.Track, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// WriteMetadata writes track informations.
|
// WriteMetadata writes track informations.
|
||||||
func (c *Conn) WriteMetadata(videoTrack *gortsplib.Track, audioTrack *gortsplib.Track) error {
|
func (c *Conn) WriteMetadata(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.TrackAAC) error {
|
||||||
err := c.WritePacket(av.Packet{
|
err := c.WritePacket(av.Packet{
|
||||||
Type: av.Metadata,
|
Type: av.Metadata,
|
||||||
Data: flvio.FillAMF0ValMalloc(flvio.AMFMap{
|
Data: flvio.FillAMF0ValMalloc(flvio.AMFMap{
|
||||||
@@ -274,17 +270,16 @@ func (c *Conn) WriteMetadata(videoTrack *gortsplib.Track, audioTrack *gortsplib.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if videoTrack != nil {
|
if videoTrack != nil {
|
||||||
conf, err := videoTrack.ExtractConfigH264()
|
if videoTrack.SPS() == nil || videoTrack.PPS() == nil {
|
||||||
if err != nil {
|
return fmt.Errorf("invalid H264 track: SPS or PPS not provided into the SDP")
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
codec := nh264.Codec{
|
codec := nh264.Codec{
|
||||||
SPS: map[int][]byte{
|
SPS: map[int][]byte{
|
||||||
0: conf.SPS,
|
0: videoTrack.SPS(),
|
||||||
},
|
},
|
||||||
PPS: map[int][]byte{
|
PPS: map[int][]byte{
|
||||||
0: conf.PPS,
|
0: videoTrack.PPS(),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
b := make([]byte, 128)
|
b := make([]byte, 128)
|
||||||
@@ -302,16 +297,11 @@ func (c *Conn) WriteMetadata(videoTrack *gortsplib.Track, audioTrack *gortsplib.
|
|||||||
}
|
}
|
||||||
|
|
||||||
if audioTrack != nil {
|
if audioTrack != nil {
|
||||||
conf, err := audioTrack.ExtractConfigAAC()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
enc, err := aac.MPEG4AudioConfig{
|
enc, err := aac.MPEG4AudioConfig{
|
||||||
Type: aac.MPEG4AudioType(conf.Type),
|
Type: aac.MPEG4AudioType(audioTrack.Type()),
|
||||||
SampleRate: conf.SampleRate,
|
SampleRate: audioTrack.ClockRate(),
|
||||||
ChannelCount: conf.ChannelCount,
|
ChannelCount: audioTrack.ChannelCount(),
|
||||||
AOTSpecificConfig: conf.AOTSpecificConfig,
|
AOTSpecificConfig: audioTrack.AOTSpecificConfig(),
|
||||||
}.Encode()
|
}.Encode()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
Reference in New Issue
Block a user