mirror of
https://github.com/AlexxIT/go2rtc.git
synced 2025-10-27 10:10:31 +08:00
204 lines
4.0 KiB
Go
204 lines
4.0 KiB
Go
package iso
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"io"
|
|
|
|
"github.com/AlexxIT/go2rtc/pkg/bits"
|
|
)
|
|
|
|
type Atom struct {
|
|
Name string
|
|
Data []byte
|
|
}
|
|
|
|
type AtomTkhd struct {
|
|
TrackID uint32
|
|
}
|
|
|
|
type AtomMdhd struct {
|
|
TimeScale uint32
|
|
}
|
|
|
|
type AtomVideo struct {
|
|
Name string
|
|
Config []byte
|
|
}
|
|
|
|
type AtomAudio struct {
|
|
Name string
|
|
Channels uint16
|
|
SampleRate uint32
|
|
Config []byte
|
|
}
|
|
|
|
type AtomMfhd struct {
|
|
Sequence uint32
|
|
}
|
|
|
|
type AtomMdat struct {
|
|
Data []byte
|
|
}
|
|
|
|
type AtomTfhd struct {
|
|
TrackID uint32
|
|
SampleDuration uint32
|
|
SampleSize uint32
|
|
SampleFlags uint32
|
|
}
|
|
type AtomTfdt struct {
|
|
DecodeTime uint64
|
|
}
|
|
|
|
type AtomTrun struct {
|
|
DataOffset uint32
|
|
FirstSampleFlags uint32
|
|
SamplesDuration []uint32
|
|
SamplesSize []uint32
|
|
SamplesFlags []uint32
|
|
SamplesCTS []uint32
|
|
}
|
|
|
|
func DecodeAtom(b []byte) (any, error) {
|
|
size := binary.BigEndian.Uint32(b)
|
|
if len(b) < int(size) {
|
|
return nil, io.EOF
|
|
}
|
|
|
|
name := string(b[4:8])
|
|
data := b[8:size]
|
|
|
|
switch name {
|
|
// useful containers
|
|
case Moov, MoovTrak, MoovTrakMdia, MoovTrakMdiaMinf, MoovTrakMdiaMinfStbl, Moof, MoofTraf:
|
|
return DecodeAtoms(data)
|
|
|
|
case MoovTrakTkhd:
|
|
return &AtomTkhd{TrackID: binary.BigEndian.Uint32(data[1+3+4+4:])}, nil
|
|
|
|
case MoovTrakMdiaMdhd:
|
|
return &AtomMdhd{TimeScale: binary.BigEndian.Uint32(data[1+3+4+4:])}, nil
|
|
|
|
case MoovTrakMdiaMinfStblStsd:
|
|
// support only 1 codec entry
|
|
if n := binary.BigEndian.Uint32(data[1+3:]); n == 1 {
|
|
return DecodeAtom(data[1+3+4:])
|
|
}
|
|
|
|
case "avc1", "hev1":
|
|
b = data[6+2+2+2+4+4+4+2+2+4+4+4+2+32+2+2:]
|
|
atom, err := DecodeAtom(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if conf, ok := atom.(*Atom); ok {
|
|
return &AtomVideo{Name: name, Config: conf.Data}, nil
|
|
}
|
|
|
|
case "mp4a":
|
|
atom := &AtomAudio{Name: name}
|
|
|
|
rd := bits.NewReader(data)
|
|
rd.ReadBytes(6 + 2 + 2 + 2 + 4) // skip
|
|
atom.Channels = rd.ReadUint16()
|
|
rd.ReadBytes(2 + 2 + 2) // skip
|
|
atom.SampleRate = uint32(rd.ReadFloat32())
|
|
|
|
atom2, _ := DecodeAtom(rd.Left())
|
|
if conf, ok := atom2.(*Atom); ok {
|
|
_, b, _ = bytes.Cut(conf.Data, []byte{5, 0x80, 0x80, 0x80})
|
|
if n := len(b); n > 0 && n > 1+int(b[0]) {
|
|
atom.Config = b[1 : 1+b[0]]
|
|
}
|
|
}
|
|
|
|
return atom, nil
|
|
|
|
case MoofMfhd:
|
|
return &AtomMfhd{Sequence: binary.BigEndian.Uint32(data[4:])}, nil
|
|
|
|
case MoofTrafTfhd:
|
|
rd := bits.NewReader(data)
|
|
_ = rd.ReadByte() // version
|
|
flags := rd.ReadUint24()
|
|
|
|
atom := &AtomTfhd{
|
|
TrackID: rd.ReadUint32(),
|
|
}
|
|
|
|
if flags&TfhdDefaultSampleDuration != 0 {
|
|
atom.SampleDuration = rd.ReadUint32()
|
|
|
|
}
|
|
if flags&TfhdDefaultSampleSize != 0 {
|
|
atom.SampleSize = rd.ReadUint32()
|
|
}
|
|
if flags&TfhdDefaultSampleFlags != 0 {
|
|
atom.SampleFlags = rd.ReadUint32() // skip
|
|
}
|
|
|
|
return atom, nil
|
|
|
|
case MoofTrafTfdt:
|
|
return &AtomTfdt{DecodeTime: binary.BigEndian.Uint64(data[4:])}, nil
|
|
|
|
case MoofTrafTrun:
|
|
rd := bits.NewReader(data)
|
|
_ = rd.ReadByte() // version
|
|
flags := rd.ReadUint24()
|
|
samples := rd.ReadUint32()
|
|
|
|
atom := &AtomTrun{}
|
|
|
|
if flags&TrunDataOffset != 0 {
|
|
atom.DataOffset = rd.ReadUint32()
|
|
}
|
|
if flags&TrunFirstSampleFlags != 0 {
|
|
atom.FirstSampleFlags = rd.ReadUint32()
|
|
}
|
|
|
|
for i := uint32(0); i < samples; i++ {
|
|
if flags&TrunSampleDuration != 0 {
|
|
atom.SamplesDuration = append(atom.SamplesDuration, rd.ReadUint32())
|
|
}
|
|
if flags&TrunSampleSize != 0 {
|
|
atom.SamplesSize = append(atom.SamplesSize, rd.ReadUint32())
|
|
}
|
|
if flags&TrunSampleFlags != 0 {
|
|
atom.SamplesFlags = append(atom.SamplesFlags, rd.ReadUint32())
|
|
}
|
|
if flags&TrunSampleCTS != 0 {
|
|
atom.SamplesCTS = append(atom.SamplesCTS, rd.ReadUint32())
|
|
}
|
|
}
|
|
|
|
return atom, nil
|
|
|
|
case Mdat:
|
|
return &AtomMdat{Data: data}, nil
|
|
}
|
|
|
|
return &Atom{Name: name, Data: data}, nil
|
|
}
|
|
|
|
func DecodeAtoms(b []byte) (atoms []any, err error) {
|
|
for len(b) > 0 {
|
|
atom, err := DecodeAtom(b)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if childs, ok := atom.([]any); ok {
|
|
atoms = append(atoms, childs...)
|
|
} else {
|
|
atoms = append(atoms, atom)
|
|
}
|
|
|
|
size := binary.BigEndian.Uint32(b)
|
|
b = b[size:]
|
|
}
|
|
|
|
return atoms, nil
|
|
}
|