|
|
// Copyright 2020, Chef. All rights reserved.
|
|
|
// https://github.com/q191201771/lal
|
|
|
//
|
|
|
// Use of this source code is governed by a MIT-style license
|
|
|
// that can be found in the License file.
|
|
|
//
|
|
|
// Author: Chef (191201771@qq.com)
|
|
|
|
|
|
package sdp
|
|
|
|
|
|
import (
|
|
|
"encoding/hex"
|
|
|
"github.com/q191201771/lal/pkg/avc"
|
|
|
"strings"
|
|
|
"testing"
|
|
|
|
|
|
"github.com/q191201771/lal/pkg/base"
|
|
|
|
|
|
"github.com/q191201771/naza/pkg/assert"
|
|
|
)
|
|
|
|
|
|
var goldenSdp = "v=0" + "\r\n" +
|
|
|
"o=- 0 0 IN IP6 ::1" + "\r\n" +
|
|
|
"s=No Name" + "\r\n" +
|
|
|
"c=IN IP6 ::1" + "\r\n" +
|
|
|
"t=0 0" + "\r\n" +
|
|
|
"a=tool:libavformat 57.83.100" + "\r\n" +
|
|
|
"m=video 0 RTP/AVP 96" + "\r\n" +
|
|
|
"b=AS:212" + "\r\n" +
|
|
|
"a=rtpmap:96 H264/90000" + "\r\n" +
|
|
|
"a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAIKzZQMApsBEAAAMAAQAAAwAyDxgxlg==,aOvssiw=; profile-level-id=640020" + "\r\n" +
|
|
|
"a=control:streamid=0" + "\r\n" +
|
|
|
"m=audio 0 RTP/AVP 97" + "\r\n" +
|
|
|
"b=AS:30" + "\r\n" +
|
|
|
"a=rtpmap:97 MPEG4-GENERIC/44100/2" + "\r\n" +
|
|
|
"a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1210" + "\r\n" +
|
|
|
"a=control:streamid=1" + "\r\n"
|
|
|
|
|
|
var goldenSps = []byte{
|
|
|
0x67, 0x64, 0x00, 0x20, 0xAC, 0xD9, 0x40, 0xC0, 0x29, 0xB0, 0x11, 0x00, 0x00, 0x03, 0x00, 0x01, 0x00, 0x00, 0x03, 0x00, 0x32, 0x0F, 0x18, 0x31, 0x96,
|
|
|
}
|
|
|
|
|
|
var goldenPps = []byte{
|
|
|
0x68, 0xEB, 0xEC, 0xB2, 0x2C,
|
|
|
}
|
|
|
|
|
|
func TestParseSdp2RawContext(t *testing.T) {
|
|
|
sdpCtx, err := ParseSdp2RawContext([]byte(goldenSdp))
|
|
|
assert.Equal(t, nil, err)
|
|
|
Log.Debugf("sdp=%+v", sdpCtx)
|
|
|
}
|
|
|
|
|
|
func TestParseARtpMap(t *testing.T) {
|
|
|
golden := map[string]ARtpMap{
|
|
|
"rtpmap:96 H264/90000": {
|
|
|
PayloadType: 96,
|
|
|
EncodingName: "H264",
|
|
|
ClockRate: 90000,
|
|
|
EncodingParameters: "",
|
|
|
},
|
|
|
"rtpmap:97 MPEG4-GENERIC/44100/2": {
|
|
|
PayloadType: 97,
|
|
|
EncodingName: "MPEG4-GENERIC",
|
|
|
ClockRate: 44100,
|
|
|
EncodingParameters: "2",
|
|
|
},
|
|
|
"a=rtpmap:96 H265/90000": {
|
|
|
PayloadType: 96,
|
|
|
EncodingName: "H265",
|
|
|
ClockRate: 90000,
|
|
|
EncodingParameters: "",
|
|
|
},
|
|
|
}
|
|
|
for in, out := range golden {
|
|
|
actual, err := ParseARtpMap(in)
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, out, actual)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func TestParseFmtPBase(t *testing.T) {
|
|
|
golden := map[string]AFmtPBase{
|
|
|
"a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAIKzZQMApsBEAAAMAAQAAAwAyDxgxlg==,aOvssiw=; profile-level-id=640020": {
|
|
|
Format: 96,
|
|
|
Parameters: map[string]string{
|
|
|
"packetization-mode": "1",
|
|
|
"sprop-parameter-sets": "Z2QAIKzZQMApsBEAAAMAAQAAAwAyDxgxlg==,aOvssiw=",
|
|
|
"profile-level-id": "640020",
|
|
|
},
|
|
|
},
|
|
|
"a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1210": {
|
|
|
Format: 97,
|
|
|
Parameters: map[string]string{
|
|
|
"profile-level-id": "1",
|
|
|
"mode": "AAC-hbr",
|
|
|
"sizelength": "13",
|
|
|
"indexlength": "3",
|
|
|
"indexdeltalength": "3",
|
|
|
"config": "1210",
|
|
|
},
|
|
|
},
|
|
|
"a=fmtp:96 sprop-vps=QAEMAf//AWAAAAMAkAAAAwAAAwA/ugJA; sprop-sps=QgEBAWAAAAMAkAAAAwAAAwA/oAUCAXHy5bpKTC8BAQAAAwABAAADAA8I; sprop-pps=RAHAc8GJ": {
|
|
|
Format: 96,
|
|
|
Parameters: map[string]string{
|
|
|
"sprop-vps": "QAEMAf//AWAAAAMAkAAAAwAAAwA/ugJA",
|
|
|
"sprop-sps": "QgEBAWAAAAMAkAAAAwAAAwA/oAUCAXHy5bpKTC8BAQAAAwABAAADAA8I",
|
|
|
"sprop-pps": "RAHAc8GJ",
|
|
|
},
|
|
|
},
|
|
|
}
|
|
|
for in, out := range golden {
|
|
|
actual, err := ParseAFmtPBase(in)
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, out, actual)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func TestParseSpsPps(t *testing.T) {
|
|
|
s := "a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAIKzZQMApsBEAAAMAAQAAAwAyDxgxlg==,aOvssiw=; profile-level-id=640020"
|
|
|
f, err := ParseAFmtPBase(s)
|
|
|
assert.Equal(t, nil, err)
|
|
|
sps, pps, err := ParseSpsPps(&f)
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, goldenSps, sps)
|
|
|
assert.Equal(t, goldenPps, pps)
|
|
|
}
|
|
|
|
|
|
func TestParseAsc(t *testing.T) {
|
|
|
s := "a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1210"
|
|
|
f, err := ParseAFmtPBase(s)
|
|
|
assert.Equal(t, nil, err)
|
|
|
asc, err := ParseAsc(&f)
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, []byte{0x12, 0x10}, asc)
|
|
|
}
|
|
|
|
|
|
// TODO chef 补充assert判断
|
|
|
// []byte{0x40, 0x01, 0x0c, 0x01, 0xff, 0xff, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x3f, 0x95, 0x98, 0x09}
|
|
|
// []byte{0x42, 0x01, 0x01, 0x01, 0x60, 0x00, 0x00, 0x03, 0x00, 0x90, 0x00, 0x00, 0x03, 0x00, 0x00, 0x03, 0x00, 0x3f, 0xa0, 0x05, 0x02, 0x01, 0x69, 0x65, 0x95, 0x9a, 0x49, 0x32, 0xbc, 0x04, 0x04, 0x00, 0x00, 0x03, 0x00, 0x04, 0x00, 0x00, 0x03, 0x00, 0x3c, 0x20}
|
|
|
// []byte{0x44, 0x01, 0xc1, 0x72, 0xb4, 0x62, 0x40}
|
|
|
func TestParseVpsSpsPps(t *testing.T) {
|
|
|
s := "a=fmtp:96 sprop-vps=QAEMAf//AWAAAAMAkAAAAwAAAwA/ugJA; sprop-sps=QgEBAWAAAAMAkAAAAwAAAwA/oAUCAXHy5bpKTC8BAQAAAwABAAADAA8I; sprop-pps=RAHAc8GJ"
|
|
|
f, err := ParseAFmtPBase(s)
|
|
|
assert.Equal(t, nil, err)
|
|
|
vps, sps, pps, err := ParseVpsSpsPps(&f)
|
|
|
assert.Equal(t, nil, err)
|
|
|
Log.Debugf("%s", hex.Dump(vps))
|
|
|
Log.Debugf("%s", hex.Dump(sps))
|
|
|
Log.Debugf("%s", hex.Dump(pps))
|
|
|
}
|
|
|
|
|
|
func TestParseSdp2LogicContext(t *testing.T) {
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(goldenSdp))
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, true, ctx.hasAudio)
|
|
|
assert.Equal(t, true, ctx.hasVideo)
|
|
|
assert.Equal(t, 44100, ctx.AudioClockRate)
|
|
|
assert.Equal(t, 90000, ctx.VideoClockRate)
|
|
|
assert.Equal(t, true, ctx.IsAudioPayloadTypeOrigin(97))
|
|
|
assert.Equal(t, true, ctx.IsVideoPayloadTypeOrigin(96))
|
|
|
assert.Equal(t, base.AvPacketPtAac, ctx.GetAudioPayloadTypeBase())
|
|
|
assert.Equal(t, base.AvPacketPtAvc, ctx.GetVideoPayloadTypeBase())
|
|
|
assert.Equal(t, "streamid=1", ctx.audioAControl)
|
|
|
assert.Equal(t, "streamid=0", ctx.videoAControl)
|
|
|
assert.IsNotNil(t, ctx.Asc)
|
|
|
assert.Equal(t, nil, ctx.Vps)
|
|
|
assert.IsNotNil(t, ctx.Sps)
|
|
|
assert.IsNotNil(t, ctx.Pps)
|
|
|
}
|
|
|
|
|
|
func TestCase2(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 2252316233 2252316233 IN IP4 0.0.0.0
|
|
|
s=Media Server
|
|
|
c=IN IP4 0.0.0.0
|
|
|
t=0 0
|
|
|
a=control:*
|
|
|
a=packetization-supported:DH
|
|
|
a=rtppayload-supported:DH
|
|
|
a=range:npt=now-
|
|
|
m=video 0 RTP/AVP 98
|
|
|
a=control:trackID=0
|
|
|
a=framerate:25.000000
|
|
|
a=rtpmap:98 H265/90000
|
|
|
a=fmtp:98 profile-id=1;sprop-sps=QgEBAWAAAAMAsAAAAwAAAwBaoAWCAJBY2uSTL5A=;sprop-pps=RAHA8vA8kA==;sprop-vps=QAEMAf//AWAAAAMAsAAAAwAAAwBarAk=
|
|
|
a=recvonly`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, false, ctx.hasAudio)
|
|
|
assert.Equal(t, true, ctx.hasVideo)
|
|
|
assert.Equal(t, 90000, ctx.VideoClockRate)
|
|
|
assert.Equal(t, true, ctx.IsVideoPayloadTypeOrigin(98))
|
|
|
assert.Equal(t, base.AvPacketPtHevc, ctx.GetVideoPayloadTypeBase())
|
|
|
assert.Equal(t, "trackID=0", ctx.videoAControl)
|
|
|
assert.Equal(t, nil, ctx.Asc)
|
|
|
assert.IsNotNil(t, ctx.Vps)
|
|
|
assert.IsNotNil(t, ctx.Sps)
|
|
|
assert.IsNotNil(t, ctx.Pps)
|
|
|
Log.Debugf("%+v", ctx)
|
|
|
}
|
|
|
|
|
|
func TestCase3(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 2252310609 2252310609 IN IP4 0.0.0.0
|
|
|
s=Media Server
|
|
|
c=IN IP4 0.0.0.0
|
|
|
t=0 0
|
|
|
a=control:*
|
|
|
a=packetization-supported:DH
|
|
|
a=rtppayload-supported:DH
|
|
|
a=range:npt=now-
|
|
|
m=video 0 RTP/AVP 96
|
|
|
a=control:trackID=0
|
|
|
a=framerate:25.000000
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 packetization-mode=1;profile-level-id=4D002A;sprop-parameter-sets=Z00AKp2oHgCJ+WbgICAoAAAfQAAGGoQgAA==,aO48gAA=
|
|
|
a=recvonly
|
|
|
m=audio 0 RTP/AVP 97
|
|
|
a=control:trackID=1
|
|
|
a=rtpmap:97 MPEG4-GENERIC/48000
|
|
|
a=fmtp:97 streamtype=5;profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3;config=1188
|
|
|
a=recvonly`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, true, ctx.hasAudio)
|
|
|
assert.Equal(t, true, ctx.hasVideo)
|
|
|
assert.Equal(t, 48000, ctx.AudioClockRate)
|
|
|
assert.Equal(t, 90000, ctx.VideoClockRate)
|
|
|
assert.Equal(t, true, ctx.IsAudioPayloadTypeOrigin(97))
|
|
|
assert.Equal(t, true, ctx.IsVideoPayloadTypeOrigin(96))
|
|
|
assert.Equal(t, base.AvPacketPtAac, ctx.GetAudioPayloadTypeBase())
|
|
|
assert.Equal(t, base.AvPacketPtAvc, ctx.GetVideoPayloadTypeBase())
|
|
|
assert.Equal(t, "trackID=1", ctx.audioAControl)
|
|
|
assert.Equal(t, "trackID=0", ctx.videoAControl)
|
|
|
assert.IsNotNil(t, ctx.Asc)
|
|
|
assert.Equal(t, nil, ctx.Vps)
|
|
|
assert.IsNotNil(t, ctx.Sps)
|
|
|
assert.IsNotNil(t, ctx.Pps)
|
|
|
Log.Debugf("%+v", ctx)
|
|
|
}
|
|
|
|
|
|
func TestCase4(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 1109162014219182 0 IN IP4 0.0.0.0
|
|
|
s=HIK Media Server V3.4.103
|
|
|
i=HIK Media Server Session Description : standard
|
|
|
e=NONE
|
|
|
c=IN IP4 0.0.0.0
|
|
|
t=0 0
|
|
|
a=control:*
|
|
|
b=AS:1034
|
|
|
a=range:npt=now-
|
|
|
m=video 0 RTP/AVP 96
|
|
|
i=Video Media
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 profile-level-id=4D0014;packetization-mode=0;sprop-parameter-sets=Z2QAIK2EAQwgCGEAQwgCGEAQwgCEO1AoA803AQEBQAAAAwBAAAAMoQ==,aO48sA==
|
|
|
a=control:trackID=video
|
|
|
b=AS:1024
|
|
|
m=audio 0 RTP/AVP 8
|
|
|
i=Audio Media
|
|
|
a=rtpmap:8 PCMA/8000
|
|
|
a=control:trackID=audio
|
|
|
b=AS:10
|
|
|
a=Media_header:MEDIAINFO=494D4B48020100000400000111710110401F000000FA000000000000000000000000000000000000;
|
|
|
a=appversion:1.0`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, true, ctx.hasAudio)
|
|
|
assert.Equal(t, true, ctx.hasVideo)
|
|
|
assert.Equal(t, 8000, ctx.AudioClockRate)
|
|
|
assert.Equal(t, 90000, ctx.VideoClockRate)
|
|
|
assert.Equal(t, true, ctx.IsAudioPayloadTypeOrigin(8))
|
|
|
assert.Equal(t, true, ctx.IsVideoPayloadTypeOrigin(96))
|
|
|
//assert.Equal(t, base.AvPacketPtAac, ctx.AudioPayloadType)
|
|
|
assert.Equal(t, base.AvPacketPtAvc, ctx.GetVideoPayloadTypeBase())
|
|
|
assert.Equal(t, "trackID=audio", ctx.audioAControl)
|
|
|
assert.Equal(t, "trackID=video", ctx.videoAControl)
|
|
|
assert.Equal(t, nil, ctx.Asc)
|
|
|
assert.Equal(t, nil, ctx.Vps)
|
|
|
assert.IsNotNil(t, ctx.Sps)
|
|
|
assert.IsNotNil(t, ctx.Pps)
|
|
|
Log.Debugf("%+v", ctx)
|
|
|
}
|
|
|
|
|
|
func TestCase5(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 1001 1 IN IP4 192.168.0.221
|
|
|
s=VCP IPC Realtime stream
|
|
|
m=video 0 RTP/AVP 105
|
|
|
c=IN IP4 192.168.0.221
|
|
|
a=control:rtsp://192.168.0.221/media/video1/video
|
|
|
a=rtpmap:105 H264/90000
|
|
|
a=fmtp:105 profile-level-id=64002a; packetization-mode=1; sprop-parameter-sets=Z2QAKq2EAQwgCGEAQwgCGEAQwgCEO1A8ARPyzcBAQFAAAD6AAAnECEA=,aO4xshs=
|
|
|
a=recvonly
|
|
|
m=application 0 RTP/AVP 107
|
|
|
c=IN IP4 192.168.0.221
|
|
|
a=control:rtsp://192.168.0.221/media/video1/metadata
|
|
|
a=rtpmap:107 vnd.onvif.metadata/90000
|
|
|
a=fmtp:107 DecoderTag=h3c-v3 RTCP=0
|
|
|
a=recvonly`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, false, ctx.hasAudio)
|
|
|
assert.Equal(t, true, ctx.hasVideo)
|
|
|
assert.Equal(t, 90000, ctx.VideoClockRate)
|
|
|
assert.Equal(t, true, ctx.IsVideoPayloadTypeOrigin(105))
|
|
|
assert.Equal(t, base.AvPacketPtAvc, ctx.GetVideoPayloadTypeBase())
|
|
|
assert.Equal(t, "rtsp://192.168.0.221/media/video1/video", ctx.videoAControl)
|
|
|
assert.Equal(t, nil, ctx.Vps)
|
|
|
assert.IsNotNil(t, ctx.Sps)
|
|
|
assert.IsNotNil(t, ctx.Pps)
|
|
|
Log.Debugf("%+v", ctx)
|
|
|
}
|
|
|
|
|
|
func TestCase6(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 1109162014219182 0 IN IP4 0.0.0.0
|
|
|
s=HIK Media Server V3.4.96
|
|
|
i=HIK Media Server Session Description : standard
|
|
|
e=NONE
|
|
|
c=IN IP4 0.0.0.0
|
|
|
t=0 0
|
|
|
a=control:*
|
|
|
b=AS:2058
|
|
|
a=range:npt=now-
|
|
|
m=video 0 RTP/AVP 96
|
|
|
i=Video Media
|
|
|
a=rtpmap:96 H265/90000
|
|
|
a=control:trackID=video
|
|
|
b=AS:2048
|
|
|
m=audio 0 RTP/AVP 8
|
|
|
i=Audio Media
|
|
|
a=rtpmap:8 PCMA/8000
|
|
|
a=control:trackID=audio
|
|
|
b=AS:10
|
|
|
a=Media_header:MEDIAINFO=494D4B48020100000400050011710110401F000000FA000000000000000000000000000000000000;
|
|
|
a=appversion:1.0`
|
|
|
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, 8000, ctx.AudioClockRate)
|
|
|
assert.Equal(t, 90000, ctx.VideoClockRate)
|
|
|
//assert.Equal(t, base.AvPacketPtUnknown, ctx.audioPayloadTypeBase)
|
|
|
assert.Equal(t, base.AvPacketPtG711A, ctx.audioPayloadTypeBase)
|
|
|
assert.Equal(t, base.AvPacketPtHevc, ctx.videoPayloadTypeBase)
|
|
|
assert.Equal(t, 8, ctx.audioPayloadTypeOrigin)
|
|
|
assert.Equal(t, 96, ctx.videoPayloadTypeOrigin)
|
|
|
assert.Equal(t, "trackID=audio", ctx.audioAControl)
|
|
|
assert.Equal(t, "trackID=video", ctx.videoAControl)
|
|
|
assert.Equal(t, nil, ctx.Asc)
|
|
|
assert.Equal(t, nil, ctx.Vps)
|
|
|
assert.Equal(t, nil, ctx.Sps)
|
|
|
assert.Equal(t, nil, ctx.Pps)
|
|
|
assert.Equal(t, true, ctx.hasAudio)
|
|
|
assert.Equal(t, true, ctx.hasVideo)
|
|
|
Log.Debugf("%+v", ctx)
|
|
|
}
|
|
|
|
|
|
// #85
|
|
|
func TestCase7(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 1109162014219182 0 IN IP4 0.0.0.0
|
|
|
s=HIK Media Server V3.4.106
|
|
|
i=HIK Media Server Session Description : standard
|
|
|
e=NONE
|
|
|
c=IN IP4 0.0.0.0
|
|
|
t=0 0
|
|
|
a=control:*
|
|
|
b=AS:4106
|
|
|
a=range:clock=20210520T063812Z-20210520T064816Z
|
|
|
m=video 0 RTP/AVP 96
|
|
|
i=Video Media
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 profile-level-id=4D0014;packetization-mode=0
|
|
|
a=control:trackID=video
|
|
|
b=AS:4096
|
|
|
m=audio 0 RTP/AVP 98
|
|
|
i=Audio Media
|
|
|
a=rtpmap:98 G7221/16000
|
|
|
a=control:trackID=audio
|
|
|
b=AS:10
|
|
|
a=Media_header:MEDIAINFO=494D4B48020100000400000121720110803E0000803E000000000000000000000000000000000000;
|
|
|
a=appversion:1.0
|
|
|
`
|
|
|
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
func TestCase8(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 1622201479405259 1622201479405259 IN IP4 192.168.3.58
|
|
|
s=Media Presentation
|
|
|
e=NONE
|
|
|
b=AS:5100
|
|
|
t=0 0
|
|
|
a=control:rtsp://192.168.3.58:554/Streaming/Channels/101/?transportmode=unicast
|
|
|
m=video 0 RTP/AVP 96
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:5000
|
|
|
a=recvonly
|
|
|
a=x-dimensions:1920,1080
|
|
|
a=control:rtsp://192.168.3.58:554/Streaming/Channels/101/trackID=1?transportmode=unicast
|
|
|
a=rtpmap:96 H265/90000
|
|
|
m=audio 0 RTP/AVP 8
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:50
|
|
|
a=recvonly
|
|
|
a=control:rtsp://192.168.3.58:554/Streaming/Channels/101/trackID=2?transportmode=unicast
|
|
|
a=rtpmap:8 PCMA/8000
|
|
|
a=Media_header:MEDIAINFO=494D4B48010200000400050011710110401F000000FA000000000000000000000000000000000000;
|
|
|
a=appversion:1.0
|
|
|
`
|
|
|
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
func TestCase9(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 13362557 1 IN IP4 192.168.1.100
|
|
|
s=RTSP/RTP stream from VZ Smart-IPC
|
|
|
i=h264
|
|
|
t=0 0
|
|
|
a=tool:LIVE555 Streaming Media v2016.07.19
|
|
|
a=type:broadcast
|
|
|
a=control:*
|
|
|
a=range:npt=0-
|
|
|
a=x-qt-text-nam:RTSP/RTP stream from VZ Smart-IPC
|
|
|
a=x-qt-text-inf:h264
|
|
|
m=video 0 RTP/AVP 96
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:4000
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 packetization-mode=1;profile-level-id=000042;sprop-parameter-sets=
|
|
|
a=control:track1
|
|
|
m=audio 0 RTP/AVP 0
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:64
|
|
|
a=control:track2
|
|
|
m=vzinfo 0 RTP/AVP 108
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:5
|
|
|
a=rtpmap:108 VND.ONVIF.METADATA/90000
|
|
|
a=control:track3
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
// sdp aac中a=fmtp缺少config字段,这个case的实际情况是后续也没有aac的rtp包
|
|
|
func TestCase10(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 0 0 IN IP4 0.0.0.0
|
|
|
s=rtsp_demo
|
|
|
t=0 0
|
|
|
a=control:rtsp://10.10.10.188:554/stream0
|
|
|
a=range:npt=0-
|
|
|
m=video 0 RTP/AVP 96
|
|
|
c=IN IP4 0.0.0.0
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 packetization-mode=1;sprop-parameter-sets=Z00AKp2oHgCJ+WbgICAgQA==,aO48gA==
|
|
|
a=control:rtsp://10.10.10.188:554/stream0/track1
|
|
|
m=audio 0 RTP/AVP 97
|
|
|
c=IN IP4 0.0.0.0
|
|
|
a=rtpmap:97 MPEG4-GENERIC/44100/2
|
|
|
a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3
|
|
|
a=control:rtsp://10.10.10.188:554/stream0/track2
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
// `fmtp:96 `后多了个`;`
|
|
|
func TestCase11(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 3331435948 1116907222000 IN IP4 192.168.2.109
|
|
|
s=Session
|
|
|
c=IN IP4 192.168.2.109
|
|
|
t=0 0
|
|
|
a=range:npt=now-
|
|
|
a=control:*
|
|
|
m=video 0 RTP/AVP 96
|
|
|
a=control:trackID=0
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 ;packetization-mode=1;sprop-parameter-sets=Z00AKpY1QPAET8s3AQEBQAABwgAAV+Qh,aO4xsg==
|
|
|
b=AS:5000
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
// `a=fmtp:104 `这行的尾部多了个`;`
|
|
|
func TestCase12(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 2733083813174 2733083813174 IN IP4 192.168.0.101
|
|
|
s=Media Presentation
|
|
|
e=NONE
|
|
|
b=AS:5100
|
|
|
t=0 0
|
|
|
a=control:rtsp://192.168.0.102:554/LiveMedia/ch1/Media1/
|
|
|
m=video 0 RTP/AVP 96
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:5000
|
|
|
a=recvonly
|
|
|
a=x-dimensions:1280,960
|
|
|
a=control:rtsp://192.168.0.102:554/LiveMedia/ch1/Media1/trackID=1
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z01AII2NQCgDz/gLcBAQFAAAD6AAAw1DoYAHmCu8uNDAA8wV3lwo,aO44gA==
|
|
|
m=audio 0 RTP/AVP 104
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:50
|
|
|
a=recvonly
|
|
|
a=control:rtsp://192.168.0.102:554/LiveMedia/ch1/Media1/trackID=2
|
|
|
a=rtpmap:104 mpeg4-generic/16000/1
|
|
|
a=fmtp:104 profile-level-id=15; streamtype=5; mode=AAC-hbr; config=1408;SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;
|
|
|
a=Media_header:MEDIAINFO=494D4B48010200000400000101200110803E0000007D000000000000000000000000000000000000;
|
|
|
a=appversion:1.0
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
// `a=fmtp:96`这行被切割成了多行
|
|
|
func TestCase13(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 16513881941979867928 16513881941979867928 IN IP4 HF-SW-P1-ROC
|
|
|
s=Unnamed
|
|
|
i=N/A
|
|
|
c=IN IP4 0.0.0.0
|
|
|
t=0 0
|
|
|
a=tool:vlc 3.0.16
|
|
|
a=recvonly
|
|
|
a=type:broadcast
|
|
|
a=charset:UTF-8
|
|
|
a=control:rtsp://127.0.0.1:8066/demo
|
|
|
m=video 0 RTP/AVP 96
|
|
|
b=RR:0
|
|
|
a=rtpmap:96 H265/90000
|
|
|
a=fmtp:96 tx-mode=SRST;profile-id=1;level-id=3;tier-flag=0;profile-space=0;sprop-vps=QAEMAf//AWAAAAMAkAAAAwAAAwBdlZgJ;sprop-sps=QgEBAWAAAAMAkAAAAwAAAwBdoAKAgC0WWVmq8rgE
|
|
|
AAADAlwAAEakIA==;sprop-pps=RAHBc9CJ;sprop-sei=TgEF/////////2Qsot4JtRdH27tVpP5/wvxOeDI2NSAoYnVpbGQgMTUxKSAtIDIuNzpbV2luZG93c11bR0NDIDYuNC4wXVs2NCBiaXRdIDhiaXQgLSBILjI2NS
|
|
|
9IRVZDIGNvZGVjIC0gQ29weXJpZ2h0IDIwMTMtMjAxOCAoYykgTXVsdGljb3Jld2FyZSwgSW5jIC0gaHR0cDovL3gyNjUub3JnIC0gb3B0aW9uczogY3B1aWQ9MTE3MzUwMyBmcmFtZS10aHJlYWRzPTggbnVtYS1wb29scz
|
|
|
04IG5vLXdwcCBuby1wbW9kZSBuby1wbWUgbm8tcHNuciBuby1zc2ltIGxvZy1sZXZlbD0yIGJpdGRlcHRoPTggaW5wdXQtY3NwPTEgZnBzPTQ1MjEvMTUxIGlucHV0LXJlcz0xMjgweDcyMCBpbnRlcmxhY2U9MCB0b3RhbC
|
|
|
1mcmFtZXM9MCBsZXZlbC1pZGM9MCBoaWdoLXRpZXI9MSB1aGQtYmQ9MCByZWY9MyBuby1hbGxvdy1ub24tY29uZm9ybWFuY2Ugbm8tcmVwZWF0LWhlYWRlcnMgYW5uZXhiIG5vLWF1ZCBuby1ocmQgaW5mbyBoYXNoPTAgbm
|
|
|
8tdGVtcG9yYWwtbGF5ZXJzIG9wZW4tZ29wIG1pbi1rZXlpbnQ9MjUga2V5aW50PTI1MCBnb3AtbG9va2FoZWFkPTAgYmZyYW1lcz00IGItYWRhcHQ9MiBiLXB5cmFtaWQgYmZyYW1lLWJpYXM9MCByYy1sb29rYWhlYWQ9Mj
|
|
|
AgbG9va2FoZWFkLXNsaWNlcz00IHNjZW5lY3V0PTQwIHJhZGw9MCBuby1pbnRyYS1yZWZyZXNoIGN0dT0xNiBtaW4tY3Utc2l6ZT04IG5vLXJlY3Qgbm8tYW1wIG1heC10dS1zaXplPTE2IHR1LWludGVyLWRlcHRoPTEgdH
|
|
|
UtaW50cmEtZGVwdGg9MSBsaW1pdC10dT0wIHJkb3EtbGV2ZWw9MCBkeW5hbWljLXJkPTAuMDAgbm8tc3NpbS1yZCBzaWduaGlkZSBuby10c2tpcCBuci1pbnRyYT0wIG5yLWludGVyPTAgbm8tY29uc3RyYWluZWQtaW50cm
|
|
|
Egc3Ryb25nLWludHJhLXNtb290aGluZyBtYXgtbWVyZ2U9MiBsaW1pdC1yZWZzPTMgbm8tbGltaXQtbW9kZXMgbWU9MSBzdWJtZT0yIG1lcmFuZ2U9NTcgdGVtcG9yYWwtbXZwIHdlaWdodHAgbm8td2VpZ2h0YiBuby1hbm
|
|
|
FseXplLXNyYy1waWNzIGRlYmxvY2s9MDowIHNhbyBuby1zYW8tbm9uLWRlYmxvY2sgcmQ9MyBuby1lYXJseS1za2lwIHJza2lwIG5vLWZhc3QtaW50cmEgbm8tdHNraXAtZmFzdCBuby1jdS1sb3NzbGVzcyBuby1iLWludH
|
|
|
JhIG5vLXNwbGl0cmQtc2tpcCByZHBlbmFsdHk9MCBwc3ktcmQ9Mi4wMCBwc3ktcmRvcT0wLjAwIG5vLXJkLXJlZmluZSBuby1sb3NzbGVzcyBjYnFwb2Zmcz0wIGNycXBvZmZzPTAgcmM9Y3JmIGNyZj0yOC4wIHFjb21wPT
|
|
|
AuNjAgcXBzdGVwPTQgc3RhdHMtd3JpdGU9MCBzdGF0cy1yZWFkPTAgaXByYXRpbz0xLjQwIHBicmF0aW89MS4zMCBhcS1tb2RlPTEgYXEtc3RyZW5ndGg9MS4wMCBjdXRyZWUgem9uZS1jb3VudD0wIG5vLXN0cmljdC1jYn
|
|
|
IgcWctc2l6ZT0xNiBuby1yYy1ncmFpbiBxcG1heD02OSBxcG1pbj0wIG5vLWNvbnN0LXZidiBzYXI9MCBvdmVyc2Nhbj0wIHZpZGVvZm9ybWF0PTUgcmFuZ2U9MCBjb2xvcnByaW09MiB0cmFuc2Zlcj0yIGNvbG9ybWF0cm
|
|
|
l4PTIgY2hyb21hbG9jPTAgZGlzcGxheS13aW5kb3c9MCBtYXgtY2xsPTAsMCBtaW4tbHVtYT0wIG1heC1sdW1hPTI1NSBsb2cyLW1heC1wb2MtbHNiPTggdnVpLXRpbWluZy1pbmZvIHZ1aS1ocmQtaW5mbyBzbGljZXM9MS
|
|
|
Buby1vcHQtcXAtcHBzIG5vLW9wdC1yZWYtbGlzdC1sZW5ndGgtcHBzIG5vLW11bHRpLXBhc3Mtb3B0LXJwcyBzY2VuZWN1dC1iaWFzPTAuMDUgbm8tb3B0LWN1LWRlbHRhLXFwIG5vLWFxLW1vdGlvbiBuby1oZHIgbm8taG
|
|
|
RyLW9wdCBuby1kaGRyMTAtb3B0IGFuYWx5c2lzLXJldXNlLWxldmVsPTUgc2NhbGUtZmFjdG9yPTAgcmVmaW5lLWludHJhPTAgcmVmaW5lLWludGVyPTAgcmVmaW5lLW12PTAgbm8tbGltaXQtc2FvIGN0dS1pbmZvPTAgbm
|
|
|
8tbG93cGFzcy1kY3QgcmVmaW5lLW12LXR5cGU9MCBjb3B5LXBpYz0xgA==;
|
|
|
a=control:rtsp://127.0.0.1:8066/demo/trackID=0
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
func TestCase14(t *testing.T) {
|
|
|
golden := `a=tool:lal 0.27.1
|
|
|
m=video 0 RTP/AVP 96
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAH6zZQFAEX5P/AycDJmoCAgKAAAADAIAAABkHjBjL,aOk5csA=; profile-level-id=640016
|
|
|
a=control:streamid=0
|
|
|
m=audio 0 RTP/AVP 97
|
|
|
b=AS:128
|
|
|
a=rtpmap:97 MPEG4-GENERIC/44100/2
|
|
|
a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=1210
|
|
|
a=control:streamid=1
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
var avcCtx avc.Context
|
|
|
avc.ParseSps(ctx.Sps, &avcCtx)
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
func TestCase15(t *testing.T) {
|
|
|
// TODO(chef): [fix] 有多路音频的情况 202209
|
|
|
//v=0
|
|
|
//o=- 2266397444 2266397444 IN IP4 0.0.0.0
|
|
|
//s=Media Server
|
|
|
//c=IN IP4 0.0.0.0
|
|
|
//t=0 0
|
|
|
//a=control:*
|
|
|
// a=packetization-supported:DH
|
|
|
//a=rtppayload-supported:DH
|
|
|
//a=range:npt=now-
|
|
|
// a=x-packetization-supported:IV
|
|
|
//a=x-rtppayload-supported:IV
|
|
|
//m=video 0 RTP/AVP 96
|
|
|
//a=control:trackID=0
|
|
|
//a=framerate:25.000000
|
|
|
//a=rtpmap:96 H264/90000
|
|
|
//a=fmtp:96 packetization-mode=1;profile-level-id=64103C;sprop-parameter-sets=Z2QQPKwbGqAIAA4/lmyAAAADAIAAABlHhEI1AA==,aO4xshsA
|
|
|
//a=recvonly
|
|
|
//m=audio 0 RTP/AVP 8
|
|
|
//a=control:trackID=1
|
|
|
//a=rtpmap:8 PCMA/8000
|
|
|
//a=recvonly
|
|
|
//m=audio 0 RTP/AVP 8
|
|
|
//a=control:trackID=2
|
|
|
//a=rtpmap:8 PCMA/8000
|
|
|
//a=recvonly
|
|
|
}
|
|
|
|
|
|
func TestCase16(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 1667405799319376 1667405799319376 IN IP4 192.168.1.64
|
|
|
s=Media Presentation
|
|
|
e=NONE
|
|
|
b=AS:5100
|
|
|
t=0 0
|
|
|
a=control:rtsp://192.168.1.64/
|
|
|
m=video 0 RTP/AVP 96
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:5000
|
|
|
a=recvonly
|
|
|
a=x-dimensions:1920,1080
|
|
|
a=control:rtsp://192.168.1.64/trackID=1
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AKY2NQDwBE/LNwEBAUAAAcIAAFfkAQA==,aO48gA==
|
|
|
m=audio 0 RTP/AVP 104
|
|
|
c=IN IP4 0.0.0.0
|
|
|
b=AS:50
|
|
|
a=recvonly
|
|
|
a=control:rtsp://192.168.1.64/trackID=2
|
|
|
a=rtpmap:104 mpeg4-generic/16000/1
|
|
|
a=fmtp:104 profile-level-id=15; streamtype=5; mode=AAC-hbr; config=1408;SizeLength=13; IndexLength=3; IndexDeltaLength=3; Profile=1;
|
|
|
a=Media_header:MEDIAINFO=494D4B48010300000400000101200110803E0000007D000000000000000000000000000000000000;
|
|
|
a=appversion:1.0
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
var avcCtx avc.Context
|
|
|
avc.ParseSps(ctx.Sps, &avcCtx)
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
func TestCase17(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=08154911501886160101 1672977368 1672977368 IN IP4 192.168.8.105
|
|
|
s=Unnamed
|
|
|
t=0 0
|
|
|
c=IN IP4 127.0.0.1
|
|
|
m=video 0 RTP/AVP 99
|
|
|
a=rtpmap:99 H264/90000
|
|
|
a=control:trackID=1
|
|
|
m=audio 0 RTP/AVP 0
|
|
|
a=rtpmap:0 PCMU/8000
|
|
|
a=control:trackID=2
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
_ = ctx
|
|
|
}
|
|
|
|
|
|
// 见 #251
|
|
|
func TestCase18(t *testing.T) {
|
|
|
golden := `v=0
|
|
|
o=- 0 0 IN IP4 127.0.0.1
|
|
|
s=No Name
|
|
|
c=IN IP4 127.0.0.1
|
|
|
t=0 0
|
|
|
a=tool:libavformat LIBAVFORMAT_VERSION
|
|
|
m=video 0 RTP/AVP 96
|
|
|
a=rtpmap:96 H264/90000
|
|
|
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAFqyyAUBf8uAiAAADAAIAAAMAPB4sXJA=,aOvDyyLA; profile-level-id=640016
|
|
|
a=control:streamid=0
|
|
|
m=audio 0 RTP/AVP 8
|
|
|
b=AS:64
|
|
|
a=control:streamid=1
|
|
|
`
|
|
|
golden = strings.ReplaceAll(golden, "\n", "\r\n")
|
|
|
ctx, err := ParseSdp2LogicContext([]byte(golden))
|
|
|
assert.Equal(t, nil, err)
|
|
|
assert.Equal(t, true, ctx.IsAudioPayloadTypeOrigin(8))
|
|
|
assert.Equal(t, true, ctx.IsAudioUnpackable())
|
|
|
}
|