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

191 lines
4.2 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 2019, 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 avc
import (
"errors"
"io"
"math"
"github.com/q191201771/naza/pkg/bele"
"github.com/q191201771/naza/pkg/nazabits"
)
var ErrAVC = errors.New("lal.avc: fxxk")
var NaluStartCode = []byte{0x0, 0x0, 0x0, 0x1}
var NaluUintTypeMapping = map[uint8]string{
1: "SLICE",
5: "IDR",
6: "SEI",
7: "SPS",
8: "PPS",
9: "AUD",
}
var SliceTypeMapping = map[uint8]string{
0: "P",
1: "B",
2: "I",
3: "SP",
4: "SI",
5: "P",
6: "B",
7: "I",
8: "SP",
9: "SI",
}
const (
NaluUnitTypeSlice uint8 = 1
NaluUnitTypeIDRSlice uint8 = 5
NaluUnitTypeSEI uint8 = 6
NaluUintTypeSPS uint8 = 7
NaluUintTypePPS uint8 = 8
NaluUintTypeAUD uint8 = 9
)
const (
SliceTypeP uint8 = 0
SliceTypeB uint8 = 1
SliceTypeI uint8 = 2
SliceTypeSP uint8 = 3 // TODO chef
SliceTypeSI uint8 = 4 // TODO chef
)
func CalcNaluType(nalu []byte) uint8 {
return nalu[0] & 0x1f
}
func CalcSliceType(nalu []byte) uint8 {
c := nalu[1]
var leadingZeroBits int
index := 6 // can't unsigned
for ; index >= 0; index-- {
v := nazabits.GetBit8(c, uint(index))
if v == 0 {
leadingZeroBits++
} else {
break
}
}
rbLeadingZeroBits := nazabits.GetBits8(c, uint(index-1), uint(leadingZeroBits))
codeNum := int(math.Pow(2, float64(leadingZeroBits))) - 1 + int(rbLeadingZeroBits)
if codeNum > 4 {
codeNum -= 5
}
return uint8(codeNum)
}
func CalcNaluTypeReadable(nalu []byte) string {
t := nalu[0] & 0x1f
ret, ok := NaluUintTypeMapping[t]
if !ok {
return "unknown"
}
return ret
}
func CalcSliceTypeReadable(nalu []byte) string {
naluType := CalcNaluType(nalu)
switch naluType {
case NaluUnitTypeSEI:
fallthrough
case NaluUintTypeSPS:
fallthrough
case NaluUintTypePPS:
return ""
}
t := CalcSliceType(nalu)
ret, ok := SliceTypeMapping[t]
if !ok {
return "unknown"
}
return ret
}
// TODO chef: 参考 hls session的代码重构这个函数
// 从 rtmp avc sequence header 中解析 sps 和 pps
// @param <payload> rtmp message的payload部分 或者 flv tag的payload部分
func ParseAVCSeqHeader(payload []byte) (sps, pps []byte, err error) {
// TODO chef: check if read out of <payload> range
if payload[0] != 0x17 || payload[1] != 0x00 || payload[2] != 0 || payload[3] != 0 || payload[4] != 0 {
err = ErrAVC
return
}
// H.264-AVC-ISO_IEC_14496-15.pdf
// 5.2.4 Decoder configuration information
//configurationVersion := payload[5]
//avcProfileIndication := payload[6]
//profileCompatibility := payload[7]
//avcLevelIndication := payload[8]
//lengthSizeMinusOne := payload[9] & 0x03
index := 10
numOfSPS := int(payload[index] & 0x1F)
index++
// TODO chef: if the situation of multi sps exist?
// only take the last one.
for i := 0; i < numOfSPS; i++ {
lenOfSPS := int(bele.BEUint16(payload[index:]))
index += 2
sps = append(sps, payload[index:index+lenOfSPS]...)
index += lenOfSPS
}
numOfPPS := int(payload[index] & 0x1F)
index++
for i := 0; i < numOfPPS; i++ {
lenOfPPS := int(bele.BEUint16(payload[index:]))
index += 2
pps = append(pps, payload[index:index+lenOfPPS]...)
index += lenOfPPS
}
return
}
// 将rtmp avc数据转换成avc裸流
// @param <payload> rtmp message的payload部分 或者 flv tag的payload部分
func CaptureAVC(w io.Writer, payload []byte) error {
// sps pps
if payload[0] == 0x17 && payload[1] == 0x00 {
sps, pps, err := ParseAVCSeqHeader(payload)
if err != nil {
return err
}
//utilErrors.PanicIfErrorOccur(err)
_, _ = w.Write(NaluStartCode)
_, _ = w.Write(sps)
_, _ = w.Write(NaluStartCode)
_, _ = w.Write(pps)
return nil
}
// payload中可能存在多个nalu
// 先跳过前面type的2字节以及composition time的3字节
for i := 5; i != len(payload); {
naluLen := int(bele.BEUint32(payload[i:]))
i += 4
//naluUintType := payload[i] & 0x1f
//log.Debugf("naluLen:%d t:%d %s\n", naluLen, naluUintType, avc.NaluUintTypeMapping[naluUintType])
_, _ = w.Write(NaluStartCode)
_, _ = w.Write(payload[i : i+naluLen])
i += naluLen
break
}
return nil
}