Add HEVC support

This commit is contained in:
Ingo Oppermann 2024-01-03 12:59:10 +01:00
parent fde0595744
commit 4ee14576ae
12 changed files with 1054 additions and 83 deletions

View File

@ -116,6 +116,9 @@ type CodecType uint32
var (
H264 = MakeVideoCodecType(avCodecTypeMagic + 1)
HEVC = MakeVideoCodecType(avCodecTypeMagic + 2)
VP9 = MakeVideoCodecType(avCodecTypeMagic + 3)
AV1 = MakeVideoCodecType(avCodecTypeMagic + 4)
AAC = MakeAudioCodecType(avCodecTypeMagic + 1)
PCM_MULAW = MakeAudioCodecType(avCodecTypeMagic + 2)
PCM_ALAW = MakeAudioCodecType(avCodecTypeMagic + 3)
@ -126,10 +129,16 @@ var (
const codecTypeAudioBit = 0x1
const codecTypeOtherBits = 1
func (self CodecType) String() string {
switch self {
func (c CodecType) String() string {
switch c {
case H264:
return "H264"
case HEVC:
return "HEVC"
case VP9:
return "VP9"
case AV1:
return "AV1"
case AAC:
return "AAC"
case PCM_MULAW:
@ -144,12 +153,12 @@ func (self CodecType) String() string {
return ""
}
func (self CodecType) IsAudio() bool {
return self&codecTypeAudioBit != 0
func (c CodecType) IsAudio() bool {
return c&codecTypeAudioBit != 0
}
func (self CodecType) IsVideo() bool {
return self&codecTypeAudioBit == 0
func (c CodecType) IsVideo() bool {
return c&codecTypeAudioBit == 0
}
// Make a new audio codec type.

View File

@ -2,8 +2,9 @@
package pktque
import (
"github.com/datarhei/joy4/av"
"time"
"github.com/datarhei/joy4/av"
)
type Filter interface {

View File

@ -3,10 +3,11 @@ package aacparser
import (
"bytes"
"fmt"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits"
"io"
"time"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits"
)
// copied from libavcodec/mpeg4audio.h

View File

@ -3,6 +3,7 @@ package h264parser
import (
"bytes"
"fmt"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits"
"github.com/datarhei/joy4/utils/bits/pio"

View File

@ -6,16 +6,16 @@ import (
)
func TestParser(t *testing.T) {
var ok bool
var typ int
var nalus [][]byte
annexbFrame, _ := hex.DecodeString("00000001223322330000000122332233223300000133000001000001")
nalus, ok = SplitNALUs(annexbFrame)
t.Log(ok, len(nalus))
nalus, typ = SplitNALUs(annexbFrame)
t.Log(typ, len(nalus))
avccFrame, _ := hex.DecodeString(
"00000008aabbccaabbccaabb00000001aa",
)
nalus, ok = SplitNALUs(avccFrame)
t.Log(ok, len(nalus))
nalus, typ = SplitNALUs(avccFrame)
t.Log(typ, len(nalus))
}

695
codec/hevcparser/parser.go Normal file
View File

@ -0,0 +1,695 @@
package hevcparser
// based on https://github.com/deepch/vdk/blob/v0.0.21/codec/h265parser/parser.go
import (
"bytes"
"errors"
"fmt"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits"
"github.com/datarhei/joy4/utils/bits/pio"
)
type SPSInfo struct {
ProfileIdc uint
LevelIdc uint
CropLeft uint
CropRight uint
CropTop uint
CropBottom uint
Width uint
Height uint
PicWidthInLumaSamples uint
PicHeightInLumaSamples uint
generalProfileSpace uint
generalTierFlag uint
generalProfileIDC uint
generalProfileCompatibilityFlags uint32
generalConstraintIndicatorFlags uint64
generalLevelIDC uint
}
const (
NAL_UNIT_CODED_SLICE_TRAIL_N = 0
NAL_UNIT_CODED_SLICE_TRAIL_R = 1
NAL_UNIT_CODED_SLICE_TSA_N = 2
NAL_UNIT_CODED_SLICE_TSA_R = 3
NAL_UNIT_CODED_SLICE_STSA_N = 4
NAL_UNIT_CODED_SLICE_STSA_R = 5
NAL_UNIT_CODED_SLICE_RADL_N = 6
NAL_UNIT_CODED_SLICE_RADL_R = 7
NAL_UNIT_CODED_SLICE_RASL_N = 8
NAL_UNIT_CODED_SLICE_RASL_R = 9
NAL_UNIT_RESERVED_VCL_N10 = 10
NAL_UNIT_RESERVED_VCL_R11 = 11
NAL_UNIT_RESERVED_VCL_N12 = 12
NAL_UNIT_RESERVED_VCL_R13 = 13
NAL_UNIT_RESERVED_VCL_N14 = 14
NAL_UNIT_RESERVED_VCL_R15 = 15
NAL_UNIT_CODED_SLICE_BLA_W_LP = 16
NAL_UNIT_CODED_SLICE_BLA_W_RADL = 17
NAL_UNIT_CODED_SLICE_BLA_N_LP = 18
NAL_UNIT_CODED_SLICE_IDR_W_RADL = 19
NAL_UNIT_CODED_SLICE_IDR_N_LP = 20
NAL_UNIT_CODED_SLICE_CRA = 21
NAL_UNIT_RESERVED_IRAP_VCL22 = 22
NAL_UNIT_RESERVED_IRAP_VCL23 = 23
NAL_UNIT_RESERVED_VCL24 = 24
NAL_UNIT_RESERVED_VCL25 = 25
NAL_UNIT_RESERVED_VCL26 = 26
NAL_UNIT_RESERVED_VCL27 = 27
NAL_UNIT_RESERVED_VCL28 = 28
NAL_UNIT_RESERVED_VCL29 = 29
NAL_UNIT_RESERVED_VCL30 = 30
NAL_UNIT_RESERVED_VCL31 = 31
NAL_UNIT_VPS = 32
NAL_UNIT_SPS = 33
NAL_UNIT_PPS = 34
NAL_UNIT_ACCESS_UNIT_DELIMITER = 35
NAL_UNIT_EOS = 36
NAL_UNIT_EOB = 37
NAL_UNIT_FILLER_DATA = 38
NAL_UNIT_PREFIX_SEI = 39
NAL_UNIT_SUFFIX_SEI = 40
NAL_UNIT_RESERVED_NVCL41 = 41
NAL_UNIT_RESERVED_NVCL42 = 42
NAL_UNIT_RESERVED_NVCL43 = 43
NAL_UNIT_RESERVED_NVCL44 = 44
NAL_UNIT_RESERVED_NVCL45 = 45
NAL_UNIT_RESERVED_NVCL46 = 46
NAL_UNIT_RESERVED_NVCL47 = 47
NAL_UNIT_UNSPECIFIED_48 = 48
NAL_UNIT_UNSPECIFIED_49 = 49
NAL_UNIT_UNSPECIFIED_50 = 50
NAL_UNIT_UNSPECIFIED_51 = 51
NAL_UNIT_UNSPECIFIED_52 = 52
NAL_UNIT_UNSPECIFIED_53 = 53
NAL_UNIT_UNSPECIFIED_54 = 54
NAL_UNIT_UNSPECIFIED_55 = 55
NAL_UNIT_UNSPECIFIED_56 = 56
NAL_UNIT_UNSPECIFIED_57 = 57
NAL_UNIT_UNSPECIFIED_58 = 58
NAL_UNIT_UNSPECIFIED_59 = 59
NAL_UNIT_UNSPECIFIED_60 = 60
NAL_UNIT_UNSPECIFIED_61 = 61
NAL_UNIT_UNSPECIFIED_62 = 62
NAL_UNIT_UNSPECIFIED_63 = 63
NAL_UNIT_INVALID = 64
)
const (
MAX_VPS_COUNT = 16
MAX_SUB_LAYERS = 7
MAX_SPS_COUNT = 32
)
var (
ErrorHEVCIncorectUnitSize = errors.New("incorrect unit size")
ErrorHECVIncorectUnitType = errors.New("incorrect unit type")
)
var StartCodeBytes = []byte{0, 0, 1}
var AUDBytes = []byte{0, 0, 0, 1, 0x9, 0xf0, 0, 0, 0, 1} // AUD
const (
NALU_RAW = iota
NALU_AVCC
NALU_ANNEXB
)
func SplitNALUs(b []byte) (nalus [][]byte, typ int) {
if len(b) < 4 {
return [][]byte{b}, NALU_RAW
}
val3 := pio.U24BE(b)
val4 := pio.U32BE(b)
if val4 <= uint32(len(b)) {
_val4 := val4
_b := b[4:]
nalus := [][]byte{}
for {
nalus = append(nalus, _b[:_val4])
_b = _b[_val4:]
if len(_b) < 4 {
break
}
_val4 = pio.U32BE(_b)
_b = _b[4:]
if _val4 > uint32(len(_b)) {
break
}
}
if len(_b) == 0 {
return nalus, NALU_AVCC
}
}
if val3 == 1 || val4 == 1 {
_val3 := val3
_val4 := val4
start := 0
pos := 0
for {
if start != pos {
nalus = append(nalus, b[start:pos])
}
if _val3 == 1 {
pos += 3
} else if _val4 == 1 {
pos += 4
}
start = pos
if start == len(b) {
break
}
_val3 = 0
_val4 = 0
for pos < len(b) {
if pos+2 < len(b) && b[pos] == 0 {
_val3 = pio.U24BE(b[pos:])
if _val3 == 0 {
if pos+3 < len(b) {
_val4 = uint32(b[pos+3])
if _val4 == 1 {
break
}
}
} else if _val3 == 1 {
break
}
pos++
} else {
pos++
}
}
}
typ = NALU_ANNEXB
return
}
return [][]byte{b}, NALU_RAW
}
func ParseSPS(sps []byte) (ctx SPSInfo, err error) {
if len(sps) < 2 {
err = ErrorHEVCIncorectUnitSize
return
}
rbsp := nal2rbsp(sps[2:])
br := &bits.GolombBitReader{R: bytes.NewReader(rbsp)}
// sps_video_parameter_set_id
if _, err = br.ReadBits(4); err != nil {
return
}
// sps_max_sub_layers_minus1
spsMaxSubLayersMinus1, err := br.ReadBits(3)
if err != nil {
return
}
// sps_temporal_id_nesting_flag
if _, err = br.ReadBit(); err != nil {
return
}
// profile_tier_level( 1, sps_max_sub_layers_minus1 )
if err = parsePTL(br, &ctx, spsMaxSubLayersMinus1); err != nil {
return
}
// sps_seq_parameter_set_id
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// chroma_format_idc
var chroma_format_idc uint
if chroma_format_idc, err = br.ReadExponentialGolombCode(); err != nil {
return
}
if chroma_format_idc == 3 {
// separate_colour_plane_flag
if _, err = br.ReadBit(); err != nil {
return
}
}
// Table 6-1, Section 6.2
var subWidthC uint
var subHeightC uint
switch chroma_format_idc {
case 0:
subWidthC, subHeightC = 1, 1
case 1:
subWidthC, subHeightC = 2, 2
case 2:
subWidthC, subHeightC = 2, 1
case 3:
subWidthC, subHeightC = 1, 1
}
// pic_width_in_luma_samples
if ctx.PicWidthInLumaSamples, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// pic_height_in_luma_samples
if ctx.PicHeightInLumaSamples, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// conformance_window_flag
conformanceWindowFlag, err := br.ReadBit()
if err != nil {
return
}
var conf_win_left_offset uint
var conf_win_right_offset uint
var conf_win_top_offset uint
var conf_win_bottom_offset uint
if conformanceWindowFlag != 0 {
// conf_win_left_offset
conf_win_left_offset, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
ctx.CropLeft = subWidthC * conf_win_left_offset
// conf_win_right_offset
conf_win_right_offset, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
ctx.CropRight = subWidthC * conf_win_right_offset
// conf_win_top_offset
conf_win_top_offset, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
ctx.CropTop = subHeightC * conf_win_top_offset
// conf_win_bottom_offset
conf_win_bottom_offset, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
ctx.CropBottom = subHeightC * conf_win_bottom_offset
}
ctx.Width = ctx.PicWidthInLumaSamples - ctx.CropLeft - ctx.CropRight
ctx.Height = ctx.PicHeightInLumaSamples - ctx.CropTop - ctx.CropBottom
// bit_depth_luma_minus8
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// bit_depth_chroma_minus8
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// log2_max_pic_order_cnt_lsb_minus4
_, err = br.ReadExponentialGolombCode()
if err != nil {
return
}
// sps_sub_layer_ordering_info_present_flag
spsSubLayerOrderingInfoPresentFlag, err := br.ReadBit()
if err != nil {
return
}
var i uint
if spsSubLayerOrderingInfoPresentFlag != 0 {
i = 0
} else {
i = spsMaxSubLayersMinus1
}
for ; i <= spsMaxSubLayersMinus1; i++ {
// sps_max_dec_pic_buffering_minus1[ i ]
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// sps_max_num_reorder_pics[ i ]
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// sps_max_latency_increase_plus1[ i ]
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
}
// log2_min_luma_coding_block_size_minus3
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// log2_diff_max_min_luma_coding_block_size
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// log2_min_luma_transform_block_size_minus2
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// log2_diff_max_min_luma_transform_block_size
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// max_transform_hierarchy_depth_inter
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
// max_transform_hierarchy_depth_intra
if _, err = br.ReadExponentialGolombCode(); err != nil {
return
}
return
}
func parsePTL(br *bits.GolombBitReader, ctx *SPSInfo, maxSubLayersMinus1 uint) error {
var err error
var ptl SPSInfo
if ptl.generalProfileSpace, err = br.ReadBits(2); err != nil {
return err
}
if ptl.generalTierFlag, err = br.ReadBit(); err != nil {
return err
}
if ptl.generalProfileIDC, err = br.ReadBits(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.ReadBits(8); err != nil {
return err
}
updatePTL(ctx, &ptl)
if maxSubLayersMinus1 == 0 {
return nil
}
subLayerProfilePresentFlag := make([]uint, maxSubLayersMinus1)
subLayerLevelPresentFlag := make([]uint, maxSubLayersMinus1)
for i := uint(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.ReadBits(2); err != nil {
return err
}
}
}
for i := uint(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.ReadBits(8); err != nil {
return err
}
}
}
return nil
}
func updatePTL(ctx, ptl *SPSInfo) {
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 nal2rbsp(nal []byte) []byte {
return bytes.Replace(nal, []byte{0x0, 0x0, 0x3}, []byte{0x0, 0x0}, -1)
}
type CodecData struct {
Record []byte
RecordInfo HEVCDecoderConfRecord
SPSInfo SPSInfo
}
func (codec CodecData) Type() av.CodecType {
return av.HEVC
}
func (codec CodecData) HEVCDecoderConfRecordBytes() []byte {
return codec.Record
}
func (codec CodecData) SPS() []byte {
return codec.RecordInfo.SPS[0]
}
func (codec CodecData) PPS() []byte {
return codec.RecordInfo.PPS[0]
}
func (codec CodecData) VPS() []byte {
return codec.RecordInfo.VPS[0]
}
func (codec CodecData) Width() int {
return int(codec.SPSInfo.Width)
}
func (codec CodecData) Height() int {
return int(codec.SPSInfo.Height)
}
func NewCodecDataFromHEVCDecoderConfRecord(record []byte) (self CodecData, err error) {
self.Record = record
if _, err = (&self.RecordInfo).Unmarshal(record); err != nil {
return
}
if len(self.RecordInfo.SPS) == 0 {
err = fmt.Errorf("hevcparser: no SPS found in HEVCDecoderConfRecord")
return
}
if len(self.RecordInfo.PPS) == 0 {
err = fmt.Errorf("hevcparser: no PPS found in HEVCDecoderConfRecord")
return
}
if len(self.RecordInfo.VPS) == 0 {
err = fmt.Errorf("hevcparser: no VPS found in HEVCDecoderConfRecord")
return
}
if self.SPSInfo, err = ParseSPS(self.RecordInfo.SPS[0]); err != nil {
err = fmt.Errorf("hevcparser: parse SPS failed(%s)", err)
return
}
return
}
func NewCodecDataFromVPSAndSPSAndPPS(vps, sps, pps []byte) (self CodecData, err error) {
recordinfo := HEVCDecoderConfRecord{}
recordinfo.HEVCProfileIndication = sps[3]
recordinfo.ProfileCompatibility = sps[4]
recordinfo.HEVCLevelIndication = sps[5]
recordinfo.SPS = [][]byte{sps}
recordinfo.PPS = [][]byte{pps}
recordinfo.VPS = [][]byte{vps}
recordinfo.LengthSizeMinusOne = 3
if self.SPSInfo, err = ParseSPS(sps); err != nil {
return
}
buf := make([]byte, recordinfo.Len())
recordinfo.Marshal(buf, self.SPSInfo)
self.RecordInfo = recordinfo
self.Record = buf
return
}
type HEVCDecoderConfRecord struct {
HEVCProfileIndication uint8
ProfileCompatibility uint8
HEVCLevelIndication uint8
LengthSizeMinusOne uint8
VPS [][]byte
SPS [][]byte
PPS [][]byte
}
var ErrDecconfInvalid = fmt.Errorf("hevcparser: HEVCDecoderConfRecord invalid")
func (record *HEVCDecoderConfRecord) Unmarshal(b []byte) (n int, err error) {
if len(b) < 30 {
err = ErrDecconfInvalid
return
}
record.HEVCProfileIndication = b[1]
record.ProfileCompatibility = b[2]
record.HEVCLevelIndication = b[3]
record.LengthSizeMinusOne = b[4] & 0x03
vpscount := int(b[25] & 0x1f)
n += 26
for i := 0; i < vpscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
vpslen := int(pio.U16BE(b[n:]))
n += 2
if len(b) < n+vpslen {
err = ErrDecconfInvalid
return
}
record.VPS = append(record.VPS, b[n:n+vpslen])
n += vpslen
}
if len(b) < n+1 {
err = ErrDecconfInvalid
return
}
n++
n++
spscount := int(b[n])
n++
for i := 0; i < spscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
spslen := int(pio.U16BE(b[n:]))
n += 2
if len(b) < n+spslen {
err = ErrDecconfInvalid
return
}
record.SPS = append(record.SPS, b[n:n+spslen])
n += spslen
}
n++
n++
ppscount := int(b[n])
n++
for i := 0; i < ppscount; i++ {
if len(b) < n+2 {
err = ErrDecconfInvalid
return
}
ppslen := int(pio.U16BE(b[n:]))
n += 2
if len(b) < n+ppslen {
err = ErrDecconfInvalid
return
}
record.PPS = append(record.PPS, b[n:n+ppslen])
n += ppslen
}
return
}
func (record HEVCDecoderConfRecord) Len() (n int) {
n = 23
for _, sps := range record.SPS {
n += 5 + len(sps)
}
for _, pps := range record.PPS {
n += 5 + len(pps)
}
for _, vps := range record.VPS {
n += 5 + len(vps)
}
return
}
func (record HEVCDecoderConfRecord) Marshal(b []byte, si SPSInfo) (n int) {
b[0] = 1
b[1] = record.HEVCProfileIndication
b[2] = record.ProfileCompatibility
b[3] = record.HEVCLevelIndication
b[21] = 3
b[22] = 3
n += 23
b[n] = (record.VPS[0][0] >> 1) & 0x3f
n++
b[n] = byte(len(record.VPS) >> 8)
n++
b[n] = byte(len(record.VPS))
n++
for _, vps := range record.VPS {
pio.PutU16BE(b[n:], uint16(len(vps)))
n += 2
copy(b[n:], vps)
n += len(vps)
}
b[n] = (record.SPS[0][0] >> 1) & 0x3f
n++
b[n] = byte(len(record.SPS) >> 8)
n++
b[n] = byte(len(record.SPS))
n++
for _, sps := range record.SPS {
pio.PutU16BE(b[n:], uint16(len(sps)))
n += 2
copy(b[n:], sps)
n += len(sps)
}
b[n] = (record.PPS[0][0] >> 1) & 0x3f
n++
b[n] = byte(len(record.PPS) >> 8)
n++
b[n] = byte(len(record.PPS))
n++
for _, pps := range record.PPS {
pio.PutU16BE(b[n:], uint16(len(pps)))
n += 2
copy(b[n:], pps)
n += len(pps)
}
return
}

View File

@ -0,0 +1,58 @@
package main
import (
"fmt"
"log"
"os"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/av/avutil"
"github.com/datarhei/joy4/codec/h264parser"
"github.com/datarhei/joy4/format"
)
func init() {
format.RegisterAll()
}
func main() {
if len(os.Args) < 2 {
log.Fatalf("%s [url]", os.Args[0])
}
src, err := avutil.Open(os.Args[1])
if err != nil {
log.Fatalf("error connecting: %s", err.Error())
}
defer src.Close()
var streams []av.CodecData
if streams, err = src.Streams(); err != nil {
log.Fatalf("error streams: %s", err.Error())
}
idx := int8(-1)
for i, s := range streams {
if s.Type().IsVideo() {
fmt.Printf("video: %s\n", s.Type().String())
v := s.(h264parser.CodecData)
os.Stdout.Write(v.AVCDecoderConfRecordBytes())
idx = int8(i)
}
}
for {
p, err := src.ReadPacket()
if err != nil {
log.Fatalf("error reading: %s", err.Error())
}
if p.Idx != idx {
continue
}
os.Stdout.Write(p.Data)
}
}

View File

@ -116,7 +116,7 @@ func (s *server) handlePlay(conn *rtmp.Conn) {
s.lock.RUnlock()
if ch != nil {
conn.SetMetaData(ch.metadata)
//conn.SetMetaData(ch.metadata)
s.log("PLAY", "START", conn.URL.Path, "", client)
cursor := ch.que.Oldest()
@ -139,8 +139,6 @@ func (s *server) handlePlay(conn *rtmp.Conn) {
} else {
s.log("PLAY", "NOTFOUND", conn.URL.Path, "", client)
}
return
}
func (s *server) handlePublish(conn *rtmp.Conn) {
@ -169,14 +167,14 @@ func (s *server) handlePublish(conn *rtmp.Conn) {
return
}
metadata := conn.GetMetaData()
//metadata := conn.GetMetaData()
s.lock.Lock()
ch := s.channels[conn.URL.Path]
if ch == nil {
ch = &channel{}
ch.metadata = metadata
//ch.metadata = metadata
ch.que = pubsub.NewQueue()
ch.que.WriteHeader(streams)
for _, stream := range streams {
@ -221,8 +219,6 @@ func (s *server) handlePublish(conn *rtmp.Conn) {
ch.que.Close()
s.log("PUBLISH", "STOP", conn.URL.Path, "", client)
return
}
func main() {

View File

@ -2,16 +2,19 @@ package flv
import (
"bufio"
"encoding/hex"
"fmt"
"io"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/av/avutil"
"github.com/datarhei/joy4/codec"
"github.com/datarhei/joy4/codec/aacparser"
"github.com/datarhei/joy4/codec/fake"
"github.com/datarhei/joy4/codec/h264parser"
"github.com/datarhei/joy4/codec/hevcparser"
"github.com/datarhei/joy4/format/flv/flvio"
"github.com/datarhei/joy4/utils/bits/pio"
"io"
)
var MaxProbePacketCount = 20
@ -27,6 +30,8 @@ func NewMetadataByStreams(streams []av.CodecData) (metadata flvio.AMFMap, err er
switch typ {
case av.H264:
metadata["videocodecid"] = flvio.VIDEO_H264
case av.HEVC:
metadata["videocodecid"] = flvio.FourCCToFloat(flvio.FOURCC_HEVC)
default:
err = fmt.Errorf("flv: metadata: unsupported video codecType=%v", stream.Type())
@ -83,10 +88,30 @@ func (self *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) {
switch tag.Type {
case flvio.TAG_VIDEO:
if tag.IsExHeader {
if tag.FourCC == flvio.FOURCC_HEVC {
if tag.PacketType == flvio.PKTTYPE_SEQUENCE_START {
if !self.GotVideo {
var stream hevcparser.CodecData
fmt.Printf("got HEVC sequence start:\n%s\n", hex.Dump(tag.Data))
if stream, err = hevcparser.NewCodecDataFromHEVCDecoderConfRecord(tag.Data); err != nil {
err = fmt.Errorf("flv: hevc seqhdr invalid: %s", err.Error())
return
}
self.VideoStreamIdx = len(self.Streams)
self.Streams = append(self.Streams, stream)
self.GotVideo = true
}
} else if tag.PacketType == flvio.PKTTYPE_CODED_FRAMES || tag.PacketType == flvio.PKTTYPE_CODED_FRAMESX {
self.CacheTag(tag, timestamp)
}
}
} else {
switch tag.AVCPacketType {
case flvio.AVC_SEQHDR:
if !self.GotVideo {
var stream h264parser.CodecData
fmt.Printf("got H264 sequence start:\n%s\n", hex.Dump(tag.Data))
if stream, err = h264parser.NewCodecDataFromAVCDecoderConfRecord(tag.Data); err != nil {
err = fmt.Errorf("flv: h264 seqhdr invalid: %s", err.Error())
return
@ -99,6 +124,7 @@ func (self *Prober) PushTag(tag flvio.Tag, timestamp int32) (err error) {
case flvio.AVC_NALU:
self.CacheTag(tag, timestamp)
}
}
case flvio.TAG_AUDIO:
switch tag.SoundFormat {
@ -166,8 +192,8 @@ func (self *Prober) TagToPacket(tag flvio.Tag, timestamp int32) (pkt av.Packet,
switch tag.Type {
case flvio.TAG_VIDEO:
pkt.Idx = int8(self.VideoStreamIdx)
switch tag.AVCPacketType {
case flvio.AVC_NALU:
switch tag.PacketType {
case flvio.PKTTYPE_CODED_FRAMES, flvio.PKTTYPE_CODED_FRAMESX:
ok = true
pkt.Data = tag.Data
pkt.CompositionTime = flvio.TsToTime(tag.CompositionTime)
@ -219,6 +245,22 @@ func CodecDataToTag(stream av.CodecData) (_tag flvio.Tag, ok bool, err error) {
Data: h264.AVCDecoderConfRecordBytes(),
FrameType: flvio.FRAME_KEY,
}
fmt.Printf("set H264 sequence start:\n%v\n", hex.Dump(h264.AVCDecoderConfRecordBytes()))
ok = true
_tag = tag
case av.HEVC:
fmt.Printf("CodecDataToTag for HEVC\n")
hevc := stream.(hevcparser.CodecData)
tag := flvio.Tag{
Type: flvio.TAG_VIDEO,
IsExHeader: true,
PacketType: flvio.PKTTYPE_SEQUENCE_START,
FourCC: flvio.FOURCC_HEVC,
Data: hevc.HEVCDecoderConfRecordBytes(),
FrameType: flvio.FRAME_KEY,
}
fmt.Printf("set HEVC sequence start:\n%v\n", hex.Dump(hevc.HEVCDecoderConfRecordBytes()))
ok = true
_tag = tag
@ -272,6 +314,27 @@ func PacketToTag(pkt av.Packet, stream av.CodecData) (tag flvio.Tag, timestamp i
tag.FrameType = flvio.FRAME_INTER
}
case av.HEVC:
//fmt.Printf("PacketToTag for HEVC\n")
tag = flvio.Tag{
Type: flvio.TAG_VIDEO,
IsExHeader: true,
PacketType: flvio.PKTTYPE_CODED_FRAMES,
CompositionTime: flvio.TimeToTs(pkt.CompositionTime),
FourCC: flvio.FOURCC_HEVC,
Data: pkt.Data,
}
if pkt.CompositionTime == 0 {
tag.PacketType = flvio.PKTTYPE_CODED_FRAMESX
}
if pkt.IsKeyFrame {
tag.FrameType = flvio.FRAME_KEY
} else {
tag.FrameType = flvio.FRAME_INTER
}
case av.AAC:
tag = flvio.Tag{
Type: flvio.TAG_AUDIO,

View File

@ -2,10 +2,11 @@ package flvio
import (
"fmt"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits/pio"
"io"
"time"
"github.com/datarhei/joy4/av"
"github.com/datarhei/joy4/utils/bits/pio"
)
func TsToTime(ts int32) time.Duration {
@ -60,6 +61,27 @@ const (
VIDEO_H264 = 7
)
const (
PKTTYPE_SEQUENCE_START = 0
PKTTYPE_CODED_FRAMES = 1
PKTTYPE_SEQUENCE_END = 2
PKTTYPE_CODED_FRAMESX = 3
PKTTYPE_METADATA = 4
PKTTYPE_MPEG2TS_SEQUENCE_START = 5
)
var (
FOURCC_AV1 = [4]byte{'a', 'v', '0', '1'}
FOURCC_VP9 = [4]byte{'v', 'p', '0', '9'}
FOURCC_HEVC = [4]byte{'h', 'v', 'c', '1'}
)
func FourCCToFloat(fourcc [4]byte) float64 {
i := int(fourcc[0])<<24 | int(fourcc[1])<<16 | int(fourcc[2])<<8 | int(fourcc[3])
return float64(i)
}
type Tag struct {
/*
8 = Audio
@ -126,14 +148,22 @@ type Tag struct {
AACPacketType uint8
/*
0: reserved
1: keyframe (for AVC, a seekable frame)
2: inter frame (for AVC, a non- seekable frame)
3: disposable inter frame (H.263 only)
4: generated keyframe (reserved for server use only)
5: video info/command frame
6: reserved
7: reserved
*/
FrameType uint8
/*
FrameType & 0b1000 != 0
*/
IsExHeader bool
/*
1: JPEG (currently unused)
2: Sorenson H.263
@ -145,6 +175,16 @@ type Tag struct {
*/
CodecID uint8
/*
0: PacketTypeSequenceStart
1: PacketTypeCodedFrames
2: PacketTypeSequenceEnd
3: PacketTypeCodedFramesX
4: PacketTypeMetadata
5: PacketTypeMPEG2TSSequenceStart
*/
PacketType uint8
/*
0: AVC sequence header
1: AVC NALU
@ -154,18 +194,20 @@ type Tag struct {
CompositionTime int32
FourCC [4]byte
Data []byte
}
func (self Tag) ChannelLayout() av.ChannelLayout {
if self.SoundType == SOUND_MONO {
func (t Tag) ChannelLayout() av.ChannelLayout {
if t.SoundType == SOUND_MONO {
return av.CH_MONO
} else {
return av.CH_STEREO
}
}
func (self *Tag) audioParseHeader(b []byte) (n int, err error) {
func (t *Tag) audioParseHeader(b []byte) (n int, err error) {
if len(b) < n+1 {
err = fmt.Errorf("audiodata: parse invalid")
return
@ -173,97 +215,163 @@ func (self *Tag) audioParseHeader(b []byte) (n int, err error) {
flags := b[n]
n++
self.SoundFormat = flags >> 4
self.SoundRate = (flags >> 2) & 0x3
self.SoundSize = (flags >> 1) & 0x1
self.SoundType = flags & 0x1
t.SoundFormat = flags >> 4
t.SoundRate = (flags >> 2) & 0x3
t.SoundSize = (flags >> 1) & 0x1
t.SoundType = flags & 0x1
switch self.SoundFormat {
switch t.SoundFormat {
case SOUND_AAC:
if len(b) < n+1 {
err = fmt.Errorf("audiodata: parse invalid")
return
}
self.AACPacketType = b[n]
t.AACPacketType = b[n]
n++
}
return
}
func (self Tag) audioFillHeader(b []byte) (n int) {
func (t Tag) audioFillHeader(b []byte) (n int) {
var flags uint8
flags |= self.SoundFormat << 4
flags |= self.SoundRate << 2
flags |= self.SoundSize << 1
flags |= self.SoundType
flags |= t.SoundFormat << 4
flags |= t.SoundRate << 2
flags |= t.SoundSize << 1
flags |= t.SoundType
b[n] = flags
n++
switch self.SoundFormat {
switch t.SoundFormat {
case SOUND_AAC:
b[n] = self.AACPacketType
b[n] = t.AACPacketType
n++
}
return
}
func (self *Tag) videoParseHeader(b []byte) (n int, err error) {
func (t *Tag) videoParseHeader(b []byte) (n int, err error) {
if len(b) < n+1 {
err = fmt.Errorf("videodata: parse invalid")
return
}
flags := b[n]
self.FrameType = flags >> 4
self.CodecID = flags & 0xf
t.FrameType = flags >> 4
t.CodecID = flags & 0b1111
//fmt.Printf("%#8b\n", flags)
n++
if self.FrameType == FRAME_INTER || self.FrameType == FRAME_KEY {
if (t.FrameType & 0b1000) != 0 {
t.IsExHeader = true
t.PacketType = t.CodecID
t.CodecID = 0
if t.PacketType != PKTTYPE_METADATA {
t.FrameType = t.FrameType & 0b0111
}
}
if !t.IsExHeader {
if t.FrameType == FRAME_INTER || t.FrameType == FRAME_KEY {
if len(b) < n+4 {
err = fmt.Errorf("videodata: parse invalid")
err = fmt.Errorf("videodata: parse invalid: neither interframe nor keyframe")
return
}
self.AVCPacketType = b[n]
t.AVCPacketType = b[n]
switch t.AVCPacketType {
case AVC_SEQHDR:
t.PacketType = PKTTYPE_SEQUENCE_START
case AVC_NALU:
t.PacketType = PKTTYPE_CODED_FRAMES
case AVC_EOS:
t.PacketType = PKTTYPE_SEQUENCE_END
}
n++
self.CompositionTime = pio.I24BE(b[n:])
t.CompositionTime = pio.I24BE(b[n:])
n += 3
}
} else {
if len(b) < n+4 {
err = fmt.Errorf("videodata: parse invalid: not enough bytes for the fourCC value")
return
}
t.FourCC[0] = b[n]
t.FourCC[1] = b[n+1]
t.FourCC[2] = b[n+2]
t.FourCC[3] = b[n+3]
n += 4
t.CompositionTime = 0
if t.FourCC == FOURCC_HEVC {
if t.PacketType == PKTTYPE_CODED_FRAMES {
t.CompositionTime = pio.I24BE(b[n:])
n += 3
}
}
}
//fmt.Printf("parseVideoHeader: PacketType: %d\n%s\n", t.PacketType, hex.Dump(b[:n]))
return
}
func (self Tag) videoFillHeader(b []byte) (n int) {
flags := self.FrameType<<4 | self.CodecID
func (t Tag) videoFillHeader(b []byte) (n int) {
if t.IsExHeader {
flags := t.FrameType<<4 | t.PacketType | 0b10000000
b[n] = flags
n++
b[n] = self.AVCPacketType
n++
pio.PutI24BE(b[n:], self.CompositionTime)
b[n] = t.FourCC[0]
b[n+1] = t.FourCC[1]
b[n+2] = t.FourCC[2]
b[n+3] = t.FourCC[3]
n += 4
if t.FourCC == FOURCC_HEVC {
if t.PacketType == PKTTYPE_CODED_FRAMES {
pio.PutI24BE(b[n:], t.CompositionTime)
n += 3
}
}
} else {
flags := t.FrameType<<4 | t.CodecID
b[n] = flags
n++
b[n] = t.AVCPacketType
n++
pio.PutI24BE(b[n:], t.CompositionTime)
n += 3
}
//fmt.Printf("videoFillHeader: PacketType: %d\n%s\n", t.PacketType, hex.Dump(b[:n]))
return
}
func (self Tag) FillHeader(b []byte) (n int) {
switch self.Type {
func (t Tag) FillHeader(b []byte) (n int) {
switch t.Type {
case TAG_AUDIO:
return self.audioFillHeader(b)
return t.audioFillHeader(b)
case TAG_VIDEO:
return self.videoFillHeader(b)
return t.videoFillHeader(b)
}
return
}
func (self *Tag) ParseHeader(b []byte) (n int, err error) {
switch self.Type {
func (t *Tag) ParseHeader(b []byte) (n int, err error) {
switch t.Type {
case TAG_AUDIO:
return self.audioParseHeader(b)
return t.audioParseHeader(b)
case TAG_VIDEO:
return self.videoParseHeader(b)
return t.videoParseHeader(b)
}
return

View File

@ -78,6 +78,7 @@ func (self *Server) handleConn(conn *Conn) (err error) {
}
if conn.playing {
fmt.Printf("play\n")
if self.HandlePlay != nil {
self.HandlePlay(conn)
}
@ -463,6 +464,8 @@ func (self *Conn) readConnect() (err error) {
return
}
fmt.Printf("readConnect: %+v\n", self.commandobj)
var ok bool
var _app, _tcurl interface{}
if _app, ok = self.commandobj["app"]; !ok {
@ -691,6 +694,8 @@ func (self *Conn) writeConnect(path string) (err error) {
return
}
fmt.Printf("writeConnect: app: %s\n", path)
// > connect("app")
if Debug {
fmt.Printf("rtmp: > connect('%s') host=%s\n", path, self.URL.Host)
@ -705,6 +710,7 @@ func (self *Conn) writeConnect(path string) (err error) {
"audioCodecs": 4071,
"videoCodecs": 252,
"videoFunction": 1,
"fourCcList": flvio.AMFArray{"av01", "vp09", "hvc1"},
},
); err != nil {
return
@ -984,14 +990,19 @@ func (self *Conn) WriteHeader(streams []av.CodecData) (err error) {
var metadata flvio.AMFMap = nil
metadata = self.GetMetaData()
//metadata = self.GetMetaData()
fmt.Printf("WriteHeader\n")
if metadata == nil {
if metadata, err = flv.NewMetadataByStreams(streams); err != nil {
fmt.Printf("WriteHeader error: %s\n", err.Error())
return
}
}
fmt.Printf("WriteHeader: %#v\n", metadata)
// > onMetaData()
if err = self.writeDataMsg(5, self.avmsgsid, "onMetaData", metadata); err != nil {
return
@ -1547,17 +1558,21 @@ func (self *Conn) handleMsg(timestamp uint32, msgsid uint32, msgtypeid uint8, ms
if metaindex != -1 && metaindex < len(self.datamsgvals) {
self.metadata = self.datamsgvals[metaindex].(flvio.AMFMap)
fmt.Printf("onMetadata: %+v\n", self.metadata)
fmt.Printf("videocodecid: %#08x (%f)\n", int64(self.metadata["videocodecid"].(float64)), self.metadata["videocodecid"].(float64))
}
case msgtypeidVideoMsg:
if len(msgdata) == 0 {
return
}
//fmt.Printf("msgdata: %#08x\n", msgdata[:5])
tag := flvio.Tag{Type: flvio.TAG_VIDEO}
var n int
if n, err = (&tag).ParseHeader(msgdata); err != nil {
return
}
//fmt.Printf("tag: %+v\n", tag)
if !(tag.FrameType == flvio.FRAME_INTER || tag.FrameType == flvio.FRAME_KEY) {
return
}

View File

@ -33,6 +33,30 @@ func (self *GolombBitReader) ReadBits(n int) (res uint, err error) {
return
}
func (self *GolombBitReader) ReadBits32(n uint) (r uint32, err error) {
var t uint
for i := uint(0); i < n; i++ {
t, err = self.ReadBit()
if err != nil {
return
}
r = (r << 1) | uint32(t)
}
return
}
func (self *GolombBitReader) ReadBits64(n uint) (r uint64, err error) {
var t uint
for i := uint(0); i < n; i++ {
t, err = self.ReadBit()
if err != nil {
return
}
r = (r << 1) | uint64(t)
}
return
}
func (self *GolombBitReader) ReadExponentialGolombCode() (res uint, err error) {
i := 0
for {