565 lines
10 KiB
Go
565 lines
10 KiB
Go
package fmp4io
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"git.r-2.top/kunmeng/vdk/utils/bits/pio"
|
|
)
|
|
|
|
const MOOV = Tag(0x6d6f6f76)
|
|
|
|
type Movie struct {
|
|
Header *MovieHeader
|
|
MovieExtend *MovieExtend
|
|
Tracks []*Track
|
|
Unknowns []Atom
|
|
AtomPos
|
|
}
|
|
|
|
func (a Movie) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(MOOV))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a Movie) marshal(b []byte) (n int) {
|
|
if a.Header != nil {
|
|
n += a.Header.Marshal(b[n:])
|
|
}
|
|
for _, atom := range a.Tracks {
|
|
n += atom.Marshal(b[n:])
|
|
}
|
|
if a.MovieExtend != nil {
|
|
n += a.MovieExtend.Marshal(b[n:])
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Marshal(b[n:])
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a Movie) Len() (n int) {
|
|
n += 8
|
|
if a.Header != nil {
|
|
n += a.Header.Len()
|
|
}
|
|
for _, atom := range a.Tracks {
|
|
n += atom.Len()
|
|
}
|
|
if a.MovieExtend != nil {
|
|
n += a.MovieExtend.Len()
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Len()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *Movie) Unmarshal(b []byte, offset int) (n int, err error) {
|
|
(&a.AtomPos).setPos(offset, len(b))
|
|
n += 8
|
|
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 MVHD:
|
|
{
|
|
atom := &MovieHeader{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("mvhd", n+offset, err)
|
|
return
|
|
}
|
|
a.Header = atom
|
|
}
|
|
case MVEX:
|
|
{
|
|
atom := &MovieExtend{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("mvex", n+offset, err)
|
|
return
|
|
}
|
|
a.MovieExtend = atom
|
|
}
|
|
case TRAK:
|
|
{
|
|
atom := &Track{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("trak", n+offset, err)
|
|
return
|
|
}
|
|
a.Tracks = append(a.Tracks, 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
|
|
}
|
|
a.Unknowns = append(a.Unknowns, atom)
|
|
}
|
|
}
|
|
n += size
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a Movie) Children() (r []Atom) {
|
|
if a.Header != nil {
|
|
r = append(r, a.Header)
|
|
}
|
|
if a.MovieExtend != nil {
|
|
r = append(r, a.MovieExtend)
|
|
}
|
|
for _, atom := range a.Tracks {
|
|
r = append(r, atom)
|
|
}
|
|
r = append(r, a.Unknowns...)
|
|
return
|
|
}
|
|
|
|
func (a Movie) Tag() Tag {
|
|
return MOOV
|
|
}
|
|
|
|
const MVHD = Tag(0x6d766864)
|
|
|
|
type MovieHeader struct {
|
|
Version uint8
|
|
Flags uint32
|
|
CreateTime time.Time
|
|
ModifyTime time.Time
|
|
TimeScale uint32
|
|
Duration uint32
|
|
PreferredRate float64
|
|
PreferredVolume float64
|
|
Matrix [9]int32
|
|
NextTrackID uint32
|
|
AtomPos
|
|
}
|
|
|
|
func (a MovieHeader) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(MVHD))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a MovieHeader) marshal(b []byte) (n int) {
|
|
pio.PutU8(b[n:], a.Version)
|
|
n += 1
|
|
pio.PutU24BE(b[n:], a.Flags)
|
|
n += 3
|
|
PutTime32(b[n:], a.CreateTime)
|
|
n += 4
|
|
PutTime32(b[n:], a.ModifyTime)
|
|
n += 4
|
|
pio.PutU32BE(b[n:], a.TimeScale)
|
|
n += 4
|
|
pio.PutU32BE(b[n:], a.Duration)
|
|
n += 4
|
|
PutFixed32(b[n:], a.PreferredRate)
|
|
n += 4
|
|
PutFixed16(b[n:], a.PreferredVolume)
|
|
n += 2
|
|
n += 10
|
|
for _, entry := range a.Matrix {
|
|
pio.PutI32BE(b[n:], entry)
|
|
n += 4
|
|
}
|
|
n += 24
|
|
pio.PutU32BE(b[n:], a.NextTrackID)
|
|
n += 4
|
|
return
|
|
}
|
|
|
|
func (a MovieHeader) Len() (n int) {
|
|
n += 8
|
|
n += 1
|
|
n += 3
|
|
n += 4
|
|
n += 4
|
|
n += 4
|
|
n += 4
|
|
n += 4
|
|
n += 2
|
|
n += 10
|
|
n += 4 * len(a.Matrix[:])
|
|
n += 24
|
|
n += 4
|
|
return
|
|
}
|
|
|
|
func (a *MovieHeader) Unmarshal(b []byte, offset int) (n int, err error) {
|
|
(&a.AtomPos).setPos(offset, len(b))
|
|
n += 8
|
|
if len(b) < n+1 {
|
|
err = parseErr("Version", n+offset, err)
|
|
return
|
|
}
|
|
a.Version = pio.U8(b[n:])
|
|
n += 1
|
|
if len(b) < n+3 {
|
|
err = parseErr("Flags", n+offset, err)
|
|
return
|
|
}
|
|
a.Flags = pio.U24BE(b[n:])
|
|
n += 3
|
|
if len(b) < n+4 {
|
|
err = parseErr("CreateTime", n+offset, err)
|
|
return
|
|
}
|
|
a.CreateTime = GetTime32(b[n:])
|
|
n += 4
|
|
if len(b) < n+4 {
|
|
err = parseErr("ModifyTime", n+offset, err)
|
|
return
|
|
}
|
|
a.ModifyTime = GetTime32(b[n:])
|
|
n += 4
|
|
if len(b) < n+4 {
|
|
err = parseErr("TimeScale", n+offset, err)
|
|
return
|
|
}
|
|
a.TimeScale = pio.U32BE(b[n:])
|
|
n += 4
|
|
if len(b) < n+4 {
|
|
err = parseErr("Duration", n+offset, err)
|
|
return
|
|
}
|
|
a.Duration = pio.U32BE(b[n:])
|
|
n += 4
|
|
if len(b) < n+4 {
|
|
err = parseErr("PreferredRate", n+offset, err)
|
|
return
|
|
}
|
|
a.PreferredRate = GetFixed32(b[n:])
|
|
n += 4
|
|
if len(b) < n+2 {
|
|
err = parseErr("PreferredVolume", n+offset, err)
|
|
return
|
|
}
|
|
a.PreferredVolume = GetFixed16(b[n:])
|
|
n += 2
|
|
n += 10
|
|
if len(b) < n+4*len(a.Matrix) {
|
|
err = parseErr("Matrix", n+offset, err)
|
|
return
|
|
}
|
|
for i := range a.Matrix {
|
|
a.Matrix[i] = pio.I32BE(b[n:])
|
|
n += 4
|
|
}
|
|
n += 24
|
|
if len(b) < n+4 {
|
|
err = parseErr("NextTrackID", n+offset, err)
|
|
return
|
|
}
|
|
a.NextTrackID = pio.U32BE(b[n:])
|
|
n += 4
|
|
return
|
|
}
|
|
|
|
func (a MovieHeader) Children() (r []Atom) {
|
|
return
|
|
}
|
|
|
|
func (a MovieHeader) Tag() Tag {
|
|
return MVHD
|
|
}
|
|
|
|
func (a MovieHeader) String() string {
|
|
return fmt.Sprintf("dur=%d", a.Duration)
|
|
}
|
|
|
|
const TRAK = Tag(0x7472616b)
|
|
|
|
type Track struct {
|
|
Header *TrackHeader
|
|
Media *Media
|
|
Unknowns []Atom
|
|
AtomPos
|
|
}
|
|
|
|
func (a Track) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(TRAK))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a Track) marshal(b []byte) (n int) {
|
|
if a.Header != nil {
|
|
n += a.Header.Marshal(b[n:])
|
|
}
|
|
if a.Media != nil {
|
|
n += a.Media.Marshal(b[n:])
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Marshal(b[n:])
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a Track) Len() (n int) {
|
|
n += 8
|
|
if a.Header != nil {
|
|
n += a.Header.Len()
|
|
}
|
|
if a.Media != nil {
|
|
n += a.Media.Len()
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Len()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *Track) Unmarshal(b []byte, offset int) (n int, err error) {
|
|
(&a.AtomPos).setPos(offset, len(b))
|
|
n += 8
|
|
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 TKHD:
|
|
{
|
|
atom := &TrackHeader{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("tkhd", n+offset, err)
|
|
return
|
|
}
|
|
a.Header = atom
|
|
}
|
|
case MDIA:
|
|
{
|
|
atom := &Media{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("mdia", n+offset, err)
|
|
return
|
|
}
|
|
a.Media = 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
|
|
}
|
|
a.Unknowns = append(a.Unknowns, atom)
|
|
}
|
|
}
|
|
n += size
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a Track) Children() (r []Atom) {
|
|
if a.Header != nil {
|
|
r = append(r, a.Header)
|
|
}
|
|
if a.Media != nil {
|
|
r = append(r, a.Media)
|
|
}
|
|
r = append(r, a.Unknowns...)
|
|
return
|
|
}
|
|
|
|
func (a Track) Tag() Tag {
|
|
return TRAK
|
|
}
|
|
|
|
func (a *Track) GetAVC1Conf() (conf *AVC1Conf) {
|
|
atom := FindChildren(a, AVCC)
|
|
conf, _ = atom.(*AVC1Conf)
|
|
return
|
|
}
|
|
|
|
func (a *Track) GetElemStreamDesc() (esds *ElemStreamDesc) {
|
|
atom := FindChildren(a, ESDS)
|
|
esds, _ = atom.(*ElemStreamDesc)
|
|
return
|
|
}
|
|
|
|
const TKHD = Tag(0x746b6864)
|
|
|
|
type TrackHeader struct {
|
|
Version uint8
|
|
Flags uint32
|
|
CreateTime time.Time
|
|
ModifyTime time.Time
|
|
TrackID uint32
|
|
Duration uint32
|
|
Layer int16
|
|
AlternateGroup int16
|
|
Volume float64
|
|
Matrix [9]int32
|
|
TrackWidth float64
|
|
TrackHeight float64
|
|
AtomPos
|
|
}
|
|
|
|
func (a TrackHeader) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(TKHD))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a TrackHeader) marshal(b []byte) (n int) {
|
|
pio.PutU8(b[n:], a.Version)
|
|
n += 1
|
|
pio.PutU24BE(b[n:], a.Flags)
|
|
n += 3
|
|
PutTime32(b[n:], a.CreateTime)
|
|
n += 4
|
|
PutTime32(b[n:], a.ModifyTime)
|
|
n += 4
|
|
pio.PutU32BE(b[n:], a.TrackID)
|
|
n += 4
|
|
n += 4
|
|
pio.PutU32BE(b[n:], a.Duration)
|
|
n += 4
|
|
n += 8
|
|
pio.PutI16BE(b[n:], a.Layer)
|
|
n += 2
|
|
pio.PutI16BE(b[n:], a.AlternateGroup)
|
|
n += 2
|
|
PutFixed16(b[n:], a.Volume)
|
|
n += 2
|
|
n += 2
|
|
for _, entry := range a.Matrix {
|
|
pio.PutI32BE(b[n:], entry)
|
|
n += 4
|
|
}
|
|
PutFixed32(b[n:], a.TrackWidth)
|
|
n += 4
|
|
PutFixed32(b[n:], a.TrackHeight)
|
|
n += 4
|
|
return
|
|
}
|
|
|
|
func (a TrackHeader) Len() (n int) {
|
|
n += 8
|
|
n += 1
|
|
n += 3
|
|
n += 4
|
|
n += 4
|
|
n += 4
|
|
n += 4
|
|
n += 4
|
|
n += 8
|
|
n += 2
|
|
n += 2
|
|
n += 2
|
|
n += 2
|
|
n += 4 * len(a.Matrix[:])
|
|
n += 4
|
|
n += 4
|
|
return
|
|
}
|
|
|
|
func (a *TrackHeader) Unmarshal(b []byte, offset int) (n int, err error) {
|
|
(&a.AtomPos).setPos(offset, len(b))
|
|
n += 8
|
|
if len(b) < n+1 {
|
|
err = parseErr("Version", n+offset, err)
|
|
return
|
|
}
|
|
a.Version = pio.U8(b[n:])
|
|
n += 1
|
|
if len(b) < n+3 {
|
|
err = parseErr("Flags", n+offset, err)
|
|
return
|
|
}
|
|
a.Flags = pio.U24BE(b[n:])
|
|
n += 3
|
|
if len(b) < n+4 {
|
|
err = parseErr("CreateTime", n+offset, err)
|
|
return
|
|
}
|
|
a.CreateTime = GetTime32(b[n:])
|
|
n += 4
|
|
if len(b) < n+4 {
|
|
err = parseErr("ModifyTime", n+offset, err)
|
|
return
|
|
}
|
|
a.ModifyTime = GetTime32(b[n:])
|
|
n += 4
|
|
if len(b) < n+4 {
|
|
err = parseErr("TrackId", n+offset, err)
|
|
return
|
|
}
|
|
a.TrackID = pio.U32BE(b[n:])
|
|
n += 4
|
|
n += 4
|
|
if len(b) < n+4 {
|
|
err = parseErr("Duration", n+offset, err)
|
|
return
|
|
}
|
|
a.Duration = pio.U32BE(b[n:])
|
|
n += 4
|
|
n += 8
|
|
if len(b) < n+2 {
|
|
err = parseErr("Layer", n+offset, err)
|
|
return
|
|
}
|
|
a.Layer = pio.I16BE(b[n:])
|
|
n += 2
|
|
if len(b) < n+2 {
|
|
err = parseErr("AlternateGroup", n+offset, err)
|
|
return
|
|
}
|
|
a.AlternateGroup = pio.I16BE(b[n:])
|
|
n += 2
|
|
if len(b) < n+2 {
|
|
err = parseErr("Volume", n+offset, err)
|
|
return
|
|
}
|
|
a.Volume = GetFixed16(b[n:])
|
|
n += 2
|
|
n += 2
|
|
if len(b) < n+4*len(a.Matrix) {
|
|
err = parseErr("Matrix", n+offset, err)
|
|
return
|
|
}
|
|
for i := range a.Matrix {
|
|
a.Matrix[i] = pio.I32BE(b[n:])
|
|
n += 4
|
|
}
|
|
if len(b) < n+4 {
|
|
err = parseErr("TrackWidth", n+offset, err)
|
|
return
|
|
}
|
|
a.TrackWidth = GetFixed32(b[n:])
|
|
n += 4
|
|
if len(b) < n+4 {
|
|
err = parseErr("TrackHeight", n+offset, err)
|
|
return
|
|
}
|
|
a.TrackHeight = GetFixed32(b[n:])
|
|
n += 4
|
|
return
|
|
}
|
|
|
|
func (a TrackHeader) Children() (r []Atom) {
|
|
return
|
|
}
|
|
|
|
func (a TrackHeader) Tag() Tag {
|
|
return TKHD
|
|
}
|
|
|
|
const MDAT = Tag(0x6d646174)
|