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/rtmp2avpacket.go

156 lines
4.2 KiB
Go

// Copyright 2022, 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/base"
"github.com/q191201771/lal/pkg/h2645"
)
// TODO(chef): 该文件处于开发阶段,请不要直接使用
// TODO(chef): 支持音频 202206
// Rtmp2AvPacketRemuxer
//
// 用途:
// - 将rtmp流中的视频转换成ffmpeg可解码的格式
type Rtmp2AvPacketRemuxer struct {
option Rtmp2AvPacketRemuxerOption
onAvPacket func(pkt base.AvPacket, arg interface{})
spspps []byte // annexb格式
}
type Rtmp2AvPacketRemuxerOption struct {
// TODO(chef): impl me 202206
TryInPlaceFlag bool // 尝试在原有内存上直接修改
}
var defaultRtmp2AvPacketRemuxerOption = Rtmp2AvPacketRemuxerOption{
TryInPlaceFlag: false,
}
func NewRtmp2AvPacketRemuxer() *Rtmp2AvPacketRemuxer {
return &Rtmp2AvPacketRemuxer{
option: defaultRtmp2AvPacketRemuxerOption,
onAvPacket: defaultOnAvPacket,
}
}
func (r *Rtmp2AvPacketRemuxer) WithOption(modOption func(option *Rtmp2AvPacketRemuxerOption)) *Rtmp2AvPacketRemuxer {
modOption(&r.option)
return r
}
// WithOnAvPacket
//
// @param onAvPacket: pkt 内存由内部新申请,回调后内部不再使用
func (r *Rtmp2AvPacketRemuxer) WithOnAvPacket(onAvPacket func(pkt base.AvPacket, arg interface{})) *Rtmp2AvPacketRemuxer {
r.onAvPacket = onAvPacket
return r
}
func (r *Rtmp2AvPacketRemuxer) FeedRtmpMsg(msg base.RtmpMsg, arg interface{}) error {
switch msg.Header.MsgTypeId {
case base.RtmpTypeIdVideo:
return r.feedVideo(msg, arg)
}
return nil
}
// ---------------------------------------------------------------------------------------------------------------------
func (r *Rtmp2AvPacketRemuxer) feedVideo(msg base.RtmpMsg, arg interface{}) error {
if len(msg.Payload) <= 5 {
return nil
}
isH264 := msg.VideoCodecId() == base.RtmpCodecIdAvc
var err error
if msg.IsVideoKeySeqHeader() {
r.spspps, err = h2645.SeqHeader2Annexb(isH264, msg.Payload)
return err
}
var out []byte
var vps, sps, pps []byte
err = h2645.IterateNaluAvcc(msg.Payload[5:], func(nal []byte) {
nalType := h2645.ParseNaluType(isH264, nal[0])
if isH264 {
if nalType == h2645.H264NaluTypeSps {
sps = nal
} else if nalType == h2645.H264NaluTypePps {
pps = nal
if len(sps) != 0 && len(pps) != 0 {
r.spspps = r.spspps[0:0]
r.spspps = append(r.spspps, h2645.NaluStartCode4...)
r.spspps = append(r.spspps, sps...)
r.spspps = append(r.spspps, h2645.NaluStartCode4...)
r.spspps = append(r.spspps, pps...)
}
} else if nalType == h2645.H264NaluTypeIdrSlice {
out = append(out, r.spspps...)
out = append(out, h2645.NaluStartCode4...)
out = append(out, nal...)
} else {
out = append(out, h2645.NaluStartCode4...)
out = append(out, nal...)
}
} else {
if nalType == h2645.H265NaluTypeVps {
vps = nal
} else if nalType == h2645.H265NaluTypeSps {
sps = nal
} else if nalType == h2645.H265NaluTypePps {
pps = nal
if len(vps) != 0 && len(sps) != 0 && len(pps) != 0 {
r.spspps = r.spspps[0:0]
r.spspps = append(r.spspps, h2645.NaluStartCode4...)
r.spspps = append(r.spspps, vps...)
r.spspps = append(r.spspps, h2645.NaluStartCode4...)
r.spspps = append(r.spspps, sps...)
r.spspps = append(r.spspps, h2645.NaluStartCode4...)
r.spspps = append(r.spspps, pps...)
}
} else if h2645.H265IsIrapNalu(nalType) {
out = append(out, r.spspps...)
out = append(out, h2645.NaluStartCode4...)
out = append(out, nal...)
} else {
out = append(out, h2645.NaluStartCode4...)
out = append(out, nal...)
}
}
})
if len(out) > 0 {
pkt := base.AvPacket{
Timestamp: int64(msg.Header.TimestampAbs),
Pts: int64(msg.Pts()),
Payload: out,
}
if isH264 {
pkt.PayloadType = base.AvPacketPtAvc
} else {
pkt.PayloadType = base.AvPacketPtHevc
}
r.onAvPacket(pkt, arg)
}
return err
}
// ---------------------------------------------------------------------------------------------------------------------
func defaultOnAvPacket(pkt base.AvPacket, arg interface{}) {
// noop
}