change way tracks are initialized

A config object is now required to initialize tracks.
Config objects are provided for H264 and AAC.
This allows to pass parameters easily and treat AAC
parameters explicitly.
This commit is contained in:
aler9
2021-08-25 19:16:51 +02:00
parent accfc7cd5d
commit d744a2e0d3
13 changed files with 283 additions and 165 deletions

View File

@@ -171,7 +171,7 @@ func TestClientPublishSerial(t *testing.T) {
}(),
}
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
conn, err := c.DialPublish("rtsp://localhost:8554/teststream",
@@ -313,7 +313,7 @@ func TestClientPublishParallel(t *testing.T) {
}(),
}
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
writerDone := make(chan struct{})
@@ -471,7 +471,7 @@ func TestClientPublishPauseSerial(t *testing.T) {
}(),
}
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
conn, err := c.DialPublish("rtsp://localhost:8554/teststream",
@@ -609,7 +609,7 @@ func TestClientPublishPauseParallel(t *testing.T) {
}(),
}
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
conn, err := c.DialPublish("rtsp://localhost:8554/teststream",
@@ -747,7 +747,7 @@ func TestClientPublishAutomaticProtocol(t *testing.T) {
require.NoError(t, err)
}()
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
conn, err := DialPublish("rtsp://localhost:8554/teststream",
@@ -888,7 +888,7 @@ func TestClientPublishRTCPReport(t *testing.T) {
senderReportPeriod: 1 * time.Second,
}
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
conn, err := c.DialPublish("rtsp://localhost:8554/teststream",

View File

@@ -23,13 +23,13 @@ import (
)
func TestClientReadTracks(t *testing.T) {
track1, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track1, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
track2, err := NewTrackAAC(96, []byte{17, 144})
track2, err := NewTrackAAC(96, &TrackConfigAAC{Type: 1, SampleRate: 44100, ChannelCount: 2})
require.NoError(t, err)
track3, err := NewTrackAAC(96, []byte{0x12, 0x30})
track3, err := NewTrackAAC(96, &TrackConfigAAC{Type: 1, SampleRate: 96000, ChannelCount: 2})
require.NoError(t, err)
l, err := net.Listen("tcp", "localhost:8554")
@@ -221,7 +221,7 @@ func TestClientRead(t *testing.T) {
require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL(scheme+"://"+listenIP+":8554/teststream"), req.URL)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -463,10 +463,10 @@ func TestClientReadPartial(t *testing.T) {
require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL("rtsp://"+listenIP+":8554/teststream"), req.URL)
track1, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track1, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
track2, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track2, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track1, track2})
@@ -611,7 +611,7 @@ func TestClientReadNoContentBase(t *testing.T) {
require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -719,7 +719,7 @@ func TestClientReadAnyPort(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -847,7 +847,7 @@ func TestClientReadAutomaticProtocol(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -979,7 +979,7 @@ func TestClientReadAutomaticProtocol(t *testing.T) {
err = v.ValidateRequest(req, nil)
require.NoError(t, err)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -1172,7 +1172,7 @@ func TestClientReadDifferentInterleavedIDs(t *testing.T) {
require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL)
track1, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track1, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track1})
@@ -1334,7 +1334,7 @@ func TestClientReadRedirect(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -1493,7 +1493,7 @@ func TestClientReadPause(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -1679,7 +1679,7 @@ func TestClientReadRTCPReport(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -1854,7 +1854,7 @@ func TestClientReadErrorTimeout(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -2011,7 +2011,7 @@ func TestClientReadIgnoreTCPInvalidTrack(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -2141,7 +2141,7 @@ func TestClientReadSeek(t *testing.T) {
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})

View File

@@ -69,7 +69,7 @@ func TestClientSession(t *testing.T) {
require.Equal(t, base.HeaderValue{"123456"}, req.Header["Session"])
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -149,7 +149,7 @@ func TestClientAuth(t *testing.T) {
err = v.ValidateRequest(req, nil)
require.NoError(t, err)
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track})
@@ -212,7 +212,7 @@ func TestClientDescribeCharset(t *testing.T) {
require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL)
track1, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track1, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
err = base.Response{

View File

@@ -36,7 +36,7 @@ func main() {
fmt.Println("stream connected")
// create a H264 track
track, err := gortsplib.NewTrackH264(96, sps, pps)
track, err := gortsplib.NewTrackH264(96, &gortsplib.TrackConfigH264{sps, pps})
if err != nil {
panic(err)
}

View File

@@ -37,7 +37,7 @@ func main() {
fmt.Println("stream connected")
// create a H264 track
track, err := gortsplib.NewTrackH264(96, sps, pps)
track, err := gortsplib.NewTrackH264(96, &gortsplib.TrackConfigH264{sps, pps})
if err != nil {
panic(err)
}

View File

@@ -34,7 +34,7 @@ func main() {
fmt.Println("stream connected")
// create a H264 track
track, err := gortsplib.NewTrackH264(96, sps, pps)
track, err := gortsplib.NewTrackH264(96, &gortsplib.TrackConfigH264{sps, pps})
if err != nil {
panic(err)
}

View File

@@ -3,6 +3,7 @@ package rtpaac
import (
"bytes"
"fmt"
"io"
"github.com/icza/bitio"
)
@@ -43,12 +44,13 @@ var channelCounts = []int{
// MPEG4AudioConfig is a MPEG-4 Audio configuration.
type MPEG4AudioConfig struct {
Type MPEG4AudioType
SampleRate int
ChannelCount int
Type MPEG4AudioType
SampleRate int
ChannelCount int
AOTSpecificConfig []byte
}
// Decode decodes an MPEG-4 Audio configuration.
// Decode decodes an MPEG4AudioConfig.
func (c *MPEG4AudioConfig) Decode(byts []byte) error {
// ref: https://wiki.multimedia.cx/index.php/MPEG-4_Audio
@@ -60,14 +62,6 @@ func (c *MPEG4AudioConfig) Decode(byts []byte) error {
}
c.Type = MPEG4AudioType(tmp)
if tmp == 31 {
tmp, err = r.ReadBits(6)
if err != nil {
return err
}
c.Type = MPEG4AudioType(tmp + 32)
}
switch c.Type {
case MPEG4AudioTypeAACLC:
default:
@@ -110,5 +104,64 @@ func (c *MPEG4AudioConfig) Decode(byts []byte) error {
return fmt.Errorf("invalid channel configuration (%d)", channelConfig)
}
for {
byt, err := r.ReadBits(8)
if err != nil {
if err == io.EOF {
break
}
return err
}
c.AOTSpecificConfig = append(c.AOTSpecificConfig, uint8(byt))
}
return nil
}
// Encode encodes an MPEG4AudioConfig.
func (c MPEG4AudioConfig) Encode() ([]byte, error) {
var buf bytes.Buffer
w := bitio.NewWriter(&buf)
w.WriteBits(uint64(c.Type), 5)
sampleRateIndex := func() int {
for i, s := range sampleRates {
if s == c.SampleRate {
return i
}
}
return -1
}()
if sampleRateIndex != -1 {
w.WriteBits(uint64(sampleRateIndex), 4)
} else {
w.WriteBits(uint64(15), 4)
w.WriteBits(uint64(c.SampleRate), 24)
}
channelConfig := func() int {
for i, co := range channelCounts {
if co == c.ChannelCount {
return i + 1
}
}
return -1
}()
if channelConfig == -1 {
return nil, fmt.Errorf("invalid channel count (%d)", c.ChannelCount)
}
w.WriteBits(uint64(channelConfig), 4)
for _, b := range c.AOTSpecificConfig {
w.WriteBits(uint64(b), 8)
}
w.Close()
return buf.Bytes(), nil
}

View File

@@ -15,9 +15,10 @@ var configCases = []struct {
"aac-lc 44.1khz mono",
[]byte{0x12, 0x08, 0x56, 0xe5, 0x00},
MPEG4AudioConfig{
Type: MPEG4AudioTypeAACLC,
SampleRate: 44100,
ChannelCount: 1,
Type: MPEG4AudioTypeAACLC,
SampleRate: 44100,
ChannelCount: 1,
AOTSpecificConfig: []byte{0x0A, 0xDC, 0xA0},
},
},
{
@@ -33,9 +34,10 @@ var configCases = []struct {
"aac-lc 96khz stereo",
[]byte{0x10, 0x10, 0x56, 0xE5, 0x00},
MPEG4AudioConfig{
Type: MPEG4AudioTypeAACLC,
SampleRate: 96000,
ChannelCount: 2,
Type: MPEG4AudioTypeAACLC,
SampleRate: 96000,
ChannelCount: 2,
AOTSpecificConfig: []byte{0x0A, 0xDC, 0xA0},
},
},
{
@@ -81,14 +83,9 @@ func TestConfigDecodeErrors(t *testing.T) {
"EOF",
},
{
"extended type missing",
[]byte{31 << 3},
"EOF",
},
{
"extended type invalid",
[]byte{31 << 3, 20},
"unsupported type: 32",
"unsupported type",
[]byte{18 << 3},
"unsupported type: 18",
},
{
"sample rate missing",
@@ -123,3 +120,36 @@ func TestConfigDecodeErrors(t *testing.T) {
})
}
}
func TestConfigEncode(t *testing.T) {
for _, ca := range configCases {
t.Run(ca.name, func(t *testing.T) {
enc, err := ca.dec.Encode()
require.NoError(t, err)
require.Equal(t, ca.enc, enc)
})
}
}
func TestConfigEncodeErrors(t *testing.T) {
for _, ca := range []struct {
name string
conf MPEG4AudioConfig
err string
}{
{
"invalid channel config",
MPEG4AudioConfig{
Type: 2,
SampleRate: 44100,
ChannelCount: 0,
},
"invalid channel count (0)",
},
} {
t.Run(ca.name, func(t *testing.T) {
_, err := ca.conf.Encode()
require.Equal(t, ca.err, err.Error())
})
}
}

View File

@@ -100,7 +100,7 @@ func TestServerPublishErrorAnnounce(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: func() []byte {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
Key: "control",
@@ -139,7 +139,7 @@ func TestServerPublishErrorAnnounce(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: func() []byte {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
Key: "control",
@@ -178,7 +178,7 @@ func TestServerPublishErrorAnnounce(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"},
},
Body: func() []byte {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
Key: "control",
@@ -320,7 +320,7 @@ func TestServerPublishSetupPath(t *testing.T) {
defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
Key: "control",
@@ -415,7 +415,7 @@ func TestServerPublishErrorSetupDifferentPaths(t *testing.T) {
defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}
@@ -497,7 +497,7 @@ func TestServerPublishErrorSetupTrackTwice(t *testing.T) {
defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}
@@ -596,10 +596,10 @@ func TestServerPublishErrorRecordPartialTracks(t *testing.T) {
defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
track1, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track1, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
track2, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track2, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track1, track2}
@@ -749,7 +749,7 @@ func TestServerPublish(t *testing.T) {
<-connOpened
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}
@@ -937,7 +937,7 @@ func TestServerPublishErrorInvalidProtocol(t *testing.T) {
defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}
@@ -1038,7 +1038,7 @@ func TestServerPublishRTCPReport(t *testing.T) {
defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}
@@ -1197,7 +1197,7 @@ func TestServerPublishTimeout(t *testing.T) {
defer nconn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(nconn), bufio.NewWriter(nconn))
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}
@@ -1324,7 +1324,7 @@ func TestServerPublishWithoutTeardown(t *testing.T) {
require.NoError(t, err)
bconn := bufio.NewReadWriter(bufio.NewReader(nconn), bufio.NewWriter(nconn))
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}
@@ -1443,7 +1443,7 @@ func TestServerPublishUDPChangeConn(t *testing.T) {
defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn))
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}

View File

@@ -89,7 +89,7 @@ func TestServerReadSetupPath(t *testing.T) {
},
} {
t.Run(ca.name, func(t *testing.T) {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track, track, track, track, track})
@@ -145,7 +145,7 @@ func TestServerReadSetupPath(t *testing.T) {
func TestServerReadErrorSetupDifferentPaths(t *testing.T) {
connClosed := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -217,7 +217,7 @@ func TestServerReadErrorSetupDifferentPaths(t *testing.T) {
func TestServerReadErrorSetupTrackTwice(t *testing.T) {
connClosed := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -300,7 +300,7 @@ func TestServerRead(t *testing.T) {
sessionClosed := make(chan struct{})
framesReceived := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -593,7 +593,7 @@ func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
writerDone := make(chan struct{})
writerTerminate := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -686,7 +686,7 @@ func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
}
func TestServerReadPlayPlay(t *testing.T) {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -767,7 +767,7 @@ func TestServerReadPlayPausePlay(t *testing.T) {
writerDone := make(chan struct{})
writerTerminate := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -884,7 +884,7 @@ func TestServerReadPlayPausePause(t *testing.T) {
writerDone := make(chan struct{})
writerTerminate := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -1008,7 +1008,7 @@ func TestServerReadTimeout(t *testing.T) {
t.Run(proto, func(t *testing.T) {
sessionClosed := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -1100,7 +1100,7 @@ func TestServerReadWithoutTeardown(t *testing.T) {
connClosed := make(chan struct{})
sessionClosed := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -1197,7 +1197,7 @@ func TestServerReadWithoutTeardown(t *testing.T) {
}
func TestServerReadUDPChangeConn(t *testing.T) {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -1295,7 +1295,7 @@ func TestServerReadUDPChangeConn(t *testing.T) {
}
func TestServerReadNonSetuppedPath(t *testing.T) {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -1459,7 +1459,7 @@ func TestServerReadAdditionalInfos(t *testing.T) {
return &ri, ssrcs
}
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track, track})

View File

@@ -633,7 +633,7 @@ func TestServerErrorInvalidMethod(t *testing.T) {
}
func TestServerErrorTCPTwoConnOneSession(t *testing.T) {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -730,7 +730,7 @@ func TestServerErrorTCPTwoConnOneSession(t *testing.T) {
}
func TestServerErrorTCPOneConnTwoSessions(t *testing.T) {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -989,7 +989,7 @@ func TestServerSessionClose(t *testing.T) {
func TestServerSessionAutoClose(t *testing.T) {
sessionClosed := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -1055,7 +1055,7 @@ func TestServerErrorInvalidPath(t *testing.T) {
t.Run(string(method), func(t *testing.T) {
connClosed := make(chan struct{})
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
stream := NewServerStream(Tracks{track})
@@ -1096,7 +1096,7 @@ func TestServerErrorInvalidPath(t *testing.T) {
sxID := ""
if method == base.Record {
track, err := NewTrackH264(96, []byte("123456"), []byte("123456"))
track, err := NewTrackH264(96, &TrackConfigH264{[]byte{0x01, 0x02, 0x03, 0x04}, []byte{0x01, 0x02, 0x03, 0x04}})
require.NoError(t, err)
tracks := Tracks{track}

107
track.go
View File

@@ -20,6 +20,20 @@ type Track struct {
Media *psdp.MediaDescription
}
// TrackConfigH264 is the configuration of an H264 track.
type TrackConfigH264 struct {
SPS []byte
PPS []byte
}
// TrackConfigAAC is the configuration of an AAC track.
type TrackConfigAAC struct {
Type int
SampleRate int
ChannelCount int
AOTSpecificConfig []byte
}
func (t *Track) hasControlAttribute() bool {
for _, attr := range t.Media.Attributes {
if attr.Key == "control" {
@@ -127,10 +141,10 @@ func (t *Track) ClockRate() (int, error) {
}
// NewTrackH264 initializes an H264 track from a SPS and PPS.
func NewTrackH264(payloadType uint8, sps []byte, pps []byte) (*Track, error) {
spropParameterSets := base64.StdEncoding.EncodeToString(sps) +
"," + base64.StdEncoding.EncodeToString(pps)
profileLevelID := strings.ToUpper(hex.EncodeToString(sps[1:4]))
func NewTrackH264(payloadType uint8, conf *TrackConfigH264) (*Track, error) {
spropParameterSets := base64.StdEncoding.EncodeToString(conf.SPS) +
"," + base64.StdEncoding.EncodeToString(conf.PPS)
profileLevelID := strings.ToUpper(hex.EncodeToString(conf.SPS[1:4]))
typ := strconv.FormatInt(int64(payloadType), 10)
@@ -176,21 +190,18 @@ func (t *Track) IsH264() bool {
return vals[1] == "H264/90000"
}
// ExtractDataH264 extracts the SPS and PPS from an H264 track.
func (t *Track) ExtractDataH264() ([]byte, []byte, error) {
// ExtractConfigH264 extracts the configuration from an H264 track.
func (t *Track) ExtractConfigH264() (*TrackConfigH264, error) {
v, ok := t.Media.Attribute("fmtp")
if !ok {
return nil, nil, fmt.Errorf("fmtp attribute is missing")
return nil, fmt.Errorf("fmtp attribute is missing")
}
tmp := strings.SplitN(v, " ", 2)
if len(tmp) != 2 {
return nil, nil, fmt.Errorf("invalid fmtp attribute (%v)", v)
return nil, fmt.Errorf("invalid fmtp attribute (%v)", v)
}
var sps []byte
var pps []byte
for _, kv := range strings.Split(tmp[1], ";") {
kv = strings.Trim(kv, " ")
@@ -200,41 +211,47 @@ func (t *Track) ExtractDataH264() ([]byte, []byte, error) {
tmp := strings.SplitN(kv, "=", 2)
if len(tmp) != 2 {
return nil, nil, fmt.Errorf("invalid fmtp attribute (%v)", v)
return nil, fmt.Errorf("invalid fmtp attribute (%v)", v)
}
if tmp[0] == "sprop-parameter-sets" {
tmp := strings.SplitN(tmp[1], ",", 2)
if len(tmp) != 2 {
return nil, nil, fmt.Errorf("invalid sprop-parameter-sets (%v)", v)
return nil, fmt.Errorf("invalid sprop-parameter-sets (%v)", v)
}
var err error
sps, err = base64.StdEncoding.DecodeString(tmp[0])
sps, err := base64.StdEncoding.DecodeString(tmp[0])
if err != nil {
return nil, nil, fmt.Errorf("invalid sprop-parameter-sets (%v)", v)
return nil, fmt.Errorf("invalid sprop-parameter-sets (%v)", v)
}
pps, err = base64.StdEncoding.DecodeString(tmp[1])
pps, err := base64.StdEncoding.DecodeString(tmp[1])
if err != nil {
return nil, nil, fmt.Errorf("invalid sprop-parameter-sets (%v)", v)
return nil, fmt.Errorf("invalid sprop-parameter-sets (%v)", v)
}
conf := &TrackConfigH264{
SPS: sps,
PPS: pps,
}
return conf, nil
}
}
if sps == nil || pps == nil {
return nil, nil, fmt.Errorf("sprop-parameter-sets is missing (%v)", v)
}
return sps, pps, nil
return nil, fmt.Errorf("sprop-parameter-sets is missing (%v)", v)
}
// NewTrackAAC initializes an AAC track from a configuration.
func NewTrackAAC(payloadType uint8, config []byte) (*Track, error) {
var conf rtpaac.MPEG4AudioConfig
err := conf.Decode(config)
// NewTrackAAC initializes an AAC track.
func NewTrackAAC(payloadType uint8, conf *TrackConfigAAC) (*Track, error) {
mpegConf, err := rtpaac.MPEG4AudioConfig{
Type: rtpaac.MPEG4AudioType(conf.Type),
SampleRate: conf.SampleRate,
ChannelCount: conf.ChannelCount,
AOTSpecificConfig: conf.AOTSpecificConfig,
}.Encode()
if err != nil {
return nil, fmt.Errorf("invalid MPEG-4 Audio config: %v", err)
return nil, err
}
typ := strconv.FormatInt(int64(payloadType), 10)
@@ -259,7 +276,7 @@ func NewTrackAAC(payloadType uint8, config []byte) (*Track, error) {
"sizelength=13; " +
"indexlength=3; " +
"indexdeltalength=3; " +
"config=" + hex.EncodeToString(config),
"config=" + hex.EncodeToString(mpegConf),
},
},
},
@@ -285,8 +302,8 @@ func (t *Track) IsAAC() bool {
return strings.HasPrefix(strings.ToLower(vals[1]), "mpeg4-generic/")
}
// ExtractDataAAC extracts the config from an AAC track.
func (t *Track) ExtractDataAAC() ([]byte, error) {
// ExtractConfigAAC extracts the configuration from an AAC track.
func (t *Track) ExtractConfigAAC() (*TrackConfigAAC, error) {
v, ok := t.Media.Attribute("fmtp")
if !ok {
return nil, fmt.Errorf("fmtp attribute is missing")
@@ -297,8 +314,6 @@ func (t *Track) ExtractDataAAC() ([]byte, error) {
return nil, fmt.Errorf("invalid fmtp (%v)", v)
}
var config []byte
for _, kv := range strings.Split(tmp[1], ";") {
kv = strings.Trim(kv, " ")
@@ -312,19 +327,29 @@ func (t *Track) ExtractDataAAC() ([]byte, error) {
}
if tmp[0] == "config" {
var err error
config, err = hex.DecodeString(tmp[1])
enc, err := hex.DecodeString(tmp[1])
if err != nil {
return nil, fmt.Errorf("invalid config (%v)", v)
return nil, fmt.Errorf("invalid AAC config (%v)", tmp[1])
}
var mpegConf rtpaac.MPEG4AudioConfig
err = mpegConf.Decode(enc)
if err != nil {
return nil, fmt.Errorf("invalid AAC config (%v)", tmp[1])
}
conf := &TrackConfigAAC{
Type: int(mpegConf.Type),
SampleRate: mpegConf.SampleRate,
ChannelCount: mpegConf.ChannelCount,
AOTSpecificConfig: mpegConf.AOTSpecificConfig,
}
return conf, nil
}
}
if config == nil {
return nil, fmt.Errorf("config is missing (%v)", v)
}
return config, nil
return nil, fmt.Errorf("config is missing (%v)", v)
}
// Tracks is a list of tracks.

View File

@@ -225,7 +225,7 @@ func TestTrackH264New(t *testing.T) {
0x68, 0xee, 0x3c, 0x80,
}
tr, err := NewTrackH264(96, sps, pps)
tr, err := NewTrackH264(96, &TrackConfigH264{sps, pps})
require.NoError(t, err)
require.Equal(t, &Track{
Media: &psdp.MediaDescription{
@@ -282,12 +282,11 @@ func TestTrackIsH264(t *testing.T) {
}
}
func TestTrackH264Extract(t *testing.T) {
func TestTrackExtractConfigH264(t *testing.T) {
for _, ca := range []struct {
name string
track *Track
sps []byte
pps []byte
conf *TrackConfigH264
}{
{
"generic",
@@ -310,13 +309,15 @@ func TestTrackH264Extract(t *testing.T) {
},
},
},
[]byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x00, 0x03, 0x00, 0x3d, 0x08,
},
[]byte{
0x68, 0xee, 0x3c, 0x80,
&TrackConfigH264{
SPS: []byte{
0x67, 0x64, 0x00, 0x0c, 0xac, 0x3b, 0x50, 0xb0,
0x4b, 0x42, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
0x00, 0x03, 0x00, 0x3d, 0x08,
},
PPS: []byte{
0x68, 0xee, 0x3c, 0x80,
},
},
},
{
@@ -340,27 +341,28 @@ func TestTrackH264Extract(t *testing.T) {
},
},
},
[]byte{
0x67, 0x64, 0x00, 0x1f, 0xac, 0xd9, 0x40, 0x50,
0x05, 0xbb, 0x01, 0x6c, 0x80, 0x00, 0x00, 0x03,
0x00, 0x80, 0x00, 0x00, 0x1e, 0x07, 0x8c, 0x18,
0xcb,
},
[]byte{
0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0,
&TrackConfigH264{
SPS: []byte{
0x67, 0x64, 0x00, 0x1f, 0xac, 0xd9, 0x40, 0x50,
0x05, 0xbb, 0x01, 0x6c, 0x80, 0x00, 0x00, 0x03,
0x00, 0x80, 0x00, 0x00, 0x1e, 0x07, 0x8c, 0x18,
0xcb,
},
PPS: []byte{
0x68, 0xeb, 0xe3, 0xcb, 0x22, 0xc0,
},
},
},
} {
t.Run(ca.name, func(t *testing.T) {
sps, pps, err := ca.track.ExtractDataH264()
conf, err := ca.track.ExtractConfigH264()
require.NoError(t, err)
require.Equal(t, ca.sps, sps)
require.Equal(t, ca.pps, pps)
require.Equal(t, ca.conf, conf)
})
}
}
func TestTrackH264ExtractErrors(t *testing.T) {
func TestTrackConfigH264Errors(t *testing.T) {
for _, ca := range []struct {
name string
track *Track
@@ -525,14 +527,14 @@ func TestTrackH264ExtractErrors(t *testing.T) {
},
} {
t.Run(ca.name, func(t *testing.T) {
_, _, err := ca.track.ExtractDataH264()
_, err := ca.track.ExtractConfigH264()
require.Equal(t, ca.err, err.Error())
})
}
}
func TestTrackAACNew(t *testing.T) {
tr, err := NewTrackAAC(96, []byte{17, 144})
track, err := NewTrackAAC(96, &TrackConfigAAC{Type: 2, SampleRate: 48000, ChannelCount: 2})
require.NoError(t, err)
require.Equal(t, &Track{
Media: &psdp.MediaDescription{
@@ -552,7 +554,7 @@ func TestTrackAACNew(t *testing.T) {
},
},
},
}, tr)
}, track)
}
func TestTrackIsAAC(t *testing.T) {
@@ -611,11 +613,11 @@ func TestTrackIsAAC(t *testing.T) {
}
}
func TestTrackAACExtract(t *testing.T) {
func TestTrackExtractConfigAAC(t *testing.T) {
for _, ca := range []struct {
name string
track *Track
config []byte
name string
track *Track
conf *TrackConfigAAC
}{
{
"generic",
@@ -638,7 +640,11 @@ func TestTrackAACExtract(t *testing.T) {
},
},
},
[]byte{17, 144},
&TrackConfigAAC{
Type: 2,
SampleRate: 48000,
ChannelCount: 2,
},
},
{
"vlc rtsp server",
@@ -661,18 +667,22 @@ func TestTrackAACExtract(t *testing.T) {
},
},
},
[]byte{17, 144},
&TrackConfigAAC{
Type: 2,
SampleRate: 48000,
ChannelCount: 2,
},
},
} {
t.Run(ca.name, func(t *testing.T) {
config, err := ca.track.ExtractDataAAC()
conf, err := ca.track.ExtractConfigAAC()
require.NoError(t, err)
require.Equal(t, ca.config, config)
require.Equal(t, ca.conf, conf)
})
}
}
func TestTrackAACExtractErrors(t *testing.T) {
func TestTrackConfigAACErrors(t *testing.T) {
for _, ca := range []struct {
name string
track *Track
@@ -787,11 +797,11 @@ func TestTrackAACExtractErrors(t *testing.T) {
},
},
},
"invalid config (96 profile-level-id=1; config=zz)",
"invalid AAC config (zz)",
},
} {
t.Run(ca.name, func(t *testing.T) {
_, err := ca.track.ExtractDataAAC()
_, err := ca.track.ExtractConfigAAC()
require.Equal(t, ca.err, err.Error())
})
}