mirror of
https://github.com/aler9/gortsplib
synced 2025-10-05 15:16:51 +08:00
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:
@@ -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",
|
||||
|
@@ -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})
|
||||
|
@@ -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{
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -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())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -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}
|
||||
|
@@ -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})
|
||||
|
@@ -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
107
track.go
@@ -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.
|
||||
|
@@ -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())
|
||||
})
|
||||
}
|
||||
|
Reference in New Issue
Block a user