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) require.NoError(t, err)
conn, err := c.DialPublish("rtsp://localhost:8554/teststream", 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) require.NoError(t, err)
writerDone := make(chan struct{}) 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) require.NoError(t, err)
conn, err := c.DialPublish("rtsp://localhost:8554/teststream", 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) require.NoError(t, err)
conn, err := c.DialPublish("rtsp://localhost:8554/teststream", conn, err := c.DialPublish("rtsp://localhost:8554/teststream",
@@ -747,7 +747,7 @@ func TestClientPublishAutomaticProtocol(t *testing.T) {
require.NoError(t, err) 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) require.NoError(t, err)
conn, err := DialPublish("rtsp://localhost:8554/teststream", conn, err := DialPublish("rtsp://localhost:8554/teststream",
@@ -888,7 +888,7 @@ func TestClientPublishRTCPReport(t *testing.T) {
senderReportPeriod: 1 * time.Second, 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) require.NoError(t, err)
conn, err := c.DialPublish("rtsp://localhost:8554/teststream", conn, err := c.DialPublish("rtsp://localhost:8554/teststream",

View File

@@ -23,13 +23,13 @@ import (
) )
func TestClientReadTracks(t *testing.T) { 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) 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) 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) require.NoError(t, err)
l, err := net.Listen("tcp", "localhost:8554") 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, base.Describe, req.Method)
require.Equal(t, mustParseURL(scheme+"://"+listenIP+":8554/teststream"), req.URL) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -463,10 +463,10 @@ func TestClientReadPartial(t *testing.T) {
require.Equal(t, base.Describe, req.Method) require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL("rtsp://"+listenIP+":8554/teststream"), req.URL) 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) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track1, track2}) tracks := cloneAndClearTracks(Tracks{track1, track2})
@@ -611,7 +611,7 @@ func TestClientReadNoContentBase(t *testing.T) {
require.Equal(t, base.Describe, req.Method) require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -719,7 +719,7 @@ func TestClientReadAnyPort(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, base.Describe, req.Method) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -847,7 +847,7 @@ func TestClientReadAutomaticProtocol(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, base.Describe, req.Method) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -979,7 +979,7 @@ func TestClientReadAutomaticProtocol(t *testing.T) {
err = v.ValidateRequest(req, nil) err = v.ValidateRequest(req, nil)
require.NoError(t, err) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -1172,7 +1172,7 @@ func TestClientReadDifferentInterleavedIDs(t *testing.T) {
require.Equal(t, base.Describe, req.Method) require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track1}) tracks := cloneAndClearTracks(Tracks{track1})
@@ -1334,7 +1334,7 @@ func TestClientReadRedirect(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, base.Describe, req.Method) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -1493,7 +1493,7 @@ func TestClientReadPause(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, base.Describe, req.Method) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -1679,7 +1679,7 @@ func TestClientReadRTCPReport(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, base.Describe, req.Method) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -1854,7 +1854,7 @@ func TestClientReadErrorTimeout(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, base.Describe, req.Method) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -2011,7 +2011,7 @@ func TestClientReadIgnoreTCPInvalidTrack(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, base.Describe, req.Method) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -2141,7 +2141,7 @@ func TestClientReadSeek(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, base.Describe, req.Method) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})

View File

