617 lines
11 KiB
Go
617 lines
11 KiB
Go
package fmp4io
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/deepch/vdk/utils/bits/pio"
|
|
)
|
|
|
|
const MDIA = Tag(0x6d646961)
|
|
|
|
type Media struct {
|
|
Header *MediaHeader
|
|
Handler *HandlerRefer
|
|
Info *MediaInfo
|
|
Unknowns []Atom
|
|
AtomPos
|
|
}
|
|
|
|
func (a Media) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(MDIA))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a Media) marshal(b []byte) (n int) {
|
|
if a.Header != nil {
|
|
n += a.Header.Marshal(b[n:])
|
|
}
|
|
if a.Handler != nil {
|
|
n += a.Handler.Marshal(b[n:])
|
|
}
|
|
if a.Info != nil {
|
|
n += a.Info.Marshal(b[n:])
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Marshal(b[n:])
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a Media) Len() (n int) {
|
|
n += 8
|
|
if a.Header != nil {
|
|
n += a.Header.Len()
|
|
}
|
|
if a.Handler != nil {
|
|
n += a.Handler.Len()
|
|
}
|
|
if a.Info != nil {
|
|
n += a.Info.Len()
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Len()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *Media) 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 MDHD:
|
|
{
|
|
atom := &MediaHeader{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("mdhd", n+offset, err)
|
|
return
|
|
}
|
|
a.Header = atom
|
|
}
|
|
case HDLR:
|
|
{
|
|
atom := &HandlerRefer{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("hdlr", n+offset, err)
|
|
return
|
|
}
|
|
a.Handler = atom
|
|
}
|
|
case MINF:
|
|
{
|
|
atom := &MediaInfo{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("minf", n+offset, err)
|
|
return
|
|
}
|
|
a.Info = 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 Media) Children() (r []Atom) {
|
|
if a.Header != nil {
|
|
r = append(r, a.Header)
|
|
}
|
|
if a.Handler != nil {
|
|
r = append(r, a.Handler)
|
|
}
|
|
if a.Info != nil {
|
|
r = append(r, a.Info)
|
|
}
|
|
r = append(r, a.Unknowns...)
|
|
return
|
|
}
|
|
|
|
func (a Media) Tag() Tag {
|
|
return MDIA
|
|
}
|
|
|
|
const MDHD = Tag(0x6d646864)
|
|
|
|
type MediaHeader struct {
|
|
Version uint8
|
|
Flags uint32
|
|
CreateTime time.Time
|
|
ModifyTime time.Time
|
|
TimeScale uint32
|
|
Duration uint32
|
|
Language int16
|
|
Quality int16
|
|
AtomPos
|
|
}
|
|
|
|
func (a MediaHeader) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(MDHD))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a MediaHeader) 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
|
|
pio.PutI16BE(b[n:], a.Language)
|
|
n += 2
|
|
pio.PutI16BE(b[n:], a.Quality)
|
|
n += 2
|
|
return
|
|
}
|
|
|
|
func (a MediaHeader) Len() (n int) {
|
|
n += 8
|
|
n += 1
|
|
n += 3
|
|
n += 4
|
|
n += 4
|
|
n += 4
|
|
n += 4
|
|
n += 2
|
|
n += 2
|
|
return
|
|
}
|
|
|
|
func (a *MediaHeader) 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+2 {
|
|
err = parseErr("Language", n+offset, err)
|
|
return
|
|
}
|
|
a.Language = pio.I16BE(b[n:])
|
|
n += 2
|
|
if len(b) < n+2 {
|
|
err = parseErr("Quality", n+offset, err)
|
|
return
|
|
}
|
|
a.Quality = pio.I16BE(b[n:])
|
|
n += 2
|
|
return
|
|
}
|
|
|
|
func (a MediaHeader) Children() (r []Atom) {
|
|
return
|
|
}
|
|
|
|
func (a MediaHeader) Tag() Tag {
|
|
return MDHD
|
|
}
|
|
|
|
const MINF = Tag(0x6d696e66)
|
|
|
|
type MediaInfo struct {
|
|
Sound *SoundMediaInfo
|
|
Video *VideoMediaInfo
|
|
Data *DataInfo
|
|
Sample *SampleTable
|
|
Unknowns []Atom
|
|
AtomPos
|
|
}
|
|
|
|
func (a MediaInfo) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(MINF))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a MediaInfo) marshal(b []byte) (n int) {
|
|
if a.Sound != nil {
|
|
n += a.Sound.Marshal(b[n:])
|
|
}
|
|
if a.Video != nil {
|
|
n += a.Video.Marshal(b[n:])
|
|
}
|
|
if a.Data != nil {
|
|
n += a.Data.Marshal(b[n:])
|
|
}
|
|
if a.Sample != nil {
|
|
n += a.Sample.Marshal(b[n:])
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Marshal(b[n:])
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a MediaInfo) Len() (n int) {
|
|
n += 8
|
|
if a.Sound != nil {
|
|
n += a.Sound.Len()
|
|
}
|
|
if a.Video != nil {
|
|
n += a.Video.Len()
|
|
}
|
|
if a.Data != nil {
|
|
n += a.Data.Len()
|
|
}
|
|
if a.Sample != nil {
|
|
n += a.Sample.Len()
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Len()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *MediaInfo) 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 SMHD:
|
|
{
|
|
atom := &SoundMediaInfo{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("smhd", n+offset, err)
|
|
return
|
|
}
|
|
a.Sound = atom
|
|
}
|
|
case VMHD:
|
|
{
|
|
atom := &VideoMediaInfo{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("vmhd", n+offset, err)
|
|
return
|
|
}
|
|
a.Video = atom
|
|
}
|
|
case DINF:
|
|
{
|
|
atom := &DataInfo{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("dinf", n+offset, err)
|
|
return
|
|
}
|
|
a.Data = atom
|
|
}
|
|
case STBL:
|
|
{
|
|
atom := &SampleTable{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("stbl", n+offset, err)
|
|
return
|
|
}
|
|
a.Sample = 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 MediaInfo) Children() (r []Atom) {
|
|
if a.Sound != nil {
|
|
r = append(r, a.Sound)
|
|
}
|
|
if a.Video != nil {
|
|
r = append(r, a.Video)
|
|
}
|
|
if a.Data != nil {
|
|
r = append(r, a.Data)
|
|
}
|
|
if a.Sample != nil {
|
|
r = append(r, a.Sample)
|
|
}
|
|
r = append(r, a.Unknowns...)
|
|
return
|
|
}
|
|
|
|
func (a MediaInfo) Tag() Tag {
|
|
return MINF
|
|
}
|
|
|
|
const VMHD = Tag(0x766d6864)
|
|
|
|
type VideoMediaInfo struct {
|
|
Version uint8
|
|
Flags uint32
|
|
GraphicsMode int16
|
|
Opcolor [3]int16
|
|
AtomPos
|
|
}
|
|
|
|
func (a VideoMediaInfo) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(VMHD))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a VideoMediaInfo) marshal(b []byte) (n int) {
|
|
pio.PutU8(b[n:], a.Version)
|
|
n += 1
|
|
pio.PutU24BE(b[n:], a.Flags)
|
|
n += 3
|
|
pio.PutI16BE(b[n:], a.GraphicsMode)
|
|
n += 2
|
|
for _, entry := range a.Opcolor {
|
|
pio.PutI16BE(b[n:], entry)
|
|
n += 2
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a VideoMediaInfo) Len() (n int) {
|
|
n += 8
|
|
n += 1
|
|
n += 3
|
|
n += 2
|
|
n += 2 * len(a.Opcolor[:])
|
|
return
|
|
}
|
|
|
|
func (a *VideoMediaInfo) 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+2 {
|
|
err = parseErr("GraphicsMode", n+offset, err)
|
|
return
|
|
}
|
|
a.GraphicsMode = pio.I16BE(b[n:])
|
|
n += 2
|
|
if len(b) < n+2*len(a.Opcolor) {
|
|
err = parseErr("Opcolor", n+offset, err)
|
|
return
|
|
}
|
|
for i := range a.Opcolor {
|
|
a.Opcolor[i] = pio.I16BE(b[n:])
|
|
n += 2
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a VideoMediaInfo) Children() (r []Atom) {
|
|
return
|
|
}
|
|
|
|
func (a VideoMediaInfo) Tag() Tag {
|
|
return VMHD
|
|
}
|
|
|
|
const SMHD = Tag(0x736d6864)
|
|
|
|
type SoundMediaInfo struct {
|
|
Version uint8
|
|
Flags uint32
|
|
Balance int16
|
|
AtomPos
|
|
}
|
|
|
|
func (a SoundMediaInfo) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(SMHD))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a SoundMediaInfo) marshal(b []byte) (n int) {
|
|
pio.PutU8(b[n:], a.Version)
|
|
n += 1
|
|
pio.PutU24BE(b[n:], a.Flags)
|
|
n += 3
|
|
pio.PutI16BE(b[n:], a.Balance)
|
|
n += 2
|
|
n += 2
|
|
return
|
|
}
|
|
|
|
func (a SoundMediaInfo) Len() (n int) {
|
|
n += 8
|
|
n += 1
|
|
n += 3
|
|
n += 2
|
|
n += 2
|
|
return
|
|
}
|
|
|
|
func (a *SoundMediaInfo) 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+2 {
|
|
err = parseErr("Balance", n+offset, err)
|
|
return
|
|
}
|
|
a.Balance = pio.I16BE(b[n:])
|
|
n += 2
|
|
n += 2
|
|
return
|
|
}
|
|
|
|
func (a SoundMediaInfo) Children() (r []Atom) {
|
|
return
|
|
}
|
|
|
|
func (a SoundMediaInfo) Tag() Tag {
|
|
return SMHD
|
|
}
|
|
|
|
const DINF = Tag(0x64696e66)
|
|
|
|
func (a DataInfo) Tag() Tag {
|
|
return DINF
|
|
}
|
|
|
|
type DataInfo struct {
|
|
Refer *DataRefer
|
|
Unknowns []Atom
|
|
AtomPos
|
|
}
|
|
|
|
func (a DataInfo) Marshal(b []byte) (n int) {
|
|
pio.PutU32BE(b[4:], uint32(DINF))
|
|
n += a.marshal(b[8:]) + 8
|
|
pio.PutU32BE(b[0:], uint32(n))
|
|
return
|
|
}
|
|
|
|
func (a DataInfo) marshal(b []byte) (n int) {
|
|
if a.Refer != nil {
|
|
n += a.Refer.Marshal(b[n:])
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Marshal(b[n:])
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a DataInfo) Len() (n int) {
|
|
n += 8
|
|
if a.Refer != nil {
|
|
n += a.Refer.Len()
|
|
}
|
|
for _, atom := range a.Unknowns {
|
|
n += atom.Len()
|
|
}
|
|
return
|
|
}
|
|
|
|
func (a *DataInfo) 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 DREF:
|
|
{
|
|
atom := &DataRefer{}
|
|
if _, err = atom.Unmarshal(b[n:n+size], offset+n); err != nil {
|
|
err = parseErr("dref", n+offset, err)
|
|
return
|
|
}
|
|
a.Refer = 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 DataInfo) Children() (r []Atom) {
|
|
if a.Refer != nil {
|
|
r = append(r, a.Refer)
|
|
}
|
|
r = append(r, a.Unknowns...)
|
|
return
|
|
}
|