rtmp2rtsp support g711a

pull/266/head
ZSC714725 2 years ago
parent 3029db08fd
commit 411ba82181

@ -17,6 +17,7 @@ const (
// AudioCodecAac StatGroup.AudioCodec
AudioCodecAac = "AAC"
AudioCodecG711U = "PCMU"
AudioCodecG711A = "PCMA"
// VideoCodecAvc StatGroup.VideoCodec
VideoCodecAvc = "H264"

@ -88,6 +88,7 @@ const (
// AACPacketType UI8
// Data UI8[n]
// 注意视频的CodecId是后4位音频是前4位
RtmpSoundFormatG711A uint8 = 7
RtmpSoundFormatG711U uint8 = 8
RtmpSoundFormatAac uint8 = 10

@ -402,6 +402,8 @@ func (group *Group) broadcastByRtmpMsg(msg base.RtmpMsg) {
}
case base.RtmpSoundFormatG711U:
group.stat.AudioCodec = base.AudioCodecG711U
case base.RtmpSoundFormatG711A:
group.stat.AudioCodec = base.AudioCodecG711A
}
}
}

@ -12,4 +12,4 @@ package remux
var _ iRtmp2MpegtsFilterObserver = &Rtmp2MpegtsRemuxer{}
const g711uSampleRate = 8000
const pcmDefaultSampleRate = 8000