@@ -69,7 +69,7 @@ func TestClientSession(t *testing.T) {
require.Equal(t, base.HeaderValue{"123456"}, req.Header["Session"]) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -149,7 +149,7 @@ func TestClientAuth(t *testing.T) {
err = v.ValidateRequest(req, nil) err = v.ValidateRequest(req, nil)
require.NoError(t, err) 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) require.NoError(t, err)
tracks := cloneAndClearTracks(Tracks{track}) tracks := cloneAndClearTracks(Tracks{track})
@@ -212,7 +212,7 @@ func TestClientDescribeCharset(t *testing.T) {
require.Equal(t, base.Describe, req.Method) require.Equal(t, base.Describe, req.Method)
require.Equal(t, mustParseURL("rtsp://localhost:8554/teststream"), req.URL) 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) require.NoError(t, err)
err = base.Response{ err = base.Response{

View File

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

View File

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

View File

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

View File

@@ -3,6 +3,7 @@ package rtpaac
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"io"
"github.com/icza/bitio" "github.com/icza/bitio"
) )
@@ -46,9 +47,10 @@ type MPEG4AudioConfig struct {
Type MPEG4AudioType Type MPEG4AudioType
SampleRate int SampleRate int
ChannelCount int ChannelCount int
AOTSpecificConfig []byte
} }
// Decode decodes an MPEG-4 Audio configuration. // Decode decodes an MPEG4AudioConfig.
func (c *MPEG4AudioConfig) Decode(byts []byte) error { func (c *MPEG4AudioConfig) Decode(byts []byte) error {
// ref: https://wiki.multimedia.cx/index.php/MPEG-4_Audio // 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) 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 { switch c.Type {
case MPEG4AudioTypeAACLC: case MPEG4AudioTypeAACLC:
default: default:
@@ -110,5 +104,64 @@ func (c *MPEG4AudioConfig) Decode(byts []byte) error {
return fmt.Errorf("invalid channel configuration (%d)", channelConfig) 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 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

@@ -18,6 +18,7 @@ var configCases = []struct {
Type: MPEG4AudioTypeAACLC, Type: MPEG4AudioTypeAACLC,
SampleRate: 44100, SampleRate: 44100,
ChannelCount: 1, ChannelCount: 1,
AOTSpecificConfig: []byte{0x0A, 0xDC, 0xA0},
}, },
}, },
{ {
@@ -36,6 +37,7 @@ var configCases = []struct {
Type: MPEG4AudioTypeAACLC, Type: MPEG4AudioTypeAACLC,
SampleRate: 96000, SampleRate: 96000,
ChannelCount: 2, ChannelCount: 2,
AOTSpecificConfig: []byte{0x0A, 0xDC, 0xA0},
}, },
}, },
{ {
@@ -81,14 +83,9 @@ func TestConfigDecodeErrors(t *testing.T) {
"EOF", "EOF",
}, },
{ {
"extended type missing", "unsupported type",
[]byte{31 << 3}, []byte{18 << 3},
"EOF", "unsupported type: 18",
},
{
"extended type invalid",
[]byte{31 << 3, 20},
"unsupported type: 32",
}, },
{ {
"sample rate missing", "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"}, "Content-Type": base.HeaderValue{"application/sdp"},
}, },
Body: func() []byte { 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) require.NoError(t, err)
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{ track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
Key: "control", Key: "control",
@@ -139,7 +139,7 @@ func TestServerPublishErrorAnnounce(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"}, "Content-Type": base.HeaderValue{"application/sdp"},
}, },
Body: func() []byte { 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) require.NoError(t, err)
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{ track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
Key: "control", Key: "control",
@@ -178,7 +178,7 @@ func TestServerPublishErrorAnnounce(t *testing.T) {
"Content-Type": base.HeaderValue{"application/sdp"}, "Content-Type": base.HeaderValue{"application/sdp"},
}, },
Body: func() []byte { 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) require.NoError(t, err)
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{ track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
Key: "control", Key: "control",
@@ -320,7 +320,7 @@ func TestServerPublishSetupPath(t *testing.T) {
defer conn.Close() defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) 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) require.NoError(t, err)
track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{ track.Media.Attributes = append(track.Media.Attributes, psdp.Attribute{
Key: "control", Key: "control",
@@ -415,7 +415,7 @@ func TestServerPublishErrorSetupDifferentPaths(t *testing.T) {
defer conn.Close() defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) 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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}
@@ -497,7 +497,7 @@ func TestServerPublishErrorSetupTrackTwice(t *testing.T) {
defer conn.Close() defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) 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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}
@@ -596,10 +596,10 @@ func TestServerPublishErrorRecordPartialTracks(t *testing.T) {
defer conn.Close() defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) 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) 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) require.NoError(t, err)
tracks := Tracks{track1, track2} tracks := Tracks{track1, track2}
@@ -749,7 +749,7 @@ func TestServerPublish(t *testing.T) {
<-connOpened <-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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}
@@ -937,7 +937,7 @@ func TestServerPublishErrorInvalidProtocol(t *testing.T) {
defer conn.Close() defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) 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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}
@@ -1038,7 +1038,7 @@ func TestServerPublishRTCPReport(t *testing.T) {
defer conn.Close() defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) 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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}
@@ -1197,7 +1197,7 @@ func TestServerPublishTimeout(t *testing.T) {
defer nconn.Close() defer nconn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(nconn), bufio.NewWriter(nconn)) 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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}
@@ -1324,7 +1324,7 @@ func TestServerPublishWithoutTeardown(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
bconn := bufio.NewReadWriter(bufio.NewReader(nconn), bufio.NewWriter(nconn)) 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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}
@@ -1443,7 +1443,7 @@ func TestServerPublishUDPChangeConn(t *testing.T) {
defer conn.Close() defer conn.Close()
bconn := bufio.NewReadWriter(bufio.NewReader(conn), bufio.NewWriter(conn)) 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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}

