You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
lal/pkg/remux/avpacket2rtmp.go

294 lines
7.8 KiB
Go

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// 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 remux
import (
"github.com/q191201771/lal/pkg/aac"
"github.com/q191201771/lal/pkg/avc"
"github.com/q191201771/lal/pkg/base"
"github.com/q191201771/lal/pkg/hevc"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/lal/pkg/rtprtcp"
"github.com/q191201771/lal/pkg/sdp"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
// AvPacket转换为RTMP
// 目前AvPacket来自RTSP的sdp以及rtp的合帧包。理论上也支持webrtc后续接入webrtc时再验证
//
type AvPacket2RtmpRemuxer struct {
onRtmpAvMsg rtmp.OnReadRtmpAvMsg
hasEmittedMetadata bool
audioType base.AvPacketPt
videoType base.AvPacketPt
vps []byte // 从AvPacket数据中获取
sps []byte
pps []byte
}
func NewAvPacket2RtmpRemuxer(onRtmpAvMsg rtmp.OnReadRtmpAvMsg) *AvPacket2RtmpRemuxer {
return &AvPacket2RtmpRemuxer{
onRtmpAvMsg: onRtmpAvMsg,
audioType: base.AvPacketPtUnknown,
videoType: base.AvPacketPtUnknown,
}
}
// 实现RTSP回调数据的三个接口使得接入时方便些
func (r *AvPacket2RtmpRemuxer) OnRtpPacket(pkt rtprtcp.RtpPacket) {
// noop
}
func (r *AvPacket2RtmpRemuxer) OnSdp(sdpCtx sdp.LogicContext) {
r.InitWithAvConfig(sdpCtx.Asc, sdpCtx.Vps, sdpCtx.Sps, sdpCtx.Pps)
}
func (r *AvPacket2RtmpRemuxer) OnAvPacket(pkt base.AvPacket) {
r.FeedAvPacket(pkt)
}
// rtsp场景下有时sps、pps等信息只包含在sdp中有时包含在rtp包中
// 这里提供输入sdp的sps、pps等信息的机会如果没有可以不调用
//
// 内部不持有输入参数的内存块
//
func (r *AvPacket2RtmpRemuxer) InitWithAvConfig(asc, vps, sps, pps []byte) {
var err error
var bVsh []byte
var bAsh []byte
if asc != nil {
r.audioType = base.AvPacketPtAac
}
if sps != nil && pps != nil {
if vps != nil {
r.videoType = base.AvPacketPtHevc
} else {
r.videoType = base.AvPacketPtAvc
}
}
if r.audioType == base.AvPacketPtUnknown && r.videoType == base.AvPacketPtUnknown {
nazalog.Warn("has no audio or video")
return
}
if r.audioType != base.AvPacketPtUnknown {
bAsh, err = aac.MakeAudioDataSeqHeaderWithAsc(asc)
if err != nil {
nazalog.Errorf("build aac seq header failed. err=%+v", err)
return
}
}
if r.videoType != base.AvPacketPtUnknown {
if r.videoType == base.AvPacketPtHevc {
bVsh, err = hevc.BuildSeqHeaderFromVpsSpsPps(vps, sps, pps)
if err != nil {
nazalog.Errorf("build hevc seq header failed. err=%+v", err)
return
}
} else {
bVsh, err = avc.BuildSeqHeaderFromSpsPps(sps, pps)
if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err)
return
}
}
}
if r.audioType != base.AvPacketPtUnknown {
r.emitRtmpAvMsg(true, bAsh, 0)
}
if r.videoType != base.AvPacketPtUnknown {
r.emitRtmpAvMsg(false, bVsh, 0)
}
}
// @param pkt: 内部不持有该内存块
//
func (r *AvPacket2RtmpRemuxer) FeedAvPacket(pkt base.AvPacket) {
switch pkt.PayloadType {
case base.AvPacketPtAvc:
fallthrough
case base.AvPacketPtHevc:
nals, err := avc.SplitNaluAvcc(pkt.Payload)
if err != nil {
nazalog.Errorf("iterate nalu failed. err=%+v", err)
return
}
pos := 5
maxLength := len(pkt.Payload) + pos
payload := make([]byte, maxLength)
for _, nal := range nals {
if pkt.PayloadType == base.AvPacketPtAvc {
t := avc.ParseNaluType(nal[0])
if t == avc.NaluTypeSps || t == avc.NaluTypePps {
// 如果有spspps先把它们抽离出来进行缓存
if t == avc.NaluTypeSps {
r.setSps(nal)
} else {
r.setPps(nal)
}
// 注意由于sps空值时可能是nil也可能是[0:0]所以这里不用nil做判断而用len
if len(r.sps) > 0 && len(r.pps) > 0 {
// 凑齐了发送video seq header
//
// TODO(chef): 是否应该判断sps、pps是连续的比如rtp seq的关系或者timestamp是相等的
bVsh, err := avc.BuildSeqHeaderFromSpsPps(r.sps, r.pps)
if err != nil {
nazalog.Errorf("build avc seq header failed. err=%+v", err)
continue
}
r.emitRtmpAvMsg(false, bVsh, pkt.Timestamp)
r.clearVideoSeqHeader()
}
} else {
// 重组实际数据
if t == avc.NaluTypeIdrSlice {
payload[0] = base.RtmpAvcKeyFrame
} else {
payload[0] = base.RtmpAvcInterFrame
}
payload[1] = base.RtmpAvcPacketTypeNalu
bele.BePutUint32(payload[pos:], uint32(len(nal)))
pos += 4
copy(payload[pos:], nal)
pos += len(nal)
}
} else if pkt.PayloadType == base.AvPacketPtHevc {
t := hevc.ParseNaluType(nal[0])
if t == hevc.NaluTypeVps || t == hevc.NaluTypeSps || t == hevc.NaluTypePps {
if t == hevc.NaluTypeVps {
r.setVps(nal)
} else if t == hevc.NaluTypeSps {
r.setSps(nal)
} else {
r.setPps(nal)
}
if len(r.vps) > 0 && len(r.sps) > 0 && len(r.pps) > 0 {
bVsh, err := hevc.BuildSeqHeaderFromVpsSpsPps(r.vps, r.sps, r.pps)
if err != nil {
nazalog.Errorf("build hevc seq header failed. err=%+v", err)
continue
}
r.emitRtmpAvMsg(false, bVsh, pkt.Timestamp)
r.clearVideoSeqHeader()
}
} else {
if t == hevc.NaluTypeSliceIdr || t == hevc.NaluTypeSliceIdrNlp || t == hevc.NaluTypeSliceCranut {
payload[0] = base.RtmpHevcKeyFrame
} else {
payload[0] = base.RtmpHevcInterFrame
}
payload[1] = base.RtmpHevcPacketTypeNalu
bele.BePutUint32(payload[pos:], uint32(len(nal)))
pos += 4
copy(payload[pos:], nal)
pos += len(nal)
}
}
}
// 有实际数据
if pos > 5 {
r.emitRtmpAvMsg(false, payload[:pos], pkt.Timestamp)
}
case base.AvPacketPtAac:
length := len(pkt.Payload) + 2
payload := make([]byte, length)
// TODO(chef) 处理此处的魔数0xAF
payload[0] = 0xAF
payload[1] = base.RtmpAacPacketTypeRaw
copy(payload[2:], pkt.Payload)
r.emitRtmpAvMsg(true, payload, pkt.Timestamp)
default:
nazalog.Warnf("unsupported packet. type=%d", pkt.PayloadType)
}
}
func (r *AvPacket2RtmpRemuxer) emitRtmpAvMsg(isAudio bool, payload []byte, timestamp uint32) {
if !r.hasEmittedMetadata {
// TODO(chef): 此处简化了从sps中获取宽高写入metadata的逻辑
audiocodecid := -1
videocodecid := -1
if r.audioType == base.AvPacketPtAac {
audiocodecid = int(base.RtmpSoundFormatAac)
}
switch r.videoType {
case base.AvPacketPtAvc:
videocodecid = int(base.RtmpCodecIdAvc)
case base.AvPacketPtHevc:
videocodecid = int(base.RtmpCodecIdHevc)
}
bMetadata, err := rtmp.BuildMetadata(-1, -1, audiocodecid, videocodecid)
if err != nil {
nazalog.Errorf("build metadata failed. err=%+v", err)
return
}
r.onRtmpAvMsg(base.RtmpMsg{
Header: base.RtmpHeader{
Csid: rtmp.CsidAmf,
MsgLen: uint32(len(bMetadata)),
MsgTypeId: base.RtmpTypeIdMetadata,
MsgStreamId: rtmp.Msid1,
TimestampAbs: 0,
},
Payload: bMetadata,
})
r.hasEmittedMetadata = true
}
var msg base.RtmpMsg
msg.Header.MsgStreamId = rtmp.Msid1
if isAudio {
msg.Header.Csid = rtmp.CsidAudio
msg.Header.MsgTypeId = base.RtmpTypeIdAudio
} else {
msg.Header.Csid = rtmp.CsidVideo
msg.Header.MsgTypeId = base.RtmpTypeIdVideo
}
msg.Header.MsgLen = uint32(len(payload))
msg.Header.TimestampAbs = timestamp
msg.Payload = payload
r.onRtmpAvMsg(msg)
}
func (r *AvPacket2RtmpRemuxer) setVps(b []byte) {
r.vps = r.vps[0:0]
r.vps = append(r.vps, b...)
}
func (r *AvPacket2RtmpRemuxer) setSps(b []byte) {
r.sps = r.sps[0:0]
r.sps = append(r.sps, b...)
}
func (r *AvPacket2RtmpRemuxer) setPps(b []byte) {
r.pps = r.pps[0:0]
r.pps = append(r.pps, b...)
}
func (r *AvPacket2RtmpRemuxer) clearVideoSeqHeader() {
r.vps = r.vps[0:0]
r.sps = r.sps[0:0]
r.pps = r.pps[0:0]
}