@ -88,7 +88,12 @@ func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) {
case base.RtmpSoundFormatG711U:
r.audioPt = base.AvPacketPtG711U
if r.audioSampleRate < 0 {
r.audioSampleRate = g711uSampleRate
r.audioSampleRate = pcmDefaultSampleRate
}
case base.RtmpSoundFormatG711A:
r.audioPt = base.AvPacketPtG711A
if r.audioSampleRate < 0 {
r.audioSampleRate = pcmDefaultSampleRate
}
}
}
@ -151,10 +156,37 @@ func (r *Rtmp2RtspRemuxer) doAnalyze() {
}
if r.asc != nil {
r.audioPt = base.AvPacketPtAac
var ascCtx *aac.AscContext
ascCtx, err := aac.NewAscContext(r.asc)
if err != nil {
r.asc = nil
Log.Warn("invalid asc")
return
}
// aac的采样率以asc为准
r.audioSampleRate, err = ascCtx.GetSamplingFrequency()
if err != nil {
r.asc = nil
Log.Warn("invalid asc")
return
}
}
// 回调sdp
ctx, err := sdp.Pack(r.vps, r.sps, r.pps, r.asc, r.audioPt, r.audioSampleRate)
videoInfo := sdp.VideoInfo{
VideoPt: r.videoPt,
Vps: r.vps,
Sps: r.sps,
Pps: r.pps,
}
audioInfo := sdp.AudioInfo{
AudioPt: r.audioPt,
Asc: r.asc,
SamplingFrequency: r.audioSampleRate,
}
ctx, err := sdp.Pack(videoInfo, audioInfo)
Log.Assert(nil, err)
r.onSdp(ctx)
@ -229,6 +261,8 @@ func (r *Rtmp2RtspRemuxer) getAudioPacker() *rtprtcp.RtpPacker {
r.audioSsrc = rand.Uint32()
switch r.audioPt {
case base.AvPacketPtG711A:
fallthrough
case base.AvPacketPtG711U:
pp := rtprtcp.NewRtpPackerPayloadPcm()
r.audioPacker = rtprtcp.NewRtpPacker(pp, r.audioSampleRate, r.audioSsrc)

@ -14,53 +14,22 @@ import (
"fmt"
"strings"
"github.com/q191201771/naza/pkg/nazaerrors"
"github.com/q191201771/lal/pkg/aac"
"github.com/q191201771/lal/pkg/base"
)
// Pack
//
// @param samplingFrequency: 音频采样率注意当前G711U使用这个参数传递AAC的采样率通过 asc 参数传递 TODO(chef): 考虑 AAC 采样率通过 samplingFrequency 传递 202303
func Pack(vps, sps, pps, asc []byte, audioPt base.AvPacketPt, samplingFrequency int) (ctx LogicContext, err error) {
// 判断音频、视频是否存在以及视频是H264还是H265
var hasAudio, hasVideo, isHevc, isAac bool
if sps != nil && pps != nil {
hasVideo = true
if vps != nil {
isHevc = true
}
}
if audioPt != base.AvPacketPtUnknown {
switch audioPt {
case base.AvPacketPtG711U:
hasAudio = true
case base.AvPacketPtAac:
if asc != nil {
isAac = true
hasAudio = true
// 判断AAC的采样率
var ascCtx *aac.AscContext
ascCtx, err = aac.NewAscContext(asc)
if err != nil {
return
}
samplingFrequency, err = ascCtx.GetSamplingFrequency()
if err != nil {
return
}
}
}
}
type VideoInfo struct {
VideoPt base.AvPacketPt
Vps, Sps, Pps []byte
}
if !hasAudio && !hasVideo {
err = nazaerrors.Wrap(base.ErrSdp)
return
}
type AudioInfo struct {
AudioPt base.AvPacketPt
SamplingFrequency int
Asc []byte
}
func Pack(videoInfo VideoInfo, audioInfo AudioInfo) (ctx LogicContext, err error) {
// 组装SDP头部
sdpStr := fmt.Sprintf(`v=0
o=- 0 0 IN IP4 127.0.0.1
s=No Name
@ -69,47 +38,79 @@ t=0 0
a=tool:%s
`, base.LalPackSdp)
// 组装视频SDP信息
streamid := 0
videoSdpStr := buildVideoSdpInfo(videoInfo, streamid)
if videoSdpStr != "" {
sdpStr += videoSdpStr
streamid++
}
if hasVideo {
if isHevc {
tmpl := `m=video 0 RTP/AVP 98
a=rtpmap:98 H265/90000
a=fmtp:98 profile-id=1;sprop-sps=%s;sprop-pps=%s;sprop-vps=%s
a=control:streamid=%d
`
sdpStr += fmt.Sprintf(tmpl, base64.StdEncoding.EncodeToString(sps), base64.StdEncoding.EncodeToString(pps), base64.StdEncoding.EncodeToString(vps), streamid)
} else {
tmpl := `m=video 0 RTP/AVP 96
// 组装音频SDP信息
audioSdpStr := buildAudioSdpInfo(audioInfo, streamid)
if audioSdpStr != "" {
sdpStr += audioSdpStr
}
raw := []byte(strings.ReplaceAll(sdpStr, "\n", "\r\n"))
ctx, err = ParseSdp2LogicContext(raw)
return
}
func buildVideoSdpInfo(videoInfo VideoInfo, streamid int) string {
if videoInfo.VideoPt == base.AvPacketPtAvc {
if videoInfo.Sps == nil || videoInfo.Pps == nil {
return ""
}
tmpl := `m=video 0 RTP/AVP %d
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1; sprop-parameter-sets=%s,%s; profile-level-id=640016
a=control:streamid=%d
`
sdpStr += fmt.Sprintf(tmpl, base64.StdEncoding.EncodeToString(sps), base64.StdEncoding.EncodeToString(pps), streamid)
return fmt.Sprintf(tmpl, base.AvPacketPtAvc, base64.StdEncoding.EncodeToString(videoInfo.Sps), base64.StdEncoding.EncodeToString(videoInfo.Pps), streamid)
} else if videoInfo.VideoPt == base.AvPacketPtHevc {
if videoInfo.Sps == nil || videoInfo.Pps == nil || videoInfo.Vps == nil {
return ""
}
streamid++
tmpl := `m=video 0 RTP/AVP %d
a=rtpmap:98 H265/90000
a=fmtp:98 profile-id=1;sprop-sps=%s;sprop-pps=%s;sprop-vps=%s
a=control:streamid=%d
`
return fmt.Sprintf(tmpl, base.AvPacketPtHevc, base64.StdEncoding.EncodeToString(videoInfo.Sps), base64.StdEncoding.EncodeToString(videoInfo.Pps), base64.StdEncoding.EncodeToString(videoInfo.Vps), streamid)
}
if hasAudio {
if isAac {
tmpl := `m=audio 0 RTP/AVP 97
return ""
}
func buildAudioSdpInfo(audioInfo AudioInfo, streamid int) string {
if audioInfo.AudioPt == base.AvPacketPtAac {
if audioInfo.Asc == nil {
return ""
}
tmpl := `m=audio 0 RTP/AVP %d
b=AS:128
a=rtpmap:97 MPEG4-GENERIC/%d/2
a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=%s
a=rtpmap:%d MPEG4-GENERIC/%d/2
a=fmtp:%d profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=%s
a=control:streamid=%d
`
sdpStr += fmt.Sprintf(tmpl, samplingFrequency, hex.EncodeToString(asc), streamid)
} else {
tmpl := `m=audio 0 RTP/AVP 0
a=rtpmap:0 PCMU/%d
return fmt.Sprintf(tmpl, base.AvPacketPtAac, base.AvPacketPtAac, audioInfo.SamplingFrequency, base.AvPacketPtAac, hex.EncodeToString(audioInfo.Asc), streamid)
} else if audioInfo.AudioPt == base.AvPacketPtG711A {
tmpl := `m=audio 0 RTP/AVP %d
a=rtpmap:%d PCMA/%d
a=control:streamid=%d
`
sdpStr += fmt.Sprintf(tmpl, samplingFrequency, streamid)
}
return fmt.Sprintf(tmpl, base.AvPacketPtG711A, base.AvPacketPtG711A, audioInfo.SamplingFrequency, streamid)
} else if audioInfo.AudioPt == base.AvPacketPtG711U {
tmpl := `m=audio 0 RTP/AVP %d
a=rtpmap:%d PCMU/%d
a=control:streamid=%d
`
return fmt.Sprintf(tmpl, base.AvPacketPtG711U, base.AvPacketPtG711U, audioInfo.SamplingFrequency, streamid)
}
raw := []byte(strings.ReplaceAll(sdpStr, "\n", "\r\n"))
ctx, err = ParseSdp2LogicContext(raw)
return
return ""
}

Loading…
Cancel
Save