diff --git a/pkg/base/avpacket.go b/pkg/base/avpacket.go index 5f6a2e9..376db3d 100644 --- a/pkg/base/avpacket.go +++ b/pkg/base/avpacket.go @@ -21,6 +21,7 @@ type AvPacketPt int const ( AvPacketPtUnknown AvPacketPt = -1 + AvPacketPtG711U AvPacketPt = 0 // g711u AvPacketPtG711A AvPacketPt = 8 // g711a AvPacketPtAvc AvPacketPt = 96 // h264 AvPacketPtHevc AvPacketPt = 98 // h265 diff --git a/pkg/base/t_http_an__.go b/pkg/base/t_http_an__.go index 2372caf..9a109bb 100644 --- a/pkg/base/t_http_an__.go +++ b/pkg/base/t_http_an__.go @@ -15,7 +15,8 @@ package base const ( // AudioCodecAac StatGroup.AudioCodec - AudioCodecAac = "AAC" + AudioCodecAac = "AAC" + AudioCodecG711U = "PCMU" // VideoCodecAvc StatGroup.VideoCodec VideoCodecAvc = "H264" diff --git a/pkg/base/t_rtmp.go b/pkg/base/t_rtmp.go index 6570d3b..fa1adbd 100644 --- a/pkg/base/t_rtmp.go +++ b/pkg/base/t_rtmp.go @@ -11,6 +11,7 @@ package base import ( "encoding/hex" "fmt" + "github.com/q191201771/naza/pkg/bele" "github.com/q191201771/naza/pkg/nazabytes" ) @@ -86,9 +87,12 @@ const ( // AACAUDIODATA // AACPacketType UI8 // Data UI8[n] - RtmpSoundFormatAac uint8 = 10 // 注意,视频的CodecId是后4位,音频是前4位 - RtmpAacPacketTypeSeqHeader = 0 - RtmpAacPacketTypeRaw = 1 + // 注意,视频的CodecId是后4位,音频是前4位 + RtmpSoundFormatG711U uint8 = 8 + RtmpSoundFormatAac uint8 = 10 + + RtmpAacPacketTypeSeqHeader = 0 + RtmpAacPacketTypeRaw = 1 ) type RtmpHeader struct { @@ -129,13 +133,17 @@ func (msg RtmpMsg) IsVideoKeyNalu() bool { } func (msg RtmpMsg) IsAacSeqHeader() bool { - return msg.Header.MsgTypeId == RtmpTypeIdAudio && (msg.Payload[0]>>4) == RtmpSoundFormatAac && msg.Payload[1] == RtmpAacPacketTypeSeqHeader + return msg.Header.MsgTypeId == RtmpTypeIdAudio && msg.AudioCodecId() == RtmpSoundFormatAac && msg.Payload[1] == RtmpAacPacketTypeSeqHeader } func (msg RtmpMsg) VideoCodecId() uint8 { return msg.Payload[0] & 0xF } +func (msg RtmpMsg) AudioCodecId() uint8 { + return msg.Payload[0] >> 4 +} + func (msg RtmpMsg) Clone() (ret RtmpMsg) { ret.Header = msg.Header ret.Payload = make([]byte, len(msg.Payload)) diff --git a/pkg/logic/group__core_streaming.go b/pkg/logic/group__core_streaming.go index ab8729b..1973fe8 100644 --- a/pkg/logic/group__core_streaming.go +++ b/pkg/logic/group__core_streaming.go @@ -394,8 +394,15 @@ func (group *Group) broadcastByRtmpMsg(msg base.RtmpMsg) { // # 记录stat if group.stat.AudioCodec == "" { - if msg.IsAacSeqHeader() { - group.stat.AudioCodec = base.AudioCodecAac + if msg.Header.MsgTypeId == base.RtmpTypeIdAudio { + switch msg.AudioCodecId() { + case base.RtmpSoundFormatAac: + if msg.IsAacSeqHeader() { + group.stat.AudioCodec = base.AudioCodecAac + } + case base.RtmpSoundFormatG711U: + group.stat.AudioCodec = base.AudioCodecG711U + } } } if group.stat.VideoCodec == "" { diff --git a/pkg/remux/rtmp2rtsp.go b/pkg/remux/rtmp2rtsp.go index e157597..57da422 100644 --- a/pkg/remux/rtmp2rtsp.go +++ b/pkg/remux/rtmp2rtsp.go @@ -10,10 +10,11 @@ package remux import ( "encoding/hex" - "github.com/q191201771/lal/pkg/h2645" "math/rand" "time" + "github.com/q191201771/lal/pkg/h2645" + "github.com/q191201771/lal/pkg/aac" "github.com/q191201771/lal/pkg/avc" "github.com/q191201771/lal/pkg/base" @@ -74,6 +75,12 @@ func (r *Rtmp2RtspRemuxer) FeedRtmpMsg(msg base.RtmpMsg) { Log.Warnf("rtmp msg too short, ignore. header=%+v, payload=%s", msg.Header, hex.Dump(msg.Payload)) return } + if r.audioPt == base.AvPacketPtUnknown { + switch msg.AudioCodecId() { + case base.RtmpSoundFormatG711U: + r.audioPt = base.AvPacketPtG711U + } + } case base.RtmpTypeIdVideo: if len(msg.Payload) <= 5 { Log.Warnf("rtmp msg too short, ignore. header=%+v, payload=%s", msg.Header, hex.Dump(msg.Payload)) @@ -136,7 +143,7 @@ func (r *Rtmp2RtspRemuxer) doAnalyze() { } // 回调sdp - ctx, err := sdp.Pack(r.vps, r.sps, r.pps, r.asc) + ctx, err := sdp.Pack(r.vps, r.sps, r.pps, r.asc, r.audioPt) Log.Assert(nil, err) r.onSdp(ctx) @@ -206,26 +213,32 @@ func (r *Rtmp2RtspRemuxer) remux(msg base.RtmpMsg) { } func (r *Rtmp2RtspRemuxer) getAudioPacker() *rtprtcp.RtpPacker { - if r.asc == nil { - return nil - } - if r.audioPacker == nil { // TODO(chef): ssrc随机产生,并且整个lal没有在setup信令中传递ssrc r.audioSsrc = rand.Uint32() - ascCtx, err := aac.NewAscContext(r.asc) - if err != nil { - Log.Errorf("parse asc failed. err=%+v", err) - return nil - } - clockRate, err := ascCtx.GetSamplingFrequency() - if err != nil { - Log.Errorf("get sampling frequency failed. err=%+v, asc=%s", err, hex.Dump(r.asc)) - } + switch r.audioPt { + case base.AvPacketPtG711U: + pp := rtprtcp.NewRtpPackerPayloadPcm() + r.audioPacker = rtprtcp.NewRtpPacker(pp, 8000, r.audioSsrc) + case base.AvPacketPtAac: + if r.asc == nil { + return nil + } - pp := rtprtcp.NewRtpPackerPayloadAac() - r.audioPacker = rtprtcp.NewRtpPacker(pp, clockRate, r.audioSsrc) + ascCtx, err := aac.NewAscContext(r.asc) + if err != nil { + Log.Errorf("parse asc failed. err=%+v", err) + return nil + } + clockRate, err := ascCtx.GetSamplingFrequency() + if err != nil { + Log.Errorf("get sampling frequency failed. err=%+v, asc=%s", err, hex.Dump(r.asc)) + } + + pp := rtprtcp.NewRtpPackerPayloadAac() + r.audioPacker = rtprtcp.NewRtpPacker(pp, clockRate, r.audioSsrc) + } } return r.audioPacker } diff --git a/pkg/rtprtcp/rtp_packer_payload_pcm.go b/pkg/rtprtcp/rtp_packer_payload_pcm.go new file mode 100644 index 0000000..f587c86 --- /dev/null +++ b/pkg/rtprtcp/rtp_packer_payload_pcm.go @@ -0,0 +1,31 @@ +// Copyright 2021, 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 rtprtcp + +type RtpPackerPayloadPcm struct { +} + +func NewRtpPackerPayloadPcm() *RtpPackerPayloadPcm { + return &RtpPackerPayloadPcm{} +} + +func (r *RtpPackerPayloadPcm) Pack(in []byte, maxSize int) (out [][]byte) { + if in == nil || maxSize <= 0 { + return + } + + if len(in) > maxSize { + Log.Warnf("frame size bigger than rtp payload size while packing. len(in)=%d, maxSize=%d", len(in), maxSize) + } + + item := make([]byte, len(in)) + copy(item, in) + out = append(out, item) + return +} diff --git a/pkg/sdp/pack.go b/pkg/sdp/pack.go index 9babe9a..f39fb0d 100644 --- a/pkg/sdp/pack.go +++ b/pkg/sdp/pack.go @@ -21,17 +21,38 @@ import ( "github.com/q191201771/lal/pkg/base" ) -func Pack(vps, sps, pps, asc []byte) (ctx LogicContext, err error) { +func Pack(vps, sps, pps, asc []byte, audioPt base.AvPacketPt) (ctx LogicContext, err error) { // 判断音频、视频是否存在,以及视频是H264还是H265 - var hasAudio, hasVideo, isHevc bool + var hasAudio, hasVideo, isHevc, isAac bool if sps != nil && pps != nil { hasVideo = true if vps != nil { isHevc = true } } - if asc != nil { - hasAudio = true + + var samplingFrequency int + if audioPt != base.AvPacketPtUnknown { + switch audioPt { + case base.AvPacketPtG711U: + hasAudio = true + samplingFrequency = 8000 + 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 + } + } + } } if !hasAudio && !hasVideo { @@ -39,20 +60,6 @@ func Pack(vps, sps, pps, asc []byte) (ctx LogicContext, err error) { return } - // 判断AAC的采样率 - var samplingFrequency int - if asc != nil { - var ascCtx *aac.AscContext - ascCtx, err = aac.NewAscContext(asc) - if err != nil { - return - } - samplingFrequency, err = ascCtx.GetSamplingFrequency() - if err != nil { - return - } - } - sdpStr := fmt.Sprintf(`v=0 o=- 0 0 IN IP4 127.0.0.1 s=No Name @@ -84,13 +91,21 @@ a=control:streamid=%d } if hasAudio { - tmpl := `m=audio 0 RTP/AVP 97 + if isAac { + tmpl := `m=audio 0 RTP/AVP 97 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=control:streamid=%d ` - sdpStr += fmt.Sprintf(tmpl, samplingFrequency, hex.EncodeToString(asc), streamid) + sdpStr += fmt.Sprintf(tmpl, samplingFrequency, hex.EncodeToString(asc), streamid) + } else { + tmpl := `m=audio 0 RTP/AVP 0 +a=rtpmap:0 PCMU/%d +a=control:streamid=%d +` + sdpStr += fmt.Sprintf(tmpl, samplingFrequency, streamid) + } } raw := []byte(strings.ReplaceAll(sdpStr, "\n", "\r\n")) diff --git a/pkg/sdp/parse_logic.go b/pkg/sdp/parse_logic.go index 521711f..0115458 100644 --- a/pkg/sdp/parse_logic.go +++ b/pkg/sdp/parse_logic.go @@ -129,6 +129,8 @@ func ParseSdp2LogicContext(b []byte) (LogicContext, error) { // 例子:a=rtpmap:8 PCMA/8000/1 // rtmpmap中有PCMA字段表示G711A ret.audioPayloadTypeBase = base.AvPacketPtG711A + } else if strings.EqualFold(md.ARtpMap.EncodingName, ARtpMapEncodingNameG711U) { + ret.audioPayloadTypeBase = base.AvPacketPtG711U } else { if md.M.PT == 8 { // ffmpeg推流情况下不会填充rtpmap字段,m中pt值为8也可以表示是PCMA,采样率默认为8000Hz diff --git a/pkg/sdp/sdp.go b/pkg/sdp/sdp.go index 826bb49..8dc74c0 100644 --- a/pkg/sdp/sdp.go +++ b/pkg/sdp/sdp.go @@ -15,4 +15,5 @@ const ( ARtpMapEncodingNameH264 = "H264" ARtpMapEncodingNameAac = "MPEG4-GENERIC" ARtpMapEncodingNameG711A = "PCMA" + ARtpMapEncodingNameG711U = "PCMU" )