Browse Source

switch to gortsplib v2 (#1301)

Fixes #1103

gortsplib/v2 supports multiple formats inside a single track (media). This allows to apply the resizing algorithm to single formats inside medias.

For instance, if a media contains a a proprietary format and an H264 format, and the latter has oversized packets, they can now be resized.
pull/1242/head
Alessandro Ros 2 years ago committed by GitHub
parent
commit
c778c049ce
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      go.mod
  2. 4
      go.sum
  3. 2
      internal/conf/authmethod.go
  4. 4
      internal/conf/conf.go
  5. 2
      internal/conf/conf_test.go
  6. 2
      internal/conf/path.go
  7. 2
      internal/conf/protocol.go
  8. 2
      internal/conf/sourceprotocol.go
  9. 131
      internal/core/api_test.go
  10. 2
      internal/core/core.go
  11. 69
      internal/core/core_test.go
  12. 16
      internal/core/data.go
  13. 22
      internal/core/formatprocessor.go
  14. 39
      internal/core/formatprocessor_generic.go
  15. 58
      internal/core/formatprocessor_h264.go
  16. 28
      internal/core/formatprocessor_mpeg4audio.go
  17. 94
      internal/core/hls_muxer.go
  18. 51
      internal/core/hls_source.go
  19. 63
      internal/core/hls_source_test.go
  20. 23
      internal/core/metrics_test.go
  21. 145
      internal/core/path.go
  22. 1
      internal/core/reader.go
  23. 27
      internal/core/rpicamera_source.go
  24. 149
      internal/core/rtmp_conn.go
  25. 28
      internal/core/rtmp_server_test.go
  26. 57
      internal/core/rtmp_source.go
  27. 47
      internal/core/rtmp_source_test.go
  28. 20
      internal/core/rtsp_conn.go
  29. 14
      internal/core/rtsp_server.go
  30. 128
      internal/core/rtsp_server_test.go
  31. 129
      internal/core/rtsp_session.go
  32. 88
      internal/core/rtsp_source.go
  33. 303
      internal/core/rtsp_source_test.go
  34. 26
      internal/core/source.go
  35. 110
      internal/core/stream.go
  36. 66
      internal/core/streamformat.go
  37. 26
      internal/core/streammedia.go
  38. 27
      internal/core/streamtrack.go
  39. 33
      internal/core/streamtrack_generic.go
  40. 6
      internal/hls/client.go
  41. 34
      internal/hls/client_downloader_primary.go
  42. 12
      internal/hls/client_downloader_stream.go
  43. 20
      internal/hls/client_processor_fmp4.go
  44. 26
      internal/hls/client_processor_mpegts.go
  45. 6
      internal/hls/client_test.go
  46. 12
      internal/hls/fmp4/init.go
  47. 28
      internal/hls/fmp4/init_test.go
  48. 31
      internal/hls/fmp4/init_track.go
  49. 16
      internal/hls/mpegts/tracks.go
  50. 34
      internal/hls/mpegts/writer.go
  51. 14
      internal/hls/mpegts/writer_test.go
  52. 6
      internal/hls/muxer.go
  53. 10
      internal/hls/muxer_primary_playlist.go
  54. 32
      internal/hls/muxer_test.go
  55. 14
      internal/hls/muxer_variant_fmp4.go
  56. 12
      internal/hls/muxer_variant_fmp4_part.go
  57. 10
      internal/hls/muxer_variant_fmp4_playlist.go
  58. 10
      internal/hls/muxer_variant_fmp4_segment.go
  59. 12
      internal/hls/muxer_variant_fmp4_segmenter.go
  60. 6
      internal/hls/muxer_variant_mpegts.go
  61. 10
      internal/hls/muxer_variant_mpegts_segment.go
  62. 12
      internal/hls/muxer_variant_mpegts_segmenter.go
  63. 2
      internal/rpicamera/rpicamera.go
  64. 32
      internal/rtmp/conn.go
  65. 40
      internal/rtmp/conn_test.go

2
go.mod

@ -5,7 +5,7 @@ go 1.18 @@ -5,7 +5,7 @@ go 1.18
require (
code.cloudfoundry.org/bytefmt v0.0.0-20211005130812-5bb3c17173e5
github.com/abema/go-mp4 v0.8.0
github.com/aler9/gortsplib v1.0.1
github.com/aler9/gortsplib/v2 v2.0.0-20221213180201-60596c32d1bf
github.com/asticode/go-astits v1.10.1-0.20220319093903-4abe66a9b757
github.com/fsnotify/fsnotify v1.4.9
github.com/gin-gonic/gin v1.8.1

4
go.sum

@ -6,8 +6,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafo @@ -6,8 +6,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/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/aler9/gortsplib v1.0.1 h1:R13+hxlvg2Hvu98+0hzg0o5fPjyUA9ZPJneMIBxKGXk=
github.com/aler9/gortsplib v1.0.1/go.mod h1:BOWNZ/QBkY/eVcRqUzJbPFEsRJshwxaxBT01K260Jeo=
github.com/aler9/gortsplib/v2 v2.0.0-20221213180201-60596c32d1bf h1:kPSTROCTa8pyQ13LzDgcA2mYfBZKU8ma6KVi/mwWux4=
github.com/aler9/gortsplib/v2 v2.0.0-20221213180201-60596c32d1bf/go.mod h1:zJ+fWtakOMN6cKV169EMNVBLPTITArrJKu/fyM+dov8=
github.com/aler9/writerseeker v0.0.0-20220601075008-6f0e685b9c82 h1:9WgSzBLo3a9ToSVV7sRTBYZ1GGOZUpq4+5H3SN0UZq4=
github.com/aler9/writerseeker v0.0.0-20220601075008-6f0e685b9c82/go.mod h1:qsMrZCbeBf/mCLOeF16KDkPu4gktn/pOWyaq1aYQE7U=
github.com/asticode/go-astikit v0.20.0 h1:+7N+J4E4lWx2QOkRdOf6DafWJMv6O4RRfgClwQokrH8=

2
internal/conf/authmethod.go

@ -6,7 +6,7 @@ import ( @@ -6,7 +6,7 @@ import (
"sort"
"strings"
"github.com/aler9/gortsplib/pkg/headers"
"github.com/aler9/gortsplib/v2/pkg/headers"
)
// AuthMethods is the authMethods parameter.

4
internal/conf/conf.go

@ -11,8 +11,8 @@ import ( @@ -11,8 +11,8 @@ import (
"strings"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/headers"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/headers"
"golang.org/x/crypto/nacl/secretbox"
"gopkg.in/yaml.v2"

2
internal/conf/conf_test.go

@ -8,7 +8,7 @@ import ( @@ -8,7 +8,7 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2"
"github.com/stretchr/testify/require"
"golang.org/x/crypto/nacl/secretbox"

2
internal/conf/path.go

@ -8,7 +8,7 @@ import ( @@ -8,7 +8,7 @@ import (
"strings"
"time"
"github.com/aler9/gortsplib/pkg/url"
"github.com/aler9/gortsplib/v2/pkg/url"
)
var rePathName = regexp.MustCompile(`^[0-9a-zA-Z_\-/\.~]+$`)

2
internal/conf/protocol.go

@ -6,7 +6,7 @@ import ( @@ -6,7 +6,7 @@ import (
"sort"
"strings"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2"
)
// Protocol is a RTSP transport.

2
internal/conf/sourceprotocol.go

@ -4,7 +4,7 @@ import ( @@ -4,7 +4,7 @@ import (
"encoding/json"
"fmt"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2"
)
// SourceProtocol is the sourceProtocol parameter.

131
internal/core/api_test.go

@ -13,14 +13,28 @@ import ( @@ -13,14 +13,28 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
"github.com/aler9/rtsp-simple-server/internal/rtmp"
)
var testFormatH264 = &format.H264{
PayloadTyp: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
var testMediaH264 = &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{testFormatH264},
}
func httpRequest(method string, ur string, in interface{}, out interface{}) error {
buf, err := func() (io.Reader, error) {
if in == nil {
@ -188,32 +202,32 @@ func TestAPIPathsList(t *testing.T) { @@ -188,32 +202,32 @@ func TestAPIPathsList(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
media0 := testMediaH264
source := gortsplib.Client{}
err := source.StartPublishing(
err := source.StartRecording(
"rtsp://localhost:8554/mypath",
gortsplib.Tracks{
&gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
},
&gortsplib.TrackMPEG4Audio{
PayloadType: 96,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
},
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
media.Medias{
media0,
{
Type: media.TypeAudio,
Formats: []format.Format{&format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
},
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
}},
},
})
require.NoError(t, err)
defer source.Close()
source.WritePacketRTP(0, &rtp.Packet{
source.WritePacketRTP(media0, &rtp.Packet{
Header: rtp.Header{
Version: 2,
PayloadType: 96,
@ -256,28 +270,29 @@ func TestAPIPathsList(t *testing.T) { @@ -256,28 +270,29 @@ func TestAPIPathsList(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
tracks := gortsplib.Tracks{
&gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
medias := media.Medias{
{
Type: media.TypeVideo,
Formats: []format.Format{testFormatH264},
},
&gortsplib.TrackMPEG4Audio{
PayloadType: 97,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
},
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
{
Type: media.TypeAudio,
Formats: []format.Format{&format.MPEG4Audio{
PayloadTyp: 97,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
ChannelCount: 2,
},
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
}},
},
}
source := gortsplib.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}}
err = source.StartPublishing("rtsps://localhost:8322/mypath", tracks)
err = source.StartRecording("rtsps://localhost:8322/mypath", medias)
require.NoError(t, err)
defer source.Close()
@ -414,19 +429,13 @@ func TestAPIProtocolSpecificList(t *testing.T) { @@ -414,19 +429,13 @@ func TestAPIProtocolSpecificList(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
switch ca {
case "rtsp conns", "rtsp sessions":
source := gortsplib.Client{}
err := source.StartPublishing("rtsp://localhost:8554/mypath",
gortsplib.Tracks{track})
err := source.StartRecording("rtsp://localhost:8554/mypath", media.Medias{medi})
require.NoError(t, err)
defer source.Close()
@ -435,8 +444,7 @@ func TestAPIProtocolSpecificList(t *testing.T) { @@ -435,8 +444,7 @@ func TestAPIProtocolSpecificList(t *testing.T) {
TLSConfig: &tls.Config{InsecureSkipVerify: true},
}
err := source.StartPublishing("rtsps://localhost:8322/mypath",
gortsplib.Tracks{track})
err := source.StartRecording("rtsps://localhost:8322/mypath", media.Medias{medi})
require.NoError(t, err)
defer source.Close()
@ -464,8 +472,8 @@ func TestAPIProtocolSpecificList(t *testing.T) { @@ -464,8 +472,8 @@ func TestAPIProtocolSpecificList(t *testing.T) {
err = conn.InitializeClient(u, true)
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
0x27, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04,
@ -481,8 +489,8 @@ func TestAPIProtocolSpecificList(t *testing.T) { @@ -481,8 +489,8 @@ func TestAPIProtocolSpecificList(t *testing.T) {
case "hls":
source := gortsplib.Client{}
err := source.StartPublishing("rtsp://localhost:8554/mypath",
gortsplib.Tracks{track})
err := source.StartRecording("rtsp://localhost:8554/mypath",
media.Medias{medi})
require.NoError(t, err)
defer source.Close()
@ -588,19 +596,14 @@ func TestAPIKick(t *testing.T) { @@ -588,19 +596,14 @@ func TestAPIKick(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
switch ca {
case "rtsp":
source := gortsplib.Client{}
err := source.StartPublishing("rtsp://localhost:8554/mypath",
gortsplib.Tracks{track})
err := source.StartRecording("rtsp://localhost:8554/mypath",
media.Medias{medi})
require.NoError(t, err)
defer source.Close()
@ -609,8 +612,8 @@ func TestAPIKick(t *testing.T) { @@ -609,8 +612,8 @@ func TestAPIKick(t *testing.T) {
TLSConfig: &tls.Config{InsecureSkipVerify: true},
}
err := source.StartPublishing("rtsps://localhost:8322/mypath",
gortsplib.Tracks{track})
err := source.StartRecording("rtsps://localhost:8322/mypath",
media.Medias{medi})
require.NoError(t, err)
defer source.Close()
@ -626,8 +629,8 @@ func TestAPIKick(t *testing.T) { @@ -626,8 +629,8 @@ func TestAPIKick(t *testing.T) {
err = conn.InitializeClient(u, true)
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
0x27, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04,

2
internal/core/core.go

@ -8,7 +8,7 @@ import ( @@ -8,7 +8,7 @@ import (
"os/signal"
"reflect"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2"
"github.com/gin-gonic/gin"
"gopkg.in/alecthomas/kingpin.v2"

69
internal/core/core_test.go

@ -10,10 +10,11 @@ import ( @@ -10,10 +10,11 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/gortsplib/pkg/headers"
"github.com/aler9/gortsplib/pkg/url"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/base"
"github.com/aler9/gortsplib/v2/pkg/headers"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/stretchr/testify/require"
)
@ -131,7 +132,7 @@ func TestCorePathAutoDeletion(t *testing.T) { @@ -131,7 +132,7 @@ func TestCorePathAutoDeletion(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.StatusNotFound, res.StatusCode)
} else {
u, err := url.Parse("rtsp://localhost:8554/mypath/trackID=0")
u, err := url.Parse("rtsp://localhost:8554/mypath/mediaID=0")
require.NoError(t, err)
byts, _ := base.Request{
@ -182,7 +183,9 @@ import ( @@ -182,7 +183,9 @@ import (
"os"
"os/signal"
"syscall"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/format"
)
func main() {
@ -190,18 +193,21 @@ func main() { @@ -190,18 +193,21 @@ func main() {
panic("environment not set")
}
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
medi := &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}},
}
source := gortsplib.Client{}
err := source.StartPublishing(
err := source.StartRecording(
"rtsp://localhost:" + os.Getenv("RTSP_PORT") + "/" + os.Getenv("RTSP_PATH"),
gortsplib.Tracks{track})
media.Medias{medi})
if err != nil {
panic(err)
}
@ -269,7 +275,7 @@ func main() { @@ -269,7 +275,7 @@ func main() {
}
if ca == "setup" || ca == "describe and setup" {
u, err := url.Parse("rtsp://localhost:8554/ondemand/trackID=0")
u, err := url.Parse("rtsp://localhost:8554/ondemand/mediaID=0")
require.NoError(t, err)
byts, _ := base.Request{
@ -321,18 +327,13 @@ func TestCorePathRunOnReady(t *testing.T) { @@ -321,18 +327,13 @@ func TestCorePathRunOnReady(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
c := gortsplib.Client{}
err := c.StartPublishing(
err := c.StartRecording(
"rtsp://localhost:8554/test",
gortsplib.Tracks{track})
media.Medias{medi})
require.NoError(t, err)
defer c.Close()
@ -358,18 +359,10 @@ func TestCoreHotReloading(t *testing.T) { @@ -358,18 +359,10 @@ func TestCoreHotReloading(t *testing.T) {
defer p.Close()
func() {
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
c := gortsplib.Client{}
err = c.StartPublishing(
"rtsp://localhost:8554/test1",
gortsplib.Tracks{track})
err = c.StartRecording("rtsp://localhost:8554/test1", media.Medias{medi})
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
}()
@ -381,18 +374,10 @@ func TestCoreHotReloading(t *testing.T) { @@ -381,18 +374,10 @@ func TestCoreHotReloading(t *testing.T) {
time.Sleep(1 * time.Second)
func() {
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
conn := gortsplib.Client{}
err = conn.StartPublishing(
"rtsp://localhost:8554/test1",
gortsplib.Tracks{track})
err = conn.StartRecording("rtsp://localhost:8554/test1", media.Medias{medi})
require.NoError(t, err)
defer conn.Close()
}()

16
internal/core/data.go

@ -8,21 +8,15 @@ import ( @@ -8,21 +8,15 @@ import (
// data is the data unit routed across the server.
type data interface {
getTrackID() int
getRTPPackets() []*rtp.Packet
getNTP() time.Time
}
type dataGeneric struct {
trackID int
rtpPackets []*rtp.Packet
ntp time.Time
}
func (d *dataGeneric) getTrackID() int {
return d.trackID
}
func (d *dataGeneric) getRTPPackets() []*rtp.Packet {
return d.rtpPackets
}
@ -32,17 +26,12 @@ func (d *dataGeneric) getNTP() time.Time { @@ -32,17 +26,12 @@ func (d *dataGeneric) getNTP() time.Time {
}
type dataH264 struct {
trackID int
rtpPackets []*rtp.Packet
ntp time.Time
pts time.Duration
nalus [][]byte
}
func (d *dataH264) getTrackID() int {
return d.trackID
}
func (d *dataH264) getRTPPackets() []*rtp.Packet {
return d.rtpPackets
}
@ -52,17 +41,12 @@ func (d *dataH264) getNTP() time.Time { @@ -52,17 +41,12 @@ func (d *dataH264) getNTP() time.Time {
}
type dataMPEG4Audio struct {
trackID int
rtpPackets []*rtp.Packet
ntp time.Time
pts time.Duration
aus [][]byte
}
func (d *dataMPEG4Audio) getTrackID() int {
return d.trackID
}
func (d *dataMPEG4Audio) getRTPPackets() []*rtp.Packet {
return d.rtpPackets
}

22
internal/core/formatprocessor.go

@ -0,0 +1,22 @@ @@ -0,0 +1,22 @@
package core
import (
"github.com/aler9/gortsplib/v2/pkg/format"
)
type formatProcessor interface {
process(data, bool) error
}
func newFormatProcessor(forma format.Format, generateRTPPackets bool) (formatProcessor, error) {
switch forma := forma.(type) {
case *format.H264:
return newFormatProcessorH264(forma, generateRTPPackets)
case *format.MPEG4Audio:
return newFormatProcessorMPEG4Audio(forma, generateRTPPackets)
default:
return newFormatProcessorGeneric(forma, generateRTPPackets)
}
}

39
internal/core/formatprocessor_generic.go

@ -0,0 +1,39 @@ @@ -0,0 +1,39 @@
package core
import (
"fmt"
"github.com/aler9/gortsplib/v2/pkg/format"
)
const (
// 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header)
maxPacketSize = 1472
)
type formatProcessorGeneric struct{}
func newFormatProcessorGeneric(forma format.Format, generateRTPPackets bool) (*formatProcessorGeneric, error) {
if generateRTPPackets {
return nil, fmt.Errorf("we don't know how to generate RTP packets of format %+v", forma)
}
return &formatProcessorGeneric{}, nil
}
func (t *formatProcessorGeneric) process(dat data, hasNonRTSPReaders bool) error {
tdata := dat.(*dataGeneric)
pkt := tdata.rtpPackets[0]
// remove padding
pkt.Header.Padding = false
pkt.PaddingSize = 0
if pkt.MarshalSize() > maxPacketSize {
return fmt.Errorf("payload size (%d) is greater than maximum allowed (%d)",
pkt.MarshalSize(), maxPacketSize)
}
return nil
}

58
internal/core/streamtrack_h264.go → internal/core/formatprocessor_h264.go

@ -3,9 +3,9 @@ package core @@ -3,9 +3,9 @@ package core
import (
"bytes"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/pkg/rtpcodecs/rtph264"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/formatdecenc/rtph264"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/pion/rtp"
)
@ -61,53 +61,53 @@ func rtpH264ExtractSPSPPS(pkt *rtp.Packet) ([]byte, []byte) { @@ -61,53 +61,53 @@ func rtpH264ExtractSPSPPS(pkt *rtp.Packet) ([]byte, []byte) {
}
}
type streamTrackH264 struct {
track *gortsplib.TrackH264
type formatProcessorH264 struct {
format *format.H264
encoder *rtph264.Encoder
decoder *rtph264.Decoder
}
func newStreamTrackH264(
track *gortsplib.TrackH264,
func newFormatProcessorH264(
forma *format.H264,
allocateEncoder bool,
) *streamTrackH264 {
t := &streamTrackH264{
track: track,
) (*formatProcessorH264, error) {
t := &formatProcessorH264{
format: forma,
}
if allocateEncoder {
t.encoder = track.CreateEncoder()
t.encoder = forma.CreateEncoder()
}
return t
return t, nil
}
func (t *streamTrackH264) updateTrackParametersFromRTPPacket(pkt *rtp.Packet) {
func (t *formatProcessorH264) updateTrackParametersFromRTPPacket(pkt *rtp.Packet) {
sps, pps := rtpH264ExtractSPSPPS(pkt)
if sps != nil && !bytes.Equal(sps, t.track.SafeSPS()) {
t.track.SafeSetSPS(sps)
if sps != nil && !bytes.Equal(sps, t.format.SafeSPS()) {
t.format.SafeSetSPS(sps)
}
if pps != nil && !bytes.Equal(pps, t.track.SafePPS()) {
t.track.SafeSetPPS(pps)
if pps != nil && !bytes.Equal(pps, t.format.SafePPS()) {
t.format.SafeSetPPS(pps)
}
}
func (t *streamTrackH264) updateTrackParametersFromNALUs(nalus [][]byte) {
func (t *formatProcessorH264) updateTrackParametersFromNALUs(nalus [][]byte) {
for _, nalu := range nalus {
typ := h264.NALUType(nalu[0] & 0x1F)
switch typ {
case h264.NALUTypeSPS:
if !bytes.Equal(nalu, t.track.SafeSPS()) {
t.track.SafeSetSPS(nalu)
if !bytes.Equal(nalu, t.format.SafeSPS()) {
t.format.SafeSetSPS(nalu)
}
case h264.NALUTypePPS:
if !bytes.Equal(nalu, t.track.SafePPS()) {
t.track.SafeSetPPS(nalu)
if !bytes.Equal(nalu, t.format.SafePPS()) {
t.format.SafeSetPPS(nalu)
}
}
}
@ -115,7 +115,7 @@ func (t *streamTrackH264) updateTrackParametersFromNALUs(nalus [][]byte) { @@ -115,7 +115,7 @@ func (t *streamTrackH264) updateTrackParametersFromNALUs(nalus [][]byte) {
// remux is needed to fix corrupted streams and make streams
// compatible with all protocols.
func (t *streamTrackH264) remuxNALUs(nalus [][]byte) [][]byte {
func (t *formatProcessorH264) remuxNALUs(nalus [][]byte) [][]byte {
addSPSPPS := false
n := 0
for _, nalu := range nalus {
@ -143,8 +143,8 @@ func (t *streamTrackH264) remuxNALUs(nalus [][]byte) [][]byte { @@ -143,8 +143,8 @@ func (t *streamTrackH264) remuxNALUs(nalus [][]byte) [][]byte {
i := 0
if addSPSPPS {
filteredNALUs[0] = t.track.SafeSPS()
filteredNALUs[1] = t.track.SafePPS()
filteredNALUs[0] = t.format.SafeSPS()
filteredNALUs[1] = t.format.SafePPS()
i = 2
}
@ -167,7 +167,7 @@ func (t *streamTrackH264) remuxNALUs(nalus [][]byte) [][]byte { @@ -167,7 +167,7 @@ func (t *streamTrackH264) remuxNALUs(nalus [][]byte) [][]byte {
return filteredNALUs
}
func (t *streamTrackH264) generateRTPPackets(tdata *dataH264) error {
func (t *formatProcessorH264) generateRTPPackets(tdata *dataH264) error {
pkts, err := t.encoder.Encode(tdata.nalus, tdata.pts)
if err != nil {
return err
@ -177,7 +177,7 @@ func (t *streamTrackH264) generateRTPPackets(tdata *dataH264) error { @@ -177,7 +177,7 @@ func (t *streamTrackH264) generateRTPPackets(tdata *dataH264) error {
return nil
}
func (t *streamTrackH264) onData(dat data, hasNonRTSPReaders bool) error {
func (t *formatProcessorH264) process(dat data, hasNonRTSPReaders bool) error {
tdata := dat.(*dataH264)
if tdata.rtpPackets != nil {
@ -199,7 +199,7 @@ func (t *streamTrackH264) onData(dat data, hasNonRTSPReaders bool) error { @@ -199,7 +199,7 @@ func (t *streamTrackH264) onData(dat data, hasNonRTSPReaders bool) error {
SSRC: &v1,
InitialSequenceNumber: &v2,
InitialTimestamp: &v3,
PacketizationMode: t.track.PacketizationMode,
PacketizationMode: t.format.PacketizationMode,
}
t.encoder.Init()
}
@ -208,7 +208,7 @@ func (t *streamTrackH264) onData(dat data, hasNonRTSPReaders bool) error { @@ -208,7 +208,7 @@ func (t *streamTrackH264) onData(dat data, hasNonRTSPReaders bool) error {
// decode from RTP
if hasNonRTSPReaders || t.encoder != nil {
if t.decoder == nil {
t.decoder = t.track.CreateDecoder()
t.decoder = t.format.CreateDecoder()
}
nalus, pts, err := t.decoder.Decode(pkt)

28
internal/core/streamtrack_mpeg4audio.go → internal/core/formatprocessor_mpeg4audio.go

@ -3,32 +3,32 @@ package core @@ -3,32 +3,32 @@ package core
import (
"fmt"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/rtpcodecs/rtpmpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/formatdecenc/rtpmpeg4audio"
)
type streamTrackMPEG4Audio struct {
track *gortsplib.TrackMPEG4Audio
type formatProcessorMPEG4Audio struct {
format *format.MPEG4Audio
encoder *rtpmpeg4audio.Encoder
decoder *rtpmpeg4audio.Decoder
}
func newStreamTrackMPEG4Audio(
track *gortsplib.TrackMPEG4Audio,
func newFormatProcessorMPEG4Audio(
forma *format.MPEG4Audio,
allocateEncoder bool,
) *streamTrackMPEG4Audio {
t := &streamTrackMPEG4Audio{
track: track,
) (*formatProcessorMPEG4Audio, error) {
t := &formatProcessorMPEG4Audio{
format: forma,
}
if allocateEncoder {
t.encoder = track.CreateEncoder()
t.encoder = forma.CreateEncoder()
}
return t
return t, nil
}
func (t *streamTrackMPEG4Audio) generateRTPPackets(tdata *dataMPEG4Audio) error {
func (t *formatProcessorMPEG4Audio) generateRTPPackets(tdata *dataMPEG4Audio) error {
pkts, err := t.encoder.Encode(tdata.aus, tdata.pts)
if err != nil {
return err
@ -38,7 +38,7 @@ func (t *streamTrackMPEG4Audio) generateRTPPackets(tdata *dataMPEG4Audio) error @@ -38,7 +38,7 @@ func (t *streamTrackMPEG4Audio) generateRTPPackets(tdata *dataMPEG4Audio) error
return nil
}
func (t *streamTrackMPEG4Audio) onData(dat data, hasNonRTSPReaders bool) error {
func (t *formatProcessorMPEG4Audio) process(dat data, hasNonRTSPReaders bool) error {
tdata := dat.(*dataMPEG4Audio)
if tdata.rtpPackets != nil {
@ -56,7 +56,7 @@ func (t *streamTrackMPEG4Audio) onData(dat data, hasNonRTSPReaders bool) error { @@ -56,7 +56,7 @@ func (t *streamTrackMPEG4Audio) onData(dat data, hasNonRTSPReaders bool) error {
// decode from RTP
if hasNonRTSPReaders {
if t.decoder == nil {
t.decoder = t.track.CreateDecoder()
t.decoder = t.format.CreateDecoder()
}
aus, pts, err := t.decoder.Decode(pkt)

94
internal/core/hls_muxer.go

@ -11,9 +11,10 @@ import ( @@ -11,9 +11,10 @@ import (
"sync/atomic"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/pkg/ringbuffer"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/ringbuffer"
"github.com/gin-gonic/gin"
"github.com/aler9/rtsp-simple-server/internal/conf"
@ -307,32 +308,13 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{}) @@ -307,32 +308,13 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
m.path.readerRemove(pathReaderRemoveReq{author: m})
}()
var videoTrack *gortsplib.TrackH264
videoTrackID := -1
var audioTrack *gortsplib.TrackMPEG4Audio
audioTrackID := -1
var videoFormat *format.H264
videoMedia := res.stream.medias().FindFormat(&videoFormat)
for i, track := range res.stream.tracks() {
switch tt := track.(type) {
case *gortsplib.TrackH264:
if videoTrack != nil {
return fmt.Errorf("can't encode track %d with HLS: too many tracks", i+1)
}
videoTrack = tt
videoTrackID = i
case *gortsplib.TrackMPEG4Audio:
if audioTrack != nil {
return fmt.Errorf("can't encode track %d with HLS: too many tracks", i+1)
}
audioTrack = tt
audioTrackID = i
}
}
var audioFormat *format.MPEG4Audio
audioMedia := res.stream.medias().FindFormat(&audioFormat)
if videoTrack == nil && audioTrack == nil {
if videoFormat == nil && audioFormat == nil {
return fmt.Errorf("the stream doesn't contain an H264 track or an AAC track")
}
@ -343,8 +325,8 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{}) @@ -343,8 +325,8 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
time.Duration(m.hlsSegmentDuration),
time.Duration(m.hlsPartDuration),
uint64(m.hlsSegmentMaxSize),
videoTrack,
audioTrack,
videoFormat,
audioFormat,
)
if err != nil {
return fmt.Errorf("muxer error: %v", err)
@ -355,26 +337,35 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{}) @@ -355,26 +337,35 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
m.ringBuffer, _ = ringbuffer.New(uint64(m.readBufferCount))
m.path.readerStart(pathReaderStartReq{author: m})
var medias media.Medias
var tracks []gortsplib.Track
if videoTrack != nil {
tracks = append(tracks, videoTrack)
if videoMedia != nil {
medias = append(medias, videoMedia)
res.stream.readerAdd(m, videoMedia, videoFormat, func(dat data) {
m.ringBuffer.Push(dat)
})
}
if audioTrack != nil {
tracks = append(tracks, audioTrack)
if audioMedia != nil {
medias = append(medias, audioMedia)
res.stream.readerAdd(m, audioMedia, audioFormat, func(dat data) {
m.ringBuffer.Push(dat)
})
}
defer res.stream.readerRemove(m)
m.log(logger.Info, "is converting into HLS, %s",
sourceTrackInfo(tracks))
sourceMediaInfo(medias))
writerDone := make(chan error)
go func() {
writerDone <- m.runWriter(
videoTrack,
videoTrackID,
audioTrack,
audioTrackID,
videoMedia,
videoFormat,
audioMedia,
audioFormat,
)
}()
@ -403,10 +394,10 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{}) @@ -403,10 +394,10 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
}
func (m *hlsMuxer) runWriter(
videoTrack *gortsplib.TrackH264,
videoTrackID int,
audioTrack *gortsplib.TrackMPEG4Audio,
audioTrackID int,
videoMedia *media.Media,
videoFormat *format.H264,
audioMedia *media.Media,
audioFormat *format.MPEG4Audio,
) error {
videoStartPTSFilled := false
var videoStartPTS time.Duration
@ -420,9 +411,8 @@ func (m *hlsMuxer) runWriter( @@ -420,9 +411,8 @@ func (m *hlsMuxer) runWriter(
}
data := item.(data)
if videoTrack != nil && data.getTrackID() == videoTrackID {
tdata := data.(*dataH264)
switch tdata := data.(type) {
case *dataH264:
if tdata.nalus == nil {
continue
}
@ -437,9 +427,8 @@ func (m *hlsMuxer) runWriter( @@ -437,9 +427,8 @@ func (m *hlsMuxer) runWriter(
if err != nil {
return fmt.Errorf("muxer error: %v", err)
}
} else if audioTrack != nil && data.getTrackID() == audioTrackID {
tdata := data.(*dataMPEG4Audio)
case *dataMPEG4Audio:
if tdata.aus == nil {
continue
}
@ -454,7 +443,7 @@ func (m *hlsMuxer) runWriter( @@ -454,7 +443,7 @@ func (m *hlsMuxer) runWriter(
err := m.muxer.WriteAAC(
tdata.ntp,
pts+time.Duration(i)*mpeg4audio.SamplesPerAccessUnit*
time.Second/time.Duration(audioTrack.ClockRate()),
time.Second/time.Duration(audioFormat.ClockRate()),
au)
if err != nil {
return fmt.Errorf("muxer error: %v", err)
@ -593,11 +582,6 @@ func (m *hlsMuxer) apiHLSMuxersList(req hlsServerAPIMuxersListSubReq) { @@ -593,11 +582,6 @@ func (m *hlsMuxer) apiHLSMuxersList(req hlsServerAPIMuxersListSubReq) {
}
}
// onReaderData implements reader.
func (m *hlsMuxer) onReaderData(data data) {
m.ringBuffer.Push(data)
}
// apiReaderDescribe implements reader.
func (m *hlsMuxer) apiReaderDescribe() interface{} {
return struct {

51
internal/core/hls_source.go

@ -4,7 +4,8 @@ import ( @@ -4,7 +4,8 @@ import (
"context"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/rtsp-simple-server/internal/hls"
"github.com/aler9/rtsp-simple-server/internal/logger"
@ -41,8 +42,8 @@ func (s *hlsSource) Log(level logger.Level, format string, args ...interface{}) @@ -41,8 +42,8 @@ func (s *hlsSource) Log(level logger.Level, format string, args ...interface{})
// run implements sourceStaticImpl.
func (s *hlsSource) run(ctx context.Context) error {
var stream *stream
var videoTrackID int
var audioTrackID int
var videoMedia *media.Media
var audioMedia *media.Media
defer func() {
if stream != nil {
@ -50,39 +51,44 @@ func (s *hlsSource) run(ctx context.Context) error { @@ -50,39 +51,44 @@ func (s *hlsSource) run(ctx context.Context) error {
}
}()
onTracks := func(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.TrackMPEG4Audio) error {
var tracks gortsplib.Tracks
onTracks := func(videoFormat *format.H264, audioFormat *format.MPEG4Audio) error {
var medias media.Medias
if videoTrack != nil {
videoTrackID = len(tracks)
tracks = append(tracks, videoTrack)
if videoFormat != nil {
videoMedia = &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{videoFormat},
}
medias = append(medias, videoMedia)
}
if audioTrack != nil {
audioTrackID = len(tracks)
tracks = append(tracks, audioTrack)
if audioFormat != nil {
audioMedia = &media.Media{
Type: media.TypeAudio,
Formats: []format.Format{audioFormat},
}
medias = append(medias, audioMedia)
}
res := s.parent.sourceStaticImplSetReady(pathSourceStaticSetReadyReq{
tracks: tracks,
medias: medias,
generateRTPPackets: true,
})
if res.err != nil {
return res.err
}
s.Log(logger.Info, "ready: %s", sourceTrackInfo(tracks))
s.Log(logger.Info, "ready: %s", sourceMediaInfo(medias))
stream = res.stream
return nil
}
onVideoData := func(pts time.Duration, nalus [][]byte) {
err := stream.writeData(&dataH264{
trackID: videoTrackID,
pts: pts,
nalus: nalus,
ntp: time.Now(),
err := stream.writeData(videoMedia, videoMedia.Formats[0], &dataH264{
pts: pts,
nalus: nalus,
ntp: time.Now(),
})
if err != nil {
s.Log(logger.Warn, "%v", err)
@ -90,11 +96,10 @@ func (s *hlsSource) run(ctx context.Context) error { @@ -90,11 +96,10 @@ func (s *hlsSource) run(ctx context.Context) error {
}
onAudioData := func(pts time.Duration, au []byte) {
err := stream.writeData(&dataMPEG4Audio{
trackID: audioTrackID,
pts: pts,
aus: [][]byte{au},
ntp: time.Now(),
err := stream.writeData(audioMedia, audioMedia.Formats[0], &dataMPEG4Audio{
pts: pts,
aus: [][]byte{au},
ntp: time.Now(),
})
if err != nil {
s.Log(logger.Warn, "%v", err)

63
internal/core/hls_source_test.go

@ -9,9 +9,9 @@ import ( @@ -9,9 +9,9 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/pkg/url"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/asticode/go-astits"
"github.com/gin-gonic/gin"
"github.com/pion/rtp"
@ -135,31 +135,7 @@ func TestHLSSource(t *testing.T) { @@ -135,31 +135,7 @@ func TestHLSSource(t *testing.T) {
frameRecv := make(chan struct{})
c := gortsplib.Client{
OnPacketRTP: func(ctx *gortsplib.ClientOnPacketRTPCtx) {
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: ctx.Packet.SequenceNumber,
Timestamp: ctx.Packet.Timestamp,
SSRC: ctx.Packet.SSRC,
CSRC: []uint32{},
},
Payload: []byte{
0x18,
0x00, 0x04,
0x07, 0x01, 0x02, 0x03, // SPS
0x00, 0x01,
0x08, // PPS
0x00, 0x01,
0x05, // ODR
},
}, ctx.Packet)
close(frameRecv)
},
}
c := gortsplib.Client{}
u, err := url.Parse("rtsp://localhost:8554/proxied")
require.NoError(t, err)
@ -168,10 +144,37 @@ func TestHLSSource(t *testing.T) { @@ -168,10 +144,37 @@ func TestHLSSource(t *testing.T) {
require.NoError(t, err)
defer c.Close()
tracks, baseURL, _, err := c.Describe(u)
medias, baseURL, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
require.NoError(t, err)
err = c.SetupAndPlay(tracks, baseURL)
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: pkt.SequenceNumber,
Timestamp: pkt.Timestamp,
SSRC: pkt.SSRC,
CSRC: []uint32{},
},
Payload: []byte{
0x18,
0x00, 0x04,
0x07, 0x01, 0x02, 0x03, // SPS
0x00, 0x01,
0x08, // PPS
0x00, 0x01,
0x05, // ODR
},
}, pkt)
close(frameRecv)
})
_, err = c.Play(nil)
require.NoError(t, err)
<-frameRecv

23
internal/core/metrics_test.go

@ -9,7 +9,9 @@ import ( @@ -9,7 +9,9 @@ import (
"os"
"testing"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/stretchr/testify/require"
"github.com/aler9/rtsp-simple-server/internal/rtmp"
@ -33,22 +35,17 @@ func TestMetrics(t *testing.T) { @@ -33,22 +35,17 @@ func TestMetrics(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
source := gortsplib.Client{}
err = source.StartPublishing("rtsp://localhost:8554/rtsp_path",
gortsplib.Tracks{track})
err = source.StartRecording("rtsp://localhost:8554/rtsp_path",
media.Medias{medi})
require.NoError(t, err)
defer source.Close()
source2 := gortsplib.Client{TLSConfig: &tls.Config{InsecureSkipVerify: true}}
err = source2.StartPublishing("rtsps://localhost:8322/rtsps_path",
gortsplib.Tracks{track})
err = source2.StartRecording("rtsps://localhost:8322/rtsps_path",
media.Medias{medi})
require.NoError(t, err)
defer source2.Close()
@ -63,8 +60,8 @@ func TestMetrics(t *testing.T) { @@ -63,8 +60,8 @@ func TestMetrics(t *testing.T) {
err = conn.InitializeClient(u, true)
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
0x27, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04,

145
internal/core/path.go

@ -10,9 +10,9 @@ import ( @@ -10,9 +10,9 @@ import (
"sync/atomic"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/gortsplib/pkg/url"
"github.com/aler9/gortsplib/v2/pkg/base"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/aler9/rtsp-simple-server/internal/conf"
"github.com/aler9/rtsp-simple-server/internal/externalcmd"
@ -67,17 +67,6 @@ type pathParent interface { @@ -67,17 +67,6 @@ type pathParent interface {
onPathClose(*path)
}
type pathRTSPSession interface {
isRTSPSession()
}
type pathReaderState int
const (
pathReaderStatePrePlay pathReaderState = iota
pathReaderStatePlay
)
type pathOnDemandState int
const (
@ -93,7 +82,7 @@ type pathSourceStaticSetReadyRes struct { @@ -93,7 +82,7 @@ type pathSourceStaticSetReadyRes struct {
}
type pathSourceStaticSetReadyReq struct {
tracks gortsplib.Tracks
medias media.Medias
generateRTPPackets bool
res chan pathSourceStaticSetReadyRes
}
@ -151,11 +140,6 @@ type pathPublisherAddReq struct { @@ -151,11 +140,6 @@ type pathPublisherAddReq struct {
res chan pathPublisherAnnounceRes
}
type pathReaderStartReq struct {
author reader
res chan struct{}
}
type pathPublisherRecordRes struct {
stream *stream
err error
@ -163,16 +147,11 @@ type pathPublisherRecordRes struct { @@ -163,16 +147,11 @@ type pathPublisherRecordRes struct {
type pathPublisherStartReq struct {
author publisher
tracks gortsplib.Tracks
medias media.Medias
generateRTPPackets bool
res chan pathPublisherRecordRes
}
type pathReaderStopReq struct {
author reader
res chan struct{}
}
type pathPublisherStopReq struct {
author publisher
res chan struct{}
@ -225,7 +204,7 @@ type path struct { @@ -225,7 +204,7 @@ type path struct {
source source
bytesReceived *uint64
stream *stream
readers map[reader]pathReaderState
readers map[reader]struct{}
describeRequestsOnHold []pathDescribeReq
readerAddRequestsOnHold []pathReaderAddReq
onDemandCmd *externalcmd.Cmd
@ -245,10 +224,8 @@ type path struct { @@ -245,10 +224,8 @@ type path struct {
chPublisherAdd chan pathPublisherAddReq
chPublisherStart chan pathPublisherStartReq
chPublisherStop chan pathPublisherStopReq
chReaderRemove chan pathReaderRemoveReq
chReaderAdd chan pathReaderAddReq
chReaderStart chan pathReaderStartReq
chReaderStop chan pathReaderStopReq
chReaderRemove chan pathReaderRemoveReq
chAPIPathsList chan pathAPIPathsListSubReq
}
@ -283,7 +260,7 @@ func newPath( @@ -283,7 +260,7 @@ func newPath(
ctx: ctx,
ctxCancel: ctxCancel,
bytesReceived: new(uint64),
readers: make(map[reader]pathReaderState),
readers: make(map[reader]struct{}),
onDemandStaticSourceReadyTimer: newEmptyTimer(),
onDemandStaticSourceCloseTimer: newEmptyTimer(),
onDemandPublisherReadyTimer: newEmptyTimer(),
@ -295,10 +272,8 @@ func newPath( @@ -295,10 +272,8 @@ func newPath(
chPublisherAdd: make(chan pathPublisherAddReq),
chPublisherStart: make(chan pathPublisherStartReq),
chPublisherStop: make(chan pathPublisherStopReq),
chReaderRemove: make(chan pathReaderRemoveReq),
chReaderAdd: make(chan pathReaderAddReq),
chReaderStart: make(chan pathReaderStartReq),
chReaderStop: make(chan pathReaderStopReq),
chReaderRemove: make(chan pathReaderRemoveReq),
chAPIPathsList: make(chan pathAPIPathsListSubReq),
}
@ -436,7 +411,7 @@ func (pa *path) run() { @@ -436,7 +411,7 @@ func (pa *path) run() {
}
case req := <-pa.chSourceStaticSetReady:
err := pa.sourceSetReady(req.tracks, req.generateRTPPackets)
err := pa.sourceSetReady(req.medias, req.generateRTPPackets)
if err != nil {
req.res <- pathSourceStaticSetReadyRes{err: err}
} else {
@ -454,7 +429,7 @@ func (pa *path) run() { @@ -454,7 +429,7 @@ func (pa *path) run() {
pa.describeRequestsOnHold = nil
for _, req := range pa.readerAddRequestsOnHold {
pa.handleReaderSetupPlayPost(req)
pa.handleReaderAddPost(req)
}
pa.readerAddRequestsOnHold = nil
}
@ -492,33 +467,27 @@ func (pa *path) run() { @@ -492,33 +467,27 @@ func (pa *path) run() {
}
case req := <-pa.chPublisherAdd:
pa.handlePublisherAnnounce(req)
pa.handlePublisherAdd(req)
case req := <-pa.chPublisherStart:
pa.handlePublisherRecord(req)
pa.handlePublisherStart(req)
case req := <-pa.chPublisherStop:
pa.handlePublisherPause(req)
pa.handlePublisherStop(req)
if pa.shouldClose() {
return fmt.Errorf("not in use")
}
case req := <-pa.chReaderRemove:
pa.handleReaderRemove(req)
case req := <-pa.chReaderAdd:
pa.handleReaderSetupPlay(req)
pa.handleReaderAdd(req)
if pa.shouldClose() {
return fmt.Errorf("not in use")
}
case req := <-pa.chReaderStart:
pa.handleReaderPlay(req)
case req := <-pa.chReaderStop:
pa.handleReaderPause(req)
case req := <-pa.chReaderRemove:
pa.handleReaderRemove(req)
case req := <-pa.chAPIPathsList:
pa.handleAPIPathsList(req)
@ -667,8 +636,8 @@ func (pa *path) onDemandPublisherStop() { @@ -667,8 +636,8 @@ func (pa *path) onDemandPublisherStop() {
}
}
func (pa *path) sourceSetReady(tracks gortsplib.Tracks, allocateEncoder bool) error {
stream, err := newStream(tracks, allocateEncoder, pa.bytesReceived)
func (pa *path) sourceSetReady(medias media.Medias, allocateEncoder bool) error {
stream, err := newStream(medias, allocateEncoder, pa.bytesReceived)
if err != nil {
return err
}
@ -713,12 +682,6 @@ func (pa *path) sourceSetNotReady() { @@ -713,12 +682,6 @@ func (pa *path) sourceSetNotReady() {
}
func (pa *path) doReaderRemove(r reader) {
state := pa.readers[r]
if state == pathReaderStatePlay {
pa.stream.readerRemove(r)
}
delete(pa.readers, r)
}
@ -792,7 +755,7 @@ func (pa *path) handlePublisherRemove(req pathPublisherRemoveReq) { @@ -792,7 +755,7 @@ func (pa *path) handlePublisherRemove(req pathPublisherRemoveReq) {
close(req.res)
}
func (pa *path) handlePublisherAnnounce(req pathPublisherAddReq) {
func (pa *path) handlePublisherAdd(req pathPublisherAddReq) {
if pa.conf.Source != "publisher" {
req.res <- pathPublisherAnnounceRes{
err: fmt.Errorf("can't publish to path '%s' since 'source' is not 'publisher'", pa.name),
@ -816,13 +779,13 @@ func (pa *path) handlePublisherAnnounce(req pathPublisherAddReq) { @@ -816,13 +779,13 @@ func (pa *path) handlePublisherAnnounce(req pathPublisherAddReq) {
req.res <- pathPublisherAnnounceRes{path: pa}
}
func (pa *path) handlePublisherRecord(req pathPublisherStartReq) {
func (pa *path) handlePublisherStart(req pathPublisherStartReq) {
if pa.source != req.author {
req.res <- pathPublisherRecordRes{err: fmt.Errorf("publisher is not assigned to this path anymore")}
return
}
err := pa.sourceSetReady(req.tracks, req.generateRTPPackets)
err := pa.sourceSetReady(req.medias, req.generateRTPPackets)
if err != nil {
req.res <- pathPublisherRecordRes{err: err}
return
@ -842,7 +805,7 @@ func (pa *path) handlePublisherRecord(req pathPublisherStartReq) { @@ -842,7 +805,7 @@ func (pa *path) handlePublisherRecord(req pathPublisherStartReq) {
pa.describeRequestsOnHold = nil
for _, req := range pa.readerAddRequestsOnHold {
pa.handleReaderSetupPlayPost(req)
pa.handleReaderAddPost(req)
}
pa.readerAddRequestsOnHold = nil
}
@ -850,7 +813,7 @@ func (pa *path) handlePublisherRecord(req pathPublisherStartReq) { @@ -850,7 +813,7 @@ func (pa *path) handlePublisherRecord(req pathPublisherStartReq) {
req.res <- pathPublisherRecordRes{stream: pa.stream}
}
func (pa *path) handlePublisherPause(req pathPublisherStopReq) {
func (pa *path) handlePublisherStop(req pathPublisherStopReq) {
if req.author == pa.source && pa.stream != nil {
if pa.hasOnDemandPublisher() && pa.onDemandPublisherState != pathOnDemandStateInitial {
pa.onDemandPublisherStop()
@ -880,9 +843,9 @@ func (pa *path) handleReaderRemove(req pathReaderRemoveReq) { @@ -880,9 +843,9 @@ func (pa *path) handleReaderRemove(req pathReaderRemoveReq) {
}
}
func (pa *path) handleReaderSetupPlay(req pathReaderAddReq) {
func (pa *path) handleReaderAdd(req pathReaderAddReq) {
if pa.stream != nil {
pa.handleReaderSetupPlayPost(req)
pa.handleReaderAddPost(req)
return
}
@ -905,8 +868,8 @@ func (pa *path) handleReaderSetupPlay(req pathReaderAddReq) { @@ -905,8 +868,8 @@ func (pa *path) handleReaderSetupPlay(req pathReaderAddReq) {
req.res <- pathReaderSetupPlayRes{err: pathErrNoOnePublishing{pathName: pa.name}}
}
func (pa *path) handleReaderSetupPlayPost(req pathReaderAddReq) {
pa.readers[req.author] = pathReaderStatePrePlay
func (pa *path) handleReaderAddPost(req pathReaderAddReq) {
pa.readers[req.author] = struct{}{}
if pa.hasOnDemandStaticSource() {
if pa.onDemandStaticSourceState == pathOnDemandStateClosing {
@ -928,22 +891,6 @@ func (pa *path) handleReaderSetupPlayPost(req pathReaderAddReq) { @@ -928,22 +891,6 @@ func (pa *path) handleReaderSetupPlayPost(req pathReaderAddReq) {
}
}
func (pa *path) handleReaderPlay(req pathReaderStartReq) {
pa.readers[req.author] = pathReaderStatePlay
pa.stream.readerAdd(req.author)
close(req.res)
}
func (pa *path) handleReaderPause(req pathReaderStopReq) {
if state, ok := pa.readers[req.author]; ok && state == pathReaderStatePlay {
pa.readers[req.author] = pathReaderStatePrePlay
pa.stream.readerRemove(req.author)
}
close(req.res)
}
func (pa *path) handleAPIPathsList(req pathAPIPathsListSubReq) {
req.data.Items[pa.name] = pathAPIPathsListItem{
ConfName: pa.confName,
@ -959,7 +906,7 @@ func (pa *path) handleAPIPathsList(req pathAPIPathsListSubReq) { @@ -959,7 +906,7 @@ func (pa *path) handleAPIPathsList(req pathAPIPathsListSubReq) {
if pa.stream == nil {
return []string{}
}
return sourceTrackNames(pa.stream.tracks())
return mediasDescription(pa.stream.medias())
}(),
BytesReceived: atomic.LoadUint64(pa.bytesReceived),
Readers: func() []interface{} {
@ -1025,7 +972,7 @@ func (pa *path) publisherRemove(req pathPublisherRemoveReq) { @@ -1025,7 +972,7 @@ func (pa *path) publisherRemove(req pathPublisherRemoveReq) {
}
}
// publisherAnnounce is called by a publisher through pathManager.
// publisherAdd is called by a publisher through pathManager.
func (pa *path) publisherAdd(req pathPublisherAddReq) pathPublisherAnnounceRes {
select {
case pa.chPublisherAdd <- req:
@ -1035,7 +982,7 @@ func (pa *path) publisherAdd(req pathPublisherAddReq) pathPublisherAnnounceRes { @@ -1035,7 +982,7 @@ func (pa *path) publisherAdd(req pathPublisherAddReq) pathPublisherAnnounceRes {
}
}
// publisherRecord is called by a publisher.
// publisherStart is called by a publisher.
func (pa *path) publisherStart(req pathPublisherStartReq) pathPublisherRecordRes {
req.res = make(chan pathPublisherRecordRes)
select {
@ -1046,7 +993,7 @@ func (pa *path) publisherStart(req pathPublisherStartReq) pathPublisherRecordRes @@ -1046,7 +993,7 @@ func (pa *path) publisherStart(req pathPublisherStartReq) pathPublisherRecordRes
}
}
// publisherPause is called by a publisher.
// publisherStop is called by a publisher.
func (pa *path) publisherStop(req pathPublisherStopReq) {
req.res = make(chan struct{})
select {
@ -1056,17 +1003,7 @@ func (pa *path) publisherStop(req pathPublisherStopReq) { @@ -1056,17 +1003,7 @@ func (pa *path) publisherStop(req pathPublisherStopReq) {
}
}
// readerRemove is called by a reader.
func (pa *path) readerRemove(req pathReaderRemoveReq) {
req.res = make(chan struct{})
select {
case pa.chReaderRemove <- req:
<-req.res
case <-pa.ctx.Done():
}
}
// readerSetupPlay is called by a reader through pathManager.
// readerAdd is called by a reader through pathManager.
func (pa *path) readerAdd(req pathReaderAddReq) pathReaderSetupPlayRes {
select {
case pa.chReaderAdd <- req:
@ -1076,21 +1013,11 @@ func (pa *path) readerAdd(req pathReaderAddReq) pathReaderSetupPlayRes { @@ -1076,21 +1013,11 @@ func (pa *path) readerAdd(req pathReaderAddReq) pathReaderSetupPlayRes {
}
}
// readerPlay is called by a reader.
func (pa *path) readerStart(req pathReaderStartReq) {
req.res = make(chan struct{})
select {
case pa.chReaderStart <- req:
<-req.res
case <-pa.ctx.Done():
}
}
// readerPause is called by a reader.
func (pa *path) readerStop(req pathReaderStopReq) {
// readerRemove is called by a reader.
func (pa *path) readerRemove(req pathReaderRemoveReq) {
req.res = make(chan struct{})
select {
case pa.chReaderStop <- req:
case pa.chReaderRemove <- req:
<-req.res
case <-pa.ctx.Done():
}

1
internal/core/reader.go

@ -3,6 +3,5 @@ package core @@ -3,6 +3,5 @@ package core
// reader is an entity that can read a stream.
type reader interface {
close()
onReaderData(data)
apiReaderDescribe() interface{}
}

27
internal/core/rpicamera_source.go

@ -4,7 +4,8 @@ import ( @@ -4,7 +4,8 @@ import (
"context"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/rtsp-simple-server/internal/logger"
"github.com/aler9/rtsp-simple-server/internal/rpicamera"
@ -37,32 +38,34 @@ func (s *rpiCameraSource) Log(level logger.Level, format string, args ...interfa @@ -37,32 +38,34 @@ func (s *rpiCameraSource) Log(level logger.Level, format string, args ...interfa
// run implements sourceStaticImpl.
func (s *rpiCameraSource) run(ctx context.Context) error {
track := &gortsplib.TrackH264{
PayloadType: 96,
PacketizationMode: 1,
medi := &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}},
}
tracks := gortsplib.Tracks{track}
medias := media.Medias{medi}
var stream *stream
onData := func(dts time.Duration, nalus [][]byte) {
if stream == nil {
res := s.parent.sourceStaticImplSetReady(pathSourceStaticSetReadyReq{
tracks: tracks,
medias: medias,
generateRTPPackets: true,
})
if res.err != nil {
return
}
s.Log(logger.Info, "ready: %s", sourceTrackInfo(tracks))
s.Log(logger.Info, "ready: %s", sourceMediaInfo(medias))
stream = res.stream
}
err := stream.writeData(&dataH264{
trackID: 0,
pts: dts,
nalus: nalus,
ntp: time.Now(),
err := stream.writeData(medi, medi.Formats[0], &dataH264{
pts: dts,
nalus: nalus,
ntp: time.Now(),
})
if err != nil {
s.Log(logger.Warn, "%v", err)

149
internal/core/rtmp_conn.go

@ -10,10 +10,11 @@ import ( @@ -10,10 +10,11 @@ import (
"sync"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/pkg/ringbuffer"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/ringbuffer"
"github.com/google/uuid"
"github.com/notedit/rtmp/format/flv/flvio"
@ -76,7 +77,6 @@ type rtmpConn struct { @@ -76,7 +77,6 @@ type rtmpConn struct {
uuid uuid.UUID
created time.Time
path *path
ringBuffer *ringbuffer.RingBuffer // read
state rtmpConnState
stateMutex sync.Mutex
}
@ -253,55 +253,42 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error { @@ -253,55 +253,42 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
c.state = rtmpConnStateRead
c.stateMutex.Unlock()
var videoTrack *gortsplib.TrackH264
videoTrackID := -1
var audioTrack *gortsplib.TrackMPEG4Audio
audioTrackID := -1
var videoFormat *format.H264
videoMedia := res.stream.medias().FindFormat(&videoFormat)
for i, track := range res.stream.tracks() {
switch tt := track.(type) {
case *gortsplib.TrackH264:
if videoTrack != nil {
return fmt.Errorf("can't read track %d with RTMP: too many tracks", i+1)
}
videoTrack = tt
videoTrackID = i
case *gortsplib.TrackMPEG4Audio:
if audioTrack != nil {
return fmt.Errorf("can't read track %d with RTMP: too many tracks", i+1)
}
var audioFormat *format.MPEG4Audio
audioMedia := res.stream.medias().FindFormat(&audioFormat)
audioTrack = tt
audioTrackID = i
}
}
if videoTrack == nil && audioTrack == nil {
if videoFormat == nil && audioFormat == nil {
return fmt.Errorf("the stream doesn't contain an H264 track or an AAC track")
}
c.ringBuffer, _ = ringbuffer.New(uint64(c.readBufferCount))
ringBuffer, _ := ringbuffer.New(uint64(c.readBufferCount))
go func() {
<-ctx.Done()
c.ringBuffer.Close()
ringBuffer.Close()
}()
c.path.readerStart(pathReaderStartReq{
author: c,
})
var medias media.Medias
if videoMedia != nil {
medias = append(medias, videoMedia)
var tracks []gortsplib.Track
if videoTrack != nil {
tracks = append(tracks, videoTrack)
res.stream.readerAdd(c, videoMedia, videoFormat, func(dat data) {
ringBuffer.Push(dat)
})
}
if audioTrack != nil {
tracks = append(tracks, audioTrack)
if audioMedia != nil {
medias = append(medias, audioMedia)
res.stream.readerAdd(c, audioMedia, audioFormat, func(dat data) {
ringBuffer.Push(dat)
})
}
defer res.stream.readerRemove(c)
c.log(logger.Info, "is reading from path '%s', %s",
c.path.Name(), sourceTrackInfo(tracks))
c.path.Name(), sourceMediaInfo(medias))
if c.path.Conf().RunOnRead != "" {
c.log(logger.Info, "runOnRead command started")
@ -319,7 +306,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error { @@ -319,7 +306,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
}()
}
err := c.conn.WriteTracks(videoTrack, audioTrack)
err := c.conn.WriteTracks(videoFormat, audioFormat)
if err != nil {
return err
}
@ -337,15 +324,14 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error { @@ -337,15 +324,14 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
var videoDTSExtractor *h264.DTSExtractor
for {
item, ok := c.ringBuffer.Pull()
item, ok := ringBuffer.Pull()
if !ok {
return fmt.Errorf("terminated")
}
data := item.(data)
if videoTrack != nil && data.getTrackID() == videoTrackID {
tdata := data.(*dataH264)
switch tdata := data.(type) {
case *dataH264:
if tdata.nalus == nil {
continue
}
@ -423,9 +409,8 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error { @@ -423,9 +409,8 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
if err != nil {
return err
}
} else if audioTrack != nil && data.getTrackID() == audioTrackID {
tdata := data.(*dataMPEG4Audio)
case *dataMPEG4Audio:
if tdata.aus == nil {
continue
}
@ -436,7 +421,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error { @@ -436,7 +421,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
}
pts := tdata.pts - audioStartPTS
if videoTrack != nil {
if videoFormat != nil {
if !videoFirstIDRFound {
continue
}
@ -458,7 +443,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error { @@ -458,7 +443,7 @@ func (c *rtmpConn) runRead(ctx context.Context, u *url.URL) error {
AACType: flvio.AAC_RAW,
Payload: au,
DTS: pts + time.Duration(i)*mpeg4audio.SamplesPerAccessUnit*
time.Second/time.Duration(audioTrack.ClockRate()),
time.Second/time.Duration(audioFormat.ClockRate()),
})
if err != nil {
return err
@ -502,28 +487,34 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error { @@ -502,28 +487,34 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
c.state = rtmpConnStatePublish
c.stateMutex.Unlock()
videoTrack, audioTrack, err := c.conn.ReadTracks()
videoFormat, audioFormat, err := c.conn.ReadTracks()
if err != nil {
return err
}
var tracks gortsplib.Tracks
videoTrackID := -1
audioTrackID := -1
var medias media.Medias
var videoMedia *media.Media
var audioMedia *media.Media
if videoTrack != nil {
videoTrackID = len(tracks)
tracks = append(tracks, videoTrack)
if videoFormat != nil {
videoMedia = &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{videoFormat},
}
medias = append(medias, videoMedia)
}
if audioTrack != nil {
audioTrackID = len(tracks)
tracks = append(tracks, audioTrack)
if audioFormat != nil {
audioMedia = &media.Media{
Type: media.TypeAudio,
Formats: []format.Format{audioFormat},
}
medias = append(medias, audioMedia)
}
rres := c.path.publisherStart(pathPublisherStartReq{
author: c,
tracks: tracks,
medias: medias,
generateRTPPackets: true,
})
if rres.err != nil {
@ -532,7 +523,7 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error { @@ -532,7 +523,7 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
c.log(logger.Info, "is publishing to path '%s', %s",
c.path.Name(),
sourceTrackInfo(tracks))
sourceMediaInfo(medias))
// disable write deadline to allow outgoing acknowledges
c.nconn.SetWriteDeadline(time.Time{})
@ -558,17 +549,16 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error { @@ -558,17 +549,16 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
conf.PPS,
}
err := rres.stream.writeData(&dataH264{
trackID: videoTrackID,
pts: tmsg.DTS + tmsg.PTSDelta,
nalus: nalus,
ntp: time.Now(),
err := rres.stream.writeData(videoMedia, videoFormat, &dataH264{
pts: tmsg.DTS + tmsg.PTSDelta,
nalus: nalus,
ntp: time.Now(),
})
if err != nil {
c.log(logger.Warn, "%v", err)
}
} else if tmsg.H264Type == flvio.AVC_NALU {
if videoTrack == nil {
if videoFormat == nil {
return fmt.Errorf("received an H264 packet, but track is not set up")
}
@ -597,11 +587,10 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error { @@ -597,11 +587,10 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
}
}
err = rres.stream.writeData(&dataH264{
trackID: videoTrackID,
pts: tmsg.DTS + tmsg.PTSDelta,
nalus: validNALUs,
ntp: time.Now(),
err = rres.stream.writeData(videoMedia, videoFormat, &dataH264{
pts: tmsg.DTS + tmsg.PTSDelta,
nalus: validNALUs,
ntp: time.Now(),
})
if err != nil {
c.log(logger.Warn, "%v", err)
@ -610,15 +599,14 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error { @@ -610,15 +599,14 @@ func (c *rtmpConn) runPublish(ctx context.Context, u *url.URL) error {
case *message.MsgAudio:
if tmsg.AACType == flvio.AAC_RAW {
if audioTrack == nil {
if audioFormat == nil {
return fmt.Errorf("received an AAC packet, but track is not set up")
}
err := rres.stream.writeData(&dataMPEG4Audio{
trackID: audioTrackID,
pts: tmsg.DTS,
aus: [][]byte{tmsg.Payload},
ntp: time.Now(),
err := rres.stream.writeData(audioMedia, audioFormat, &dataMPEG4Audio{
pts: tmsg.DTS,
aus: [][]byte{tmsg.Payload},
ntp: time.Now(),
})
if err != nil {
c.log(logger.Warn, "%v", err)
@ -674,11 +662,6 @@ func (c *rtmpConn) authenticate( @@ -674,11 +662,6 @@ func (c *rtmpConn) authenticate(
return nil
}
// onReaderData implements reader.
func (c *rtmpConn) onReaderData(data data) {
c.ringBuffer.Push(data)
}
// apiReaderDescribe implements reader.
func (c *rtmpConn) apiReaderDescribe() interface{} {
return struct {

28
internal/core/rtmp_server_test.go

@ -8,8 +8,8 @@ import ( @@ -8,8 +8,8 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/stretchr/testify/require"
@ -68,8 +68,8 @@ func TestRTMPServerPublishRead(t *testing.T) { @@ -68,8 +68,8 @@ func TestRTMPServerPublishRead(t *testing.T) {
err = conn1.InitializeClient(u, true)
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
0x27, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04,
@ -79,8 +79,8 @@ func TestRTMPServerPublishRead(t *testing.T) { @@ -79,8 +79,8 @@ func TestRTMPServerPublishRead(t *testing.T) {
PacketizationMode: 1,
}
audioTrack := &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
audioTrack := &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
@ -190,8 +190,8 @@ func TestRTMPServerAuth(t *testing.T) { @@ -190,8 +190,8 @@ func TestRTMPServerAuth(t *testing.T) {
err = conn1.InitializeClient(u1, true)
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
@ -254,8 +254,8 @@ func TestRTMPServerAuthFail(t *testing.T) { @@ -254,8 +254,8 @@ func TestRTMPServerAuthFail(t *testing.T) {
err = conn1.InitializeClient(u1, true)
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
@ -309,8 +309,8 @@ func TestRTMPServerAuthFail(t *testing.T) { @@ -309,8 +309,8 @@ func TestRTMPServerAuthFail(t *testing.T) {
err = conn1.InitializeClient(u1, true)
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
@ -363,8 +363,8 @@ func TestRTMPServerAuthFail(t *testing.T) { @@ -363,8 +363,8 @@ func TestRTMPServerAuthFail(t *testing.T) {
err = conn1.InitializeClient(u1, true)
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,

57
internal/core/rtmp_source.go

@ -11,8 +11,9 @@ import ( @@ -11,8 +11,9 @@ import (
"strings"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/aler9/rtsp-simple-server/internal/conf"
@ -115,34 +116,40 @@ func (s *rtmpSource) run(ctx context.Context) error { @@ -115,34 +116,40 @@ func (s *rtmpSource) run(ctx context.Context) error {
nconn.SetWriteDeadline(time.Time{})
nconn.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout)))
videoTrack, audioTrack, err := conn.ReadTracks()
videoFormat, audioFormat, err := conn.ReadTracks()
if err != nil {
return err
}
var tracks gortsplib.Tracks
videoTrackID := -1
audioTrackID := -1
var medias media.Medias
var videoMedia *media.Media
var audioMedia *media.Media
if videoTrack != nil {
videoTrackID = len(tracks)
tracks = append(tracks, videoTrack)
if videoFormat != nil {
videoMedia = &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{videoFormat},
}
medias = append(medias, videoMedia)
}
if audioTrack != nil {
audioTrackID = len(tracks)
tracks = append(tracks, audioTrack)
if audioFormat != nil {
audioMedia = &media.Media{
Type: media.TypeAudio,
Formats: []format.Format{audioFormat},
}
medias = append(medias, audioMedia)
}
res := s.parent.sourceStaticImplSetReady(pathSourceStaticSetReadyReq{
tracks: tracks,
medias: medias,
generateRTPPackets: true,
})
if res.err != nil {
return res.err
}
s.Log(logger.Info, "ready: %s", sourceTrackInfo(tracks))
s.Log(logger.Info, "ready: %s", sourceMediaInfo(medias))
defer func() {
s.parent.sourceStaticImplSetNotReady(pathSourceStaticSetNotReadyReq{})
@ -161,7 +168,7 @@ func (s *rtmpSource) run(ctx context.Context) error { @@ -161,7 +168,7 @@ func (s *rtmpSource) run(ctx context.Context) error {
switch tmsg := msg.(type) {
case *message.MsgVideo:
if tmsg.H264Type == flvio.AVC_NALU {
if videoTrack == nil {
if videoFormat == nil {
return fmt.Errorf("received an H264 packet, but track is not set up")
}
@ -170,11 +177,10 @@ func (s *rtmpSource) run(ctx context.Context) error { @@ -170,11 +177,10 @@ func (s *rtmpSource) run(ctx context.Context) error {
return fmt.Errorf("unable to decode AVCC: %v", err)
}
err = res.stream.writeData(&dataH264{
trackID: videoTrackID,
pts: tmsg.DTS + tmsg.PTSDelta,
nalus: nalus,
ntp: time.Now(),
err = res.stream.writeData(videoMedia, videoFormat, &dataH264{
pts: tmsg.DTS + tmsg.PTSDelta,
nalus: nalus,
ntp: time.Now(),
})
if err != nil {
s.Log(logger.Warn, "%v", err)
@ -183,15 +189,14 @@ func (s *rtmpSource) run(ctx context.Context) error { @@ -183,15 +189,14 @@ func (s *rtmpSource) run(ctx context.Context) error {
case *message.MsgAudio:
if tmsg.AACType == flvio.AAC_RAW {
if audioTrack == nil {
if audioFormat == nil {
return fmt.Errorf("received an AAC packet, but track is not set up")
}
err := res.stream.writeData(&dataMPEG4Audio{
trackID: audioTrackID,
pts: tmsg.DTS,
aus: [][]byte{tmsg.Payload},
ntp: time.Now(),
err := res.stream.writeData(audioMedia, audioFormat, &dataMPEG4Audio{
pts: tmsg.DTS,
aus: [][]byte{tmsg.Payload},
ntp: time.Now(),
})
if err != nil {
s.Log(logger.Warn, "%v", err)

47
internal/core/rtmp_source_test.go

@ -6,10 +6,12 @@ import ( @@ -6,10 +6,12 @@ import (
"os"
"testing"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/pkg/url"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
"github.com/aler9/rtsp-simple-server/internal/rtmp"
@ -57,8 +59,8 @@ func TestRTMPSource(t *testing.T) { @@ -57,8 +59,8 @@ func TestRTMPSource(t *testing.T) {
_, _, err = conn.InitializeServer()
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{ // 1920x1080 baseline
0x67, 0x42, 0xc0, 0x28, 0xd9, 0x00, 0x78, 0x02,
0x27, 0xe5, 0x84, 0x00, 0x00, 0x03, 0x00, 0x04,
@ -68,8 +70,8 @@ func TestRTMPSource(t *testing.T) { @@ -68,8 +70,8 @@ func TestRTMPSource(t *testing.T) {
PacketizationMode: 1,
}
audioTrack := &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
audioTrack := &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
@ -114,18 +116,7 @@ func TestRTMPSource(t *testing.T) { @@ -114,18 +116,7 @@ func TestRTMPSource(t *testing.T) {
defer p.Close()
}
c := gortsplib.Client{
OnPacketRTP: func(ctx *gortsplib.ClientOnPacketRTPCtx) {
require.Equal(t, []byte{
0x18, 0x0, 0x19, 0x67, 0x42, 0xc0, 0x28, 0xd9,
0x0, 0x78, 0x2, 0x27, 0xe5, 0x84, 0x0, 0x0,
0x3, 0x0, 0x4, 0x0, 0x0, 0x3, 0x0, 0xf0,
0x3c, 0x60, 0xc9, 0x20, 0x0, 0x4, 0x8, 0x6,
0x7, 0x8, 0x0, 0x4, 0x5, 0x2, 0x3, 0x4,
}, ctx.Packet.Payload)
close(received)
},
}
c := gortsplib.Client{}
u, err := url.Parse("rtsp://127.0.0.1:8554/proxied")
require.NoError(t, err)
@ -134,10 +125,24 @@ func TestRTMPSource(t *testing.T) { @@ -134,10 +125,24 @@ func TestRTMPSource(t *testing.T) {
require.NoError(t, err)
defer c.Close()
tracks, baseURL, _, err := c.Describe(u)
medias, baseURL, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
require.NoError(t, err)
err = c.SetupAndPlay(tracks, baseURL)
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
require.Equal(t, []byte{
0x18, 0x0, 0x19, 0x67, 0x42, 0xc0, 0x28, 0xd9,
0x0, 0x78, 0x2, 0x27, 0xe5, 0x84, 0x0, 0x0,
0x3, 0x0, 0x4, 0x0, 0x0, 0x3, 0x0, 0xf0,
0x3c, 0x60, 0xc9, 0x20, 0x0, 0x4, 0x8, 0x6,
0x7, 0x8, 0x0, 0x4, 0x5, 0x2, 0x3, 0x4,
}, pkt.Payload)
close(received)
})
_, err = c.Play(nil)
require.NoError(t, err)
close(connected)

20
internal/core/rtsp_conn.go

@ -6,10 +6,11 @@ import ( @@ -6,10 +6,11 @@ import (
"net"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/auth"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/gortsplib/pkg/headers"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/auth"
"github.com/aler9/gortsplib/v2/pkg/base"
"github.com/aler9/gortsplib/v2/pkg/headers"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/google/uuid"
"github.com/aler9/rtsp-simple-server/internal/conf"
@ -112,13 +113,14 @@ func (c *rtspConn) ip() net.IP { @@ -112,13 +113,14 @@ func (c *rtspConn) ip() net.IP {
}
func (c *rtspConn) authenticate(
pathName string,
path string,
query string,
pathIPs []fmt.Stringer,
pathUser conf.Credential,
pathPass conf.Credential,
isPublishing bool,
req *base.Request,
query string,
baseURL *url.URL,
) error {
if c.externalAuthenticationURL != "" {
username := ""
@ -136,7 +138,7 @@ func (c *rtspConn) authenticate( @@ -136,7 +138,7 @@ func (c *rtspConn) authenticate(
c.ip().String(),
username,
password,
pathName,
path,
isPublishing,
query)
if err != nil {
@ -193,7 +195,7 @@ func (c *rtspConn) authenticate( @@ -193,7 +195,7 @@ func (c *rtspConn) authenticate(
c.authValidator = auth.NewValidator(string(pathUser), string(pathPass), c.authMethods)
}
err := c.authValidator.ValidateRequest(req)
err := c.authValidator.ValidateRequest(req, baseURL)
if err != nil {
c.authFailures++
@ -260,7 +262,7 @@ func (c *rtspConn) onDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx, @@ -260,7 +262,7 @@ func (c *rtspConn) onDescribe(ctx *gortsplib.ServerHandlerOnDescribeCtx,
pathUser conf.Credential,
pathPass conf.Credential,
) error {
return c.authenticate(ctx.Path, pathIPs, pathUser, pathPass, false, ctx.Request, ctx.Query)
return c.authenticate(ctx.Path, ctx.Query, pathIPs, pathUser, pathPass, false, ctx.Request, nil)
},
})

14
internal/core/rtsp_server.go

@ -8,10 +8,10 @@ import ( @@ -8,10 +8,10 @@ import (
"sync"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/gortsplib/pkg/headers"
"github.com/aler9/gortsplib/pkg/liberrors"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/base"
"github.com/aler9/gortsplib/v2/pkg/headers"
"github.com/aler9/gortsplib/v2/pkg/liberrors"
"github.com/aler9/rtsp-simple-server/internal/conf"
"github.com/aler9/rtsp-simple-server/internal/externalcmd"
@ -349,12 +349,6 @@ func (s *rtspServer) OnPause(ctx *gortsplib.ServerHandlerOnPauseCtx) (*base.Resp @@ -349,12 +349,6 @@ func (s *rtspServer) OnPause(ctx *gortsplib.ServerHandlerOnPauseCtx) (*base.Resp
return se.onPause(ctx)
}
// OnPacketRTP implements gortsplib.ServerHandlerOnPacketRTP.
func (s *rtspServer) OnPacketRTP(ctx *gortsplib.ServerHandlerOnPacketRTPCtx) {
se := ctx.Session.UserData().(*rtspSession)
se.onPacketRTP(ctx)
}
// OnDecodeError implements gortsplib.ServerHandlerOnOnDecodeError.
func (s *rtspServer) OnDecodeError(ctx *gortsplib.ServerHandlerOnDecodeErrorCtx) {
se := ctx.Session.UserData().(*rtspSession)

128
internal/core/rtsp_server_test.go

@ -3,8 +3,9 @@ package core @@ -3,8 +3,9 @@ package core
import (
"testing"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/url"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
@ -44,18 +45,13 @@ func TestRTSPServerAuth(t *testing.T) { @@ -44,18 +45,13 @@ func TestRTSPServerAuth(t *testing.T) {
require.NoError(t, err)
}
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
source := gortsplib.Client{}
err := source.StartPublishing(
err := source.StartRecording(
"rtsp://testpublisher:testpass@127.0.0.1:8554/teststream?param=value",
gortsplib.Tracks{track})
media.Medias{medi})
require.NoError(t, err)
defer source.Close()
@ -76,10 +72,13 @@ func TestRTSPServerAuth(t *testing.T) { @@ -76,10 +72,13 @@ func TestRTSPServerAuth(t *testing.T) {
require.NoError(t, err)
defer reader.Close()
tracks, baseURL, _, err := reader.Describe(u)
medias, baseURL, _, err := reader.Describe(u)
require.NoError(t, err)
err = reader.SetupAll(medias, baseURL)
require.NoError(t, err)
err = reader.SetupAndPlay(tracks, baseURL)
_, err = reader.Play(nil)
require.NoError(t, err)
})
}
@ -94,18 +93,13 @@ func TestRTSPServerAuth(t *testing.T) { @@ -94,18 +93,13 @@ func TestRTSPServerAuth(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
source := gortsplib.Client{}
err := source.StartPublishing(
err := source.StartRecording(
"rtsp://testuser:testpass@127.0.0.1:8554/test/stream",
gortsplib.Tracks{track})
media.Medias{medi})
require.NoError(t, err)
defer source.Close()
})
@ -143,18 +137,13 @@ func TestRTSPServerAuthFail(t *testing.T) { @@ -143,18 +137,13 @@ func TestRTSPServerAuthFail(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
c := gortsplib.Client{}
err := c.StartPublishing(
err := c.StartRecording(
"rtsp://"+ca.user+":"+ca.pass+"@localhost:8554/test/stream",
gortsplib.Tracks{track},
media.Medias{medi},
)
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
})
@ -214,18 +203,13 @@ func TestRTSPServerAuthFail(t *testing.T) { @@ -214,18 +203,13 @@ func TestRTSPServerAuthFail(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
c := gortsplib.Client{}
err := c.StartPublishing(
err := c.StartRecording(
"rtsp://localhost:8554/test/stream",
gortsplib.Tracks{track},
media.Medias{medi},
)
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
})
@ -241,18 +225,13 @@ func TestRTSPServerAuthFail(t *testing.T) { @@ -241,18 +225,13 @@ func TestRTSPServerAuthFail(t *testing.T) {
require.NoError(t, err)
defer a.close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
c := gortsplib.Client{}
err = c.StartPublishing(
err = c.StartRecording(
"rtsp://testpublisher2:testpass@localhost:8554/teststream?param=value",
gortsplib.Tracks{track},
media.Medias{medi},
)
require.EqualError(t, err, "bad status code: 401 (Unauthorized)")
})
@ -277,24 +256,17 @@ func TestRTSPServerPublisherOverride(t *testing.T) { @@ -277,24 +256,17 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
require.Equal(t, true, ok)
defer p.Close()
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
medi := testMediaH264
s1 := gortsplib.Client{}
err := s1.StartPublishing("rtsp://localhost:8554/teststream",
gortsplib.Tracks{track})
err := s1.StartRecording("rtsp://localhost:8554/teststream", media.Medias{medi})
require.NoError(t, err)
defer s1.Close()
s2 := gortsplib.Client{}
err = s2.StartPublishing("rtsp://localhost:8554/teststream",
gortsplib.Tracks{track})
err = s2.StartRecording("rtsp://localhost:8554/teststream", media.Medias{medi})
if ca == "enabled" {
require.NoError(t, err)
defer s2.Close()
@ -304,16 +276,7 @@ func TestRTSPServerPublisherOverride(t *testing.T) { @@ -304,16 +276,7 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
frameRecv := make(chan struct{})
c := gortsplib.Client{
OnPacketRTP: func(ctx *gortsplib.ClientOnPacketRTPCtx) {
if ca == "enabled" {
require.Equal(t, []byte{0x05, 0x06, 0x07, 0x08}, ctx.Packet.Payload)
} else {
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, ctx.Packet.Payload)
}
close(frameRecv)
},
}
c := gortsplib.Client{}
u, err := url.Parse("rtsp://localhost:8554/teststream")
require.NoError(t, err)
@ -322,16 +285,28 @@ func TestRTSPServerPublisherOverride(t *testing.T) { @@ -322,16 +285,28 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
require.NoError(t, err)
defer c.Close()
tracks, baseURL, _, err := c.Describe(u)
medias, baseURL, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
require.NoError(t, err)
err = c.SetupAndPlay(tracks, baseURL)
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
if ca == "enabled" {
require.Equal(t, []byte{0x05, 0x06, 0x07, 0x08}, pkt.Payload)
} else {
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, pkt.Payload)
}
close(frameRecv)
})
_, err = c.Play(nil)
require.NoError(t, err)
err = s1.WritePacketRTP(0, &rtp.Packet{
err = s1.WritePacketRTP(medi, &rtp.Packet{
Header: rtp.Header{
Version: 0x02,
PayloadType: 97,
PayloadType: 96,
SequenceNumber: 57899,
Timestamp: 345234345,
SSRC: 978651231,
@ -346,10 +321,10 @@ func TestRTSPServerPublisherOverride(t *testing.T) { @@ -346,10 +321,10 @@ func TestRTSPServerPublisherOverride(t *testing.T) {
}
if ca == "enabled" {
err = s2.WritePacketRTP(0, &rtp.Packet{
err = s2.WritePacketRTP(medi, &rtp.Packet{
Header: rtp.Header{
Version: 0x02,
PayloadType: 97,
PayloadType: 96,
SequenceNumber: 57899,
Timestamp: 345234345,
SSRC: 978651231,
@ -388,13 +363,8 @@ func TestRTSPServerFallback(t *testing.T) { @@ -388,13 +363,8 @@ func TestRTSPServerFallback(t *testing.T) {
defer p1.Close()
source := gortsplib.Client{}
err := source.StartPublishing("rtsp://localhost:8554/path2",
gortsplib.Tracks{&gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}})
err := source.StartRecording("rtsp://localhost:8554/path2",
media.Medias{testMediaH264})
require.NoError(t, err)
defer source.Close()
@ -406,9 +376,9 @@ func TestRTSPServerFallback(t *testing.T) { @@ -406,9 +376,9 @@ func TestRTSPServerFallback(t *testing.T) {
require.NoError(t, err)
defer dest.Close()
tracks, _, _, err := dest.Describe(u)
medias, _, _, err := dest.Describe(u)
require.NoError(t, err)
require.Equal(t, 1, len(tracks))
require.Equal(t, 1, len(medias))
})
}
}

129
internal/core/rtsp_session.go

@ -8,8 +8,10 @@ import ( @@ -8,8 +8,10 @@ import (
"sync"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/base"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/google/uuid"
"github.com/pion/rtp"
@ -80,9 +82,6 @@ func (s *rtspSession) close() { @@ -80,9 +82,6 @@ func (s *rtspSession) close() {
s.session.Close()
}
// isRTSPSession implements pathRTSPSession.
func (s *rtspSession) isRTSPSession() {}
func (s *rtspSession) safeState() gortsplib.ServerSessionState {
s.stateMutex.Lock()
defer s.stateMutex.Unlock()
@ -132,7 +131,7 @@ func (s *rtspSession) onAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnno @@ -132,7 +131,7 @@ func (s *rtspSession) onAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnno
pathUser conf.Credential,
pathPass conf.Credential,
) error {
return c.authenticate(ctx.Path, pathIPs, pathUser, pathPass, true, ctx.Request, ctx.Query)
return c.authenticate(ctx.Path, ctx.Query, pathIPs, pathUser, pathPass, true, ctx.Request, nil)
},
})
@ -170,7 +169,7 @@ func (s *rtspSession) onAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnno @@ -170,7 +169,7 @@ func (s *rtspSession) onAnnounce(c *rtspConn, ctx *gortsplib.ServerHandlerOnAnno
func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCtx,
) (*base.Response, *gortsplib.ServerStream, error) {
// in case the client is setupping a stream with UDP or UDP-multicast, and these
// transport protocols are disabled, gortsplib already blocks the request.
// transport protocols are disabled, gortsplib/v2 already blocks the request.
// we have only to handle the case in which the transport protocol is TCP
// and it is disabled.
if ctx.Transport == gortsplib.TransportTCP {
@ -191,7 +190,19 @@ func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt @@ -191,7 +190,19 @@ func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt
pathUser conf.Credential,
pathPass conf.Credential,
) error {
return c.authenticate(ctx.Path, pathIPs, pathUser, pathPass, false, ctx.Request, ctx.Query)
baseURL := &url.URL{
Scheme: ctx.Request.URL.Scheme,
Host: ctx.Request.URL.Host,
Path: func() string {
pa := ctx.Path
if ctx.Query != "" {
pa += "?" + ctx.Query
}
pa += "/"
return pa
}(),
}
return c.authenticate(ctx.Path, ctx.Query, pathIPs, pathUser, pathPass, false, ctx.Request, baseURL)
},
})
@ -222,12 +233,6 @@ func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt @@ -222,12 +233,6 @@ func (s *rtspSession) onSetup(c *rtspConn, ctx *gortsplib.ServerHandlerOnSetupCt
s.path = res.path
s.stream = res.stream
if ctx.TrackID >= len(res.stream.tracks()) {
return &base.Response{
StatusCode: base.StatusBadRequest,
}, nil, fmt.Errorf("track %d does not exist", ctx.TrackID)
}
s.stateMutex.Lock()
s.state = gortsplib.ServerSessionStatePrePlay
s.stateMutex.Unlock()
@ -248,19 +253,10 @@ func (s *rtspSession) onPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Respo @@ -248,19 +253,10 @@ func (s *rtspSession) onPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Respo
h := make(base.Header)
if s.session.State() == gortsplib.ServerSessionStatePrePlay {
s.path.readerStart(pathReaderStartReq{author: s})
tracks := make(gortsplib.Tracks, len(s.session.SetuppedTracks()))
n := 0
for id := range s.session.SetuppedTracks() {
tracks[n] = s.stream.tracks()[id]
n++
}
s.log(logger.Info, "is reading from path '%s', with %s, %s",
s.path.Name(),
s.session.SetuppedTransport(),
sourceTrackInfo(tracks))
sourceMediaInfo(s.session.SetuppedMedias()))
if s.path.Conf().RunOnRead != "" {
s.log(logger.Info, "runOnRead command started")
@ -289,7 +285,7 @@ func (s *rtspSession) onPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Respo @@ -289,7 +285,7 @@ func (s *rtspSession) onPlay(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Respo
func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.Response, error) {
res := s.path.publisherStart(pathPublisherStartReq{
author: s,
tracks: s.session.AnnouncedTracks(),
medias: s.session.AnnouncedMedias(),
generateRTPPackets: false,
})
if res.err != nil {
@ -301,10 +297,52 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R @@ -301,10 +297,52 @@ func (s *rtspSession) onRecord(ctx *gortsplib.ServerHandlerOnRecordCtx) (*base.R
s.log(logger.Info, "is publishing to path '%s', with %s, %s",
s.path.Name(),
s.session.SetuppedTransport(),
sourceTrackInfo(s.session.AnnouncedTracks()))
sourceMediaInfo(s.session.AnnouncedMedias()))
s.stream = res.stream
for _, medi := range s.session.AnnouncedMedias() {
for _, forma := range medi.Formats {
cmedia := medi
cformat := forma
switch forma.(type) {
case *format.H264:
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
err := s.stream.writeData(cmedia, cformat, &dataH264{
rtpPackets: []*rtp.Packet{pkt},
ntp: time.Now(),
})
if err != nil {
s.log(logger.Warn, "%v", err)
}
})
case *format.MPEG4Audio:
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
err := s.stream.writeData(cmedia, cformat, &dataMPEG4Audio{
rtpPackets: []*rtp.Packet{pkt},
ntp: time.Now(),
})
if err != nil {
s.log(logger.Warn, "%v", err)
}
})
default:
ctx.Session.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
err := s.stream.writeData(cmedia, cformat, &dataGeneric{
rtpPackets: []*rtp.Packet{pkt},
ntp: time.Now(),
})
if err != nil {
s.log(logger.Warn, "%v", err)
}
})
}
}
}
s.stateMutex.Lock()
s.state = gortsplib.ServerSessionStateRecord
s.stateMutex.Unlock()
@ -323,8 +361,6 @@ func (s *rtspSession) onPause(ctx *gortsplib.ServerHandlerOnPauseCtx) (*base.Res @@ -323,8 +361,6 @@ func (s *rtspSession) onPause(ctx *gortsplib.ServerHandlerOnPauseCtx) (*base.Res
s.onReadCmd.Close()
}
s.path.readerStop(pathReaderStopReq{author: s})
s.stateMutex.Lock()
s.state = gortsplib.ServerSessionStatePrePlay
s.stateMutex.Unlock()
@ -342,11 +378,6 @@ func (s *rtspSession) onPause(ctx *gortsplib.ServerHandlerOnPauseCtx) (*base.Res @@ -342,11 +378,6 @@ func (s *rtspSession) onPause(ctx *gortsplib.ServerHandlerOnPauseCtx) (*base.Res
}, nil
}
// onReaderData implements reader.
func (s *rtspSession) onReaderData(data data) {
// packets are routed to the session by gortsplib.ServerStream.
}
// apiReaderDescribe implements reader.
func (s *rtspSession) apiReaderDescribe() interface{} {
var typ string
@ -377,38 +408,6 @@ func (s *rtspSession) apiSourceDescribe() interface{} { @@ -377,38 +408,6 @@ func (s *rtspSession) apiSourceDescribe() interface{} {
}{typ, s.uuid.String()}
}
// onPacketRTP is called by rtspServer.
func (s *rtspSession) onPacketRTP(ctx *gortsplib.ServerHandlerOnPacketRTPCtx) {
var err error
switch s.session.AnnouncedTracks()[ctx.TrackID].(type) {
case *gortsplib.TrackH264:
err = s.stream.writeData(&dataH264{
trackID: ctx.TrackID,
rtpPackets: []*rtp.Packet{ctx.Packet},
ntp: time.Now(),
})
case *gortsplib.TrackMPEG4Audio:
err = s.stream.writeData(&dataMPEG4Audio{
trackID: ctx.TrackID,
rtpPackets: []*rtp.Packet{ctx.Packet},
ntp: time.Now(),
})
default:
err = s.stream.writeData(&dataGeneric{
trackID: ctx.TrackID,
rtpPackets: []*rtp.Packet{ctx.Packet},
ntp: time.Now(),
})
}
if err != nil {
s.log(logger.Warn, "%v", err)
}
}
// onDecodeError is called by rtspServer.
func (s *rtspSession) onDecodeError(ctx *gortsplib.ServerHandlerOnDecodeErrorCtx) {
s.log(logger.Warn, "%v", ctx.Error)

88
internal/core/rtsp_source.go

@ -9,11 +9,12 @@ import ( @@ -9,11 +9,12 @@ import (
"strings"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/base"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/pion/rtp"
"github.com/aler9/gortsplib/pkg/url"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/aler9/rtsp-simple-server/internal/conf"
"github.com/aler9/rtsp-simple-server/internal/logger"
)
@ -117,60 +118,69 @@ func (s *rtspSource) run(ctx context.Context) error { @@ -117,60 +118,69 @@ func (s *rtspSource) run(ctx context.Context) error {
readErr := make(chan error)
go func() {
readErr <- func() error {
tracks, baseURL, _, err := c.Describe(u)
medias, baseURL, _, err := c.Describe(u)
if err != nil {
return err
}
for _, t := range tracks {
_, err := c.Setup(t, baseURL, 0, 0)
if err != nil {
return err
}
err = c.SetupAll(medias, baseURL)
if err != nil {
return err
}
res := s.parent.sourceStaticImplSetReady(pathSourceStaticSetReadyReq{
tracks: tracks,
medias: medias,
generateRTPPackets: false,
})
if res.err != nil {
return res.err
}
s.Log(logger.Info, "ready: %s", sourceTrackInfo(tracks))
s.Log(logger.Info, "ready: %s", sourceMediaInfo(medias))
defer func() {
s.parent.sourceStaticImplSetNotReady(pathSourceStaticSetNotReadyReq{})
}()
c.OnPacketRTP = func(ctx *gortsplib.ClientOnPacketRTPCtx) {
var err error
switch tracks[ctx.TrackID].(type) {
case *gortsplib.TrackH264:
err = res.stream.writeData(&dataH264{
trackID: ctx.TrackID,
rtpPackets: []*rtp.Packet{ctx.Packet},
ntp: time.Now(),
})
case *gortsplib.TrackMPEG4Audio:
err = res.stream.writeData(&dataMPEG4Audio{
trackID: ctx.TrackID,
rtpPackets: []*rtp.Packet{ctx.Packet},
ntp: time.Now(),
})
default:
err = res.stream.writeData(&dataGeneric{
trackID: ctx.TrackID,
rtpPackets: []*rtp.Packet{ctx.Packet},
ntp: time.Now(),
})
}
if err != nil {
s.Log(logger.Warn, "%v", err)
for _, medi := range medias {
for _, forma := range medi.Formats {
cmedia := medi
cformat := forma
switch forma.(type) {
case *format.H264:
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
err := res.stream.writeData(cmedia, cformat, &dataH264{
rtpPackets: []*rtp.Packet{pkt},
ntp: time.Now(),
})
if err != nil {
s.Log(logger.Warn, "%v", err)
}
})
case *format.MPEG4Audio:
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
err := res.stream.writeData(cmedia, cformat, &dataMPEG4Audio{
rtpPackets: []*rtp.Packet{pkt},
ntp: time.Now(),
})
if err != nil {
s.Log(logger.Warn, "%v", err)
}
})
default:
c.OnPacketRTP(medi, forma, func(pkt *rtp.Packet) {
err := res.stream.writeData(cmedia, cformat, &dataGeneric{
rtpPackets: []*rtp.Packet{pkt},
ntp: time.Now(),
})
if err != nil {
s.Log(logger.Warn, "%v", err)
}
})
}
}
}

303
internal/core/rtsp_source_test.go

@ -9,12 +9,14 @@ import ( @@ -9,12 +9,14 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/auth"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/gortsplib/pkg/conn"
"github.com/aler9/gortsplib/pkg/headers"
"github.com/aler9/gortsplib/pkg/url"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/auth"
"github.com/aler9/gortsplib/v2/pkg/base"
"github.com/aler9/gortsplib/v2/pkg/conn"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/headers"
"github.com/aler9/gortsplib/v2/pkg/media"
"github.com/aler9/gortsplib/v2/pkg/url"
"github.com/pion/rtp"
"github.com/stretchr/testify/require"
)
@ -45,14 +47,8 @@ func TestRTSPSource(t *testing.T) { @@ -45,14 +47,8 @@ func TestRTSPSource(t *testing.T) {
"tls",
} {
t.Run(source, func(t *testing.T) {
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
medi := testMediaH264
stream := gortsplib.NewServerStream(media.Medias{medi})
var authValidator *auth.Validator
@ -64,7 +60,7 @@ func TestRTSPSource(t *testing.T) { @@ -64,7 +60,7 @@ func TestRTSPSource(t *testing.T) {
authValidator = auth.NewValidator("testuser", "testpass", nil)
}
err := authValidator.ValidateRequest(ctx.Request)
err := authValidator.ValidateRequest(ctx.Request, nil)
if err != nil {
return &base.Response{
StatusCode: base.StatusUnauthorized,
@ -86,10 +82,10 @@ func TestRTSPSource(t *testing.T) { @@ -86,10 +82,10 @@ func TestRTSPSource(t *testing.T) {
onPlay: func(ctx *gortsplib.ServerHandlerOnPlayCtx) (*base.Response, error) {
go func() {
time.Sleep(1 * time.Second)
stream.WritePacketRTP(0, &rtp.Packet{
stream.WritePacketRTP(medi, &rtp.Packet{
Header: rtp.Header{
Version: 0x02,
PayloadType: 97,
PayloadType: 96,
SequenceNumber: 57899,
Timestamp: 345234345,
SSRC: 978651231,
@ -152,12 +148,7 @@ func TestRTSPSource(t *testing.T) { @@ -152,12 +148,7 @@ func TestRTSPSource(t *testing.T) {
received := make(chan struct{})
c := gortsplib.Client{
OnPacketRTP: func(ctx *gortsplib.ClientOnPacketRTPCtx) {
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, ctx.Packet.Payload)
close(received)
},
}
c := gortsplib.Client{}
u, err := url.Parse("rtsp://127.0.0.1:8554/proxied")
require.NoError(t, err)
@ -166,10 +157,18 @@ func TestRTSPSource(t *testing.T) { @@ -166,10 +157,18 @@ func TestRTSPSource(t *testing.T) {
require.NoError(t, err)
defer c.Close()
tracks, baseURL, _, err := c.Describe(u)
medias, baseURL, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
require.NoError(t, err)
err = c.SetupAndPlay(tracks, baseURL)
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
require.Equal(t, []byte{0x01, 0x02, 0x03, 0x04}, pkt.Payload)
close(received)
})
_, err = c.Play(nil)
require.NoError(t, err)
<-received
@ -178,14 +177,8 @@ func TestRTSPSource(t *testing.T) { @@ -178,14 +177,8 @@ func TestRTSPSource(t *testing.T) {
}
func TestRTSPSourceNoPassword(t *testing.T) {
track := &gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
medi := testMediaH264
stream := gortsplib.NewServerStream(media.Medias{medi})
var authValidator *auth.Validator
done := make(chan struct{})
@ -197,7 +190,7 @@ func TestRTSPSourceNoPassword(t *testing.T) { @@ -197,7 +190,7 @@ func TestRTSPSourceNoPassword(t *testing.T) {
authValidator = auth.NewValidator("testuser", "", nil)
}
err := authValidator.ValidateRequest(ctx.Request)
err := authValidator.ValidateRequest(ctx.Request, nil)
if err != nil {
return &base.Response{
StatusCode: base.StatusUnauthorized,
@ -243,11 +236,15 @@ func TestRTSPSourceNoPassword(t *testing.T) { @@ -243,11 +236,15 @@ func TestRTSPSourceNoPassword(t *testing.T) {
}
func TestRTSPSourceDynamicH264Params(t *testing.T) {
track := &gortsplib.TrackH264{
PayloadType: 96,
forma := &format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}
stream := gortsplib.NewServerStream(gortsplib.Tracks{track})
medi := &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{forma},
}
stream := gortsplib.NewServerStream(media.Medias{medi})
defer stream.Close()
s := gortsplib.Server{
@ -285,15 +282,15 @@ func TestRTSPSourceDynamicH264Params(t *testing.T) { @@ -285,15 +282,15 @@ func TestRTSPSourceDynamicH264Params(t *testing.T) {
time.Sleep(1 * time.Second)
enc := track.CreateEncoder()
enc := forma.CreateEncoder()
pkts, err := enc.Encode([][]byte{{7, 1, 2, 3}}, 0) // SPS
require.NoError(t, err)
stream.WritePacketRTP(0, pkts[0])
stream.WritePacketRTP(medi, pkts[0])
pkts, err = enc.Encode([][]byte{{8}}, 0) // PPS
require.NoError(t, err)
stream.WritePacketRTP(0, pkts[0])
stream.WritePacketRTP(medi, pkts[0])
time.Sleep(500 * time.Millisecond)
@ -307,22 +304,21 @@ func TestRTSPSourceDynamicH264Params(t *testing.T) { @@ -307,22 +304,21 @@ func TestRTSPSourceDynamicH264Params(t *testing.T) {
require.NoError(t, err)
defer c.Close()
tracks, _, _, err := c.Describe(u)
medias, _, _, err := c.Describe(u)
require.NoError(t, err)
h264Track, ok := tracks[0].(*gortsplib.TrackH264)
require.Equal(t, true, ok)
h264Track := medias[0].Formats[0].(*format.H264)
require.Equal(t, []byte{7, 1, 2, 3}, h264Track.SafeSPS())
require.Equal(t, []byte{8}, h264Track.SafePPS())
}()
pkts, err = enc.Encode([][]byte{{7, 4, 5, 6}}, 0) // SPS
require.NoError(t, err)
stream.WritePacketRTP(0, pkts[0])
stream.WritePacketRTP(medi, pkts[0])
pkts, err = enc.Encode([][]byte{{8, 1}}, 0) // PPS
require.NoError(t, err)
stream.WritePacketRTP(0, pkts[0])
stream.WritePacketRTP(medi, pkts[0])
time.Sleep(500 * time.Millisecond)
@ -336,21 +332,24 @@ func TestRTSPSourceDynamicH264Params(t *testing.T) { @@ -336,21 +332,24 @@ func TestRTSPSourceDynamicH264Params(t *testing.T) {
require.NoError(t, err)
defer c.Close()
tracks, _, _, err := c.Describe(u)
medias, _, _, err := c.Describe(u)
require.NoError(t, err)
h264Track, ok := tracks[0].(*gortsplib.TrackH264)
require.Equal(t, true, ok)
h264Track := medias[0].Formats[0].(*format.H264)
require.Equal(t, []byte{7, 4, 5, 6}, h264Track.SafeSPS())
require.Equal(t, []byte{8, 1}, h264Track.SafePPS())
}()
}
func TestRTSPSourceRemovePadding(t *testing.T) {
stream := gortsplib.NewServerStream(gortsplib.Tracks{&gortsplib.TrackH264{
PayloadType: 96,
PacketizationMode: 1,
}})
medi := &media.Media{
Type: media.TypeVideo,
Formats: []format.Format{&format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}},
}
stream := gortsplib.NewServerStream(media.Medias{medi})
defer stream.Close()
s := gortsplib.Server{
@ -390,23 +389,7 @@ func TestRTSPSourceRemovePadding(t *testing.T) { @@ -390,23 +389,7 @@ func TestRTSPSourceRemovePadding(t *testing.T) {
packetRecv := make(chan struct{})
c := gortsplib.Client{
OnPacketRTP: func(ctx *gortsplib.ClientOnPacketRTPCtx) {
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
}, ctx.Packet)
close(packetRecv)
},
}
c := gortsplib.Client{}
u, err := url.Parse("rtsp://127.0.0.1:8554/proxied")
require.NoError(t, err)
@ -415,13 +398,32 @@ func TestRTSPSourceRemovePadding(t *testing.T) { @@ -415,13 +398,32 @@ func TestRTSPSourceRemovePadding(t *testing.T) {
require.NoError(t, err)
defer c.Close()
tracks, baseURL, _, err := c.Describe(u)
medias, baseURL, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
require.NoError(t, err)
err = c.SetupAndPlay(tracks, baseURL)
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
}, pkt)
close(packetRecv)
})
_, err = c.Play(nil)
require.NoError(t, err)
stream.WritePacketRTP(0, &rtp.Packet{
stream.WritePacketRTP(medi, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
@ -475,19 +477,15 @@ func TestRTSPSourceOversizedPackets(t *testing.T) { @@ -475,19 +477,15 @@ func TestRTSPSourceOversizedPackets(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
tracks := gortsplib.Tracks{&gortsplib.TrackH264{
PayloadType: 96,
SPS: []byte{0x01, 0x02, 0x03, 0x04},
PPS: []byte{0x01, 0x02, 0x03, 0x04},
PacketizationMode: 1,
}}
medias := media.Medias{testMediaH264}
byts, _ := medias.Marshal(false).Marshal()
err = conn.WriteResponse(&base.Response{
StatusCode: base.StatusOK,
Header: base.Header{
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: tracks.Marshal(false),
Body: byts,
})
require.NoError(t, err)
@ -525,7 +523,7 @@ func TestRTSPSourceOversizedPackets(t *testing.T) { @@ -525,7 +523,7 @@ func TestRTSPSourceOversizedPackets(t *testing.T) {
<-connected
byts, _ := rtp.Packet{
byts, _ = rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
@ -603,75 +601,7 @@ func TestRTSPSourceOversizedPackets(t *testing.T) { @@ -603,75 +601,7 @@ func TestRTSPSourceOversizedPackets(t *testing.T) {
packetRecv := make(chan struct{})
i := 0
c := gortsplib.Client{
OnPacketRTP: func(ctx *gortsplib.ClientOnPacketRTPCtx) {
switch i {
case 0:
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
}, ctx.Packet)
case 1:
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: append(
append([]byte{0x1c, 0x81, 0x02, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 363)...),
[]byte{0x01, 0x02, 0x03}...,
),
}, ctx.Packet)
case 2:
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 125,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: append(
[]byte{0x1c, 0x41, 0x04},
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 135)...,
),
}, ctx.Packet)
case 3:
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 126,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
}, ctx.Packet)
close(packetRecv)
}
i++
},
}
c := gortsplib.Client{}
u, err := url.Parse("rtsp://127.0.0.1:8554/proxied")
require.NoError(t, err)
@ -680,10 +610,81 @@ func TestRTSPSourceOversizedPackets(t *testing.T) { @@ -680,10 +610,81 @@ func TestRTSPSourceOversizedPackets(t *testing.T) {
require.NoError(t, err)
defer c.Close()
tracks, baseURL, _, err := c.Describe(u)
medias, baseURL, _, err := c.Describe(u)
require.NoError(t, err)
err = c.SetupAll(medias, baseURL)
require.NoError(t, err)
err = c.SetupAndPlay(tracks, baseURL)
c.OnPacketRTP(medias[0], medias[0].Formats[0], func(pkt *rtp.Packet) {
switch i {
case 0:
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 123,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
}, pkt)
case 1:
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: false,
PayloadType: 96,
SequenceNumber: 124,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: append(
append([]byte{0x1c, 0x81, 0x02, 0x03, 0x04}, bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 363)...),
[]byte{0x01, 0x02, 0x03}...,
),
}, pkt)
case 2:
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 125,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: append(
[]byte{0x1c, 0x41, 0x04},
bytes.Repeat([]byte{0x01, 0x02, 0x03, 0x04}, 135)...,
),
}, pkt)
case 3:
require.Equal(t, &rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 126,
Timestamp: 45343,
SSRC: 563423,
CSRC: []uint32{},
},
Payload: []byte{0x01, 0x02, 0x03, 0x04},
}, pkt)
close(packetRecv)
}
i++
})
_, err = c.Play(nil)
require.NoError(t, err)
close(connected)

26
internal/core/source.go

@ -4,7 +4,7 @@ import ( @@ -4,7 +4,7 @@ import (
"fmt"
"strings"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/media"
)
// source is an entity that can provide a stream.
@ -16,22 +16,30 @@ type source interface { @@ -16,22 +16,30 @@ type source interface {
apiSourceDescribe() interface{}
}
func sourceTrackNames(tracks gortsplib.Tracks) []string {
ret := make([]string, len(tracks))
for i, t := range tracks {
ret[i] = t.String()
func mediaDescription(media *media.Media) string {
ret := make([]string, len(media.Formats))
for i, forma := range media.Formats {
ret[i] = forma.String()
}
return strings.Join(ret, "/")
}
func mediasDescription(medias media.Medias) []string {
ret := make([]string, len(medias))
for i, media := range medias {
ret[i] = mediaDescription(media)
}
return ret
}
func sourceTrackInfo(tracks gortsplib.Tracks) string {
func sourceMediaInfo(medias media.Medias) string {
return fmt.Sprintf("%d %s (%s)",
len(tracks),
len(medias),
func() string {
if len(tracks) == 1 {
if len(medias) == 1 {
return "track"
}
return "tracks"
}(),
strings.Join(sourceTrackNames(tracks), ", "))
strings.Join(mediasDescription(medias), ", "))
}

110
internal/core/stream.go

@ -1,79 +1,32 @@ @@ -1,79 +1,32 @@
package core
import (
"sync"
"sync/atomic"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
)
type streamNonRTSPReadersMap struct {
mutex sync.RWMutex
ma map[reader]struct{}
}
func newStreamNonRTSPReadersMap() *streamNonRTSPReadersMap {
return &streamNonRTSPReadersMap{
ma: make(map[reader]struct{}),
}
}
func (m *streamNonRTSPReadersMap) close() {
m.mutex.Lock()
defer m.mutex.Unlock()
m.ma = nil
}
func (m *streamNonRTSPReadersMap) add(r reader) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.ma[r] = struct{}{}
}
func (m *streamNonRTSPReadersMap) remove(r reader) {
m.mutex.Lock()
defer m.mutex.Unlock()
delete(m.ma, r)
}
func (m *streamNonRTSPReadersMap) writeData(data data) {
m.mutex.RLock()
defer m.mutex.RUnlock()
for c := range m.ma {
c.onReaderData(data)
}
}
func (m *streamNonRTSPReadersMap) hasReaders() bool {
m.mutex.RLock()
defer m.mutex.RUnlock()
return len(m.ma) > 0
}
type stream struct {
bytesReceived *uint64
nonRTSPReaders *streamNonRTSPReadersMap
rtspStream *gortsplib.ServerStream
streamTracks []streamTrack
bytesReceived *uint64
rtspStream *gortsplib.ServerStream
smedias map[*media.Media]*streamMedia
}
func newStream(
tracks gortsplib.Tracks,
medias media.Medias,
generateRTPPackets bool,
bytesReceived *uint64,
) (*stream, error) {
s := &stream{
bytesReceived: bytesReceived,
nonRTSPReaders: newStreamNonRTSPReadersMap(),
rtspStream: gortsplib.NewServerStream(tracks),
bytesReceived: bytesReceived,
rtspStream: gortsplib.NewServerStream(medias),
}
s.streamTracks = make([]streamTrack, len(s.rtspStream.Tracks()))
s.smedias = make(map[*media.Media]*streamMedia)
for i, track := range s.rtspStream.Tracks() {
for _, media := range s.rtspStream.Medias() {
var err error
s.streamTracks[i], err = newStreamTrack(track, generateRTPPackets)
s.smedias[media], err = newStreamMedia(media, generateRTPPackets)
if err != nil {
return nil, err
}
@ -83,40 +36,29 @@ func newStream( @@ -83,40 +36,29 @@ func newStream(
}
func (s *stream) close() {
s.nonRTSPReaders.close()
s.rtspStream.Close()
}
func (s *stream) tracks() gortsplib.Tracks {
return s.rtspStream.Tracks()
func (s *stream) medias() media.Medias {
return s.rtspStream.Medias()
}
func (s *stream) readerAdd(r reader) {
if _, ok := r.(pathRTSPSession); !ok {
s.nonRTSPReaders.add(r)
}
func (s *stream) readerAdd(r reader, medi *media.Media, forma format.Format, cb func(data)) {
sm := s.smedias[medi]
sf := sm.formats[forma]
sf.readerAdd(r, cb)
}
func (s *stream) readerRemove(r reader) {
if _, ok := r.(pathRTSPSession); !ok {
s.nonRTSPReaders.remove(r)
for _, sm := range s.smedias {
for _, sf := range sm.formats {
sf.readerRemove(r)
}
}
}
func (s *stream) writeData(data data) error {
err := s.streamTracks[data.getTrackID()].onData(data, s.nonRTSPReaders.hasReaders())
if err != nil {
return err
}
// forward RTP packets to RTSP readers
for _, pkt := range data.getRTPPackets() {
atomic.AddUint64(s.bytesReceived, uint64(pkt.MarshalSize()))
s.rtspStream.WritePacketRTPWithNTP(data.getTrackID(), pkt, data.getNTP())
}
// forward data to non-RTSP readers
s.nonRTSPReaders.writeData(data)
return nil
func (s *stream) writeData(medi *media.Media, forma format.Format, data data) error {
sm := s.smedias[medi]
sf := sm.formats[forma]
return sf.writeData(s, medi, data)
}

66
internal/core/streamformat.go

@ -0,0 +1,66 @@ @@ -0,0 +1,66 @@
package core
import (
"sync"
"sync/atomic"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
)
type streamFormat struct {
proc formatProcessor
mutex sync.RWMutex
nonRTSPReaders map[reader]func(data)
}
func newStreamFormat(forma format.Format, generateRTPPackets bool) (*streamFormat, error) {
proc, err := newFormatProcessor(forma, generateRTPPackets)
if err != nil {
return nil, err
}
sf := &streamFormat{
proc: proc,
nonRTSPReaders: make(map[reader]func(data)),
}
return sf, nil
}
func (sf *streamFormat) readerAdd(r reader, cb func(data)) {
sf.mutex.Lock()
defer sf.mutex.Unlock()
sf.nonRTSPReaders[r] = cb
}
func (sf *streamFormat) readerRemove(r reader) {
sf.mutex.Lock()
defer sf.mutex.Unlock()
delete(sf.nonRTSPReaders, r)
}
func (sf *streamFormat) writeData(s *stream, medi *media.Media, data data) error {
sf.mutex.RLock()
defer sf.mutex.RUnlock()
hasNonRTSPReaders := len(sf.nonRTSPReaders) > 0
err := sf.proc.process(data, hasNonRTSPReaders)
if err != nil {
return err
}
// forward RTP packets to RTSP readers
for _, pkt := range data.getRTPPackets() {
atomic.AddUint64(s.bytesReceived, uint64(pkt.MarshalSize()))
s.rtspStream.WritePacketRTPWithNTP(medi, pkt, data.getNTP())
}
// forward data to non-RTSP readers
for _, cb := range sf.nonRTSPReaders {
cb(data)
}
return nil
}

26
internal/core/streammedia.go

@ -0,0 +1,26 @@ @@ -0,0 +1,26 @@
package core
import (
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/media"
)
type streamMedia struct {
formats map[format.Format]*streamFormat
}
func newStreamMedia(medi *media.Media, generateRTPPackets bool) (*streamMedia, error) {
sm := &streamMedia{
formats: make(map[format.Format]*streamFormat),
}
for _, forma := range medi.Formats {
var err error
sm.formats[forma], err = newStreamFormat(forma, generateRTPPackets)
if err != nil {
return nil, err
}
}
return sm, nil
}

27
internal/core/streamtrack.go

@ -1,27 +0,0 @@ @@ -1,27 +0,0 @@
package core
import (
"fmt"
"github.com/aler9/gortsplib"
)
type streamTrack interface {
onData(data, bool) error
}
func newStreamTrack(track gortsplib.Track, generateRTPPackets bool) (streamTrack, error) {
switch ttrack := track.(type) {
case *gortsplib.TrackH264:
return newStreamTrackH264(ttrack, generateRTPPackets), nil
case *gortsplib.TrackMPEG4Audio:
return newStreamTrackMPEG4Audio(ttrack, generateRTPPackets), nil
default:
if generateRTPPackets {
return nil, fmt.Errorf("we don't know how to generate RTP packets of track %+v", track)
}
return newStreamTrackGeneric(), nil
}
}

33
internal/core/streamtrack_generic.go

@ -1,33 +0,0 @@ @@ -1,33 +0,0 @@
package core
import (
"fmt"
)
const (
// 1500 (UDP MTU) - 20 (IP header) - 8 (UDP header)
maxPacketSize = 1472
)
type streamTrackGeneric struct{}
func newStreamTrackGeneric() *streamTrackGeneric {
return &streamTrackGeneric{}
}
func (t *streamTrackGeneric) onData(dat data, hasNonRTSPReaders bool) error {
tdata := dat.(*dataGeneric)
pkt := tdata.rtpPackets[0]
// remove padding
pkt.Header.Padding = false
pkt.PaddingSize = 0
if pkt.MarshalSize() > maxPacketSize {
return fmt.Errorf("payload size (%d) is greater than maximum allowed (%d)",
pkt.MarshalSize(), maxPacketSize)
}
return nil
}

6
internal/hls/client.go

@ -6,7 +6,7 @@ import ( @@ -6,7 +6,7 @@ import (
"net/url"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/rtsp-simple-server/internal/logger"
)
@ -35,7 +35,7 @@ type ClientLogger interface { @@ -35,7 +35,7 @@ type ClientLogger interface {
// Client is a HLS client.
type Client struct {
fingerprint string
onTracks func(*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio) error
onTracks func(*format.H264, *format.MPEG4Audio) error
onVideoData func(time.Duration, [][]byte)
onAudioData func(time.Duration, []byte)
logger ClientLogger
@ -52,7 +52,7 @@ type Client struct { @@ -52,7 +52,7 @@ type Client struct {
func NewClient(
playlistURLStr string,
fingerprint string,
onTracks func(*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio) error,
onTracks func(*format.H264, *format.MPEG4Audio) error,
onVideoData func(time.Duration, [][]byte),
onAudioData func(time.Duration, []byte),
logger ClientLogger,

34
internal/hls/client_downloader_primary.go

@ -12,7 +12,7 @@ import ( @@ -12,7 +12,7 @@ import (
"strings"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
gm3u8 "github.com/grafov/m3u8"
"github.com/aler9/rtsp-simple-server/internal/hls/m3u8"
@ -106,7 +106,7 @@ type clientTimeSync interface{} @@ -106,7 +106,7 @@ type clientTimeSync interface{}
type clientDownloaderPrimary struct {
primaryPlaylistURL *url.URL
logger ClientLogger
onTracks func(*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio) error
onTracks func(*format.H264, *format.MPEG4Audio) error
onVideoData func(time.Duration, [][]byte)
onAudioData func(time.Duration, []byte)
rp *clientRoutinePool
@ -115,7 +115,7 @@ type clientDownloaderPrimary struct { @@ -115,7 +115,7 @@ type clientDownloaderPrimary struct {
leadingTimeSync clientTimeSync
// in
streamTracks chan []gortsplib.Track
streamFormats chan []format.Format
// out
startStreaming chan struct{}
@ -127,7 +127,7 @@ func newClientDownloaderPrimary( @@ -127,7 +127,7 @@ func newClientDownloaderPrimary(
fingerprint string,
logger ClientLogger,
rp *clientRoutinePool,
onTracks func(*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio) error,
onTracks func(*format.H264, *format.MPEG4Audio) error,
onVideoData func(time.Duration, [][]byte),
onAudioData func(time.Duration, []byte),
) *clientDownloaderPrimary {
@ -163,7 +163,7 @@ func newClientDownloaderPrimary( @@ -163,7 +163,7 @@ func newClientDownloaderPrimary(
TLSClientConfig: tlsConfig,
},
},
streamTracks: make(chan []gortsplib.Track),
streamFormats: make(chan []format.Format),
startStreaming: make(chan struct{}),
leadingTimeSyncReady: make(chan struct{}),
}
@ -189,7 +189,7 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error { @@ -189,7 +189,7 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error {
plt,
d.logger,
d.rp,
d.onStreamTracks,
d.onStreamFormats,
d.onSetLeadingTimeSync,
d.onGetLeadingTimeSync,
d.onVideoData,
@ -215,7 +215,7 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error { @@ -215,7 +215,7 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error {
nil,
d.logger,
d.rp,
d.onStreamTracks,
d.onStreamFormats,
d.onSetLeadingTimeSync,
d.onGetLeadingTimeSync,
d.onVideoData,
@ -241,7 +241,7 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error { @@ -241,7 +241,7 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error {
nil,
d.logger,
d.rp,
d.onStreamTracks,
d.onStreamFormats,
d.onSetLeadingTimeSync,
d.onGetLeadingTimeSync,
d.onVideoData,
@ -254,29 +254,29 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error { @@ -254,29 +254,29 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error {
return fmt.Errorf("invalid playlist")
}
var tracks []gortsplib.Track
var tracks []format.Format
for i := 0; i < streamCount; i++ {
select {
case streamTracks := <-d.streamTracks:
tracks = append(tracks, streamTracks...)
case streamFormats := <-d.streamFormats:
tracks = append(tracks, streamFormats...)
case <-ctx.Done():
return fmt.Errorf("terminated")
}
}
var videoTrack *gortsplib.TrackH264
var audioTrack *gortsplib.TrackMPEG4Audio
var videoTrack *format.H264
var audioTrack *format.MPEG4Audio
for _, track := range tracks {
switch ttrack := track.(type) {
case *gortsplib.TrackH264:
case *format.H264:
if videoTrack != nil {
return fmt.Errorf("multiple video tracks are not supported")
}
videoTrack = ttrack
case *gortsplib.TrackMPEG4Audio:
case *format.MPEG4Audio:
if audioTrack != nil {
return fmt.Errorf("multiple audio tracks are not supported")
}
@ -294,9 +294,9 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error { @@ -294,9 +294,9 @@ func (d *clientDownloaderPrimary) run(ctx context.Context) error {
return nil
}
func (d *clientDownloaderPrimary) onStreamTracks(ctx context.Context, tracks []gortsplib.Track) bool {
func (d *clientDownloaderPrimary) onStreamFormats(ctx context.Context, tracks []format.Format) bool {
select {
case d.streamTracks <- tracks:
case d.streamFormats <- tracks:
case <-ctx.Done():
return false
}

12
internal/hls/client_downloader_stream.go

@ -9,7 +9,7 @@ import ( @@ -9,7 +9,7 @@ import (
"strconv"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
gm3u8 "github.com/grafov/m3u8"
"github.com/aler9/rtsp-simple-server/internal/hls/m3u8"
@ -50,7 +50,7 @@ type clientDownloaderStream struct { @@ -50,7 +50,7 @@ type clientDownloaderStream struct {
initialPlaylist *m3u8.MediaPlaylist
logger ClientLogger
rp *clientRoutinePool
onStreamTracks func(context.Context, []gortsplib.Track) bool
onStreamFormats func(context.Context, []format.Format) bool
onSetLeadingTimeSync func(clientTimeSync)
onGetLeadingTimeSync func(context.Context) (clientTimeSync, bool)
onVideoData func(time.Duration, [][]byte)
@ -66,7 +66,7 @@ func newClientDownloaderStream( @@ -66,7 +66,7 @@ func newClientDownloaderStream(
initialPlaylist *m3u8.MediaPlaylist,
logger ClientLogger,
rp *clientRoutinePool,
onStreamTracks func(context.Context, []gortsplib.Track) bool,
onStreamFormats func(context.Context, []format.Format) bool,
onSetLeadingTimeSync func(clientTimeSync),
onGetLeadingTimeSync func(context.Context) (clientTimeSync, bool),
onVideoData func(time.Duration, [][]byte),
@ -79,7 +79,7 @@ func newClientDownloaderStream( @@ -79,7 +79,7 @@ func newClientDownloaderStream(
initialPlaylist: initialPlaylist,
logger: logger,
rp: rp,
onStreamTracks: onStreamTracks,
onStreamFormats: onStreamFormats,
onSetLeadingTimeSync: onSetLeadingTimeSync,
onGetLeadingTimeSync: onGetLeadingTimeSync,
onVideoData: onVideoData,
@ -113,7 +113,7 @@ func (d *clientDownloaderStream) run(ctx context.Context) error { @@ -113,7 +113,7 @@ func (d *clientDownloaderStream) run(ctx context.Context) error {
segmentQueue,
d.logger,
d.rp,
d.onStreamTracks,
d.onStreamFormats,
d.onSetLeadingTimeSync,
d.onGetLeadingTimeSync,
d.onVideoData,
@ -130,7 +130,7 @@ func (d *clientDownloaderStream) run(ctx context.Context) error { @@ -130,7 +130,7 @@ func (d *clientDownloaderStream) run(ctx context.Context) error {
segmentQueue,
d.logger,
d.rp,
d.onStreamTracks,
d.onStreamFormats,
d.onSetLeadingTimeSync,
d.onGetLeadingTimeSync,
d.onVideoData,

20
internal/hls/client_processor_fmp4.go

@ -5,8 +5,8 @@ import ( @@ -5,8 +5,8 @@ import (
"fmt"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/rtsp-simple-server/internal/hls/fmp4"
)
@ -14,7 +14,7 @@ import ( @@ -14,7 +14,7 @@ import (
func fmp4PickLeadingTrack(init *fmp4.Init) int {
// pick first video track
for _, track := range init.Tracks {
if _, ok := track.Track.(*gortsplib.TrackH264); ok {
if _, ok := track.Format.(*format.H264); ok {
return track.ID
}
}
@ -48,7 +48,7 @@ func newClientProcessorFMP4( @@ -48,7 +48,7 @@ func newClientProcessorFMP4(
segmentQueue *clientSegmentQueue,
logger ClientLogger,
rp *clientRoutinePool,
onStreamTracks func(context.Context, []gortsplib.Track) bool,
onStreamFormats func(context.Context, []format.Format) bool,
onSetLeadingTimeSync func(clientTimeSync),
onGetLeadingTimeSync func(context.Context) (clientTimeSync, bool),
onVideoData func(time.Duration, [][]byte),
@ -73,12 +73,12 @@ func newClientProcessorFMP4( @@ -73,12 +73,12 @@ func newClientProcessorFMP4(
p.leadingTrackID = fmp4PickLeadingTrack(&p.init)
tracks := make([]gortsplib.Track, len(p.init.Tracks))
tracks := make([]format.Format, len(p.init.Tracks))
for i, track := range p.init.Tracks {
tracks[i] = track.Track
tracks[i] = track.Format
}
ok := onStreamTracks(ctx, tracks)
ok := onStreamFormats(ctx, tracks)
if !ok {
return nil, fmt.Errorf("terminated")
}
@ -186,8 +186,8 @@ func (p *clientProcessorFMP4) initializeTrackProcs(ts *clientTimeSyncFMP4) { @@ -186,8 +186,8 @@ func (p *clientProcessorFMP4) initializeTrackProcs(ts *clientTimeSyncFMP4) {
for _, track := range p.init.Tracks {
var cb func(time.Duration, []byte) error
switch track.Track.(type) {
case *gortsplib.TrackH264:
switch track.Format.(type) {
case *format.H264:
cb = func(pts time.Duration, payload []byte) error {
nalus, err := h264.AVCCUnmarshal(payload)
if err != nil {
@ -198,7 +198,7 @@ func (p *clientProcessorFMP4) initializeTrackProcs(ts *clientTimeSyncFMP4) { @@ -198,7 +198,7 @@ func (p *clientProcessorFMP4) initializeTrackProcs(ts *clientTimeSyncFMP4) {
return nil
}
case *gortsplib.TrackMPEG4Audio:
case *format.MPEG4Audio:
cb = func(pts time.Duration, payload []byte) error {
p.onAudioData(pts, payload)
return nil

26
internal/hls/client_processor_mpegts.go

@ -7,9 +7,9 @@ import ( @@ -7,9 +7,9 @@ import (
"strings"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/asticode/go-astits"
"github.com/aler9/rtsp-simple-server/internal/hls/mpegts"
@ -19,7 +19,7 @@ import ( @@ -19,7 +19,7 @@ import (
func mpegtsPickLeadingTrack(mpegtsTracks []*mpegts.Track) uint16 {
// pick first video track
for _, mt := range mpegtsTracks {
if _, ok := mt.Track.(*gortsplib.TrackH264); ok {
if _, ok := mt.Format.(*format.H264); ok {
return mt.ES.ElementaryPID
}
}
@ -33,7 +33,7 @@ type clientProcessorMPEGTS struct { @@ -33,7 +33,7 @@ type clientProcessorMPEGTS struct {
segmentQueue *clientSegmentQueue
logger ClientLogger
rp *clientRoutinePool
onStreamTracks func(context.Context, []gortsplib.Track) bool
onStreamFormats func(context.Context, []format.Format) bool
onSetLeadingTimeSync func(clientTimeSync)
onGetLeadingTimeSync func(context.Context) (clientTimeSync, bool)
onVideoData func(time.Duration, [][]byte)
@ -49,7 +49,7 @@ func newClientProcessorMPEGTS( @@ -49,7 +49,7 @@ func newClientProcessorMPEGTS(
segmentQueue *clientSegmentQueue,
logger ClientLogger,
rp *clientRoutinePool,
onStreamTracks func(context.Context, []gortsplib.Track) bool,
onStreamFormats func(context.Context, []format.Format) bool,
onSetLeadingTimeSync func(clientTimeSync),
onGetLeadingTimeSync func(context.Context) (clientTimeSync, bool),
onVideoData func(time.Duration, [][]byte),
@ -60,7 +60,7 @@ func newClientProcessorMPEGTS( @@ -60,7 +60,7 @@ func newClientProcessorMPEGTS(
segmentQueue: segmentQueue,
logger: logger,
rp: rp,
onStreamTracks: onStreamTracks,
onStreamFormats: onStreamFormats,
onSetLeadingTimeSync: onSetLeadingTimeSync,
onGetLeadingTimeSync: onGetLeadingTimeSync,
onVideoData: onVideoData,
@ -92,12 +92,12 @@ func (p *clientProcessorMPEGTS) processSegment(ctx context.Context, byts []byte) @@ -92,12 +92,12 @@ func (p *clientProcessorMPEGTS) processSegment(ctx context.Context, byts []byte)
p.leadingTrackPID = mpegtsPickLeadingTrack(p.mpegtsTracks)
tracks := make([]gortsplib.Track, len(p.mpegtsTracks))
tracks := make([]format.Format, len(p.mpegtsTracks))
for i, mt := range p.mpegtsTracks {
tracks[i] = mt.Track
tracks[i] = mt.Format
}
ok := p.onStreamTracks(ctx, tracks)
ok := p.onStreamFormats(ctx, tracks)
if !ok {
return fmt.Errorf("terminated")
}
@ -177,8 +177,8 @@ func (p *clientProcessorMPEGTS) initializeTrackProcs(ts *clientTimeSyncMPEGTS) { @@ -177,8 +177,8 @@ func (p *clientProcessorMPEGTS) initializeTrackProcs(ts *clientTimeSyncMPEGTS) {
for _, mt := range p.mpegtsTracks {
var cb func(time.Duration, []byte) error
switch mt.Track.(type) {
case *gortsplib.TrackH264:
switch mt.Format.(type) {
case *format.H264:
cb = func(pts time.Duration, payload []byte) error {
nalus, err := h264.AnnexBUnmarshal(payload)
if err != nil {
@ -190,7 +190,7 @@ func (p *clientProcessorMPEGTS) initializeTrackProcs(ts *clientTimeSyncMPEGTS) { @@ -190,7 +190,7 @@ func (p *clientProcessorMPEGTS) initializeTrackProcs(ts *clientTimeSyncMPEGTS) {
return nil
}
case *gortsplib.TrackMPEG4Audio:
case *format.MPEG4Audio:
cb = func(pts time.Duration, payload []byte) error {
var adtsPkts mpeg4audio.ADTSPackets
err := adtsPkts.Unmarshal(payload)

6
internal/hls/client_test.go

@ -11,8 +11,8 @@ import ( @@ -11,8 +11,8 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/asticode/go-astits"
"github.com/gin-gonic/gin"
"github.com/stretchr/testify/require"
@ -216,7 +216,7 @@ func TestClient(t *testing.T) { @@ -216,7 +216,7 @@ func TestClient(t *testing.T) {
c, err := NewClient(
prefix+"://localhost:5780/stream.m3u8",
"33949E05FFFB5FF3E8AA16F8213A6251B4D9363804BA53233C4DA9A46D6F2739",
func(*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio) error {
func(*format.H264, *format.MPEG4Audio) error {
return nil
},
func(pts time.Duration, nalus [][]byte) {

12
internal/hls/fmp4/init.go

@ -5,8 +5,8 @@ import ( @@ -5,8 +5,8 @@ import (
"fmt"
gomp4 "github.com/abema/go-mp4"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
)
// Init is a FMP4 initialization file.
@ -105,8 +105,8 @@ func (i *Init) Unmarshal(byts []byte) error { @@ -105,8 +105,8 @@ func (i *Init) Unmarshal(byts []byte) error {
pps = conf.PictureParameterSets[0].NALUnit
}
curTrack.Track = &gortsplib.TrackH264{
PayloadType: 96,
curTrack.Format = &format.H264{
PayloadTyp: 96,
SPS: sps,
PPS: pps,
PacketizationMode: 1,
@ -149,8 +149,8 @@ func (i *Init) Unmarshal(byts []byte) error { @@ -149,8 +149,8 @@ func (i *Init) Unmarshal(byts []byte) error {
return nil, fmt.Errorf("invalid MPEG4-audio configuration: %s", err)
}
curTrack.Track = &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
curTrack.Format = &format.MPEG4Audio{
PayloadTyp: 96,
Config: &c,
SizeLength: 13,
IndexLength: 3,

28
internal/hls/fmp4/init_test.go

@ -4,8 +4,8 @@ package fmp4 @@ -4,8 +4,8 @@ package fmp4
import (
"testing"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/stretchr/testify/require"
)
@ -16,15 +16,15 @@ var testSPS = []byte{ @@ -16,15 +16,15 @@ var testSPS = []byte{
0x20,
}
var testVideoTrack = &gortsplib.TrackH264{
PayloadType: 96,
var testVideoTrack = &format.H264{
PayloadTyp: 96,
SPS: testSPS,
PPS: []byte{0x08},
PacketizationMode: 1,
}
var testAudioTrack = &gortsplib.TrackMPEG4Audio{
PayloadType: 97,
var testAudioTrack = &format.MPEG4Audio{
PayloadTyp: 97,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
@ -42,12 +42,12 @@ func TestInitMarshal(t *testing.T) { @@ -42,12 +42,12 @@ func TestInitMarshal(t *testing.T) {
{
ID: 1,
TimeScale: 90000,
Track: testVideoTrack,
Format: testVideoTrack,
},
{
ID: 2,
TimeScale: uint32(testAudioTrack.ClockRate()),
Track: testAudioTrack,
Format: testAudioTrack,
},
},
}
@ -223,7 +223,7 @@ func TestInitMarshal(t *testing.T) { @@ -223,7 +223,7 @@ func TestInitMarshal(t *testing.T) {
{
ID: 1,
TimeScale: 90000,
Track: testVideoTrack,
Format: testVideoTrack,
},
},
}
@ -337,7 +337,7 @@ func TestInitMarshal(t *testing.T) { @@ -337,7 +337,7 @@ func TestInitMarshal(t *testing.T) {
{
ID: 1,
TimeScale: uint32(testAudioTrack.ClockRate()),
Track: testAudioTrack,
Format: testAudioTrack,
},
},
}
@ -557,8 +557,8 @@ func TestInitUnmarshal(t *testing.T) { @@ -557,8 +557,8 @@ func TestInitUnmarshal(t *testing.T) {
{
ID: 256,
TimeScale: 10000000,
Track: &gortsplib.TrackH264{
PayloadType: 96,
Format: &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x42, 0xc0, 0x1f, 0xd9, 0x00, 0xf0, 0x11,
0x7e, 0xf0, 0x11, 0x00, 0x00, 0x03, 0x00, 0x01,
@ -673,8 +673,8 @@ func TestInitUnmarshal(t *testing.T) { @@ -673,8 +673,8 @@ func TestInitUnmarshal(t *testing.T) {
{
ID: 257,
TimeScale: 10000000,
Track: &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
Format: &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: mpeg4audio.ObjectTypeAACLC,
SampleRate: 48000,

31
internal/hls/fmp4/init_track.go

@ -2,16 +2,15 @@ package fmp4 @@ -2,16 +2,15 @@ package fmp4
import (
gomp4 "github.com/abema/go-mp4"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
)
// InitTrack is a track of Init.
type InitTrack struct {
ID int
TimeScale uint32
Track gortsplib.Track
Format format.Format
}
func (track *InitTrack) marshal(w *mp4Writer) error {
@ -53,8 +52,8 @@ func (track *InitTrack) marshal(w *mp4Writer) error { @@ -53,8 +52,8 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
var width int
var height int
switch ttrack := track.Track.(type) {
case *gortsplib.TrackH264:
switch ttrack := track.Format.(type) {
case *format.H264:
sps = ttrack.SafeSPS()
pps = ttrack.SafePPS()
@ -79,7 +78,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error { @@ -79,7 +78,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
case *gortsplib.TrackMPEG4Audio:
case *format.MPEG4Audio:
_, err = w.WriteBox(&gomp4.Tkhd{ // <tkhd/>
FullBox: gomp4.FullBox{
Flags: [3]byte{0, 0, 3},
@ -107,8 +106,8 @@ func (track *InitTrack) marshal(w *mp4Writer) error { @@ -107,8 +106,8 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
switch track.Track.(type) {
case *gortsplib.TrackH264:
switch track.Format.(type) {
case *format.H264:
_, err = w.WriteBox(&gomp4.Hdlr{ // <hdlr/>
HandlerType: [4]byte{'v', 'i', 'd', 'e'},
Name: "VideoHandler",
@ -117,7 +116,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error { @@ -117,7 +116,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
case *gortsplib.TrackMPEG4Audio:
case *format.MPEG4Audio:
_, err = w.WriteBox(&gomp4.Hdlr{ // <hdlr/>
HandlerType: [4]byte{'s', 'o', 'u', 'n'},
Name: "SoundHandler",
@ -132,8 +131,8 @@ func (track *InitTrack) marshal(w *mp4Writer) error { @@ -132,8 +131,8 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
switch track.Track.(type) {
case *gortsplib.TrackH264:
switch track.Format.(type) {
case *format.H264:
_, err = w.WriteBox(&gomp4.Vmhd{ // <vmhd/>
FullBox: gomp4.FullBox{
Flags: [3]byte{0, 0, 1},
@ -143,7 +142,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error { @@ -143,7 +142,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
case *gortsplib.TrackMPEG4Audio:
case *format.MPEG4Audio:
_, err = w.WriteBox(&gomp4.Smhd{ // <smhd/>
})
if err != nil {
@ -194,8 +193,8 @@ func (track *InitTrack) marshal(w *mp4Writer) error { @@ -194,8 +193,8 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
switch ttrack := track.Track.(type) {
case *gortsplib.TrackH264:
switch ttrack := track.Format.(type) {
case *format.H264:
_, err = w.writeBoxStart(&gomp4.VisualSampleEntry{ // <avc1>
SampleEntry: gomp4.SampleEntry{
AnyTypeBox: gomp4.AnyTypeBox{
@ -256,7 +255,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error { @@ -256,7 +255,7 @@ func (track *InitTrack) marshal(w *mp4Writer) error {
return err
}
case *gortsplib.TrackMPEG4Audio:
case *format.MPEG4Audio:
_, err = w.writeBoxStart(&gomp4.AudioSampleEntry{ // <mp4a>
SampleEntry: gomp4.SampleEntry{
AnyTypeBox: gomp4.AnyTypeBox{

16
internal/hls/mpegts/tracks.go

@ -5,8 +5,8 @@ import ( @@ -5,8 +5,8 @@ import (
"context"
"fmt"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/asticode/go-astits"
)
@ -38,8 +38,8 @@ func findMPEG4AudioConfig(dem *astits.Demuxer, pid uint16) (*mpeg4audio.Config, @@ -38,8 +38,8 @@ func findMPEG4AudioConfig(dem *astits.Demuxer, pid uint16) (*mpeg4audio.Config,
// Track is a MPEG-TS track.
type Track struct {
ES *astits.PMTElementaryStream
Track gortsplib.Track
ES *astits.PMTElementaryStream
Format format.Format
}
// FindTracks finds the tracks in a MPEG-TS stream.
@ -77,8 +77,8 @@ func FindTracks(byts []byte) ([]*Track, error) { @@ -77,8 +77,8 @@ func FindTracks(byts []byte) ([]*Track, error) {
for _, t := range tracks {
switch t.ES.StreamType {
case astits.StreamTypeH264Video:
t.Track = &gortsplib.TrackH264{
PayloadType: 96,
t.Format = &format.H264{
PayloadTyp: 96,
PacketizationMode: 1,
}
@ -88,8 +88,8 @@ func FindTracks(byts []byte) ([]*Track, error) { @@ -88,8 +88,8 @@ func FindTracks(byts []byte) ([]*Track, error) {
return nil, err
}
t.Track = &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
t.Format = &format.MPEG4Audio{
PayloadTyp: 96,
Config: conf,
SizeLength: 13,
IndexLength: 3,

34
internal/hls/mpegts/writer.go

@ -6,9 +6,9 @@ import ( @@ -6,9 +6,9 @@ import (
"context"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/asticode/go-astits"
)
@ -24,8 +24,8 @@ func (f writerFunc) Write(p []byte) (int, error) { @@ -24,8 +24,8 @@ func (f writerFunc) Write(p []byte) (int, error) {
// Writer is a MPEG-TS writer.
type Writer struct {
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoFormat *format.H264
audioFormat *format.MPEG4Audio
buf *bytes.Buffer
inner *astits.Muxer
@ -34,13 +34,13 @@ type Writer struct { @@ -34,13 +34,13 @@ type Writer struct {
// NewWriter allocates a Writer.
func NewWriter(
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoFormat *format.H264,
audioFormat *format.MPEG4Audio,
) *Writer {
w := &Writer{
videoTrack: videoTrack,
audioTrack: audioTrack,
buf: bytes.NewBuffer(nil),
videoFormat: videoFormat,
audioFormat: audioFormat,
buf: bytes.NewBuffer(nil),
}
w.inner = astits.NewMuxer(
@ -49,21 +49,21 @@ func NewWriter( @@ -49,21 +49,21 @@ func NewWriter(
return w.buf.Write(p)
}))
if videoTrack != nil {
if videoFormat != nil {
w.inner.AddElementaryStream(astits.PMTElementaryStream{
ElementaryPID: 256,
StreamType: astits.StreamTypeH264Video,
})
}
if audioTrack != nil {
if audioFormat != nil {
w.inner.AddElementaryStream(astits.PMTElementaryStream{
ElementaryPID: 257,
StreamType: astits.StreamTypeAACAudio,
})
}
if videoTrack != nil {
if videoFormat != nil {
w.inner.SetPCRPID(256)
} else {
w.inner.SetPCRPID(257)
@ -155,9 +155,9 @@ func (w *Writer) WriteAAC( @@ -155,9 +155,9 @@ func (w *Writer) WriteAAC(
) error {
pkts := mpeg4audio.ADTSPackets{
{
Type: w.audioTrack.Config.Type,
SampleRate: w.audioTrack.Config.SampleRate,
ChannelCount: w.audioTrack.Config.ChannelCount,
Type: w.audioFormat.Config.Type,
SampleRate: w.audioFormat.Config.SampleRate,
ChannelCount: w.audioFormat.Config.ChannelCount,
AU: au,
},
}
@ -171,7 +171,7 @@ func (w *Writer) WriteAAC( @@ -171,7 +171,7 @@ func (w *Writer) WriteAAC(
RandomAccessIndicator: true,
}
if w.videoTrack == nil {
if w.videoFormat == nil {
// send PCR once in a while
if w.pcrCounter == 0 {
af.HasPCR = true

14
internal/hls/mpegts/writer_test.go

@ -6,9 +6,9 @@ import ( @@ -6,9 +6,9 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/asticode/go-astits"
"github.com/stretchr/testify/require"
)
@ -21,15 +21,15 @@ func TestWriter(t *testing.T) { @@ -21,15 +21,15 @@ func TestWriter(t *testing.T) {
0x20,
}
testVideoTrack := &gortsplib.TrackH264{
PayloadType: 96,
testVideoTrack := &format.H264{
PayloadTyp: 96,
SPS: testSPS,
PPS: []byte{0x08},
PacketizationMode: 1,
}
testAudioTrack := &gortsplib.TrackMPEG4Audio{
PayloadType: 97,
testAudioTrack := &format.MPEG4Audio{
PayloadTyp: 97,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,

6
internal/hls/muxer.go

@ -5,7 +5,7 @@ import ( @@ -5,7 +5,7 @@ import (
"io"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
)
// MuxerFileResponse is a response of the Muxer's File() func.
@ -28,8 +28,8 @@ func NewMuxer( @@ -28,8 +28,8 @@ func NewMuxer(
segmentDuration time.Duration,
partDuration time.Duration,
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
) (*Muxer, error) {
m := &Muxer{}

10
internal/hls/muxer_primary_playlist.go

@ -8,19 +8,19 @@ import ( @@ -8,19 +8,19 @@ import (
"strconv"
"strings"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
)
type muxerPrimaryPlaylist struct {
fmp4 bool
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoTrack *format.H264
audioTrack *format.MPEG4Audio
}
func newMuxerPrimaryPlaylist(
fmp4 bool,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
) *muxerPrimaryPlaylist {
return &muxerPrimaryPlaylist{
fmp4: fmp4,

32
internal/hls/muxer_test.go

@ -6,8 +6,8 @@ import ( @@ -6,8 +6,8 @@ import (
"testing"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/stretchr/testify/require"
)
@ -22,15 +22,15 @@ var testSPS = []byte{ @@ -22,15 +22,15 @@ var testSPS = []byte{
}
func TestMuxerVideoAudio(t *testing.T) {
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: testSPS,
PPS: []byte{0x08},
PacketizationMode: 1,
}
audioTrack := &gortsplib.TrackMPEG4Audio{
PayloadType: 97,
audioTrack := &format.MPEG4Audio{
PayloadTyp: 97,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
@ -180,8 +180,8 @@ func TestMuxerVideoAudio(t *testing.T) { @@ -180,8 +180,8 @@ func TestMuxerVideoAudio(t *testing.T) {
}
func TestMuxerVideoOnly(t *testing.T) {
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: testSPS,
PPS: []byte{0x08},
PacketizationMode: 1,
@ -293,8 +293,8 @@ func TestMuxerVideoOnly(t *testing.T) { @@ -293,8 +293,8 @@ func TestMuxerVideoOnly(t *testing.T) {
}
func TestMuxerAudioOnly(t *testing.T) {
audioTrack := &gortsplib.TrackMPEG4Audio{
PayloadType: 97,
audioTrack := &format.MPEG4Audio{
PayloadTyp: 97,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
@ -405,8 +405,8 @@ func TestMuxerAudioOnly(t *testing.T) { @@ -405,8 +405,8 @@ func TestMuxerAudioOnly(t *testing.T) {
}
func TestMuxerCloseBeforeFirstSegmentReader(t *testing.T) {
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: testSPS,
PPS: []byte{0x08},
PacketizationMode: 1,
@ -430,8 +430,8 @@ func TestMuxerCloseBeforeFirstSegmentReader(t *testing.T) { @@ -430,8 +430,8 @@ func TestMuxerCloseBeforeFirstSegmentReader(t *testing.T) {
}
func TestMuxerMaxSegmentSize(t *testing.T) {
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: testSPS,
PPS: []byte{0x08},
PacketizationMode: 1,
@ -449,8 +449,8 @@ func TestMuxerMaxSegmentSize(t *testing.T) { @@ -449,8 +449,8 @@ func TestMuxerMaxSegmentSize(t *testing.T) {
}
func TestMuxerDoubleRead(t *testing.T) {
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: testSPS,
PPS: []byte{0x08},
PacketizationMode: 1,

14
internal/hls/muxer_variant_fmp4.go

@ -6,7 +6,7 @@ import ( @@ -6,7 +6,7 @@ import (
"sync"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/rtsp-simple-server/internal/hls/fmp4"
)
@ -14,8 +14,8 @@ import ( @@ -14,8 +14,8 @@ import (
type muxerVariantFMP4 struct {
playlist *muxerVariantFMP4Playlist
segmenter *muxerVariantFMP4Segmenter
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoTrack *format.H264
audioTrack *format.MPEG4Audio
mutex sync.Mutex
videoLastSPS []byte
@ -29,8 +29,8 @@ func newMuxerVariantFMP4( @@ -29,8 +29,8 @@ func newMuxerVariantFMP4(
segmentDuration time.Duration,
partDuration time.Duration,
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
) *muxerVariantFMP4 {
v := &muxerVariantFMP4{
videoTrack: videoTrack,
@ -92,7 +92,7 @@ func (v *muxerVariantFMP4) file(name string, msn string, part string, skip strin @@ -92,7 +92,7 @@ func (v *muxerVariantFMP4) file(name string, msn string, part string, skip strin
init.Tracks = append(init.Tracks, &fmp4.InitTrack{
ID: trackID,
TimeScale: 90000,
Track: v.videoTrack,
Format: v.videoTrack,
})
trackID++
}
@ -101,7 +101,7 @@ func (v *muxerVariantFMP4) file(name string, msn string, part string, skip strin @@ -101,7 +101,7 @@ func (v *muxerVariantFMP4) file(name string, msn string, part string, skip strin
init.Tracks = append(init.Tracks, &fmp4.InitTrack{
ID: trackID,
TimeScale: uint32(v.audioTrack.ClockRate()),
Track: v.audioTrack,
Format: v.audioTrack,
})
}

12
internal/hls/muxer_variant_fmp4_part.go

@ -6,8 +6,8 @@ import ( @@ -6,8 +6,8 @@ import (
"strconv"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/aler9/rtsp-simple-server/internal/hls/fmp4"
)
@ -17,8 +17,8 @@ func fmp4PartName(id uint64) string { @@ -17,8 +17,8 @@ func fmp4PartName(id uint64) string {
}
type muxerVariantFMP4Part struct {
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoTrack *format.H264
audioTrack *format.MPEG4Audio
id uint64
isIndependent bool
@ -33,8 +33,8 @@ type muxerVariantFMP4Part struct { @@ -33,8 +33,8 @@ type muxerVariantFMP4Part struct {
}
func newMuxerVariantFMP4Part(
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
id uint64,
) *muxerVariantFMP4Part {
p := &muxerVariantFMP4Part{

10
internal/hls/muxer_variant_fmp4_playlist.go

@ -10,7 +10,7 @@ import ( @@ -10,7 +10,7 @@ import (
"sync"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
)
type muxerVariantFMP4SegmentOrGap interface {
@ -70,8 +70,8 @@ func partTargetDuration( @@ -70,8 +70,8 @@ func partTargetDuration(
type muxerVariantFMP4Playlist struct {
lowLatency bool
segmentCount int
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoTrack *format.H264
audioTrack *format.MPEG4Audio
mutex sync.Mutex
cond *sync.Cond
@ -89,8 +89,8 @@ type muxerVariantFMP4Playlist struct { @@ -89,8 +89,8 @@ type muxerVariantFMP4Playlist struct {
func newMuxerVariantFMP4Playlist(
lowLatency bool,
segmentCount int,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
) *muxerVariantFMP4Playlist {
p := &muxerVariantFMP4Playlist{
lowLatency: lowLatency,

10
internal/hls/muxer_variant_fmp4_segment.go

@ -6,7 +6,7 @@ import ( @@ -6,7 +6,7 @@ import (
"strconv"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
)
type partsReader struct {
@ -45,8 +45,8 @@ type muxerVariantFMP4Segment struct { @@ -45,8 +45,8 @@ type muxerVariantFMP4Segment struct {
startTime time.Time
startDTS time.Duration
segmentMaxSize uint64
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoTrack *format.H264
audioTrack *format.MPEG4Audio
genPartID func() uint64
onPartFinalized func(*muxerVariantFMP4Part)
@ -63,8 +63,8 @@ func newMuxerVariantFMP4Segment( @@ -63,8 +63,8 @@ func newMuxerVariantFMP4Segment(
startTime time.Time,
startDTS time.Duration,
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
genPartID func() uint64,
onPartFinalized func(*muxerVariantFMP4Part),
) *muxerVariantFMP4Segment {

12
internal/hls/muxer_variant_fmp4_segmenter.go

@ -4,8 +4,8 @@ import ( @@ -4,8 +4,8 @@ import (
"bytes"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/rtsp-simple-server/internal/hls/fmp4"
)
@ -62,8 +62,8 @@ type muxerVariantFMP4Segmenter struct { @@ -62,8 +62,8 @@ type muxerVariantFMP4Segmenter struct {
segmentDuration time.Duration
partDuration time.Duration
segmentMaxSize uint64
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoTrack *format.H264
audioTrack *format.MPEG4Audio
onSegmentFinalized func(*muxerVariantFMP4Segment)
onPartFinalized func(*muxerVariantFMP4Part)
@ -87,8 +87,8 @@ func newMuxerVariantFMP4Segmenter( @@ -87,8 +87,8 @@ func newMuxerVariantFMP4Segmenter(
segmentDuration time.Duration,
partDuration time.Duration,
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
onSegmentFinalized func(*muxerVariantFMP4Segment),
onPartFinalized func(*muxerVariantFMP4Part),
) *muxerVariantFMP4Segmenter {

6
internal/hls/muxer_variant_mpegts.go

@ -3,7 +3,7 @@ package hls @@ -3,7 +3,7 @@ package hls
import (
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
)
type muxerVariantMPEGTS struct {
@ -15,8 +15,8 @@ func newMuxerVariantMPEGTS( @@ -15,8 +15,8 @@ func newMuxerVariantMPEGTS(
segmentCount int,
segmentDuration time.Duration,
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
) *muxerVariantMPEGTS {
v := &muxerVariantMPEGTS{}

10
internal/hls/muxer_variant_mpegts_segment.go

@ -7,15 +7,15 @@ import ( @@ -7,15 +7,15 @@ import (
"strconv"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/rtsp-simple-server/internal/hls/mpegts"
)
type muxerVariantMPEGTSSegment struct {
segmentMaxSize uint64
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoTrack *format.H264
audioTrack *format.MPEG4Audio
writer *mpegts.Writer
size uint64
@ -31,8 +31,8 @@ func newMuxerVariantMPEGTSSegment( @@ -31,8 +31,8 @@ func newMuxerVariantMPEGTSSegment(
id uint64,
startTime time.Time,
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
writer *mpegts.Writer,
) *muxerVariantMPEGTSSegment {
t := &muxerVariantMPEGTSSegment{

12
internal/hls/muxer_variant_mpegts_segmenter.go

@ -3,8 +3,8 @@ package hls @@ -3,8 +3,8 @@ package hls
import (
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/h264"
"github.com/aler9/rtsp-simple-server/internal/hls/mpegts"
)
@ -16,8 +16,8 @@ const ( @@ -16,8 +16,8 @@ const (
type muxerVariantMPEGTSSegmenter struct {
segmentDuration time.Duration
segmentMaxSize uint64
videoTrack *gortsplib.TrackH264
audioTrack *gortsplib.TrackMPEG4Audio
videoTrack *format.H264
audioTrack *format.MPEG4Audio
onSegmentReady func(*muxerVariantMPEGTSSegment)
writer *mpegts.Writer
@ -31,8 +31,8 @@ type muxerVariantMPEGTSSegmenter struct { @@ -31,8 +31,8 @@ type muxerVariantMPEGTSSegmenter struct {
func newMuxerVariantMPEGTSSegmenter(
segmentDuration time.Duration,
segmentMaxSize uint64,
videoTrack *gortsplib.TrackH264,
audioTrack *gortsplib.TrackMPEG4Audio,
videoTrack *format.H264,
audioTrack *format.MPEG4Audio,
onSegmentReady func(*muxerVariantMPEGTSSegment),
) *muxerVariantMPEGTSSegmenter {
m := &muxerVariantMPEGTSSegmenter{

2
internal/rpicamera/rpicamera.go

@ -13,7 +13,7 @@ import ( @@ -13,7 +13,7 @@ import (
"strings"
"time"
"github.com/aler9/gortsplib/pkg/h264"
"github.com/aler9/gortsplib/v2/pkg/h264"
)
//go:embed exe/exe

32
internal/rtmp/conn.go

@ -9,8 +9,8 @@ import ( @@ -9,8 +9,8 @@ import (
"strings"
"time"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/aler9/rtsp-simple-server/internal/rtmp/bytecounter"
@ -580,30 +580,30 @@ func (c *Conn) WriteMessage(msg message.Message) error { @@ -580,30 +580,30 @@ func (c *Conn) WriteMessage(msg message.Message) error {
return c.mrw.Write(msg)
}
func trackFromH264DecoderConfig(data []byte) (*gortsplib.TrackH264, error) {
func trackFromH264DecoderConfig(data []byte) (*format.H264, error) {
var conf h264conf.Conf
err := conf.Unmarshal(data)
if err != nil {
return nil, fmt.Errorf("unable to parse H264 config: %v", err)
}
return &gortsplib.TrackH264{
PayloadType: 96,
return &format.H264{
PayloadTyp: 96,
SPS: conf.SPS,
PPS: conf.PPS,
PacketizationMode: 1,
}, nil
}
func trackFromAACDecoderConfig(data []byte) (*gortsplib.TrackMPEG4Audio, error) {
func trackFromAACDecoderConfig(data []byte) (*format.MPEG4Audio, error) {
var mpegConf mpeg4audio.Config
err := mpegConf.Unmarshal(data)
if err != nil {
return nil, err
}
return &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
return &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpegConf,
SizeLength: 13,
IndexLength: 3,
@ -613,7 +613,7 @@ func trackFromAACDecoderConfig(data []byte) (*gortsplib.TrackMPEG4Audio, error) @@ -613,7 +613,7 @@ func trackFromAACDecoderConfig(data []byte) (*gortsplib.TrackMPEG4Audio, error)
var errEmptyMetadata = errors.New("metadata is empty")
func (c *Conn) readTracksFromMetadata(payload []interface{}) (*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio, error) {
func (c *Conn) readTracksFromMetadata(payload []interface{}) (*format.H264, *format.MPEG4Audio, error) {
if len(payload) != 1 {
return nil, nil, fmt.Errorf("invalid metadata")
}
@ -683,8 +683,8 @@ func (c *Conn) readTracksFromMetadata(payload []interface{}) (*gortsplib.TrackH2 @@ -683,8 +683,8 @@ func (c *Conn) readTracksFromMetadata(payload []interface{}) (*gortsplib.TrackH2
return nil, nil, errEmptyMetadata
}
var videoTrack *gortsplib.TrackH264
var audioTrack *gortsplib.TrackMPEG4Audio
var videoTrack *format.H264
var audioTrack *format.MPEG4Audio
for {
msg, err := c.ReadMessage()
@ -733,10 +733,10 @@ func (c *Conn) readTracksFromMetadata(payload []interface{}) (*gortsplib.TrackH2 @@ -733,10 +733,10 @@ func (c *Conn) readTracksFromMetadata(payload []interface{}) (*gortsplib.TrackH2
}
}
func (c *Conn) readTracksFromMessages(msg message.Message) (*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio, error) {
func (c *Conn) readTracksFromMessages(msg message.Message) (*format.H264, *format.MPEG4Audio, error) {
var startTime *time.Duration
var videoTrack *gortsplib.TrackH264
var audioTrack *gortsplib.TrackMPEG4Audio
var videoTrack *format.H264
var audioTrack *format.MPEG4Audio
// analyze 1 second of packets
outer:
@ -808,7 +808,7 @@ outer: @@ -808,7 +808,7 @@ outer:
}
// ReadTracks reads track informations.
func (c *Conn) ReadTracks() (*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio, error) {
func (c *Conn) ReadTracks() (*format.H264, *format.MPEG4Audio, error) {
msg, err := func() (message.Message, error) {
for {
msg, err := c.ReadMessage()
@ -867,7 +867,7 @@ func (c *Conn) ReadTracks() (*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio, e @@ -867,7 +867,7 @@ func (c *Conn) ReadTracks() (*gortsplib.TrackH264, *gortsplib.TrackMPEG4Audio, e
}
// WriteTracks writes track informations.
func (c *Conn) WriteTracks(videoTrack *gortsplib.TrackH264, audioTrack *gortsplib.TrackMPEG4Audio) error {
func (c *Conn) WriteTracks(videoTrack *format.H264, audioTrack *format.MPEG4Audio) error {
err := c.WriteMessage(&message.MsgDataAMF0{
ChunkStreamID: 4,
MessageStreamID: 0x1000000,

40
internal/rtmp/conn_test.go

@ -6,8 +6,8 @@ import ( @@ -6,8 +6,8 @@ import (
"net/url"
"testing"
"github.com/aler9/gortsplib"
"github.com/aler9/gortsplib/pkg/mpeg4audio"
"github.com/aler9/gortsplib/v2/pkg/format"
"github.com/aler9/gortsplib/v2/pkg/mpeg4audio"
"github.com/notedit/rtmp/format/flv/flvio"
"github.com/stretchr/testify/require"
@ -498,15 +498,15 @@ func TestReadTracks(t *testing.T) { @@ -498,15 +498,15 @@ func TestReadTracks(t *testing.T) {
switch ca {
case "video+audio":
require.Equal(t, &gortsplib.TrackH264{
PayloadType: 96,
require.Equal(t, &format.H264{
PayloadTyp: 96,
SPS: sps,
PPS: pps,
PacketizationMode: 1,
}, videoTrack)
require.Equal(t, &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
require.Equal(t, &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
@ -518,8 +518,8 @@ func TestReadTracks(t *testing.T) { @@ -518,8 +518,8 @@ func TestReadTracks(t *testing.T) {
}, audioTrack)
case "video":
require.Equal(t, &gortsplib.TrackH264{
PayloadType: 96,
require.Equal(t, &format.H264{
PayloadTyp: 96,
SPS: sps,
PPS: pps,
PacketizationMode: 1,
@ -528,15 +528,15 @@ func TestReadTracks(t *testing.T) { @@ -528,15 +528,15 @@ func TestReadTracks(t *testing.T) {
require.Nil(t, audioTrack)
case "metadata without codec id":
require.Equal(t, &gortsplib.TrackH264{
PayloadType: 96,
require.Equal(t, &format.H264{
PayloadTyp: 96,
SPS: sps,
PPS: pps,
PacketizationMode: 1,
}, videoTrack)
require.Equal(t, &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
require.Equal(t, &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
@ -548,15 +548,15 @@ func TestReadTracks(t *testing.T) { @@ -548,15 +548,15 @@ func TestReadTracks(t *testing.T) {
}, audioTrack)
case "missing metadata":
require.Equal(t, &gortsplib.TrackH264{
PayloadType: 96,
require.Equal(t, &format.H264{
PayloadTyp: 96,
SPS: sps,
PPS: pps,
PacketizationMode: 1,
}, videoTrack)
require.Equal(t, &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
require.Equal(t, &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,
@ -930,8 +930,8 @@ func TestWriteTracks(t *testing.T) { @@ -930,8 +930,8 @@ func TestWriteTracks(t *testing.T) {
_, _, err = rconn.InitializeServer()
require.NoError(t, err)
videoTrack := &gortsplib.TrackH264{
PayloadType: 96,
videoTrack := &format.H264{
PayloadTyp: 96,
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
@ -943,8 +943,8 @@ func TestWriteTracks(t *testing.T) { @@ -943,8 +943,8 @@ func TestWriteTracks(t *testing.T) {
PacketizationMode: 1,
}
audioTrack := &gortsplib.TrackMPEG4Audio{
PayloadType: 96,
audioTrack := &format.MPEG4Audio{
PayloadTyp: 96,
Config: &mpeg4audio.Config{
Type: 2,
SampleRate: 44100,

Loading…
Cancel
Save