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

155 lines
3.8 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 hevc
import (
"encoding/hex"
"errors"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazalog"
)
// AnnexB
//
// HVCC
var ErrHEVC = errors.New("lal.hevc: fxxk")
var (
NALUStartCode4 = []byte{0x0, 0x0, 0x0, 0x1}
)
var NALUTypeMapping = map[uint8]string{
NALUTypeSliceTrailR: "SLICE",
NALUTypeSliceIDR: "I",
NALUTypeSliceIDRNLP: "IDR",
NALUTypeSEI: "SEI",
NALUTypeSEISuffix: "SEI",
}
var (
NALUTypeSliceTrailR uint8 = 1 // 0x01
NALUTypeSliceIDR uint8 = 19 // 0x13
NALUTypeSliceIDRNLP uint8 = 20 // 0x14
NALUTypeVPS uint8 = 32 // 0x20
NALUTypeSPS uint8 = 33 // 0x21
NALUTypePPS uint8 = 34 // 0x22
NALUTypeSEI uint8 = 39 // 0x27
NALUTypeSEISuffix uint8 = 40 // 0x28
)
func ParseNALUTypeReadable(v uint8) string {
b, ok := NALUTypeMapping[ParseNALUType(v)]
if !ok {
return "unknown"
}
return b
}
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
// 注意,返回的内存块为独立的内存块,不依赖指向传输参数<payload>内存块
//
func VPSSPSPPSSeqHeader2AnnexB(payload []byte) ([]byte, error) {
vps, sps, pps, err := ParseVPSSPSPPSFromSeqHeader(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
}
// 从HVCC格式的Seq Header中得到VPSSPSPPS内存块
//
// @param <payload> rtmp message的payload部分或者flv tag的payload部分
// 注意包含了头部2字节类型以及3字节的cts
//
// @return 注意返回的vpsspspps内存块指向的是传入参数<payload>内存块的内存
//
func ParseVPSSPSPPSFromSeqHeader(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 {
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
}