1. [fix] 修复rtsp pub无法接收IPv6 RTP数据的问题 2. [feat] 部分rtsp pub支持h265的代码

pull/29/head
q191201771 4 years ago
parent 80e4a83067
commit e5845e2d3b

@ -173,7 +173,12 @@ func analysisVideoTag(tag httpflv.Tag) {
}
} else if tag.IsHEVCKeySeqHeader() {
t = typeHEVC
nazalog.Debugf("%s", nazastring.DumpSliceByte(tag.Raw[11:]))
//nazalog.Debugf("%s", nazastring.DumpSliceByte(tag.Raw[11:]))
vps, sps, pps, _ := hevc.ParseVPSSPSPPSFromSeqHeader(tag.Raw[11:])
nazalog.Debugf("%s", nazastring.DumpSliceByte(vps))
nazalog.Debugf("%s", nazastring.DumpSliceByte(sps))
nazalog.Debugf("%s", nazastring.DumpSliceByte(pps))
//nazalog.Debugf("%s %s %s %+v", hex.Dump(vps), hex.Dump(sps), hex.Dump(pps), err)
buf.WriteString(" [HEVC SeqHeader] ")
}
} else {

@ -36,15 +36,23 @@ func (obs *Obs) OnSPSPPS(sps, pps []byte) {
_, _ = avcFp.Write([]byte{0, 0, 0, 1})
_, _ = avcFp.Write(pps)
}
func (obs *Obs) OnVPSSPSPPS(vps, sps, pps []byte) {
_, _ = avcFp.Write([]byte{0, 0, 0, 1})
_, _ = avcFp.Write(vps)
_, _ = avcFp.Write([]byte{0, 0, 0, 1})
_, _ = avcFp.Write(sps)
_, _ = avcFp.Write([]byte{0, 0, 0, 1})
_, _ = avcFp.Write(pps)
}
func (obs *Obs) OnAVPacket(pkt base.AVPacket) {
nazalog.Debugf("type=%d, ts=%d, len=%d", pkt.PayloadType, pkt.Timestamp, len(pkt.Payload))
switch pkt.PayloadType {
case base.RTPPacketTypeAVC:
// TODO chef: 由于存在多nalu情况需要进行拆分
//_, _ = avcFp.Write([]byte{0, 0, 0, 1})
//_, _ = avcFp.Write(pkt.Payload)
//_ = avcFp.Sync()
_, _ = avcFp.Write([]byte{0, 0, 0, 1})
_, _ = avcFp.Write(pkt.Payload)
_ = avcFp.Sync()
case base.RTPPacketTypeAAC:
h, _ := a.CalcADTSHeader(uint16(len(pkt.Payload)))
_, _ = aacFp.Write(h)

@ -2,4 +2,4 @@ module github.com/q191201771/lal
go 1.12
require github.com/q191201771/naza v0.14.0
require github.com/q191201771/naza v0.15.0

@ -1,2 +1,2 @@
github.com/q191201771/naza v0.14.0 h1:bgjiNRmaSxoYwuyjNvCOtvJgwFYmGlBj6tQpaaK2zhE=
github.com/q191201771/naza v0.14.0/go.mod h1:SE14GBGO9mAn6JZl3NlfWGtNOT7xQjxOG7f3YOdBThM=
github.com/q191201771/naza v0.15.0 h1:HFyRrluqZhpnBu6YQ1soIk6cR9P8G/9sDMFLBhTTBRc=
github.com/q191201771/naza v0.15.0/go.mod h1:SE14GBGO9mAn6JZl3NlfWGtNOT7xQjxOG7f3YOdBThM=

@ -27,7 +27,9 @@ func (c *Client) Query(addr string, timeoutMS int) (ip string, port int, err err
addr = fmt.Sprintf("%s:%d", addr, DefaultPort)
}
uc, err := nazanet.NewUDPConnection("", addr)
uc, err := nazanet.NewUDPConnection(func(option *nazanet.UDPConnectionOption) {
option.LAddr = addr
})
if err != nil {
return "", 0, err
}

@ -20,7 +20,9 @@ type Server struct {
}
func NewServer(addr string) (*Server, error) {
conn, err := nazanet.NewUDPConnection(addr, "")
conn, err := nazanet.NewUDPConnection(func(option *nazanet.UDPConnectionOption) {
option.LAddr = addr
})
if err != nil {
return nil, err
}

@ -9,6 +9,6 @@
package base
const (
RTPPacketTypeAVC = 96
RTPPacketTypeAVC = 96 // 注意RTSP SDP中HEVC也使用96 TODO chef: 是否需要重命名
RTPPacketTypeAAC = 97
)

@ -9,11 +9,11 @@
package hevc
import (
"encoding/hex"
"errors"
"github.com/q191201771/naza/pkg/nazabits"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
// AnnexB
@ -44,6 +44,28 @@ var (
NALUTypeSEISuffix uint8 = 40 // 0x28
)
type Context struct {
// unsigned int(8) configurationVersion = 1;
configurationVersion uint8
// unsigned int(2) general_profile_space;
// unsigned int(1) general_tier_flag;
// unsigned int(5) general_profile_idc;
generalProfileSpace uint8
generalTierFlag uint8
generalProfileIDC uint8
// unsigned int(32) general_profile_compatibility_flags;
generalProfileCompatibilityFlags uint32
// unsigned int(48) general_constraint_indicator_flags;
generalConstraintIndicatorFlags uint64
generalLevelIDC uint8
numTemporalLayers uint8
chromaFormat uint32
bitDepthLumaMinus8 uint32
bitDepthChromaMinus8 uint32
}
func ParseNALUTypeReadable(v uint8) string {
b, ok := NALUTypeMapping[ParseNALUType(v)]
if !ok {
@ -92,14 +114,14 @@ func ParseVPSSPSPPSFromSeqHeader(payload []byte) (vps, sps, pps []byte, err erro
if payload[0] != 0x1c || payload[1] != 0x00 || payload[2] != 0 || payload[3] != 0 || payload[4] != 0 {
return nil, nil, nil, ErrHEVC
}
nazalog.Debugf("%s", hex.Dump(payload))
//nazalog.Debugf("%s", hex.Dump(payload))
if len(payload) < 33 {
return nil, nil, nil, ErrHEVC
}
index := 27
if numOfArrays := payload[index]; numOfArrays != 3 {
if numOfArrays := payload[index]; numOfArrays != 3 && numOfArrays != 4 {
return nil, nil, nil, ErrHEVC
}
index++
@ -152,3 +174,330 @@ func ParseVPSSPSPPSFromSeqHeader(payload []byte) (vps, sps, pps []byte, err erro
return
}
func BuildSeqHeaderFromVPSSPSPPS(vps, sps, pps []byte) ([]byte, error) {
var sh []byte
sh = make([]byte, 1024)
sh[0] = 0x1c
sh[1] = 0x0
sh[2] = 0x0
sh[3] = 0x0
sh[4] = 0x0
// unsigned int(8) configurationVersion = 1;
sh[5] = 0x1
ctx := newContext()
if err := ParseVPS(vps, ctx); err != nil {
return nil, err
}
if err := ParseSPS(sps, ctx); err != nil {
return nil, err
}
// unsigned int(2) general_profile_space;
// unsigned int(1) general_tier_flag;
// unsigned int(5) general_profile_idc;
sh[6] = ctx.generalProfileSpace<<6 | ctx.generalTierFlag<<5 | ctx.generalProfileIDC
// unsigned int(32) general_profile_compatibility_flags
bele.BEPutUint32(sh[7:], ctx.generalProfileCompatibilityFlags)
// unsigned int(48) general_constraint_indicator_flags
bele.BEPutUint32(sh[11:], uint32(ctx.generalConstraintIndicatorFlags>>16))
bele.BEPutUint16(sh[15:], uint16(ctx.generalConstraintIndicatorFlags))
sh[17] = ctx.generalLevelIDC
return sh, nil
}
func ParseVPS(vps []byte, ctx *Context) error {
br := nazabits.NewBitReader(vps)
// type
if _, err := br.ReadBits8(8); err != nil {
return err
}
// skip
// vps_video_parameter_set_id u(4)
// vps_reserved_three_2bits u(2)
// vps_max_layers_minus1 u(6)
if _, err := br.ReadBits16(12); err != nil {
return ErrHEVC
}
vpsMaxSubLayersMinus1, err := br.ReadBits8(3)
if err != nil {
return ErrHEVC
}
if vpsMaxSubLayersMinus1+1 > ctx.numTemporalLayers {
ctx.numTemporalLayers = vpsMaxSubLayersMinus1 + 1
}
// skip
// vps_temporal_id_nesting_flag u(1)
// vps_reserved_0xffff_16bits u(16)
if _, err := br.ReadBits32(17); err != nil {
return ErrHEVC
}
return parsePTL(br, ctx, vpsMaxSubLayersMinus1)
}
func ParseSPS(sps []byte, ctx *Context) error {
var err error
br := nazabits.NewBitReader(sps)
// type
if _, err = br.ReadBits8(8); err != nil {
return err
}
// sps_video_parameter_set_id
if _, err = br.ReadBits8(4); err != nil {
return err
}
spsMaxSubLayersMinus1, err := br.ReadBits8(3)
if err != nil {
return err
}
if _, err := br.ReadBit(); err != nil {
return err
}
if err = parsePTL(br, ctx, spsMaxSubLayersMinus1); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if ctx.chromaFormat, err = br.ReadGolomb(); err != nil {
return err
}
if ctx.chromaFormat == 3 {
if _, err = br.ReadBit(); err != nil {
return err
}
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
conformanceWindowFlag, err := br.ReadBit()
if err != nil {
return err
}
if conformanceWindowFlag != 0 {
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
}
if ctx.bitDepthLumaMinus8, err = br.ReadGolomb(); err != nil {
return err
}
if ctx.bitDepthChromaMinus8, err = br.ReadGolomb(); err != nil {
return err
}
_, err = br.ReadGolomb()
if err != nil {
return err
}
spsSubLayerOrderingInfoPresentFlag, err := br.ReadBit()
if err != nil {
return err
}
var i uint8
if spsSubLayerOrderingInfoPresentFlag != 0 {
i = 0
} else {
i = spsMaxSubLayersMinus1
}
for ; i <= spsMaxSubLayersMinus1; i++ {
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
if _, err = br.ReadGolomb(); err != nil {
return err
}
return nil
}
func parsePTL(br nazabits.BitReader, ctx *Context, maxSubLayersMinus1 uint8) error {
var err error
var ptl Context
if ptl.generalProfileSpace, err = br.ReadBits8(2); err != nil {
return err
}
if ptl.generalTierFlag, err = br.ReadBit(); err != nil {
return err
}
if ptl.generalProfileIDC, err = br.ReadBits8(5); err != nil {
return err
}
if ptl.generalProfileCompatibilityFlags, err = br.ReadBits32(32); err != nil {
return err
}
if ptl.generalConstraintIndicatorFlags, err = br.ReadBits64(48); err != nil {
return err
}
if ptl.generalLevelIDC, err = br.ReadBits8(8); err != nil {
return err
}
updatePTL(ctx, &ptl)
/*
subLayerProfilePresentFlag := make([]uint8, maxSubLayersMinus1)
subLayerLevelPresentFlag := make([]uint8, maxSubLayersMinus1)
for i := uint8(0); i < maxSubLayersMinus1; i++ {
if subLayerProfilePresentFlag[i], err = br.ReadBit(); err != nil {
return err
}
if subLayerLevelPresentFlag[i], err = br.ReadBit(); err != nil {
return err
}
}
if maxSubLayersMinus1 > 0 {
for i := maxSubLayersMinus1; i < 8; i++ {
if _, err = br.ReadBits8(2); err != nil {
return err
}
}
}
for i := uint8(0); i < maxSubLayersMinus1; i++ {
if subLayerProfilePresentFlag[i] != 0 {
if _, err = br.ReadBits32(32); err != nil {
return err
}
if _, err = br.ReadBits32(32); err != nil {
return err
}
if _, err = br.ReadBits32(24); err != nil {
return err
}
}
if subLayerLevelPresentFlag[i] != 0 {
if _, err = br.ReadBits8(8); err != nil {
return err
}
}
}
*/
return nil
}
func updatePTL(ctx, ptl *Context) {
ctx.generalProfileSpace = ptl.generalProfileSpace
if ctx.generalTierFlag < ptl.generalTierFlag {
ctx.generalLevelIDC = ptl.generalLevelIDC
} else {
if ptl.generalLevelIDC > ctx.generalLevelIDC {
ctx.generalLevelIDC = ptl.generalLevelIDC
}
ctx.generalTierFlag = ptl.generalTierFlag
}
if ptl.generalProfileIDC > ctx.generalProfileIDC {
ctx.generalProfileIDC = ptl.generalProfileIDC
}
ctx.generalProfileCompatibilityFlags &= ptl.generalProfileCompatibilityFlags
ctx.generalConstraintIndicatorFlags &= ptl.generalConstraintIndicatorFlags
}
func newContext() *Context {
return &Context{
configurationVersion: 1,
//hvcc->lengthSizeMinusOne = 3; // 4 bytes
generalProfileCompatibilityFlags: 0xffffffff,
generalConstraintIndicatorFlags: 0xffffffffffff,
//hvcc->min_spatial_segmentation_idc = MAX_SPATIAL_SEGMENTATION + 1;
}
}
//func skipScalingListData(br nazabits.BitReader) error {
// var numCoeffs int
// var i int
//
// for i = 0; i < 4; i++ {
// k := 6
// if i == 3 {
// k = 2
// }
// for j := 0; j < k; j++ {
// f, err := br.ReadBit()
// if err != nil {
// return err
// }
// if f != 0 {
// if _, err := br.ReadGolomb(); err != nil {
// return err
// }
// } else {
// numCoeffs = 1 << (4+(uint32(i)<<1))
// if numCoeffs > 64 {
// numCoeffs = 64
// }
// }
// }
// }
//
// if i > 1 {
// if _, err := br.ReadGolomb(); err != nil {
// return err
// }
// }
// for i := 0; i < numCoeffs; i++ {
// if _, err := br.ReadGolomb(); err != nil {
// return err
// }
// }
//
// return nil
//}

File diff suppressed because one or more lines are too long

@ -12,6 +12,8 @@ import (
"fmt"
"sync"
"github.com/q191201771/naza/pkg/nazastring"
"github.com/q191201771/lal/pkg/httpts"
"github.com/q191201771/naza/pkg/bele"
@ -57,10 +59,11 @@ type Group struct {
gopCache *GOPCache
httpflvGopCache *GOPCache
adts aac.ADTS
asc []byte
sps []byte
pps []byte
// rtsp pub使用
asc []byte
vps []byte
sps []byte
pps []byte
}
type pushProxy struct {
@ -360,10 +363,6 @@ func (group *Group) OnReadRTMPAVMsg(msg base.RTMPMsg) {
// rtsp.PubSession
func (group *Group) OnASC(asc []byte) {
if err := group.adts.InitWithAACAudioSpecificConfig(asc); err != nil {
nazalog.Errorf("init with aac asc failed. err=%+v", err)
return
}
group.asc = asc
group.broadcastMetadataAndSeqHeader()
}
@ -375,6 +374,14 @@ func (group *Group) OnSPSPPS(sps, pps []byte) {
group.broadcastMetadataAndSeqHeader()
}
// rtsp.PubSession
func (group *Group) OnVPSSPSPPS(vps, sps, pps []byte) {
group.vps = vps
group.sps = sps
group.pps = pps
group.broadcastMetadataAndSeqHeader()
}
// rtsp.PubSession
func (group *Group) OnAVPacket(pkt base.AVPacket) {
var h base.RTMPHeader
@ -465,21 +472,32 @@ func (group *Group) broadcastMetadataAndSeqHeader() {
return
}
ctx, err := avc.ParseSPS(group.sps)
if err != nil {
nazalog.Errorf("parse sps failed. err=%+v", err)
return
}
var metadata []byte
var vsh []byte
var err error
if group.isHEVC() {
metadata, err = rtmp.BuildMetadata(-1, -1, int(base.RTMPSoundFormatAAC), int(base.RTMPCodecIDHEVC))
if err != nil {
nazalog.Errorf("build metadata failed. err=%+v", err)
return
}
} else {
ctx, err := avc.ParseSPS(group.sps)
if err != nil {
nazalog.Errorf("parse sps failed. err=%+v", err)
return
}
metadata, err := rtmp.BuildMetadata(int(ctx.Width), int(ctx.Height), int(base.RTMPSoundFormatAAC), int(base.RTMPCodecIDAVC))
if err != nil {
nazalog.Errorf("build metadata failed. err=%+v", err)
return
}
vsh, err := avc.BuildSeqHeaderFromSPSPPS(group.sps, group.pps)
if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err)
return
metadata, err = rtmp.BuildMetadata(int(ctx.Width), int(ctx.Height), int(base.RTMPSoundFormatAAC), int(base.RTMPCodecIDAVC))
if err != nil {
nazalog.Errorf("build metadata failed. err=%+v", err)
return
}
vsh, err = avc.BuildSeqHeaderFromSPSPPS(group.sps, group.pps)
if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err)
return
}
}
ash, err := aac.BuildAACSeqHeader(group.asc)
if err != nil {
@ -522,6 +540,9 @@ func (group *Group) broadcastMetadataAndSeqHeader() {
// TODO chef: 目前相当于其他类型往rtmp.AVMsg转了考虑统一往一个通用类型转
// @param msg 调用结束后内部不持有msg.Payload内存块
func (group *Group) broadcastRTMP(msg base.RTMPMsg) {
if msg.IsHEVCKeySeqHeader() {
nazalog.Debugf("%s", nazastring.DumpSliceByte(msg.Payload))
}
var (
lcd LazyChunkDivider
lrm2ft LazyRTMPMsg2FLVTag
@ -765,3 +786,8 @@ func (group *Group) delIn() {
group.gopCache.Clear()
group.httpflvGopCache.Clear()
}
// TODO chef: 后续看是否有更合适的方法判断
func (group *Group) isHEVC() bool {
return group.vps != nil
}

@ -43,6 +43,8 @@ func ParseMetadata(b []byte) (ObjectPairArray, error) {
// - author
// - version
//
// @param width 如果为-1则metadata中不写入width
// @param height 如果为-1则metadata中不写入height
// @param audiocodecid AAC 10
// @param videocodecid AVC 7
//
@ -53,14 +55,18 @@ func BuildMetadata(width int, height int, audiocodecid int, videocodecid int) ([
}
var opa ObjectPairArray
opa = append(opa, ObjectPair{
Key: "width",
Value: width,
})
opa = append(opa, ObjectPair{
Key: "height",
Value: height,
})
if width != -1 {
opa = append(opa, ObjectPair{
Key: "width",
Value: width,
})
}
if height != -1 {
opa = append(opa, ObjectPair{
Key: "height",
Value: height,
})
}
opa = append(opa, ObjectPair{
Key: "audiocodecid",
Value: audiocodecid,

@ -24,7 +24,8 @@ import (
type PubSessionObserver interface {
OnASC(asc []byte)
OnSPSPPS(sps, pps []byte)
OnSPSPPS(sps, pps []byte) // 如果是H264
OnVPSSPSPPS(vps, sps, pps []byte) // 如果是H265
// @param pkt: pkt结构体中字段含义见rtprtcp.OnAVPacket
OnAVPacket(pkt base.AVPacket)
@ -45,6 +46,7 @@ type PubSession struct {
audioSsrc uint32
videoSsrc uint32
vps []byte // 如果是H265的话
sps []byte
pps []byte
asc []byte
@ -65,7 +67,11 @@ func (p *PubSession) SetObserver(obs PubSessionObserver) {
p.observer = obs
if p.sps != nil && p.pps != nil {
p.observer.OnSPSPPS(p.sps, p.pps)
if p.vps != nil {
p.observer.OnVPSSPSPPS(p.vps, p.sps, p.pps)
} else {
p.observer.OnSPSPPS(p.sps, p.pps)
}
}
if p.asc != nil {
p.observer.OnASC(p.asc)
@ -79,12 +85,31 @@ func (p *PubSession) InitWithSDP(sdpCtx sdp.SDPContext) {
var videoPayloadType int
var audioClockRate int
var videoClockRate int
var isHEVC bool
for _, item := range sdpCtx.ARTPMapList {
switch item.PayloadType {
case base.RTPPacketTypeAVC:
videoClockRate = item.ClockRate
isHEVC = item.EncodingName == "H265"
case base.RTPPacketTypeAAC:
audioClockRate = item.ClockRate
default:
nazalog.Errorf("unknown payloadType. type=%d", item.PayloadType)
}
}
for _, item := range sdpCtx.AFmtPBaseList {
switch item.Format {
case base.RTPPacketTypeAVC:
videoPayloadType = item.Format
p.sps, p.pps, err = sdp.ParseSPSPPS(item)
if isHEVC {
p.vps, p.sps, p.pps, err = sdp.ParseVPSSPSPPS(item)
} else {
p.sps, p.pps, err = sdp.ParseSPSPPS(item)
}
if err != nil {
nazalog.Errorf("parse sps pps from sdp failed.")
}
@ -100,17 +125,6 @@ func (p *PubSession) InitWithSDP(sdpCtx sdp.SDPContext) {
}
}
for _, item := range sdpCtx.ARTPMapList {
switch item.PayloadType {
case base.RTPPacketTypeAVC:
videoClockRate = item.ClockRate
case base.RTPPacketTypeAAC:
audioClockRate = item.ClockRate
default:
nazalog.Errorf("unknown payloadType. type=%d", item.PayloadType)
}
}
p.audioUnpacker = rtprtcp.NewRTPUnpacker(audioPayloadType, audioClockRate, unpackerItemMaxSize, p.onAVPacketUnpacked)
p.videoUnpacker = rtprtcp.NewRTPUnpacker(videoPayloadType, videoClockRate, unpackerItemMaxSize, p.onAVPacketUnpacked)
@ -119,13 +133,17 @@ func (p *PubSession) InitWithSDP(sdpCtx sdp.SDPContext) {
}
func (p *PubSession) SetRTPConn(conn *net.UDPConn) {
server := nazanet.NewUDPConnectionWithConn(conn)
server, _ := nazanet.NewUDPConnection(func(option *nazanet.UDPConnectionOption) {
option.Conn = conn
})
go server.RunLoop(p.onReadUDPPacket)
p.rtpConn = server
}
func (p *PubSession) SetRTCPConn(conn *net.UDPConn) {
server := nazanet.NewUDPConnectionWithConn(conn)
server, _ := nazanet.NewUDPConnection(func(option *nazanet.UDPConnectionOption) {
option.Conn = conn
})
go server.RunLoop(p.onReadUDPPacket)
p.rtcpConn = server
}

@ -168,31 +168,60 @@ func ParseASC(a AFmtPBase) ([]byte, error) {
return r, nil
}
func ParseVPSSPSPPS(a AFmtPBase) (vps, sps, pps []byte, err error) {
if a.Format != base.RTPPacketTypeAVC {
return nil, nil, nil, ErrSDP
}
v, ok := a.Parameters["sprop-vps"]
if !ok {
return nil, nil, nil, ErrSDP
}
if vps, err = base64.StdEncoding.DecodeString(v); err != nil {
return nil, nil, nil, err
}
v, ok = a.Parameters["sprop-sps"]
if !ok {
return nil, nil, nil, ErrSDP
}
if sps, err = base64.StdEncoding.DecodeString(v); err != nil {
return nil, nil, nil, err
}
v, ok = a.Parameters["sprop-pps"]
if !ok {
return nil, nil, nil, ErrSDP
}
if pps, err = base64.StdEncoding.DecodeString(v); err != nil {
return nil, nil, nil, err
}
return
}
// 解析AVC/H264的spspps
// 例子见单元测试
func ParseSPSPPS(a AFmtPBase) (sps, pps []byte, err error) {
if a.Format != base.RTPPacketTypeAVC {
err = ErrSDP
return
return nil, nil, ErrSDP
}
v, ok := a.Parameters["sprop-parameter-sets"]
if !ok {
err = ErrSDP
return
return nil, nil, ErrSDP
}
items := strings.SplitN(v, ",", 2)
if len(items) != 2 {
err = ErrSDP
return
return nil, nil, ErrSDP
}
sps, err = base64.StdEncoding.DecodeString(items[0])
if err != nil {
return
return nil, nil, ErrSDP
}
pps, err = base64.StdEncoding.DecodeString(items[1])
return
}

@ -9,6 +9,7 @@
package sdp_test
import (
"encoding/hex"
"testing"
"github.com/q191201771/lal/pkg/sdp"
@ -63,6 +64,12 @@ func TestParseARTPMap(t *testing.T) {
ClockRate: 44100,
EncodingParameters: "2",
},
"a=rtpmap:96 H265/90000": {
PayloadType: 96,
EncodingName: "H265",
ClockRate: 90000,
EncodingParameters: "",
},
}
for in, out := range golden {
actual, err := sdp.ParseARTPMap(in)
@ -92,6 +99,14 @@ func TestParseFmtPBase(t *testing.T) {
"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 := sdp.ParseAFmtPBase(in)
@ -118,3 +133,18 @@ func TestParseASC(t *testing.T) {
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 := sdp.ParseAFmtPBase(s)
assert.Equal(t, nil, err)
vps, sps, pps, err := sdp.ParseVPSSPSPPS(f)
assert.Equal(t, nil, err)
nazalog.Debugf("%s", hex.Dump(vps))
nazalog.Debugf("%s", hex.Dump(sps))
nazalog.Debugf("%s", hex.Dump(pps))
}

Loading…
Cancel
Save