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

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 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
}