work HEVC in progress

This commit is contained in:
Andrey Semochkin 2021-02-03 04:27:25 +03:00
parent a102e9cb3e
commit d9a713ebc1
3 changed files with 349 additions and 8 deletions

View File

@ -1,7 +1,10 @@
package mp4io
import "github.com/deepch/vdk/utils/bits/pio"
import "time"
import (
"time"
"github.com/deepch/vdk/utils/bits/pio"
)
const MOOF = Tag(0x6d6f6f66)
@ -21,6 +24,17 @@ func (self AVC1Desc) Tag() Tag {
return AVC1
}
//0x31766568
const HEV1 = Tag(0x68766331)
func (self HV1Desc) Tag() Tag {
return HEV1
}
//const HVC1 = Tag(0x68766331)
//func (self HVC1Desc) Tag() Tag {
// return HVC1
//}
const URL = Tag(0x75726c20)
func (self DataReferUrl) Tag() Tag {
@ -153,6 +167,12 @@ func (self AVC1Conf) Tag() Tag {
return AVCC
}
const HVCC = Tag(0x68766343)
func (self HV1Conf) Tag() Tag {
return HVCC
}
const TFDT = Tag(0x74666474)
func (self TrackFragDecodeTime) Tag() Tag {
@ -1682,6 +1702,7 @@ func (self SampleTable) Children() (r []Atom) {
type SampleDesc struct {
Version uint8
AVC1Desc *AVC1Desc
HV1Desc *HV1Desc
MP4ADesc *MP4ADesc
Unknowns []Atom
AtomPos
@ -1701,6 +1722,9 @@ func (self SampleDesc) marshal(b []byte) (n int) {
if self.AVC1Desc != nil {
_childrenNR++
}
if self.HV1Desc != nil {
_childrenNR++
}
if self.MP4ADesc != nil {
_childrenNR++
}
@ -1710,6 +1734,9 @@ func (self SampleDesc) marshal(b []byte) (n int) {
if self.AVC1Desc != nil {
n += self.AVC1Desc.Marshal(b[n:])
}
if self.HV1Desc != nil {
n += self.HV1Desc.Marshal(b[n:])
}
if self.MP4ADesc != nil {
n += self.MP4ADesc.Marshal(b[n:])
}
@ -1726,6 +1753,9 @@ func (self SampleDesc) Len() (n int) {
if self.AVC1Desc != nil {
n += self.AVC1Desc.Len()
}
if self.HV1Desc != nil {
n += self.HV1Desc.Len()
}
if self.MP4ADesc != nil {
n += self.MP4ADesc.Len()
}
@ -1762,6 +1792,15 @@ func (self *SampleDesc) Unmarshal(b []byte, offset int) (n int, err error) {
}
self.AVC1Desc = atom
}
case HEV1:
{
atom := &HV1Desc{}
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
err = parseErr("hec1", n+offset, err)
return
}
self.HV1Desc = atom
}
case MP4A:
{
atom := &MP4ADesc{}
@ -1789,6 +1828,9 @@ func (self SampleDesc) Children() (r []Atom) {
if self.AVC1Desc != nil {
r = append(r, self.AVC1Desc)
}
if self.HV1Desc != nil {
r = append(r, self.HV1Desc)
}
if self.MP4ADesc != nil {
r = append(r, self.MP4ADesc)
}
@ -1955,6 +1997,25 @@ func (self MP4ADesc) Children() (r []Atom) {
return
}
type HV1Desc struct {
DataRefIdx int16
Version int16
Revision int16
Vendor int32
TemporalQuality int32
SpatialQuality int32
Width int16
Height int16
HorizontalResolution float64
VorizontalResolution float64
FrameCount int16
CompressorName [32]byte
Depth int16
ColorTableId int16
Conf *HV1Conf
Unknowns []Atom
AtomPos
}
type AVC1Desc struct {
DataRefIdx int16
Version int16
@ -1981,6 +2042,12 @@ func (self AVC1Desc) Marshal(b []byte) (n int) {
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self HV1Desc) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(HEV1))
n += self.marshal(b[8:]) + 8
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self AVC1Desc) marshal(b []byte) (n int) {
n += 6
pio.PutI16BE(b[n:], self.DataRefIdx)
@ -2020,6 +2087,45 @@ func (self AVC1Desc) marshal(b []byte) (n int) {
}
return
}
func (self HV1Desc) marshal(b []byte) (n int) {
n += 6
pio.PutI16BE(b[n:], self.DataRefIdx)
n += 2
pio.PutI16BE(b[n:], self.Version)
n += 2
pio.PutI16BE(b[n:], self.Revision)
n += 2
pio.PutI32BE(b[n:], self.Vendor)
n += 4
pio.PutI32BE(b[n:], self.TemporalQuality)
n += 4
pio.PutI32BE(b[n:], self.SpatialQuality)
n += 4
pio.PutI16BE(b[n:], self.Width)
n += 2
pio.PutI16BE(b[n:], self.Height)
n += 2
PutFixed32(b[n:], self.HorizontalResolution)
n += 4
PutFixed32(b[n:], self.VorizontalResolution)
n += 4
n += 4
pio.PutI16BE(b[n:], self.FrameCount)
n += 2
copy(b[n:], self.CompressorName[:])
n += len(self.CompressorName[:])
pio.PutI16BE(b[n:], self.Depth)
n += 2
pio.PutI16BE(b[n:], self.ColorTableId)
n += 2
if self.Conf != nil {
n += self.Conf.Marshal(b[n:])
}
for _, atom := range self.Unknowns {
n += atom.Marshal(b[n:])
}
return
}
func (self AVC1Desc) Len() (n int) {
n += 8
n += 6
@ -2046,6 +2152,32 @@ func (self AVC1Desc) Len() (n int) {
}
return
}
func (self HV1Desc) Len() (n int) {
n += 8
n += 6
n += 2
n += 2
n += 2
n += 4
n += 4
n += 4
n += 2
n += 2
n += 4
n += 4
n += 4
n += 2
n += len(self.CompressorName[:])
n += 2
n += 2
if self.Conf != nil {
n += self.Conf.Len()
}
for _, atom := range self.Unknowns {
n += atom.Len()
}
return
}
func (self *AVC1Desc) Unmarshal(b []byte, offset int) (n int, err error) {
(&self.AtomPos).setPos(offset, len(b))
n += 8
@ -2166,6 +2298,126 @@ func (self *AVC1Desc) Unmarshal(b []byte, offset int) (n int, err error) {
}
return
}
func (self *HV1Desc) Unmarshal(b []byte, offset int) (n int, err error) {
(&self.AtomPos).setPos(offset, len(b))
n += 8
n += 6
if len(b) < n+2 {
err = parseErr("DataRefIdx", n+offset, err)
return
}
self.DataRefIdx = pio.I16BE(b[n:])
n += 2
if len(b) < n+2 {
err = parseErr("Version", n+offset, err)
return
}
self.Version = pio.I16BE(b[n:])
n += 2
if len(b) < n+2 {
err = parseErr("Revision", n+offset, err)
return
}
self.Revision = pio.I16BE(b[n:])
n += 2
if len(b) < n+4 {
err = parseErr("Vendor", n+offset, err)
return
}
self.Vendor = pio.I32BE(b[n:])
n += 4
if len(b) < n+4 {
err = parseErr("TemporalQuality", n+offset, err)
return
}
self.TemporalQuality = pio.I32BE(b[n:])
n += 4
if len(b) < n+4 {
err = parseErr("SpatialQuality", n+offset, err)
return
}
self.SpatialQuality = pio.I32BE(b[n:])
n += 4
if len(b) < n+2 {
err = parseErr("Width", n+offset, err)
return
}
self.Width = pio.I16BE(b[n:])
n += 2
if len(b) < n+2 {
err = parseErr("Height", n+offset, err)
return
}
self.Height = pio.I16BE(b[n:])
n += 2
if len(b) < n+4 {
err = parseErr("HorizontalResolution", n+offset, err)
return
}
self.HorizontalResolution = GetFixed32(b[n:])
n += 4
if len(b) < n+4 {
err = parseErr("VorizontalResolution", n+offset, err)
return
}
self.VorizontalResolution = GetFixed32(b[n:])
n += 4
n += 4
if len(b) < n+2 {
err = parseErr("FrameCount", n+offset, err)
return
}
self.FrameCount = pio.I16BE(b[n:])
n += 2
if len(b) < n+len(self.CompressorName) {
err = parseErr("CompressorName", n+offset, err)
return
}
copy(self.CompressorName[:], b[n:])
n += len(self.CompressorName)
if len(b) < n+2 {
err = parseErr("Depth", n+offset, err)
return
}
self.Depth = pio.I16BE(b[n:])
n += 2
if len(b) < n+2 {
err = parseErr("ColorTableId", n+offset, err)
return
}
self.ColorTableId = pio.I16BE(b[n:])
n += 2
for n+8 < len(b) {
tag := Tag(pio.U32BE(b[n+4:]))
size := int(pio.U32BE(b[n:]))
if len(b) < n+size {
err = parseErr("TagSizeInvalid", n+offset, err)
return
}
switch tag {
case HVCC:
{
atom := &HV1Conf{}
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
err = parseErr("hvcC", n+offset, err)
return
}
self.Conf = atom
}
default:
{
atom := &Dummy{Tag_: tag, Data: b[n : n+size]}
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
err = parseErr("", n+offset, err)
return
}
self.Unknowns = append(self.Unknowns, atom)
}
}
n += size
}
return
}
func (self AVC1Desc) Children() (r []Atom) {
if self.Conf != nil {
r = append(r, self.Conf)
@ -2173,12 +2425,24 @@ func (self AVC1Desc) Children() (r []Atom) {
r = append(r, self.Unknowns...)
return
}
func (self HV1Desc) Children() (r []Atom) {
if self.Conf != nil {
r = append(r, self.Conf)
}
r = append(r, self.Unknowns...)
return
}
type AVC1Conf struct {
Data []byte
AtomPos
}
type HV1Conf struct {
Data []byte
AtomPos
}
func (self AVC1Conf) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(AVCC))
n += self.marshal(b[8:]) + 8
@ -2206,6 +2470,36 @@ func (self AVC1Conf) Children() (r []Atom) {
return
}
/*
HVEC
*/
func (self HV1Conf) Marshal(b []byte) (n int) {
pio.PutU32BE(b[4:], uint32(HVCC))
n += self.marshal(b[8:]) + 8
pio.PutU32BE(b[0:], uint32(n))
return
}
func (self HV1Conf) marshal(b []byte) (n int) {
copy(b[n:], self.Data[:])
n += len(self.Data[:])
return
}
func (self HV1Conf) Len() (n int) {
n += 8
n += len(self.Data[:])
return
}
func (self *HV1Conf) Unmarshal(b []byte, offset int) (n int, err error) {
(&self.AtomPos).setPos(offset, len(b))
n += 8
self.Data = b[n:]
n += len(b[n:])
return
}
func (self HV1Conf) Children() (r []Atom) {
return
}
type TimeToSample struct {
Version uint8
Flags uint32

View File

@ -29,7 +29,7 @@ func NewMuxer(w io.WriteSeeker) *Muxer {
func (self *Muxer) newStream(codec av.CodecData) (err error) {
switch codec.Type() {
case av.H264, av.AAC:
case av.H264, av.H265, av.AAC:
default:
err = fmt.Errorf("mp4: codec type=%v is not supported", codec.Type())
@ -82,6 +82,8 @@ func (self *Muxer) newStream(codec av.CodecData) (err error) {
switch codec.Type() {
case av.H264:
stream.sample.SyncSample = &mp4io.SyncSample{}
case av.H265:
stream.sample.SyncSample = &mp4io.SyncSample{}
}
stream.timeScale = 90000
@ -94,7 +96,6 @@ func (self *Muxer) newStream(codec av.CodecData) (err error) {
func (self *Stream) fillTrackAtom() (err error) {
self.trackAtom.Media.Header.TimeScale = int32(self.timeScale)
self.trackAtom.Media.Header.Duration = int32(self.duration)
if self.Type() == av.H264 {
codec := self.CodecData.(h264parser.CodecData)
width, height := codec.Width(), codec.Height()
@ -118,7 +119,29 @@ func (self *Stream) fillTrackAtom() (err error) {
}
self.trackAtom.Header.TrackWidth = float64(width)
self.trackAtom.Header.TrackHeight = float64(height)
} else if self.Type() == av.H265 {
codec := self.CodecData.(h264parser.CodecData)
width, height := codec.Width(), codec.Height()
self.sample.SampleDesc.HV1Desc = &mp4io.HV1Desc{
DataRefIdx: 1,
HorizontalResolution: 72,
VorizontalResolution: 72,
Width: int16(width),
Height: int16(height),
FrameCount: 1,
Depth: 24,
ColorTableId: -1,
Conf: &mp4io.HV1Conf{Data: codec.AVCDecoderConfRecordBytes()},
}
self.trackAtom.Media.Handler = &mp4io.HandlerRefer{
SubType: [4]byte{'v', 'i', 'd', 'e'},
Name: []byte("Video Media Handler"),
}
self.trackAtom.Media.Info.Video = &mp4io.VideoMediaInfo{
Flags: 0x000001,
}
self.trackAtom.Header.TrackWidth = float64(width)
self.trackAtom.Header.TrackHeight = float64(height)
} else if self.Type() == av.AAC {
codec := self.CodecData.(aacparser.CodecData)
self.sample.SampleDesc.MP4ADesc = &mp4io.MP4ADesc{

View File

@ -34,7 +34,7 @@ func (self *Muxer) SetMaxFrames(count int) {
}
func (self *Muxer) newStream(codec av.CodecData) (err error) {
switch codec.Type() {
case av.H264, av.AAC:
case av.H264, av.H265, av.AAC:
default:
err = fmt.Errorf("fmp4: codec type=%v is not supported", codec.Type())
return
@ -78,6 +78,9 @@ func (self *Muxer) newStream(codec av.CodecData) (err error) {
case av.H264:
stream.sample.SyncSample = &mp4io.SyncSample{}
stream.timeScale = 90000
case av.H265:
stream.sample.SyncSample = &mp4io.SyncSample{}
stream.timeScale = 90000
case av.AAC:
stream.timeScale = int64(codec.(av.AudioCodecData).SampleRate())
}
@ -174,6 +177,28 @@ func (self *Stream) fillTrackAtom() (err error) {
Flags: 0x000001,
}
self.codecString = fmt.Sprintf("avc1.%02X%02X%02X", codec.RecordInfo.AVCProfileIndication, codec.RecordInfo.ProfileCompatibility, codec.RecordInfo.AVCLevelIndication)
} else if self.Type() == av.H265 {
codec := self.CodecData.(h264parser.CodecData)
width, height := codec.Width(), codec.Height()
self.sample.SampleDesc.HV1Desc = &mp4io.HV1Desc{
DataRefIdx: 1,
HorizontalResolution: 72,
VorizontalResolution: 72,
Width: int16(width),
Height: int16(height),
FrameCount: 1,
Depth: 24,
ColorTableId: -1,
Conf: &mp4io.HV1Conf{Data: codec.AVCDecoderConfRecordBytes()},
}
self.trackAtom.Media.Handler = &mp4io.HandlerRefer{
SubType: [4]byte{'v', 'i', 'd', 'e'},
Name: []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 'G', 'G', 0, 0, 0},
}
self.trackAtom.Media.Info.Video = &mp4io.VideoMediaInfo{
Flags: 0x000001,
}
self.codecString = fmt.Sprintf("hvc1.%02X%02X%02X", codec.RecordInfo.AVCProfileIndication, codec.RecordInfo.ProfileCompatibility, codec.RecordInfo.AVCLevelIndication)
} else if self.Type() == av.AAC {
codec := self.CodecData.(aacparser.CodecData)
self.sample.SampleDesc.MP4ADesc = &mp4io.MP4ADesc{
@ -183,8 +208,7 @@ func (self *Stream) fillTrackAtom() (err error) {
SampleRate: float64(codec.SampleRate()),
Unknowns: []mp4io.Atom{self.buildEsds(codec.MPEG4AudioConfigBytes())},
}
//log.Fatalln(codec.MPEG4AudioConfigBytes())
//log.Fatalln(codec.SampleFormat().BytesPerSample())
self.trackAtom.Header.Volume = 1
self.trackAtom.Header.AlternateGroup = 1
self.trackAtom.Header.Duration = 0