mirror of
https://github.com/langhuihui/monibuca.git
synced 2026-04-22 22:57:22 +08:00
378 lines
7.6 KiB
Go
378 lines
7.6 KiB
Go
package box
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"net"
|
|
"reflect"
|
|
"unsafe"
|
|
)
|
|
|
|
type (
|
|
BoxType [4]byte
|
|
|
|
BoxHeader interface {
|
|
Type() BoxType
|
|
HeaderSize() uint32
|
|
Size() uint32
|
|
Header() BoxHeader
|
|
HeaderWriteTo(w io.Writer) (n int64, err error)
|
|
}
|
|
|
|
IBox interface {
|
|
BoxHeader
|
|
io.WriterTo
|
|
Unmarshal(buf []byte) (IBox, error)
|
|
}
|
|
|
|
// 基础Box结构,实现通用字段
|
|
BaseBox struct {
|
|
typ BoxType
|
|
size uint32
|
|
}
|
|
DataBox struct {
|
|
BaseBox
|
|
Data []byte
|
|
}
|
|
BigBox struct {
|
|
BaseBox
|
|
size uint64
|
|
}
|
|
|
|
FullBox struct {
|
|
BaseBox
|
|
Version uint8
|
|
Flags [3]byte
|
|
}
|
|
ContainerBox struct {
|
|
BaseBox
|
|
Children []IBox
|
|
}
|
|
)
|
|
|
|
func CreateBaseBox(typ BoxType, size uint64) IBox {
|
|
if size > 0xFFFFFFFF {
|
|
return &BigBox{
|
|
BaseBox: BaseBox{
|
|
typ: typ,
|
|
size: 1,
|
|
},
|
|
size: size + 8,
|
|
}
|
|
}
|
|
|
|
return &BaseBox{
|
|
typ: typ,
|
|
size: uint32(size),
|
|
}
|
|
}
|
|
|
|
func CreateDataBox(typ BoxType, data []byte) *DataBox {
|
|
return &DataBox{
|
|
BaseBox: BaseBox{
|
|
typ: typ,
|
|
size: uint32(len(data)) + BasicBoxLen,
|
|
},
|
|
Data: data,
|
|
}
|
|
}
|
|
|
|
func CreateContainerBox(typ BoxType, children ...IBox) *ContainerBox {
|
|
size := uint32(BasicBoxLen)
|
|
realChildren := make([]IBox, 0, len(children))
|
|
for _, child := range children {
|
|
if reflect.ValueOf(child).IsNil() {
|
|
continue
|
|
}
|
|
size += child.Size()
|
|
realChildren = append(realChildren, child)
|
|
}
|
|
return &ContainerBox{
|
|
BaseBox: BaseBox{
|
|
typ: typ,
|
|
size: size,
|
|
},
|
|
Children: realChildren,
|
|
}
|
|
}
|
|
|
|
func (b *BigBox) HeaderSize() uint32 { return BasicBoxLen + 8 }
|
|
|
|
func (b *BaseBox) Header() BoxHeader { return b }
|
|
func (b *BaseBox) HeaderSize() uint32 { return BasicBoxLen }
|
|
func (b *BaseBox) Size() uint32 { return b.size }
|
|
|
|
func (b *BaseBox) Type() BoxType { return b.typ }
|
|
|
|
func (b *BaseBox) HeaderWriteTo(w io.Writer) (n int64, err error) {
|
|
var tmp [4]byte
|
|
binary.BigEndian.PutUint32(tmp[:], b.size)
|
|
buffers := net.Buffers{tmp[:], b.typ[:]}
|
|
return buffers.WriteTo(w)
|
|
}
|
|
|
|
func (b *BaseBox) WriteTo(w io.Writer) (n int64, err error) {
|
|
return
|
|
}
|
|
|
|
func (b *ContainerBox) WriteTo(w io.Writer) (n int64, err error) {
|
|
return WriteTo(w, b.Children...)
|
|
}
|
|
|
|
func (b *DataBox) WriteTo(w io.Writer) (n int64, err error) {
|
|
_, err = w.Write(b.Data)
|
|
return int64(len(b.Data)), err
|
|
}
|
|
|
|
func (b *BaseBox) Unmarshal(buf []byte) (IBox, error) {
|
|
return b, nil
|
|
}
|
|
|
|
func (b *DataBox) Unmarshal(buf []byte) (IBox, error) {
|
|
b.Data = buf
|
|
return b, nil
|
|
}
|
|
|
|
func (b *FullBox) HeaderWriteTo(w io.Writer) (n int64, err error) {
|
|
|
|
var tmp [4]byte
|
|
|
|
binary.BigEndian.PutUint32(tmp[:], b.size)
|
|
buffers := net.Buffers{tmp[:], b.typ[:], []byte{b.Version}, b.Flags[:]}
|
|
return buffers.WriteTo(w)
|
|
}
|
|
|
|
func (b *BigBox) HeaderWriteTo(w io.Writer) (n int64, err error) {
|
|
n, err = b.BaseBox.HeaderWriteTo(w)
|
|
if err != nil {
|
|
return
|
|
}
|
|
var tmp [8]byte
|
|
binary.BigEndian.PutUint64(tmp[:], b.size)
|
|
_, err = w.Write(tmp[:])
|
|
return n + 8, err
|
|
}
|
|
|
|
func (b *BigBox) Header() BoxHeader { return b }
|
|
func (b *FullBox) Header() BoxHeader { return b }
|
|
func (b *FullBox) HeaderSize() uint32 { return FullBoxLen }
|
|
|
|
func WriteTo(w io.Writer, box ...IBox) (n int64, err error) {
|
|
var n1, n2 int64
|
|
for _, b := range box {
|
|
if b == nil {
|
|
continue
|
|
}
|
|
n1, err = b.HeaderWriteTo(w)
|
|
if err != nil {
|
|
return
|
|
}
|
|
n2, err = b.WriteTo(w)
|
|
if err != nil {
|
|
return
|
|
}
|
|
if n1+n2 != int64(b.Size()) {
|
|
panic(fmt.Sprintf("write to %s size error, %d != %d", b.Type(), n1+n2, b.Size()))
|
|
}
|
|
n += n1 + n2
|
|
}
|
|
return
|
|
|
|
}
|
|
|
|
func ReadFrom(r io.Reader) (box IBox, err error) {
|
|
var tmp [8]byte
|
|
if _, err = io.ReadFull(r, tmp[:]); err != nil {
|
|
return
|
|
}
|
|
var baseBox BaseBox
|
|
baseBox.size = binary.BigEndian.Uint32(tmp[:4])
|
|
baseBox.typ = BoxType(tmp[4:])
|
|
t, exists := registry[baseBox.typ.Uint32I()]
|
|
if !exists {
|
|
return nil, fmt.Errorf("unknown box type: %s", baseBox.typ)
|
|
}
|
|
b := reflect.New(t.Elem()).Interface().(IBox)
|
|
var payload []byte
|
|
if baseBox.size == 1 {
|
|
if _, err = io.ReadFull(r, tmp[:]); err != nil {
|
|
return
|
|
}
|
|
payload = make([]byte, binary.BigEndian.Uint64(tmp[:])-BasicBoxLen-8)
|
|
} else {
|
|
payload = make([]byte, baseBox.size-BasicBoxLen)
|
|
}
|
|
_, err = io.ReadFull(r, payload)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
boxHeader := b.Header()
|
|
switch header := boxHeader.(type) {
|
|
case *BaseBox:
|
|
*header = baseBox
|
|
box, err = b.Unmarshal(payload)
|
|
case *FullBox:
|
|
header.BaseBox = baseBox
|
|
header.Version = payload[0]
|
|
header.Flags = [3]byte(payload[1:4])
|
|
box, err = b.Unmarshal(payload[4:])
|
|
}
|
|
if err == io.EOF {
|
|
return box, nil
|
|
}
|
|
return
|
|
}
|
|
|
|
func (b BoxType) String() string {
|
|
return string(b[:])
|
|
}
|
|
|
|
func (b BoxType) Uint32() uint32 {
|
|
return binary.BigEndian.Uint32(b[:])
|
|
}
|
|
|
|
func (b BoxType) Uint32I() uint32 {
|
|
return *(*uint32)(unsafe.Pointer(&b[0]))
|
|
}
|
|
|
|
var registry = map[uint32]reflect.Type{}
|
|
|
|
// RegisterBox 注册box类型
|
|
func RegisterBox[T any](typ ...BoxType) {
|
|
var b T
|
|
bt := reflect.TypeOf(b)
|
|
for _, t := range typ {
|
|
registry[t.Uint32I()] = bt
|
|
}
|
|
}
|
|
|
|
const (
|
|
BasicBoxLen = 8 // size(4) + type(4)
|
|
FullBoxLen = 12 // BasicBoxLen + version(1) + flags(3)
|
|
)
|
|
|
|
func f(s string) BoxType {
|
|
return BoxType([]byte(s))
|
|
}
|
|
|
|
var (
|
|
TypeFTYP = f("ftyp")
|
|
TypeSTYP = f("styp")
|
|
TypeMOOV = f("moov")
|
|
TypeMVHD = f("mvhd")
|
|
TypeTRAK = f("trak")
|
|
TypeTKHD = f("tkhd")
|
|
TypeMDIA = f("mdia")
|
|
TypeMDHD = f("mdhd")
|
|
TypeHDLR = f("hdlr")
|
|
TypeMINF = f("minf")
|
|
TypeSTBL = f("stbl")
|
|
TypeSTSD = f("stsd")
|
|
TypeSTTS = f("stts")
|
|
TypeSTSC = f("stsc")
|
|
TypeSTSZ = f("stsz")
|
|
TypeSTCO = f("stco")
|
|
TypeMDAT = f("mdat")
|
|
TypeFREE = f("free")
|
|
TypeUUID = f("uuid")
|
|
|
|
TypeVMHD = f("vmhd")
|
|
TypeSMHD = f("smhd")
|
|
TypeHMHD = f("hmhd")
|
|
TypeNMHD = f("nmhd")
|
|
TypeCTTS = f("ctts")
|
|
TypeCO64 = f("co64")
|
|
TypePSSH = f("pssh")
|
|
|
|
TypeSTSS = f("stss")
|
|
TypeENCV = f("encv")
|
|
TypeSINF = f("sinf")
|
|
TypeFRMA = f("frma")
|
|
TypeSCHI = f("schi")
|
|
TypeTENC = f("tenc")
|
|
TypeAVC1 = f("avc1")
|
|
TypeHVC1 = f("hvc1")
|
|
TypeHEV1 = f("hev1")
|
|
TypeENCA = f("enca")
|
|
TypeMP4A = f("mp4a")
|
|
TypeULAW = f("ulaw")
|
|
TypeALAW = f("alaw")
|
|
TypeDOPS = f("dOps")
|
|
TypeOPUS = f("opus")
|
|
TypeAVCC = f("avcC")
|
|
TypeHVCC = f("hvcC")
|
|
TypeESDS = f("esds")
|
|
TypeEDTS = f("edts")
|
|
TypeELST = f("elst")
|
|
TypeMVEX = f("mvex")
|
|
TypeMOOF = f("moof")
|
|
TypeMFHD = f("mfhd")
|
|
TypeTRAF = f("traf")
|
|
TypeTFHD = f("tfhd")
|
|
TypeTFDT = f("tfdt")
|
|
TypeTRUN = f("trun")
|
|
TypeSENC = f("senc")
|
|
TypeSAIZ = f("saiz")
|
|
TypeSAIO = f("saio")
|
|
TypeSGPD = f("sgpd")
|
|
TypeWAVE = f("wave")
|
|
TypeMSDH = f("msdh")
|
|
TypeMSIX = f("msix")
|
|
TypeISOM = f("isom")
|
|
TypeISO2 = f("iso2")
|
|
TypeISO3 = f("iso3")
|
|
TypeISO4 = f("iso4")
|
|
TypeISO5 = f("iso5")
|
|
TypeISO6 = f("iso6")
|
|
TypeMP41 = f("mp41")
|
|
TypeMP42 = f("mp42")
|
|
TypeDASH = f("dash")
|
|
TypeMFRA = f("mfra")
|
|
TypeMFRO = f("mfro")
|
|
TypeTREX = f("trex")
|
|
TypeTFRA = f("tfra")
|
|
TypeSIDX = f("sidx")
|
|
TypeDINF = f("dinf")
|
|
TypeDREF = f("dref")
|
|
TypeVIDE = f("vide")
|
|
TypeSOUN = f("soun")
|
|
TypeMETA = f("meta")
|
|
TypeAUXV = f("auxv")
|
|
TypeHINT = f("hint")
|
|
)
|
|
|
|
// aligned(8) class Box (unsigned int(32) boxtype, optional unsigned int(8)[16] extended_type) {
|
|
// unsigned int(32) size;
|
|
// unsigned int(32) type = boxtype;
|
|
// if (size==1) {
|
|
// unsigned int(64) largesize;
|
|
// } else if (size==0) {
|
|
// // box extends to end of file
|
|
// }
|
|
// if (boxtype=='uuid') {
|
|
// unsigned int(8)[16] usertype = extended_type;
|
|
// }
|
|
// }
|
|
|
|
// aligned(8) class FullBox(unsigned int(32) boxtype, unsigned int(8) v, bit(24) f) extends Box(boxtype) {
|
|
// unsigned int(8) version = v;
|
|
// bit(24) flags = f;
|
|
// }
|
|
|
|
type TimeToSampleEntry struct {
|
|
SampleCount uint32
|
|
SampleDelta uint32
|
|
}
|
|
|
|
type CompositionTimeToSampleEntry struct {
|
|
SampleCount uint32
|
|
SampleOffset int32
|
|
}
|
|
|
|
type SampleToChunkEntry struct {
|
|
FirstChunk uint32
|
|
SamplesPerChunk uint32
|
|
SampleDescriptionIndex uint32
|
|
}
|