work HEVC in progress
This commit is contained in:
parent
a102e9cb3e
commit
d9a713ebc1
@ -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
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user