View File

@@ -89,7 +89,7 @@ func TestServerReadSetupPath(t *testing.T) {
}, },
} { } {
t.Run(ca.name, func(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) require.NoError(t, err)
stream := NewServerStream(Tracks{track, track, track, track, track}) stream := NewServerStream(Tracks{track, track, track, track, track})
@@ -145,7 +145,7 @@ func TestServerReadSetupPath(t *testing.T) {
func TestServerReadErrorSetupDifferentPaths(t *testing.T) { func TestServerReadErrorSetupDifferentPaths(t *testing.T) {
connClosed := make(chan struct{}) 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -217,7 +217,7 @@ func TestServerReadErrorSetupDifferentPaths(t *testing.T) {
func TestServerReadErrorSetupTrackTwice(t *testing.T) { func TestServerReadErrorSetupTrackTwice(t *testing.T) {
connClosed := make(chan struct{}) 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -300,7 +300,7 @@ func TestServerRead(t *testing.T) {
sessionClosed := make(chan struct{}) sessionClosed := make(chan struct{})
framesReceived := 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -593,7 +593,7 @@ func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
writerDone := make(chan struct{}) writerDone := make(chan struct{})
writerTerminate := 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -686,7 +686,7 @@ func TestServerReadTCPResponseBeforeFrames(t *testing.T) {
} }
func TestServerReadPlayPlay(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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -767,7 +767,7 @@ func TestServerReadPlayPausePlay(t *testing.T) {
writerDone := make(chan struct{}) writerDone := make(chan struct{})
writerTerminate := 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -884,7 +884,7 @@ func TestServerReadPlayPausePause(t *testing.T) {
writerDone := make(chan struct{}) writerDone := make(chan struct{})
writerTerminate := 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -1008,7 +1008,7 @@ func TestServerReadTimeout(t *testing.T) {
t.Run(proto, func(t *testing.T) { t.Run(proto, func(t *testing.T) {
sessionClosed := 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -1100,7 +1100,7 @@ func TestServerReadWithoutTeardown(t *testing.T) {
connClosed := make(chan struct{}) connClosed := make(chan struct{})
sessionClosed := 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -1197,7 +1197,7 @@ func TestServerReadWithoutTeardown(t *testing.T) {
} }
func TestServerReadUDPChangeConn(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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -1295,7 +1295,7 @@ func TestServerReadUDPChangeConn(t *testing.T) {
} }
func TestServerReadNonSetuppedPath(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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -1459,7 +1459,7 @@ func TestServerReadAdditionalInfos(t *testing.T) {
return &ri, ssrcs 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track, track}) stream := NewServerStream(Tracks{track, track})

View File

@@ -633,7 +633,7 @@ func TestServerErrorInvalidMethod(t *testing.T) {
} }
func TestServerErrorTCPTwoConnOneSession(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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -730,7 +730,7 @@ func TestServerErrorTCPTwoConnOneSession(t *testing.T) {
} }
func TestServerErrorTCPOneConnTwoSessions(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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -989,7 +989,7 @@ func TestServerSessionClose(t *testing.T) {
func TestServerSessionAutoClose(t *testing.T) { func TestServerSessionAutoClose(t *testing.T) {
sessionClosed := 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -1055,7 +1055,7 @@ func TestServerErrorInvalidPath(t *testing.T) {
t.Run(string(method), func(t *testing.T) { t.Run(string(method), func(t *testing.T) {
connClosed := make(chan struct{}) 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) require.NoError(t, err)
stream := NewServerStream(Tracks{track}) stream := NewServerStream(Tracks{track})
@@ -1096,7 +1096,7 @@ func TestServerErrorInvalidPath(t *testing.T) {
sxID := "" sxID := ""
if method == base.Record { 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) require.NoError(t, err)
tracks := Tracks{track} tracks := Tracks{track}

105
track.go
View File

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

View File

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