|
|
// 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 hevc
|
|
|
|
|
|
import (
|
|
|
"bytes"
|
|
|
"errors"
|
|
|
|
|
|
"github.com/q191201771/naza/pkg/nazabits"
|
|
|
|
|
|
"github.com/q191201771/naza/pkg/bele"
|
|
|
)
|
|
|
|
|
|
// HVCC
|
|
|
//
|
|
|
// ISO_IEC_23008-2_2013.pdf
|
|
|
|
|
|
// NAL Unit Header
|
|
|
//
|
|
|
// +---------------+---------------+
|
|
|
// |0|1|2|3|4|5|6|7|0|1|2|3|4|5|6|7|
|
|
|
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
|
// |F| Type | LayerId | TID |
|
|
|
// +-------------+-----------------+
|
|
|
|
|
|
var ErrHevc = errors.New("lal.hevc: fxxk")
|
|
|
|
|
|
var (
|
|
|
NaluStartCode4 = []byte{0x0, 0x0, 0x0, 0x1}
|
|
|
|
|
|
// aud nalu
|
|
|
AudNalu = []byte{0x00, 0x00, 0x00, 0x01, 0x46, 0x01, 0x10}
|
|
|
)
|
|
|
|
|
|
var NaluTypeMapping = map[uint8]string{
|
|
|
NaluTypeSliceTrailN: "TrailN",
|
|
|
NaluTypeSliceTrailR: "TrailR",
|
|
|
NaluTypeSliceIdr: "IDR",
|
|
|
NaluTypeSliceIdrNlp: "IDRNLP",
|
|
|
NaluTypeSliceCranut: "CRANUT",
|
|
|
NaluTypeVps: "VPS",
|
|
|
NaluTypeSps: "SPS",
|
|
|
NaluTypePps: "PPS",
|
|
|
NaluTypeAud: "AUD",
|
|
|
NaluTypeSei: "SEI",
|
|
|
NaluTypeSeiSuffix: "SEISuffix",
|
|
|
}
|
|
|
|
|
|
// ISO_IEC_23008-2_2013.pdf
|
|
|
// Table 7-1 – NAL unit type codes and NAL unit type classes
|
|
|
const (
|
|
|
NaluTypeSliceTrailN uint8 = 0 // 0x0
|
|
|
NaluTypeSliceTrailR uint8 = 1 // 0x01
|
|
|
|
|
|
// 19, 20, 21都是关键帧
|
|
|
// TODO(chef): 16,17,18也是关键帧吗?
|
|
|
NaluTypeSliceIdr uint8 = 19 // 0x13
|
|
|
NaluTypeSliceIdrNlp uint8 = 20 // 0x14
|
|
|
NaluTypeSliceCranut uint8 = 21 // 0x15
|
|
|
|
|
|
NaluTypeVps uint8 = 32 // 0x20
|
|
|
NaluTypeSps uint8 = 33 // 0x21
|
|
|
NaluTypePps uint8 = 34 // 0x22
|
|
|
NaluTypeAud uint8 = 35 // 0x23
|
|
|
NaluTypeSei uint8 = 39 // 0x27
|
|
|
NaluTypeSeiSuffix uint8 = 40 // 0x28
|
|
|
)
|
|
|
|
|
|
type Context struct {
|
|
|
PicWidthInLumaSamples uint32 // sps
|
|
|
PicHeightInLumaSamples uint32 // sps
|
|
|
|
|
|
configurationVersion uint8
|
|
|
|
|
|
generalProfileSpace uint8
|
|
|
generalTierFlag uint8
|
|
|
generalProfileIdc uint8
|
|
|
generalProfileCompatibilityFlags uint32
|
|
|
generalConstraintIndicatorFlags uint64
|
|
|
generalLevelIdc uint8
|
|
|
|
|
|
lengthSizeMinusOne uint8
|
|
|
|
|
|
numTemporalLayers uint8
|
|
|
temporalIdNested uint8
|
|
|
|
|
|
chromaFormat uint8
|
|
|
bitDepthLumaMinus8 uint8
|
|
|
bitDepthChromaMinus8 uint8
|
|
|
}
|
|
|
|
|
|
func ParseNaluTypeReadable(v uint8) string {
|
|
|
b, ok := NaluTypeMapping[ParseNaluType(v)]
|
|
|
if !ok {
|
|
|
return "unknown"
|
|
|
}
|
|
|
return b
|
|
|
}
|
|
|
|
|
|
// @param v 第一个字节
|
|
|
func ParseNaluType(v uint8) uint8 {
|
|
|
// 6 bit in middle
|
|
|
// 0*** ***0
|
|
|
// or return (nalu[0] >> 1) & 0x3F
|
|
|
return (v & 0x7E) >> 1
|
|
|
}
|
|
|
|
|
|
// HVCC Seq Header -> Annexb
|
|
|
//
|
|
|
// @return 返回的内存块为内部独立新申请
|
|
|
//
|
|
|
func VpsSpsPpsSeqHeader2Annexb(payload []byte) ([]byte, error) {
|
|
|
vps, sps, pps, err := ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(payload)
|
|
|
if err != nil {
|
|
|
return nil, ErrHevc
|
|
|
}
|
|
|
var ret []byte
|
|
|
ret = append(ret, NaluStartCode4...)
|
|
|
ret = append(ret, vps...)
|
|
|
ret = append(ret, NaluStartCode4...)
|
|
|
ret = append(ret, sps...)
|
|
|
ret = append(ret, NaluStartCode4...)
|
|
|
ret = append(ret, pps...)
|
|
|
return ret, nil
|
|
|
}
|
|
|
|
|
|
// 见func ParseVpsSpsPpsFromSeqHeaderWithoutMalloc
|
|
|
//
|
|
|
// @return vps, sps, pps: 内存块为内部独立新申请
|
|
|
//
|
|
|
func ParseVpsSpsPpsFromSeqHeader(payload []byte) (vps, sps, pps []byte, err error) {
|
|
|
v, s, p, e := ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(payload)
|
|
|
if e != nil {
|
|
|
return nil, nil, nil, e
|
|
|
}
|
|
|
vps = append(vps, v...)
|
|
|
sps = append(sps, s...)
|
|
|
pps = append(pps, p...)
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// 从HVCC格式的Seq Header中得到VPS,SPS,PPS内存块
|
|
|
//
|
|
|
// @param <payload> rtmp message的payload部分或者flv tag的payload部分
|
|
|
// 注意,包含了头部2字节类型以及3字节的cts
|
|
|
//
|
|
|
// @return vps, sps, pps: 复用传入参数`payload`的内存块
|
|
|
//
|
|
|
func ParseVpsSpsPpsFromSeqHeaderWithoutMalloc(payload []byte) (vps, sps, pps []byte, err error) {
|
|
|
if len(payload) < 5 {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
|
|
|
if payload[0] != 0x1c || payload[1] != 0x00 || payload[2] != 0 || payload[3] != 0 || payload[4] != 0 {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
//nazalog.Debugf("%s", hex.Dump(payload))
|
|
|
|
|
|
if len(payload) < 33 {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
|
|
|
index := 27
|
|
|
if numOfArrays := payload[index]; numOfArrays != 3 && numOfArrays != 4 {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
index++
|
|
|
|
|
|
if payload[index] != NaluTypeVps&0x3f {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
if numNalus := int(bele.BeUint16(payload[index+1:])); numNalus != 1 {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
vpsLen := int(bele.BeUint16(payload[index+3:]))
|
|
|
|
|
|
if len(payload) < 33+vpsLen {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
|
|
|
vps = payload[index+5 : index+5+vpsLen]
|
|
|
index += 5 + vpsLen
|
|
|
|
|
|
if len(payload) < 38+vpsLen {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
if payload[index] != NaluTypeSps&0x3f {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
if numNalus := int(bele.BeUint16(payload[index+1:])); numNalus != 1 {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
spsLen := int(bele.BeUint16(payload[index+3:]))
|
|
|
if len(payload) < 38+vpsLen+spsLen {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
sps = payload[index+5 : index+5+spsLen]
|
|
|
index += 5 + spsLen
|
|
|
|
|
|
if len(payload) < 43+vpsLen+spsLen {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
if payload[index] != NaluTypePps&0x3f {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
if numNalus := int(bele.BeUint16(payload[index+1:])); numNalus != 1 {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
ppsLen := int(bele.BeUint16(payload[index+3:]))
|
|
|
if len(payload) < 43+vpsLen+spsLen+ppsLen {
|
|
|
return nil, nil, nil, ErrHevc
|
|
|
}
|
|
|
pps = payload[index+5 : index+5+ppsLen]
|
|
|
|
|
|
return
|
|
|
}
|
|
|
|
|
|
// @return 内存块为内部独立新申请
|
|
|
//
|
|
|
func BuildSeqHeaderFromVpsSpsPps(vps, sps, pps []byte) ([]byte, error) {
|
|
|
var sh []byte
|
|
|
sh = make([]byte, 43+len(vps)+len(sps)+len(pps))
|
|
|
sh[0] = 0x1c
|
|
|
sh[1] = 0x0
|
|
|
sh[2] = 0x0
|
|
|
sh[3] = 0x0
|
|
|
sh[4] = 0x0
|
|
|
|
|
|
// unsigned int(8) configurationVersion = 1;
|
|
|
sh[5] = 0x1
|
|
|
|
|
|
ctx := newContext()
|
|
|
if err := ParseVps(vps, ctx); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
if err := ParseSps(sps, ctx); err != nil {
|
|
|
return nil, err
|
|
|
}
|
|
|
|
|
|
// unsigned int(2) general_profile_space;
|
|
|
// unsigned int(1) general_tier_flag;
|
|
|
// unsigned int(5) general_profile_idc;
|
|
|
sh[6] = ctx.generalProfileSpace<<6 | ctx.generalTierFlag<<5 | ctx.generalProfileIdc
|
|
|
// unsigned int(32) general_profile_compatibility_flags
|
|
|
bele.BePutUint32(sh[7:], ctx.generalProfileCompatibilityFlags)
|
|
|
// unsigned int(48) general_constraint_indicator_flags
|
|
|
bele.BePutUint32(sh[11:], uint32(ctx.generalConstraintIndicatorFlags>>16))
|
|
|
bele.BePutUint16(sh[15:], uint16(ctx.generalConstraintIndicatorFlags))
|
|
|
// unsigned int(8) general_level_idc;
|
|
|
sh[17] = ctx.generalLevelIdc
|
|
|
|
|
|
// bit(4) reserved = ‘1111’b;
|
|
|
// unsigned int(12) min_spatial_segmentation_idc;
|
|
|
// bit(6) reserved = ‘111111’b;
|
|
|
// unsigned int(2) parallelismType;
|
|
|
// TODO chef: 这两个字段没有解析
|
|
|
bele.BePutUint16(sh[18:], 0xf000)
|
|
|
sh[20] = 0xfc
|
|
|
|
|
|
// bit(6) reserved = ‘111111’b;
|
|
|
// unsigned int(2) chromaFormat;
|
|
|
sh[21] = ctx.chromaFormat | 0xfc
|
|
|
|
|
|
// bit(5) reserved = ‘11111’b;
|
|
|
// unsigned int(3) bitDepthLumaMinus8;
|
|
|
sh[22] = ctx.bitDepthLumaMinus8 | 0xf8
|
|
|
|
|
|
// bit(5) reserved = ‘11111’b;
|
|
|
// unsigned int(3) bitDepthChromaMinus8;
|
|
|
sh[23] = ctx.bitDepthChromaMinus8 | 0xf8
|
|
|
|
|
|
// bit(16) avgFrameRate;
|
|
|
bele.BePutUint16(sh[24:], 0)
|
|
|
|
|
|
// bit(2) constantFrameRate;
|
|
|
// bit(3) numTemporalLayers;
|
|
|
// bit(1) temporalIdNested;
|
|
|
// unsigned int(2) lengthSizeMinusOne;
|
|
|
sh[26] = 0<<6 | ctx.numTemporalLayers<<3 | ctx.temporalIdNested<<2 | ctx.lengthSizeMinusOne
|
|
|
|
|
|
// num of vps sps pps
|
|
|
sh[27] = 0x03
|
|
|
i := 28
|
|
|
sh[i] = NaluTypeVps
|
|
|
// num of vps
|
|
|
bele.BePutUint16(sh[i+1:], 1)
|
|
|
// length
|
|
|
bele.BePutUint16(sh[i+3:], uint16(len(vps)))
|
|
|
copy(sh[i+5:], vps)
|
|
|
i = i + 5 + len(vps)
|
|
|
sh[i] = NaluTypeSps
|
|
|
bele.BePutUint16(sh[i+1:], 1)
|
|
|
bele.BePutUint16(sh[i+3:], uint16(len(sps)))
|
|
|
copy(sh[i+5:], sps)
|
|
|
i = i + 5 + len(sps)
|
|
|
sh[i] = NaluTypePps
|
|
|
bele.BePutUint16(sh[i+1:], 1)
|
|
|
bele.BePutUint16(sh[i+3:], uint16(len(pps)))
|
|
|
copy(sh[i+5:], pps)
|
|
|
|
|
|
return sh, nil
|
|
|
}
|
|
|
|
|
|
func ParseVps(vps []byte, ctx *Context) error {
|
|
|
if len(vps) < 2 {
|
|
|
return ErrHevc
|
|
|
}
|
|
|
|
|
|
rbsp := nal2rbsp(vps[2:])
|
|
|
br := nazabits.NewBitReader(rbsp)
|
|
|
|
|
|
// skip
|
|
|
// vps_video_parameter_set_id u(4)
|
|
|
// vps_reserved_three_2bits u(2)
|
|
|
// vps_max_layers_minus1 u(6)
|
|
|
if _, err := br.ReadBits16(12); err != nil {
|
|
|
return ErrHevc
|
|
|
}
|
|
|
|
|
|
vpsMaxSubLayersMinus1, err := br.ReadBits8(3)
|
|
|
if err != nil {
|
|
|
return ErrHevc
|
|
|
}
|
|
|
if vpsMaxSubLayersMinus1+1 > ctx.numTemporalLayers {
|
|
|
ctx.numTemporalLayers = vpsMaxSubLayersMinus1 + 1
|
|
|
}
|
|
|
|
|
|
// skip
|
|
|
// vps_temporal_id_nesting_flag u(1)
|
|
|
// vps_reserved_0xffff_16bits u(16)
|
|
|
if _, err := br.ReadBits32(17); err != nil {
|
|
|
return ErrHevc
|
|
|
}
|
|
|
|
|
|
return parsePtl(&br, ctx, vpsMaxSubLayersMinus1)
|
|
|
}
|
|
|
|
|
|
func ParseSps(sps []byte, ctx *Context) error {
|
|
|
var err error
|
|
|
|
|
|
if len(sps) < 2 {
|
|
|
return ErrHevc
|
|
|
}
|
|
|
|
|
|
rbsp := nal2rbsp(sps[2:])
|
|
|
br := nazabits.NewBitReader(rbsp)
|
|
|
|
|
|
// sps_video_parameter_set_id
|
|
|
if _, err = br.ReadBits8(4); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
spsMaxSubLayersMinus1, err := br.ReadBits8(3)
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
if spsMaxSubLayersMinus1+1 > ctx.numTemporalLayers {
|
|
|
ctx.numTemporalLayers = spsMaxSubLayersMinus1 + 1
|
|
|
}
|
|
|
|
|
|
// sps_temporal_id_nesting_flag
|
|
|
if ctx.temporalIdNested, err = br.ReadBit(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
if err = parsePtl(&br, ctx, spsMaxSubLayersMinus1); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
// sps_seq_parameter_set_id
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
var cf uint32
|
|
|
if cf, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
ctx.chromaFormat = uint8(cf)
|
|
|
if ctx.chromaFormat == 3 {
|
|
|
if _, err = br.ReadBit(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if ctx.PicWidthInLumaSamples, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if ctx.PicHeightInLumaSamples, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
conformanceWindowFlag, err := br.ReadBit()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if conformanceWindowFlag != 0 {
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
var bdlm8 uint32
|
|
|
if bdlm8, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
ctx.bitDepthChromaMinus8 = uint8(bdlm8)
|
|
|
var bdcm8 uint32
|
|
|
if bdcm8, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
ctx.bitDepthChromaMinus8 = uint8(bdcm8)
|
|
|
|
|
|
_, err = br.ReadGolomb()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
spsSubLayerOrderingInfoPresentFlag, err := br.ReadBit()
|
|
|
if err != nil {
|
|
|
return err
|
|
|
}
|
|
|
var i uint8
|
|
|
if spsSubLayerOrderingInfoPresentFlag != 0 {
|
|
|
i = 0
|
|
|
} else {
|
|
|
i = spsMaxSubLayersMinus1
|
|
|
}
|
|
|
for ; i <= spsMaxSubLayersMinus1; i++ {
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadGolomb(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func parsePtl(br *nazabits.BitReader, ctx *Context, maxSubLayersMinus1 uint8) error {
|
|
|
var err error
|
|
|
var ptl Context
|
|
|
if ptl.generalProfileSpace, err = br.ReadBits8(2); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if ptl.generalTierFlag, err = br.ReadBit(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if ptl.generalProfileIdc, err = br.ReadBits8(5); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if ptl.generalProfileCompatibilityFlags, err = br.ReadBits32(32); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if ptl.generalConstraintIndicatorFlags, err = br.ReadBits64(48); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if ptl.generalLevelIdc, err = br.ReadBits8(8); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
updatePtl(ctx, &ptl)
|
|
|
|
|
|
if maxSubLayersMinus1 == 0 {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
subLayerProfilePresentFlag := make([]uint8, maxSubLayersMinus1)
|
|
|
subLayerLevelPresentFlag := make([]uint8, maxSubLayersMinus1)
|
|
|
for i := uint8(0); i < maxSubLayersMinus1; i++ {
|
|
|
if subLayerProfilePresentFlag[i], err = br.ReadBit(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if subLayerLevelPresentFlag[i], err = br.ReadBit(); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
if maxSubLayersMinus1 > 0 {
|
|
|
for i := maxSubLayersMinus1; i < 8; i++ {
|
|
|
if _, err = br.ReadBits8(2); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
for i := uint8(0); i < maxSubLayersMinus1; i++ {
|
|
|
if subLayerProfilePresentFlag[i] != 0 {
|
|
|
if _, err = br.ReadBits32(32); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadBits32(32); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
if _, err = br.ReadBits32(24); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if subLayerLevelPresentFlag[i] != 0 {
|
|
|
if _, err = br.ReadBits8(8); err != nil {
|
|
|
return err
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
func updatePtl(ctx, ptl *Context) {
|
|
|
ctx.generalProfileSpace = ptl.generalProfileSpace
|
|
|
|
|
|
if ptl.generalTierFlag > ctx.generalTierFlag {
|
|
|
ctx.generalLevelIdc = ptl.generalLevelIdc
|
|
|
|
|
|
ctx.generalTierFlag = ptl.generalTierFlag
|
|
|
} else {
|
|
|
if ptl.generalLevelIdc > ctx.generalLevelIdc {
|
|
|
ctx.generalLevelIdc = ptl.generalLevelIdc
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if ptl.generalProfileIdc > ctx.generalProfileIdc {
|
|
|
ctx.generalProfileIdc = ptl.generalProfileIdc
|
|
|
}
|
|
|
|
|
|
ctx.generalProfileCompatibilityFlags &= ptl.generalProfileCompatibilityFlags
|
|
|
|
|
|
ctx.generalConstraintIndicatorFlags &= ptl.generalConstraintIndicatorFlags
|
|
|
}
|
|
|
|
|
|
func newContext() *Context {
|
|
|
return &Context{
|
|
|
configurationVersion: 1,
|
|
|
lengthSizeMinusOne: 3, // 4 bytes
|
|
|
generalProfileCompatibilityFlags: 0xffffffff,
|
|
|
generalConstraintIndicatorFlags: 0xffffffffffff,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
func nal2rbsp(nal []byte) []byte {
|
|
|
// TODO chef:
|
|
|
// 1. 输出应该可由外部申请
|
|
|
// 2. 替换性能
|
|
|
// 3. 该函数应该放入avc中
|
|
|
return bytes.Replace(nal, []byte{0x0, 0x0, 0x3}, []byte{0x0, 0x0}, -1)
|
|
|
}
|