From 411ba82181a388f4a9897149159c3aff4afbfec4 Mon Sep 17 00:00:00 2001 From: ZSC714725 Date: Wed, 15 Mar 2023 20:54:43 +0800 Subject: [PATCH] rtmp2rtsp support g711a --- pkg/base/t_http_an__.go | 1 + pkg/base/t_rtmp.go | 1 + pkg/logic/group__core_streaming.go | 2 + pkg/remux/remux.go | 2 +- pkg/remux/rtmp2rtsp.go | 38 +++++++- pkg/sdp/pack.go | 137 +++++++++++++++-------------- 6 files changed, 110 insertions(+), 71 deletions(-) diff --git a/pkg/base/t_http_an__.go b/pkg/base/t_http_an__.go index 9a109bb..15f3f25 100644 --- a/pkg/base/t_http_an__.go +++ b/pkg/base/t_http_an__.go @@ -17,6 +17,7 @@ const ( // AudioCodecAac StatGroup.AudioCodec AudioCodecAac = "AAC" AudioCodecG711U = "PCMU" + AudioCodecG711A = "PCMA" // VideoCodecAvc StatGroup.VideoCodec VideoCodecAvc = "H264" diff --git a/pkg/base/t_rtmp.go b/pkg/base/t_rtmp.go index fa1adbd..299f0e3 100644 --- a/pkg/base/t_rtmp.go +++ b/pkg/base/t_rtmp.go @@ -88,6 +88,7 @@ const ( // AACPacketType UI8 // Data UI8[n] // 注意,视频的CodecId是后4位,音频是前4位 + RtmpSoundFormatG711A uint8 = 7 RtmpSoundFormatG711U uint8 = 8 RtmpSoundFormatAac uint8 = 10 diff --git a/pkg/logic/group__core_streaming.go b/pkg/logic/group__core_streaming.go index 1973fe8..f06d560 100644 --- a/pkg/logic/group__core_streaming.go +++ b/pkg/logic/group__core_streaming.go @@ -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 } } } diff --git a/pkg/remux/remux.go b/pkg/remux/remux.go index 8887705..3ac0eb0 100644 --- a/pkg/remux/remux.go +++ b/pkg/remux/remux.go @@ -12,4 +12,4 @@ package remux var _ iRtmp2MpegtsFilterObserver = &Rtmp2MpegtsRemuxer{} -const g711uSampleRate = 8000 +const pcmDefaultSampleRate = 8000 diff --git a/pkg/remux/rtmp2rtsp.go b/pkg/remux/rtmp2rtsp.go index 6eef0b6..9252c5f 100644 --- a/pkg/remux/rtmp2rtsp.go +++ b/pkg/remux/rtmp2rtsp.go @@ -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) diff --git a/pkg/sdp/pack.go b/pkg/sdp/pack.go index 3b82d22..624dcde 100644 --- a/pkg/sdp/pack.go +++ b/pkg/sdp/pack.go @@ -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 "" }