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

188 lines
4.4 KiB
Go

// Copyright 2020, 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/naza/pkg/bele"
)
// @param asc 如果为nil则没有音频
// @param vps 如果为nil则是H264如果不为nil则是H265
// @return 返回的内存块为新申请的独立内存块
func AVConfig2RTMPMsg(asc, vps, sps, pps []byte) (metadata, ash, vsh *base.RTMPMsg, err error) {
var bMetadata []byte
var bVsh []byte
var bAsh []byte
hasAudio := asc != nil
hasVideo := sps != nil && pps != nil
isHEVC := vps != nil
if !hasAudio && !hasVideo {
err = ErrRemux
return
}
audiocodecid := -1
if hasAudio {
audiocodecid = int(base.RTMPSoundFormatAAC)
}
videocodecid := -1
width := -1
height := -1
if hasVideo {
if isHEVC {
videocodecid = int(base.RTMPCodecIDHEVC)
var ctx hevc.Context
if err = hevc.ParseSPS(sps, &ctx); err != nil {
return
}
width = int(ctx.PicWidthInLumaSamples)
height = int(ctx.PicHeightInLumaSamples)
bVsh, err = hevc.BuildSeqHeaderFromVPSSPSPPS(vps, sps, pps)
if err != nil {
return
}
} else {
videocodecid = int(base.RTMPCodecIDAVC)
var ctx avc.Context
ctx, err = avc.ParseSPS(sps)
if err != nil {
return
}
width = int(ctx.Width)
height = int(ctx.Height)
bVsh, err = avc.BuildSeqHeaderFromSPSPPS(sps, pps)
if err != nil {
return
}
}
}
if hasAudio {
bAsh, err = aac.BuildAACSeqHeader(asc)
if err != nil {
return
}
}
var h base.RTMPHeader
bMetadata, err = rtmp.BuildMetadata(width, height, audiocodecid, videocodecid)
if err != nil {
return
}
h.MsgLen = uint32(len(bMetadata))
h.TimestampAbs = 0
h.MsgTypeID = base.RTMPTypeIDMetadata
h.MsgStreamID = rtmp.MSID1
h.CSID = rtmp.CSIDAMF
metadata = &base.RTMPMsg{
Header: h,
Payload: bMetadata,
}
if hasVideo {
h.MsgLen = uint32(len(bVsh))
h.TimestampAbs = 0
h.MsgTypeID = base.RTMPTypeIDVideo
h.MsgStreamID = rtmp.MSID1
h.CSID = rtmp.CSIDVideo
vsh = &base.RTMPMsg{
Header: h,
Payload: bVsh,
}
}
if hasAudio {
h.MsgLen = uint32(len(bAsh))
h.TimestampAbs = 0
h.MsgTypeID = base.RTMPTypeIDAudio
h.MsgStreamID = rtmp.MSID1
h.CSID = rtmp.CSIDAudio
ash = &base.RTMPMsg{
Header: h,
Payload: bAsh,
}
}
return
}
// @return 返回的内存块为新申请的独立内存块
func AVPacket2RTMPMsg(pkt base.AVPacket) (msg base.RTMPMsg, err error) {
switch pkt.PayloadType {
case base.AVPacketPTAVC:
fallthrough
case base.AVPacketPTHEVC:
msg.Header.TimestampAbs = pkt.Timestamp
msg.Header.MsgStreamID = rtmp.MSID1
msg.Header.MsgTypeID = base.RTMPTypeIDVideo
msg.Header.CSID = rtmp.CSIDVideo
msg.Header.MsgLen = uint32(len(pkt.Payload)) + 5
msg.Payload = make([]byte, msg.Header.MsgLen)
// TODO chef: 这段代码应该放在更合适的地方或者在AVPacket中标识是否包含关键帧
for i := 0; i != len(pkt.Payload); {
naluSize := int(bele.BEUint32(pkt.Payload[i:]))
t := avc.ParseNALUType(pkt.Payload[i+4])
switch pkt.PayloadType {
case base.AVPacketPTAVC:
if t == avc.NALUTypeIDRSlice {
msg.Payload[0] = base.RTMPAVCKeyFrame
} else {
msg.Payload[0] = base.RTMPAVCInterFrame
}
msg.Payload[1] = base.RTMPAVCPacketTypeNALU
case base.AVPacketPTHEVC:
if t == hevc.NALUTypeSliceIDR || t == hevc.NALUTypeSliceIDRNLP {
msg.Payload[0] = base.RTMPHEVCKeyFrame
} else {
msg.Payload[0] = base.RTMPHEVCInterFrame
}
msg.Payload[1] = base.RTMPHEVCPacketTypeNALU
}
i += 4 + naluSize
}
msg.Payload[2] = 0x0 // cts
msg.Payload[3] = 0x0
msg.Payload[4] = 0x0
copy(msg.Payload[5:], pkt.Payload)
//nazalog.Debugf("%d %s", len(msg.Payload), hex.Dump(msg.Payload[:32]))
case base.AVPacketPTAAC:
msg.Header.TimestampAbs = pkt.Timestamp
msg.Header.MsgStreamID = rtmp.MSID1
msg.Header.MsgTypeID = base.RTMPTypeIDAudio
msg.Header.CSID = rtmp.CSIDAudio
msg.Header.MsgLen = uint32(len(pkt.Payload)) + 2
msg.Payload = make([]byte, msg.Header.MsgLen)
msg.Payload[0] = 0xAF
msg.Payload[1] = base.RTMPAACPacketTypeRaw
copy(msg.Payload[2:], pkt.Payload)
default:
err = ErrRemux
return
}
return
}