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

201 lines
5.7 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/httpflv"
"github.com/q191201771/lal/pkg/rtmp"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
// @param asc 如果为nil则没有音频
// @param vps 如果为nil则是H264如果不为nil则是H265
// @return 返回的内存块为新申请的独立内存块
func AVConfig2FLVTag(asc, vps, sps, pps []byte) (metadata, ash, vsh *httpflv.Tag, 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
err = hevc.ParseSPS(sps, &ctx)
if err == nil {
width = int(ctx.PicWidthInLumaSamples)
height = int(ctx.PicHeightInLumaSamples)
} else {
// TODO chef: 如果解析错误先忽略将失败的case收集起来解决
nazalog.Warnf("parse sps failed.")
}
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 {
width = int(ctx.Width)
height = int(ctx.Height)
} else {
// TODO chef: 如果解析错误先忽略将失败的case收集起来解决
nazalog.Warnf("parse sps failed.")
}
bVsh, err = avc.BuildSeqHeaderFromSPSPPS(sps, pps)
if err != nil {
return
}
}
}
if hasAudio {
bAsh, err = aac.BuildAACSeqHeader(asc)
if err != nil {
return
}
}
var h httpflv.TagHeader
var tagRaw []byte
bMetadata, err = rtmp.BuildMetadata(width, height, audiocodecid, videocodecid)
if err != nil {
return
}
h.Type = base.RTMPTypeIDMetadata
h.DataSize = uint32(len(bMetadata))
h.Timestamp = 0
tagRaw = httpflv.PackHTTPFLVTag(h.Type, h.Timestamp, bMetadata)
metadata = &httpflv.Tag{
Header: h,
Raw: tagRaw,
}
if hasVideo {
h.Type = base.RTMPTypeIDVideo
h.DataSize = uint32(len(bVsh))
h.Timestamp = 0
tagRaw = httpflv.PackHTTPFLVTag(h.Type, h.Timestamp, bVsh)
vsh = &httpflv.Tag{
Header: h,
Raw: tagRaw,
}
}
if hasAudio {
h.Type = base.RTMPTypeIDAudio
h.DataSize = uint32(len(bAsh))
h.Timestamp = 0
tagRaw = httpflv.PackHTTPFLVTag(h.Type, h.Timestamp, bAsh)
ash = &httpflv.Tag{
Header: h,
Raw: tagRaw,
}
}
return
}
// @return 返回的内存块为新申请的独立内存块
func AVPacket2FLVTag(pkt base.AVPacket) (tag httpflv.Tag, err error) {
switch pkt.PayloadType {
case base.AVPacketPTAVC:
fallthrough
case base.AVPacketPTHEVC:
tag.Header.Type = base.RTMPTypeIDVideo
tag.Header.DataSize = uint32(len(pkt.Payload)) + 5
tag.Header.Timestamp = pkt.Timestamp
tag.Raw = make([]byte, httpflv.TagHeaderSize+int(tag.Header.DataSize)+httpflv.PrevTagSizeFieldSize)
tag.Raw[0] = tag.Header.Type
bele.BEPutUint24(tag.Raw[1:], tag.Header.DataSize)
bele.BEPutUint24(tag.Raw[4:], tag.Header.Timestamp&0xFFFFFF)
tag.Raw[7] = uint8(tag.Header.Timestamp >> 24)
tag.Raw[8] = 0
tag.Raw[9] = 0
tag.Raw[10] = 0
// 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 {
tag.Raw[httpflv.TagHeaderSize] = base.RTMPAVCKeyFrame
} else {
tag.Raw[httpflv.TagHeaderSize] = base.RTMPAVCInterFrame
}
tag.Raw[httpflv.TagHeaderSize+1] = base.RTMPAVCPacketTypeNALU
case base.AVPacketPTHEVC:
if t == hevc.NALUTypeSliceIDR || t == hevc.NALUTypeSliceIDRNLP {
tag.Raw[httpflv.TagHeaderSize] = base.RTMPHEVCKeyFrame
} else {
tag.Raw[httpflv.TagHeaderSize] = base.RTMPHEVCInterFrame
}
tag.Raw[httpflv.TagHeaderSize+1] = base.RTMPHEVCPacketTypeNALU
}
i += 4 + naluSize
}
tag.Raw[httpflv.TagHeaderSize+2] = 0x0 // cts
tag.Raw[httpflv.TagHeaderSize+3] = 0x0
tag.Raw[httpflv.TagHeaderSize+4] = 0x0
copy(tag.Raw[httpflv.TagHeaderSize+5:], pkt.Payload)
bele.BEPutUint32(tag.Raw[httpflv.TagHeaderSize+int(tag.Header.DataSize):], uint32(httpflv.TagHeaderSize)+tag.Header.DataSize)
//nazalog.Debugf("%d %s", len(msg.Payload), hex.Dump(msg.Payload[:32]))
case base.AVPacketPTAAC:
tag.Header.Type = base.RTMPTypeIDAudio
tag.Header.DataSize = uint32(len(pkt.Payload)) + 2
tag.Header.Timestamp = pkt.Timestamp
tag.Raw = make([]byte, httpflv.TagHeaderSize+int(tag.Header.DataSize)+httpflv.PrevTagSizeFieldSize)
tag.Raw[0] = tag.Header.Type
bele.BEPutUint24(tag.Raw[1:], tag.Header.DataSize)
bele.BEPutUint24(tag.Raw[4:], tag.Header.Timestamp&0xFFFFFF)
tag.Raw[7] = uint8(tag.Header.Timestamp >> 24)
tag.Raw[8] = 0
tag.Raw[9] = 0
tag.Raw[10] = 0
tag.Raw[httpflv.TagHeaderSize] = 0xAF
tag.Raw[httpflv.TagHeaderSize+1] = base.RTMPAACPacketTypeRaw
copy(tag.Raw[httpflv.TagHeaderSize+2:], pkt.Payload)
bele.BEPutUint32(tag.Raw[httpflv.TagHeaderSize+int(tag.Header.DataSize):], uint32(httpflv.TagHeaderSize)+tag.Header.DataSize)
default:
err = ErrRemux
return
}
return
}