mirror of
https://github.com/langhuihui/monibuca.git
synced 2026-04-23 03:17:16 +08:00
in progress
This commit is contained in:
+240
-94
@@ -2,16 +2,253 @@ 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).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)
|
||||
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:])
|
||||
}
|
||||
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) [4]byte {
|
||||
return [4]byte([]byte(s))
|
||||
func f(s string) BoxType {
|
||||
return BoxType([]byte(s))
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -56,6 +293,7 @@ var (
|
||||
TypeMP4A = f("mp4a")
|
||||
TypeULAW = f("ulaw")
|
||||
TypeALAW = f("alaw")
|
||||
TypeDOPS = f("dOps")
|
||||
TypeOPUS = f("opus")
|
||||
TypeAVCC = f("avcC")
|
||||
TypeHVCC = f("hvcC")
|
||||
@@ -99,18 +337,6 @@ var (
|
||||
TypeHINT = f("hint")
|
||||
)
|
||||
|
||||
type BoxEncoder interface {
|
||||
Encode(buf []byte) (int, []byte)
|
||||
}
|
||||
|
||||
type BoxDecoder interface {
|
||||
Decode(buf []byte) (int, error)
|
||||
}
|
||||
|
||||
type BoxSize interface {
|
||||
Size() uint64
|
||||
}
|
||||
|
||||
// aligned(8) class Box (unsigned int(32) boxtype, optional unsigned int(8)[16] extended_type) {
|
||||
// unsigned int(32) size;
|
||||
// unsigned int(32) type = boxtype;
|
||||
@@ -123,92 +349,12 @@ type BoxSize interface {
|
||||
// unsigned int(8)[16] usertype = extended_type;
|
||||
// }
|
||||
// }
|
||||
type IBox interface {
|
||||
Decode(io.Reader, *BasicBox) (int, error)
|
||||
}
|
||||
type BasicBox struct {
|
||||
Offset int64
|
||||
Size uint64
|
||||
Type [4]byte
|
||||
}
|
||||
|
||||
func NewBasicBox(boxtype [4]byte) *BasicBox {
|
||||
return &BasicBox{
|
||||
Type: boxtype,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *BasicBox) Decode(r io.Reader) (nn int, err error) {
|
||||
if _, err = io.ReadFull(r, box.Type[:]); err != nil {
|
||||
return
|
||||
}
|
||||
nn = 4
|
||||
if box.Size = uint64(binary.BigEndian.Uint32(box.Type[:])); box.Size == 1 {
|
||||
var largeSize [8]byte
|
||||
if _, err = io.ReadFull(r, largeSize[:]); err != nil {
|
||||
return
|
||||
}
|
||||
box.Size = binary.BigEndian.Uint64(largeSize[:])
|
||||
nn += 8
|
||||
}
|
||||
if _, err = io.ReadFull(r, box.Type[:]); err != nil {
|
||||
return
|
||||
}
|
||||
nn += 4
|
||||
return
|
||||
}
|
||||
|
||||
func (box *BasicBox) Encode() (int, []byte) {
|
||||
buf := make([]byte, box.Size)
|
||||
binary.BigEndian.PutUint32(buf, uint32(box.Size))
|
||||
copy(buf[4:], box.Type[:])
|
||||
return BasicBoxLen, buf
|
||||
}
|
||||
|
||||
// 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 FullBox struct {
|
||||
Box *BasicBox
|
||||
Version uint8
|
||||
Flags [3]byte
|
||||
}
|
||||
|
||||
func NewFullBox(boxtype [4]byte, version uint8) *FullBox {
|
||||
return &FullBox{
|
||||
Box: NewBasicBox(boxtype),
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *FullBox) Size() uint64 {
|
||||
if box.Box.Size > 0 {
|
||||
return box.Box.Size
|
||||
} else {
|
||||
return FullBoxLen
|
||||
}
|
||||
}
|
||||
|
||||
func (box *FullBox) Decode(r io.Reader) (int, error) {
|
||||
buf := make([]byte, 4)
|
||||
if n, err := io.ReadFull(r, buf); err != nil {
|
||||
return n, err
|
||||
}
|
||||
box.Version = buf[0]
|
||||
copy(box.Flags[:], buf[1:])
|
||||
return 4, nil
|
||||
}
|
||||
|
||||
func (box *FullBox) Encode() (int, []byte) {
|
||||
box.Box.Size = box.Size()
|
||||
offset, buf := box.Box.Encode()
|
||||
buf[offset] = box.Version
|
||||
copy(buf[offset+1:], box.Flags[:])
|
||||
return offset + 4, buf
|
||||
}
|
||||
|
||||
type TimeToSampleEntry struct {
|
||||
SampleCount uint32
|
||||
SampleDelta uint32
|
||||
|
||||
+42
-54
@@ -5,68 +5,56 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class CompositionOffsetBox extends FullBox(‘ctts’, version = 0, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// int i;
|
||||
// if (version==0) {
|
||||
// for (i=0; i < entry_count; i++) {
|
||||
// unsigned int(32) sample_count;
|
||||
// unsigned int(32) sample_offset;
|
||||
// }
|
||||
// }
|
||||
// else if (version == 1) {
|
||||
// for (i=0; i < entry_count; i++) {
|
||||
// unsigned int(32) sample_count;
|
||||
// signed int(32) sample_offset;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
type CompositionOffsetBox []CTTSEntry
|
||||
|
||||
func (ctts CompositionOffsetBox) Size() uint64 {
|
||||
return FullBoxLen + 4 + 8*uint64(len(ctts))
|
||||
type CTTSBox struct {
|
||||
FullBox
|
||||
Entries []CTTSEntry
|
||||
}
|
||||
|
||||
func (ctts *CompositionOffsetBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
func CreateCTTSBox(entries []CTTSEntry) *CTTSBox {
|
||||
return &CTTSBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeCTTS,
|
||||
size: uint32(FullBoxLen + 4 + len(entries)*8),
|
||||
},
|
||||
},
|
||||
Entries: entries,
|
||||
}
|
||||
entryCountBuf := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, entryCountBuf); err != nil {
|
||||
return
|
||||
}
|
||||
offset = 8
|
||||
l := binary.BigEndian.Uint32(entryCountBuf)
|
||||
*ctts = make([]CTTSEntry, l)
|
||||
}
|
||||
|
||||
buf := make([]byte, l*8)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
func (box *CTTSBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
buf := make([]byte, 4+len(box.Entries)*8)
|
||||
// Write entry count
|
||||
binary.BigEndian.PutUint32(buf[:4], uint32(len(box.Entries)))
|
||||
|
||||
// Write entries
|
||||
for i, entry := range box.Entries {
|
||||
binary.BigEndian.PutUint32(buf[4+i*8:], entry.SampleCount)
|
||||
binary.BigEndian.PutUint32(buf[4+i*8+4:], entry.SampleOffset)
|
||||
}
|
||||
idx := 0
|
||||
for i := 0; i < int(l); i++ {
|
||||
(*ctts)[i].SampleCount = binary.BigEndian.Uint32(buf[idx:])
|
||||
|
||||
_, err = w.Write(buf)
|
||||
return int64(len(buf)), err
|
||||
}
|
||||
|
||||
func (box *CTTSBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
entryCount := binary.BigEndian.Uint32(buf[:4])
|
||||
box.Entries = make([]CTTSEntry, entryCount)
|
||||
|
||||
if len(buf) < 4+int(entryCount)*8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
idx := 4
|
||||
for i := 0; i < int(entryCount); i++ {
|
||||
box.Entries[i].SampleCount = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
(*ctts)[i].SampleOffset = binary.BigEndian.Uint32(buf[idx:])
|
||||
box.Entries[i].SampleOffset = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
}
|
||||
offset += idx
|
||||
return
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (ctts CompositionOffsetBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeCTTS, 0)
|
||||
fullbox.Box.Size = ctts.Size()
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(len(ctts)))
|
||||
offset += 4
|
||||
for _, entry := range ctts {
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.SampleCount)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.SampleOffset)
|
||||
offset += 4
|
||||
}
|
||||
return offset, buf
|
||||
func init() {
|
||||
RegisterBox[CTTSBox](TypeCTTS)
|
||||
}
|
||||
|
||||
+179
-20
@@ -1,35 +1,194 @@
|
||||
package box
|
||||
|
||||
import "encoding/binary"
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class DataEntryUrlBox (bit(24) flags) extends FullBox(‘url ’, version = 0, flags) {
|
||||
var (
|
||||
TypeURL = BoxType{'u', 'r', 'l', ' '}
|
||||
TypeURN = BoxType{'u', 'r', 'n', ' '}
|
||||
)
|
||||
|
||||
// aligned(8) class DataEntryUrlBox (bit(24) flags) extends FullBox('url ', version = 0, flags) {
|
||||
// string location;
|
||||
// }
|
||||
// aligned(8) class DataEntryUrnBox (bit(24) flags) extends FullBox(‘urn ’, version = 0, flags) {
|
||||
// aligned(8) class DataEntryUrnBox (bit(24) flags) extends FullBox('urn ', version = 0, flags) {
|
||||
// string name;
|
||||
// string location;
|
||||
// }
|
||||
// aligned(8) class DataReferenceBox extends FullBox(‘dref’, version = 0, 0) {
|
||||
// aligned(8) class DataReferenceBox extends FullBox('dref', version = 0, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// for (i=1; i <= entry_count; i++) {
|
||||
// DataEntryBox(entry_version, entry_flags) data_entry;
|
||||
// }
|
||||
// }
|
||||
|
||||
func MakeDefaultDinfBox() []byte {
|
||||
dinf := BasicBox{Type: TypeDINF, Size: 36}
|
||||
offset, dinfbox := dinf.Encode()
|
||||
binary.BigEndian.PutUint32(dinfbox[offset:], 28)
|
||||
offset += 4
|
||||
copy(dinfbox[offset:], TypeDREF[:])
|
||||
offset += 4
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(dinfbox[offset:], 1)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(dinfbox[offset:], 0xc)
|
||||
offset += 4
|
||||
copy(dinfbox[offset:], []byte("url "))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(dinfbox[offset:], 1)
|
||||
return dinfbox
|
||||
type DataInformationBox struct {
|
||||
BaseBox
|
||||
Dref *DataReferenceBox
|
||||
}
|
||||
|
||||
type DataReferenceBox struct {
|
||||
FullBox
|
||||
Entries []IBox
|
||||
}
|
||||
|
||||
type DataEntryUrlBox struct {
|
||||
FullBox
|
||||
Location string
|
||||
}
|
||||
|
||||
type DataEntryUrnBox struct {
|
||||
FullBox
|
||||
Name string
|
||||
Location string
|
||||
}
|
||||
|
||||
func CreateDataInformationBox() *DataInformationBox {
|
||||
dref := CreateDataReferenceBox()
|
||||
return &DataInformationBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeDINF,
|
||||
size: uint32(BasicBoxLen + dref.size),
|
||||
},
|
||||
Dref: dref,
|
||||
}
|
||||
}
|
||||
|
||||
func CreateDataReferenceBox() *DataReferenceBox {
|
||||
url := CreateDataEntryUrlBox("")
|
||||
return &DataReferenceBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeDREF,
|
||||
size: uint32(FullBoxLen + 4 + url.size), // 4 for entry_count
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
Entries: []IBox{url},
|
||||
}
|
||||
}
|
||||
|
||||
func CreateDataEntryUrlBox(location string) *DataEntryUrlBox {
|
||||
return &DataEntryUrlBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeURL,
|
||||
size: uint32(FullBoxLen + len(location)),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 1}, // self-contained flag
|
||||
},
|
||||
Location: location,
|
||||
}
|
||||
}
|
||||
|
||||
func CreateDataEntryUrnBox(name, location string) *DataEntryUrnBox {
|
||||
return &DataEntryUrnBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeURN,
|
||||
size: uint32(FullBoxLen + len(name) + 1 + len(location)),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
Name: name,
|
||||
Location: location,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *DataInformationBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, box.Dref)
|
||||
}
|
||||
|
||||
func (box *DataInformationBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
r := bytes.NewReader(buf)
|
||||
b, err := ReadFrom(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if dref, ok := b.(*DataReferenceBox); ok {
|
||||
box.Dref = dref
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (box *DataReferenceBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(len(box.Entries)))
|
||||
nn, err := w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return int64(nn), err
|
||||
}
|
||||
n = int64(nn)
|
||||
|
||||
for _, entry := range box.Entries {
|
||||
var en int64
|
||||
en, err = WriteTo(w, entry)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += en
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (box *DataReferenceBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
entryCount := binary.BigEndian.Uint32(buf)
|
||||
r := bytes.NewReader(buf[4:])
|
||||
box.Entries = make([]IBox, 0, entryCount)
|
||||
|
||||
for i := uint32(0); i < entryCount; i++ {
|
||||
entry, err := ReadFrom(r)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
box.Entries = append(box.Entries, entry)
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (box *DataEntryUrlBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
if len(box.Location) > 0 {
|
||||
nn, err := w.Write([]byte(box.Location))
|
||||
return int64(nn), err
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (box *DataEntryUrlBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) > 0 {
|
||||
box.Location = string(buf)
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (box *DataEntryUrnBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
nn, err := w.Write([]byte(box.Name + "\x00" + box.Location))
|
||||
return int64(nn), err
|
||||
}
|
||||
|
||||
func (box *DataEntryUrnBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
parts := bytes.SplitN(buf, []byte{0}, 2)
|
||||
if len(parts) > 0 {
|
||||
box.Name = string(parts[0])
|
||||
if len(parts) > 1 {
|
||||
box.Location = string(parts[1])
|
||||
}
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*DataInformationBox](TypeDINF)
|
||||
RegisterBox[*DataReferenceBox](TypeDREF)
|
||||
RegisterBox[*DataEntryUrlBox](TypeURL)
|
||||
RegisterBox[*DataEntryUrnBox](TypeURN)
|
||||
}
|
||||
|
||||
+72
-68
@@ -3,6 +3,7 @@ package box
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/yapingcat/gomedia/go-codec"
|
||||
)
|
||||
@@ -31,7 +32,7 @@ type ChannelMappingTable struct {
|
||||
}
|
||||
|
||||
type OpusSpecificBox struct {
|
||||
Box *BasicBox
|
||||
BaseBox
|
||||
Version uint8
|
||||
OutputChannelCount uint8
|
||||
PreSkip uint16
|
||||
@@ -40,73 +41,15 @@ type OpusSpecificBox struct {
|
||||
ChanMapTable *ChannelMappingTable
|
||||
}
|
||||
|
||||
func NewdOpsBox() *OpusSpecificBox {
|
||||
return &OpusSpecificBox{
|
||||
Box: NewBasicBox([4]byte{'d', 'O', 'p', 's'}),
|
||||
}
|
||||
}
|
||||
|
||||
func (dops *OpusSpecificBox) Size() uint64 {
|
||||
return uint64(8 + 10 + 2 + dops.OutputChannelCount)
|
||||
}
|
||||
|
||||
func (dops *OpusSpecificBox) Encode() (int, []byte) {
|
||||
dops.Box.Size = dops.Size()
|
||||
offset, buf := dops.Box.Encode()
|
||||
buf[offset] = dops.Version
|
||||
offset++
|
||||
buf[offset] = dops.OutputChannelCount
|
||||
offset++
|
||||
binary.LittleEndian.PutUint16(buf[offset:], dops.PreSkip)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint32(buf[offset:], dops.InputSampleRate)
|
||||
offset += 4
|
||||
binary.LittleEndian.PutUint16(buf[offset:], uint16(dops.OutputGain))
|
||||
offset += 2
|
||||
if dops.ChanMapTable != nil {
|
||||
buf[offset] = dops.ChanMapTable.StreamCount
|
||||
offset++
|
||||
buf[offset] = dops.ChanMapTable.CoupledCount
|
||||
offset++
|
||||
copy(buf[offset:], dops.ChanMapTable.ChannelMapping)
|
||||
offset += len(dops.ChanMapTable.ChannelMapping)
|
||||
}
|
||||
return offset, buf
|
||||
}
|
||||
|
||||
func (dops *OpusSpecificBox) Decode(r io.Reader, size uint32) (offset int, err error) {
|
||||
|
||||
dopsBuf := make([]byte, size-BasicBoxLen)
|
||||
ChannelMappingFamily := 0
|
||||
if size-BasicBoxLen-10 > 0 {
|
||||
ChannelMappingFamily = int(size - BasicBoxLen - 10)
|
||||
}
|
||||
|
||||
if _, err = io.ReadFull(r, dopsBuf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dops.Version = dopsBuf[0]
|
||||
dops.OutputChannelCount = dopsBuf[1]
|
||||
dops.PreSkip = binary.BigEndian.Uint16(dopsBuf[2:])
|
||||
dops.InputSampleRate = binary.BigEndian.Uint32(dopsBuf[4:])
|
||||
dops.OutputGain = int16(binary.BigEndian.Uint16(dopsBuf[8:]))
|
||||
dops.ChanMapTable = nil
|
||||
if ChannelMappingFamily > 0 {
|
||||
dops.ChanMapTable = &ChannelMappingTable{}
|
||||
dops.ChanMapTable.StreamCount = dopsBuf[10]
|
||||
dops.ChanMapTable.CoupledCount = dopsBuf[11]
|
||||
dops.ChanMapTable.ChannelMapping = make([]byte, ChannelMappingFamily-2)
|
||||
copy(dops.ChanMapTable.ChannelMapping, dopsBuf[12:])
|
||||
}
|
||||
|
||||
return int(size - BasicBoxLen), nil
|
||||
}
|
||||
|
||||
func MakeOpusSpecificBox(extraData []byte) []byte {
|
||||
func CreateOpusSpecificBox(extraData []byte) *OpusSpecificBox {
|
||||
ctx := &codec.OpusContext{}
|
||||
ctx.ParseExtranData(extraData)
|
||||
dops := NewdOpsBox()
|
||||
dops := &OpusSpecificBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeDOPS,
|
||||
size: 0,
|
||||
},
|
||||
}
|
||||
dops.Version = 0
|
||||
dops.OutputChannelCount = uint8(ctx.ChannelCount)
|
||||
dops.PreSkip = uint16(ctx.Preskip)
|
||||
@@ -120,6 +63,67 @@ func MakeOpusSpecificBox(extraData []byte) []byte {
|
||||
}
|
||||
copy(dops.ChanMapTable.ChannelMapping, ctx.Channel)
|
||||
}
|
||||
_, dopsbox := dops.Encode()
|
||||
return dopsbox
|
||||
|
||||
// Calculate final size
|
||||
dops.size = uint32(BasicBoxLen + 10) // Base size
|
||||
if dops.ChanMapTable != nil {
|
||||
dops.size += uint32(2 + len(dops.ChanMapTable.ChannelMapping))
|
||||
}
|
||||
return dops
|
||||
}
|
||||
|
||||
func (box *OpusSpecificBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [12]byte // Buffer for fixed-size fields
|
||||
buffers := make(net.Buffers, 0, 4) // Estimate initial capacity
|
||||
|
||||
// Write fixed fields
|
||||
tmp[0] = box.Version
|
||||
tmp[1] = box.OutputChannelCount
|
||||
binary.BigEndian.PutUint16(tmp[2:], box.PreSkip)
|
||||
binary.BigEndian.PutUint32(tmp[4:], box.InputSampleRate)
|
||||
binary.BigEndian.PutUint16(tmp[8:], uint16(box.OutputGain))
|
||||
|
||||
if box.ChanMapTable != nil {
|
||||
tmp[10] = box.ChanMapTable.StreamCount
|
||||
tmp[11] = box.ChanMapTable.CoupledCount
|
||||
buffers = append(buffers, tmp[:12])
|
||||
buffers = append(buffers, box.ChanMapTable.ChannelMapping)
|
||||
} else {
|
||||
buffers = append(buffers, tmp[:10])
|
||||
}
|
||||
|
||||
return buffers.WriteTo(w)
|
||||
}
|
||||
|
||||
func (box *OpusSpecificBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 10 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
box.Version = buf[0]
|
||||
box.OutputChannelCount = buf[1]
|
||||
box.PreSkip = binary.BigEndian.Uint16(buf[2:])
|
||||
box.InputSampleRate = binary.BigEndian.Uint32(buf[4:])
|
||||
box.OutputGain = int16(binary.BigEndian.Uint16(buf[8:]))
|
||||
|
||||
// Check if we have channel mapping data
|
||||
if len(buf) > 10 {
|
||||
if len(buf) < 12 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.ChanMapTable = &ChannelMappingTable{
|
||||
StreamCount: buf[10],
|
||||
CoupledCount: buf[11],
|
||||
}
|
||||
if len(buf) > 12 {
|
||||
box.ChanMapTable.ChannelMapping = make([]byte, len(buf)-12)
|
||||
copy(box.ChanMapTable.ChannelMapping, buf[12:])
|
||||
}
|
||||
}
|
||||
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*OpusSpecificBox](TypeDOPS)
|
||||
}
|
||||
|
||||
@@ -1,105 +0,0 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class EditListBox extends FullBox(‘elst’, version, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// for (i=1; i <= entry_count; i++) {
|
||||
// if (version==1) {
|
||||
// unsigned int(64) segment_duration;
|
||||
// int(64) media_time;
|
||||
// } else { // version==0
|
||||
// unsigned int(32) segment_duration;
|
||||
// int(32) media_time;
|
||||
// }
|
||||
// int(16) media_rate_integer;
|
||||
// int(16) media_rate_fraction = 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
type EditListBox struct {
|
||||
Version byte
|
||||
Entrys []ELSTEntry
|
||||
}
|
||||
|
||||
func NewEditListBox(version byte) *EditListBox {
|
||||
return &EditListBox{
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func (elst *EditListBox) Encode(boxSize int) (int, []byte) {
|
||||
fullbox := NewFullBox(TypeELST, elst.Version)
|
||||
fullbox.Box.Size = uint64(boxSize)
|
||||
offset, elstdata := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(elstdata[offset:], uint32(len(elst.Entrys)))
|
||||
offset += 4
|
||||
for _, entry := range elst.Entrys {
|
||||
if elst.Version == 1 {
|
||||
binary.BigEndian.PutUint64(elstdata[offset:], entry.SegmentDuration)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint64(elstdata[offset:], uint64(entry.MediaTime))
|
||||
offset += 8
|
||||
} else {
|
||||
binary.BigEndian.PutUint32(elstdata[offset:], uint32(entry.SegmentDuration))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(elstdata[offset:], uint32(entry.MediaTime))
|
||||
offset += 4
|
||||
}
|
||||
binary.BigEndian.PutUint16(elstdata[offset:], uint16(entry.MediaRateInteger))
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(elstdata[offset:], uint16(entry.MediaRateFraction))
|
||||
offset += 2
|
||||
}
|
||||
return offset, elstdata
|
||||
}
|
||||
|
||||
func (elst *EditListBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
entryCountBuf := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, entryCountBuf); err != nil {
|
||||
return
|
||||
}
|
||||
entryCount := binary.BigEndian.Uint32(entryCountBuf)
|
||||
offset += 4
|
||||
var boxsize uint32
|
||||
if elst.Version == 0 {
|
||||
boxsize = 12 * entryCount
|
||||
} else {
|
||||
boxsize = 20 * entryCount
|
||||
}
|
||||
buf := make([]byte, boxsize)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if elst.Entrys == nil {
|
||||
elst.Entrys = make([]ELSTEntry, entryCount)
|
||||
}
|
||||
nn := 0
|
||||
for i := range entryCount {
|
||||
entry := &elst.Entrys[i]
|
||||
if elst.Version == 0 {
|
||||
entry.SegmentDuration = uint64(binary.BigEndian.Uint32(buf[nn:]))
|
||||
nn += 4
|
||||
entry.MediaTime = int64(int32(binary.BigEndian.Uint32(buf[nn:])))
|
||||
nn += 4
|
||||
} else {
|
||||
entry.SegmentDuration = uint64(binary.BigEndian.Uint64(buf[nn:]))
|
||||
nn += 8
|
||||
entry.MediaTime = int64(binary.BigEndian.Uint64(buf[nn:]))
|
||||
nn += 8
|
||||
}
|
||||
entry.MediaRateInteger = int16(binary.BigEndian.Uint16(buf[nn:]))
|
||||
nn += 2
|
||||
entry.MediaRateFraction = int16(binary.BigEndian.Uint16(buf[nn:]))
|
||||
nn += 2
|
||||
}
|
||||
return offset + nn, nil
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class EditListBox extends FullBox('elst', version, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// for (i=1; i <= entry_count; i++) {
|
||||
// if (version==1) {
|
||||
// unsigned int(64) segment_duration;
|
||||
// int(64) media_time;
|
||||
// } else { // version==0
|
||||
// unsigned int(32) segment_duration;
|
||||
// int(32) media_time;
|
||||
// }
|
||||
// int(16) media_rate_integer;
|
||||
// int(16) media_rate_fraction = 0;
|
||||
// }
|
||||
// }
|
||||
|
||||
type EditListBox struct {
|
||||
FullBox
|
||||
Entries []ELSTEntry
|
||||
}
|
||||
|
||||
func CreateEditListBox(version byte, entries []ELSTEntry) *EditListBox {
|
||||
entrySize := 12 // version 0: 4 + 4 + 2 + 2
|
||||
if version == 1 {
|
||||
entrySize = 20 // version 1: 8 + 8 + 2 + 2
|
||||
}
|
||||
return &EditListBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeELST,
|
||||
size: uint32(FullBoxLen + 4 + len(entries)*entrySize),
|
||||
},
|
||||
Version: version,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
Entries: entries,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *EditListBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
binary.BigEndian.PutUint32(tmp[:4], uint32(len(box.Entries)))
|
||||
_, err = w.Write(tmp[:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = 4
|
||||
|
||||
for _, entry := range box.Entries {
|
||||
if box.Version == 1 {
|
||||
binary.BigEndian.PutUint64(tmp[:], entry.SegmentDuration)
|
||||
_, err = w.Write(tmp[:8])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += 8
|
||||
binary.BigEndian.PutUint64(tmp[:], uint64(entry.MediaTime))
|
||||
_, err = w.Write(tmp[:8])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += 8
|
||||
} else {
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(entry.SegmentDuration))
|
||||
_, err = w.Write(tmp[:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += 4
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(entry.MediaTime))
|
||||
_, err = w.Write(tmp[:4])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += 4
|
||||
}
|
||||
binary.BigEndian.PutUint16(tmp[:], uint16(entry.MediaRateInteger))
|
||||
_, err = w.Write(tmp[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += 2
|
||||
binary.BigEndian.PutUint16(tmp[:], uint16(entry.MediaRateFraction))
|
||||
_, err = w.Write(tmp[:2])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += 2
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (box *EditListBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
entryCount := binary.BigEndian.Uint32(buf[:4])
|
||||
box.Entries = make([]ELSTEntry, entryCount)
|
||||
|
||||
offset := 4
|
||||
for i := range box.Entries {
|
||||
if box.Version == 1 {
|
||||
if offset+20 > len(buf) {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Entries[i].SegmentDuration = binary.BigEndian.Uint64(buf[offset:])
|
||||
offset += 8
|
||||
box.Entries[i].MediaTime = int64(binary.BigEndian.Uint64(buf[offset:]))
|
||||
offset += 8
|
||||
} else {
|
||||
if offset+12 > len(buf) {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Entries[i].SegmentDuration = uint64(binary.BigEndian.Uint32(buf[offset:]))
|
||||
offset += 4
|
||||
box.Entries[i].MediaTime = int64(int32(binary.BigEndian.Uint32(buf[offset:])))
|
||||
offset += 4
|
||||
}
|
||||
box.Entries[i].MediaRateInteger = int16(binary.BigEndian.Uint16(buf[offset:]))
|
||||
offset += 2
|
||||
box.Entries[i].MediaRateFraction = int16(binary.BigEndian.Uint16(buf[offset:]))
|
||||
offset += 2
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*EditListBox](TypeELST)
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package box
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
|
||||
"github.com/yapingcat/gomedia/go-codec"
|
||||
)
|
||||
@@ -50,6 +51,21 @@ func (base *BaseDescriptor) Encode() []byte {
|
||||
return bsw.Bits()[:5+int(base.sizeOfInstance)]
|
||||
}
|
||||
|
||||
type ESDSBox struct {
|
||||
FullBox
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (box *ESDSBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
_, err = w.Write(box.Data)
|
||||
return int64(len(box.Data)), err
|
||||
}
|
||||
|
||||
func (box *ESDSBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
box.Data = buf
|
||||
return box, nil
|
||||
}
|
||||
|
||||
// ffmpeg mov_write_esds_tag
|
||||
func makeBaseDescriptor(tag uint8, size uint32) []byte {
|
||||
base := BaseDescriptor{
|
||||
@@ -59,6 +75,19 @@ func makeBaseDescriptor(tag uint8, size uint32) []byte {
|
||||
return base.Encode()
|
||||
}
|
||||
|
||||
func CreateESDSBox(trackid uint16, cid MP4_CODEC_TYPE, vosData []byte) *ESDSBox {
|
||||
esData := makeESDescriptor(trackid, cid, vosData)
|
||||
return &ESDSBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeESDS,
|
||||
size: uint32(FullBoxLen + len(esData)),
|
||||
},
|
||||
},
|
||||
Data: esData,
|
||||
}
|
||||
}
|
||||
|
||||
func makeESDescriptor(trackid uint16, cid MP4_CODEC_TYPE, vosData []byte) []byte {
|
||||
dcd := makeDecoderConfigDescriptor(cid, vosData)
|
||||
sld := makeSLDescriptor()
|
||||
@@ -71,7 +100,6 @@ func makeESDescriptor(trackid uint16, cid MP4_CODEC_TYPE, vosData []byte) []byte
|
||||
}
|
||||
|
||||
func makeDecoderConfigDescriptor(cid MP4_CODEC_TYPE, vosData []byte) []byte {
|
||||
|
||||
decoder_specific_info_len := uint32(0)
|
||||
if len(vosData) > 0 {
|
||||
decoder_specific_info_len = uint32(len(vosData)) + 5
|
||||
@@ -145,3 +173,7 @@ func DecodeESDescriptor(esd []byte) (cid MP4_CODEC_TYPE, vosData []byte) {
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*ESDSBox](TypeESDS)
|
||||
}
|
||||
|
||||
@@ -1,25 +1,11 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
type FreeBox = DataBox
|
||||
|
||||
type FreeBox struct {
|
||||
Data []byte
|
||||
func CreateFreeBox(data []byte) *FreeBox {
|
||||
return CreateDataBox(TypeFREE, data)
|
||||
}
|
||||
|
||||
func (free *FreeBox) Decode(r io.Reader, baseBox *BasicBox) (int, error) {
|
||||
if BasicBoxLen < baseBox.Size {
|
||||
free.Data = make([]byte, baseBox.Size-BasicBoxLen)
|
||||
if _, err := io.ReadFull(r, free.Data); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
return int(baseBox.Size - BasicBoxLen), nil
|
||||
}
|
||||
|
||||
func (free *FreeBox) Encode() []byte {
|
||||
offset, buf := (&BasicBox{Type: TypeFREE, Size: 8 + uint64(len(free.Data))}).Encode()
|
||||
copy(buf[offset:], free.Data)
|
||||
return buf
|
||||
func init() {
|
||||
RegisterBox[*FreeBox](TypeFREE)
|
||||
}
|
||||
|
||||
+32
-42
@@ -3,58 +3,48 @@ package box
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type FileTypeBox struct {
|
||||
Major_brand [4]byte
|
||||
Minor_version uint32
|
||||
Compatible_brands [][4]byte
|
||||
BaseBox
|
||||
MajorBrand BoxType
|
||||
MinorVersion uint32
|
||||
CompatibleBrands []BoxType
|
||||
}
|
||||
|
||||
func (ftyp *FileTypeBox) Decode(r io.Reader, baseBox *BasicBox) (int, error) {
|
||||
buf := make([]byte, baseBox.Size-BasicBoxLen)
|
||||
if n, err := io.ReadFull(r, buf); err != nil {
|
||||
return n, err
|
||||
func CreateFTYPBox(major BoxType, minor uint32, compatibleBrands ...BoxType) *FileTypeBox {
|
||||
return &FileTypeBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeFTYP,
|
||||
size: uint32(BasicBoxLen + len(compatibleBrands)*4 + 8),
|
||||
},
|
||||
MajorBrand: major,
|
||||
MinorVersion: minor,
|
||||
CompatibleBrands: compatibleBrands,
|
||||
}
|
||||
ftyp.Major_brand = [4]byte(buf[0:])
|
||||
ftyp.Minor_version = binary.BigEndian.Uint32(buf[4:])
|
||||
n := 8
|
||||
for ; BasicBoxLen+n < int(baseBox.Size); n += 4 {
|
||||
ftyp.Compatible_brands = append(ftyp.Compatible_brands, [4]byte(buf[n:]))
|
||||
}
|
||||
|
||||
func (box *FileTypeBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
buffers := make(net.Buffers, 0, len(box.CompatibleBrands)+2)
|
||||
binary.BigEndian.PutUint32(tmp[:], box.MinorVersion)
|
||||
buffers = append(buffers, box.MajorBrand[:], tmp[:])
|
||||
for _, brand := range box.CompatibleBrands {
|
||||
buffers = append(buffers, brand[:])
|
||||
}
|
||||
return n, nil
|
||||
return buffers.WriteTo(w)
|
||||
}
|
||||
|
||||
func (ftyp *FileTypeBox) Encode(t [4]byte) (int, []byte) {
|
||||
var baseBox BasicBox
|
||||
baseBox.Type = t
|
||||
baseBox.Size = uint64(BasicBoxLen + len(ftyp.Compatible_brands)*4 + 8)
|
||||
offset, buf := baseBox.Encode()
|
||||
copy(buf[offset:], ftyp.Major_brand[:])
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], ftyp.Minor_version)
|
||||
offset += 4
|
||||
for i := 0; offset < int(baseBox.Size); offset += 4 {
|
||||
copy(buf[offset:], ftyp.Compatible_brands[i][:])
|
||||
i++
|
||||
func (box *FileTypeBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
box.MajorBrand = BoxType(buf[:4])
|
||||
box.MinorVersion = binary.BigEndian.Uint32(buf[4:8])
|
||||
for i := 8; i < len(buf); i += 4 {
|
||||
box.CompatibleBrands = append(box.CompatibleBrands, BoxType(buf[i:i+4]))
|
||||
}
|
||||
return offset, buf
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func MakeFtypBox(major [4]byte, minor uint32, compatibleBrands ...[4]byte) []byte {
|
||||
var ftyp FileTypeBox
|
||||
ftyp.Major_brand = major
|
||||
ftyp.Minor_version = minor
|
||||
ftyp.Compatible_brands = compatibleBrands
|
||||
_, boxData := ftyp.Encode(TypeFTYP)
|
||||
return boxData
|
||||
}
|
||||
|
||||
func MakeStypBox(major [4]byte, minor uint32, compatibleBrands ...[4]byte) []byte {
|
||||
var styp FileTypeBox
|
||||
styp.Major_brand = major
|
||||
styp.Minor_version = minor
|
||||
styp.Compatible_brands = compatibleBrands
|
||||
_, boxData := styp.Encode(TypeSTYP)
|
||||
return boxData
|
||||
func init() {
|
||||
RegisterBox[*FileTypeBox](TypeFTYP, TypeSTYP)
|
||||
}
|
||||
|
||||
+25
-25
@@ -3,6 +3,7 @@ package box
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
// Box Type: 'hdlr'
|
||||
@@ -27,44 +28,43 @@ import (
|
||||
|
||||
type HandlerType = [4]byte
|
||||
type HandlerBox struct {
|
||||
FullBox
|
||||
Pre_defined uint32
|
||||
Handler_type HandlerType
|
||||
Reserved [3]uint32
|
||||
Name string
|
||||
}
|
||||
|
||||
func NewHandlerBox(handlerType HandlerType, name string) *HandlerBox {
|
||||
return &HandlerBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeHDLR,
|
||||
size: uint32(20 + len(name) + 1 + FullBoxLen),
|
||||
},
|
||||
},
|
||||
Handler_type: handlerType,
|
||||
Name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (hdlr *HandlerBox) Decode(r io.Reader, size uint64) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf := make([]byte, size-FullBoxLen)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
func (hdlr *HandlerBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
hdlr.Pre_defined = binary.BigEndian.Uint32(buf[:4])
|
||||
copy(hdlr.Handler_type[:], buf[4:8])
|
||||
hdlr.Name = string(buf[20 : size-FullBoxLen])
|
||||
offset = int(size - FullBoxLen)
|
||||
return
|
||||
hdlr.Name = string(buf[20:])
|
||||
return hdlr, nil
|
||||
}
|
||||
|
||||
func (hdlr *HandlerBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeHDLR, 0)
|
||||
fullbox.Box.Size = 20 + uint64(len(hdlr.Name)+1) + FullBoxLen
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], hdlr.Pre_defined)
|
||||
copy(buf[offset+4:], hdlr.Handler_type[:])
|
||||
offset += 20
|
||||
copy(buf[offset:], []byte(hdlr.Name))
|
||||
return offset + len(hdlr.Name), buf
|
||||
func (hdlr *HandlerBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [20]byte
|
||||
binary.BigEndian.PutUint32(tmp[:], hdlr.Pre_defined)
|
||||
copy(tmp[4:8], hdlr.Handler_type[:])
|
||||
var buffer = net.Buffers{
|
||||
tmp[:],
|
||||
[]byte(hdlr.Name),
|
||||
[]byte{0},
|
||||
}
|
||||
return buffer.WriteTo(w)
|
||||
|
||||
}
|
||||
|
||||
func GetHandlerType(cid MP4_CODEC_TYPE) HandlerType {
|
||||
@@ -79,15 +79,15 @@ func GetHandlerType(cid MP4_CODEC_TYPE) HandlerType {
|
||||
}
|
||||
}
|
||||
|
||||
func MakeHdlrBox(hdt HandlerType) []byte {
|
||||
func MakeHdlrBox(hdt HandlerType) *HandlerBox {
|
||||
var hdlr *HandlerBox = nil
|
||||
if hdt == TypeVIDE {
|
||||
hdlr = NewHandlerBox(hdt, "VideoHandler")
|
||||
|
||||
} else if hdt == TypeSOUN {
|
||||
hdlr = NewHandlerBox(hdt, "SoundHandler")
|
||||
} else {
|
||||
hdlr = NewHandlerBox(hdt, "")
|
||||
}
|
||||
_, boxdata := hdlr.Encode()
|
||||
return boxdata
|
||||
return hdlr
|
||||
}
|
||||
|
||||
+34
-38
@@ -6,7 +6,7 @@ import (
|
||||
)
|
||||
|
||||
// aligned(8) class HintMediaHeaderBox
|
||||
// extends FullBox(‘hmhd’, version = 0, 0) {
|
||||
// extends FullBox('hmhd', version = 0, 0) {
|
||||
// unsigned int(16) maxPDUsize;
|
||||
// unsigned int(16) avgPDUsize;
|
||||
// unsigned int(32) maxbitrate;
|
||||
@@ -15,54 +15,50 @@ import (
|
||||
// }
|
||||
|
||||
type HintMediaHeaderBox struct {
|
||||
FullBox
|
||||
MaxPDUsize uint16
|
||||
AvgPDUsize uint16
|
||||
Maxbitrate uint32
|
||||
Avgbitrate uint32
|
||||
}
|
||||
|
||||
func NewHintMediaHeaderBox() *HintMediaHeaderBox {
|
||||
return &HintMediaHeaderBox{}
|
||||
func CreateHintMediaHeaderBox() *HintMediaHeaderBox {
|
||||
return &HintMediaHeaderBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeHMHD,
|
||||
size: uint32(FullBoxLen + 16),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (hmhd *HintMediaHeaderBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf := make([]byte, 16)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset = 0
|
||||
hmhd.MaxPDUsize = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
hmhd.AvgPDUsize = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
hmhd.Maxbitrate = binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
hmhd.Avgbitrate = binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 8
|
||||
func (box *HintMediaHeaderBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [16]byte
|
||||
binary.BigEndian.PutUint16(tmp[0:], box.MaxPDUsize)
|
||||
binary.BigEndian.PutUint16(tmp[2:], box.AvgPDUsize)
|
||||
binary.BigEndian.PutUint32(tmp[4:], box.Maxbitrate)
|
||||
binary.BigEndian.PutUint32(tmp[8:], box.Avgbitrate)
|
||||
// reserved already zeroed
|
||||
|
||||
nn, err := w.Write(tmp[:])
|
||||
n = int64(nn)
|
||||
return
|
||||
}
|
||||
|
||||
func (hmhd *HintMediaHeaderBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeHMHD, 0)
|
||||
fullbox.Box.Size = FullBoxLen + 16
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint16(buf[offset:], hmhd.MaxPDUsize)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], hmhd.AvgPDUsize)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint32(buf[offset:], hmhd.Maxbitrate)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], hmhd.Avgbitrate)
|
||||
offset += 8
|
||||
return offset, buf
|
||||
func (box *HintMediaHeaderBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 16 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.MaxPDUsize = binary.BigEndian.Uint16(buf[0:])
|
||||
box.AvgPDUsize = binary.BigEndian.Uint16(buf[2:])
|
||||
box.Maxbitrate = binary.BigEndian.Uint32(buf[4:])
|
||||
box.Avgbitrate = binary.BigEndian.Uint32(buf[8:])
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func makeHmhdBox() []byte {
|
||||
hmhd := NewHintMediaHeaderBox()
|
||||
_, hmhdbox := hmhd.Encode()
|
||||
return hmhdbox
|
||||
func init() {
|
||||
RegisterBox[*HintMediaHeaderBox](TypeHMHD)
|
||||
}
|
||||
|
||||
+35
-18
@@ -2,25 +2,42 @@ package box
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
type MediaDataBox uint64
|
||||
// aligned(8) class MediaDataBox extends Box('mdat') {
|
||||
// bit(8) data[];
|
||||
// }
|
||||
|
||||
func (box MediaDataBox) Encode() (int, []byte) {
|
||||
if box+BasicBoxLen > 0xFFFFFFFF {
|
||||
basicBox := NewBasicBox(TypeMDAT)
|
||||
basicBox.Size = BasicBoxLen + 8 + uint64(box)
|
||||
buf := make([]byte, BasicBoxLen*2)
|
||||
binary.BigEndian.PutUint32(buf, uint32(1))
|
||||
copy(buf[4:], basicBox.Type[:])
|
||||
binary.BigEndian.PutUint64(buf[8:], uint64(box))
|
||||
return BasicBoxLen * 2, buf
|
||||
} else {
|
||||
basicBox := NewBasicBox(TypeMDAT)
|
||||
basicBox.Size = BasicBoxLen + uint64(box)
|
||||
buf := make([]byte, BasicBoxLen)
|
||||
binary.BigEndian.PutUint32(buf, uint32(basicBox.Size))
|
||||
copy(buf[4:], basicBox.Type[:])
|
||||
return BasicBoxLen, buf
|
||||
}
|
||||
type MediaDataBox struct {
|
||||
BaseBox
|
||||
Data []byte
|
||||
net.Buffers
|
||||
}
|
||||
|
||||
func (box *MediaDataBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
var buffers net.Buffers
|
||||
if box.size == 1 {
|
||||
// 写入扩展大小头部
|
||||
binary.BigEndian.PutUint64(tmp[:], uint64(len(box.Data))+BasicBoxLen+8) // largesize
|
||||
buffers = append(buffers, tmp[:])
|
||||
}
|
||||
if box.Data != nil {
|
||||
buffers = append(buffers, box.Data)
|
||||
}
|
||||
if box.Buffers != nil {
|
||||
buffers = append(buffers, box.Buffers...)
|
||||
}
|
||||
return buffers.WriteTo(w)
|
||||
}
|
||||
|
||||
func (box *MediaDataBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
box.Data = buf
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*MediaDataBox](TypeMDAT)
|
||||
}
|
||||
|
||||
+73
-78
@@ -9,7 +9,7 @@ import (
|
||||
"m7s.live/v5/pkg/util"
|
||||
)
|
||||
|
||||
// aligned(8) class MediaHeaderBox extends FullBox(‘mdhd’, version, 0) {
|
||||
// aligned(8) class MediaHeaderBox extends FullBox('mdhd', version, 0) {
|
||||
// if (version==1) {
|
||||
// unsigned int(64) creation_time;
|
||||
// unsigned int(64) modification_time;
|
||||
@@ -40,97 +40,92 @@ func ff_mov_iso639_to_lang(lang [3]byte) (code int) {
|
||||
}
|
||||
|
||||
type MediaHeaderBox struct {
|
||||
Creation_time uint64
|
||||
Modification_time uint64
|
||||
Timescale uint32
|
||||
Duration uint64
|
||||
Pad uint8
|
||||
Language [3]uint8
|
||||
Pre_defined uint16
|
||||
FullBox
|
||||
CreationTime uint64
|
||||
ModificationTime uint64
|
||||
Timescale uint32
|
||||
Duration uint64
|
||||
Language [3]byte
|
||||
}
|
||||
|
||||
func NewMediaHeaderBox() *MediaHeaderBox {
|
||||
func CreateMediaHeaderBox(timescale uint32, duration uint64) *MediaHeaderBox {
|
||||
_, offset := time.Now().Zone()
|
||||
now := uint64(time.Now().Unix() + int64(offset) + 0x7C25B080)
|
||||
version := util.Conditional[uint8](duration > 0xFFFFFFFF, 1, 0)
|
||||
|
||||
return &MediaHeaderBox{
|
||||
Creation_time: uint64(time.Now().Unix() + int64(offset) + 0x7C25B080),
|
||||
Modification_time: uint64(time.Now().Unix() + int64(offset) + 0x7C25B080),
|
||||
Timescale: 1000,
|
||||
Language: [3]byte{'u', 'n', 'd'},
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeMDHD,
|
||||
size: util.Conditional[uint32](version == 1, 32, 20) + FullBoxLen,
|
||||
},
|
||||
Version: version,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
CreationTime: now,
|
||||
ModificationTime: now,
|
||||
Timescale: timescale,
|
||||
Duration: duration,
|
||||
Language: [3]byte{'u', 'n', 'd'},
|
||||
}
|
||||
}
|
||||
|
||||
func (mdhd *MediaHeaderBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return 0, err
|
||||
func (box *MediaHeaderBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var data []byte
|
||||
if box.Version == 1 {
|
||||
data = make([]byte, 32)
|
||||
binary.BigEndian.PutUint64(data[0:], box.CreationTime)
|
||||
binary.BigEndian.PutUint64(data[8:], box.ModificationTime)
|
||||
binary.BigEndian.PutUint32(data[16:], box.Timescale)
|
||||
binary.BigEndian.PutUint64(data[20:], box.Duration)
|
||||
binary.BigEndian.PutUint16(data[28:], uint16(ff_mov_iso639_to_lang(box.Language)&0x7FFF))
|
||||
// pre_defined already zeroed
|
||||
} else {
|
||||
data = make([]byte, 20)
|
||||
binary.BigEndian.PutUint32(data[0:], uint32(box.CreationTime))
|
||||
binary.BigEndian.PutUint32(data[4:], uint32(box.ModificationTime))
|
||||
binary.BigEndian.PutUint32(data[8:], box.Timescale)
|
||||
binary.BigEndian.PutUint32(data[12:], uint32(box.Duration))
|
||||
binary.BigEndian.PutUint16(data[16:], uint16(ff_mov_iso639_to_lang(box.Language)&0x7FFF))
|
||||
// pre_defined already zeroed
|
||||
}
|
||||
|
||||
buf := make([]byte, util.Conditional(fullbox.Version == 1, 32, 20))
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset = 0
|
||||
if fullbox.Version == 1 {
|
||||
mdhd.Creation_time = binary.BigEndian.Uint64(buf[offset:])
|
||||
offset += 8
|
||||
mdhd.Modification_time = binary.BigEndian.Uint64(buf[offset:])
|
||||
offset += 8
|
||||
mdhd.Timescale = binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
mdhd.Duration = binary.BigEndian.Uint64(buf[offset:])
|
||||
offset += 8
|
||||
} else {
|
||||
mdhd.Creation_time = uint64(binary.BigEndian.Uint32(buf[offset:]))
|
||||
offset += 4
|
||||
mdhd.Modification_time = uint64(binary.BigEndian.Uint32(buf[offset:]))
|
||||
offset += 4
|
||||
mdhd.Timescale = binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
mdhd.Duration = uint64(binary.BigEndian.Uint32(buf[offset:]))
|
||||
offset += 4
|
||||
}
|
||||
bs := codec.NewBitStream(buf[offset:])
|
||||
mdhd.Pad = bs.GetBit()
|
||||
mdhd.Language[0] = bs.Uint8(5)
|
||||
mdhd.Language[1] = bs.Uint8(5)
|
||||
mdhd.Language[2] = bs.Uint8(5)
|
||||
mdhd.Pre_defined = 0
|
||||
offset += 4
|
||||
nn, err := w.Write(data)
|
||||
n = int64(nn)
|
||||
return
|
||||
}
|
||||
|
||||
func (mdhd *MediaHeaderBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeMDHD, 0)
|
||||
fullbox.Box.Size = util.Conditional[uint64](fullbox.Version == 1, 32, 20) + FullBoxLen
|
||||
offset, buf := fullbox.Encode()
|
||||
if fullbox.Version == 1 {
|
||||
binary.BigEndian.PutUint64(buf[offset:], mdhd.Creation_time)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint64(buf[offset:], mdhd.Modification_time)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint32(buf[offset:], mdhd.Timescale)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint64(buf[offset:], mdhd.Duration)
|
||||
offset += 8
|
||||
func (box *MediaHeaderBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if box.Version == 1 {
|
||||
if len(buf) < 32 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.CreationTime = binary.BigEndian.Uint64(buf[0:])
|
||||
box.ModificationTime = binary.BigEndian.Uint64(buf[8:])
|
||||
box.Timescale = binary.BigEndian.Uint32(buf[16:])
|
||||
box.Duration = binary.BigEndian.Uint64(buf[20:])
|
||||
buf = buf[28:]
|
||||
} else {
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(mdhd.Creation_time))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(mdhd.Modification_time))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], mdhd.Timescale)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(mdhd.Duration))
|
||||
offset += 4
|
||||
if len(buf) < 20 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.CreationTime = uint64(binary.BigEndian.Uint32(buf[0:]))
|
||||
box.ModificationTime = uint64(binary.BigEndian.Uint32(buf[4:]))
|
||||
box.Timescale = binary.BigEndian.Uint32(buf[8:])
|
||||
box.Duration = uint64(binary.BigEndian.Uint32(buf[12:]))
|
||||
buf = buf[16:]
|
||||
}
|
||||
binary.BigEndian.PutUint16(buf[offset:], uint16(ff_mov_iso639_to_lang(mdhd.Language)&0x7FFF))
|
||||
offset += 2
|
||||
offset += 2
|
||||
return offset, buf
|
||||
|
||||
// Read language
|
||||
bs := codec.NewBitStream(buf)
|
||||
_ = bs.GetBit() // pad
|
||||
box.Language[0] = bs.Uint8(5)
|
||||
box.Language[1] = bs.Uint8(5)
|
||||
box.Language[2] = bs.Uint8(5)
|
||||
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func MakeMdhdBox(duration uint32) []byte {
|
||||
mdhd := NewMediaHeaderBox()
|
||||
mdhd.Duration = uint64(duration)
|
||||
_, boxdata := mdhd.Encode()
|
||||
return boxdata
|
||||
func init() {
|
||||
RegisterBox[*MediaHeaderBox](TypeMDHD)
|
||||
}
|
||||
|
||||
@@ -1 +1,72 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type MdiaBox struct {
|
||||
BaseBox
|
||||
MDHD *MediaHeaderBox
|
||||
MINF *MediaInformationBox
|
||||
HDLR *HandlerBox
|
||||
}
|
||||
|
||||
func (m *MdiaBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, m.MDHD, m.MINF, m.HDLR)
|
||||
}
|
||||
|
||||
func (m *MdiaBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *MediaHeaderBox:
|
||||
m.MDHD = box
|
||||
case *MediaInformationBox:
|
||||
m.MINF = box
|
||||
case *HandlerBox:
|
||||
m.HDLR = box
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type MediaInformationBox struct {
|
||||
BaseBox
|
||||
VMHD *VideoMediaHeaderBox
|
||||
SMHD *SoundMediaHeaderBox
|
||||
HMHD *HintMediaHeaderBox
|
||||
STBL *SampleTableBox
|
||||
DINF *DataInformationBox
|
||||
}
|
||||
|
||||
func (m *MediaInformationBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, m.VMHD, m.SMHD, m.HMHD, m.STBL, m.DINF)
|
||||
}
|
||||
|
||||
func (m *MediaInformationBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *VideoMediaHeaderBox:
|
||||
m.VMHD = box
|
||||
case *SoundMediaHeaderBox:
|
||||
m.SMHD = box
|
||||
case *HintMediaHeaderBox:
|
||||
m.HMHD = box
|
||||
case *SampleTableBox:
|
||||
m.STBL = box
|
||||
case *DataInformationBox:
|
||||
m.DINF = box
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*MdiaBox](TypeMDIA)
|
||||
}
|
||||
|
||||
+32
-26
@@ -5,39 +5,45 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class MovieFragmentHeaderBox extends FullBox(‘mfhd’, 0, 0){
|
||||
// aligned(8) class MovieFragmentHeaderBox extends FullBox('mfhd', 0, 0){
|
||||
// unsigned int(32) sequence_number;
|
||||
// }
|
||||
|
||||
type MovieFragmentHeaderBox uint32
|
||||
|
||||
func (mfhd MovieFragmentHeaderBox) Size() uint64 {
|
||||
return FullBoxLen + 4
|
||||
type MovieFragmentHeaderBox struct {
|
||||
FullBox
|
||||
SequenceNumber uint32
|
||||
}
|
||||
|
||||
func (mfhd *MovieFragmentHeaderBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
func CreateMovieFragmentHeaderBox(sequenceNumber uint32) *MovieFragmentHeaderBox {
|
||||
return &MovieFragmentHeaderBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeMFHD,
|
||||
size: uint32(FullBoxLen + 4),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
SequenceNumber: sequenceNumber,
|
||||
}
|
||||
buf := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
func (box *MovieFragmentHeaderBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
binary.BigEndian.PutUint32(tmp[:], box.SequenceNumber)
|
||||
nn, err := w.Write(tmp[:])
|
||||
n = int64(nn)
|
||||
return
|
||||
}
|
||||
|
||||
func (box *MovieFragmentHeaderBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
*mfhd = MovieFragmentHeaderBox(binary.BigEndian.Uint32(buf))
|
||||
return offset + 4, nil
|
||||
box.SequenceNumber = binary.BigEndian.Uint32(buf[:4])
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (mfhd MovieFragmentHeaderBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeMFHD, 0)
|
||||
fullbox.Box.Size = mfhd.Size()
|
||||
offset, boxdata := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], uint32(mfhd))
|
||||
return offset + 4, boxdata
|
||||
}
|
||||
|
||||
func MakeMfhdBox(frament uint32) []byte {
|
||||
mfhd := MovieFragmentHeaderBox(frament)
|
||||
_, boxData := mfhd.Encode()
|
||||
return boxData
|
||||
func init() {
|
||||
RegisterBox[*MovieFragmentHeaderBox](TypeMFHD)
|
||||
}
|
||||
|
||||
+28
-20
@@ -5,35 +5,43 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class MovieFragmentRandomAccessOffsetBox extends FullBox(‘mfro’, version, 0) {
|
||||
// aligned(8) class MovieFragmentRandomAccessOffsetBox extends FullBox('mfro', version, 0) {
|
||||
// unsigned int(32) size;
|
||||
// }
|
||||
|
||||
type MovieFragmentRandomAccessOffsetBox uint32
|
||||
type MovieFragmentRandomAccessOffsetBox struct {
|
||||
FullBox
|
||||
MfraSize uint32
|
||||
}
|
||||
|
||||
func (mfro *MovieFragmentRandomAccessOffsetBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
func CreateMfroBox(mfraSize uint32) *MovieFragmentRandomAccessOffsetBox {
|
||||
return &MovieFragmentRandomAccessOffsetBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeMFRO,
|
||||
size: uint32(FullBoxLen + 4),
|
||||
},
|
||||
},
|
||||
MfraSize: mfraSize,
|
||||
}
|
||||
buf := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
}
|
||||
|
||||
func (box *MovieFragmentRandomAccessOffsetBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
binary.BigEndian.PutUint32(tmp[:], box.MfraSize)
|
||||
var nn int
|
||||
nn, err = w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
*mfro = MovieFragmentRandomAccessOffsetBox(binary.BigEndian.Uint32(buf))
|
||||
return offset + 4, nil
|
||||
return int64(nn), nil
|
||||
}
|
||||
|
||||
func (mfro *MovieFragmentRandomAccessOffsetBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeMFRO, 0)
|
||||
fullbox.Box.Size = FullBoxLen + 4
|
||||
offset, boxdata := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], uint32(*mfro))
|
||||
return offset + 4, boxdata
|
||||
func (box *MovieFragmentRandomAccessOffsetBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
box.MfraSize = binary.BigEndian.Uint32(buf)
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func MakeMfroBox(mfraSize uint32) []byte {
|
||||
mfro := MovieFragmentRandomAccessOffsetBox(mfraSize + 16)
|
||||
_, boxData := mfro.Encode()
|
||||
return boxData
|
||||
func init() {
|
||||
RegisterBox[*MovieFragmentRandomAccessOffsetBox](TypeMFRO)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class MovieFragmentBox extends Box('moof'){
|
||||
// }
|
||||
|
||||
type MovieFragmentBox struct {
|
||||
BaseBox
|
||||
MFHD *MovieFragmentHeaderBox
|
||||
TRAFs []*TrackFragmentBox
|
||||
}
|
||||
|
||||
type TrackFragmentBox struct {
|
||||
BaseBox
|
||||
TFHD *TrackFragmentHeaderBox
|
||||
TFDT *TrackFragmentBaseMediaDecodeTimeBox
|
||||
TRUN *TrackRunBox
|
||||
}
|
||||
|
||||
func CreateTrackFragmentBox(tfhd *TrackFragmentHeaderBox, tfdt *TrackFragmentBaseMediaDecodeTimeBox, trun *TrackRunBox) *TrackFragmentBox {
|
||||
return &TrackFragmentBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeTRAF,
|
||||
size: uint32(BasicBoxLen + tfhd.size + trun.size),
|
||||
},
|
||||
TFHD: tfhd,
|
||||
TFDT: tfdt,
|
||||
TRUN: trun,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *MovieFragmentBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
boxes := []IBox{box.MFHD}
|
||||
for _, traf := range box.TRAFs {
|
||||
boxes = append(boxes, traf)
|
||||
}
|
||||
return WriteTo(w, boxes...)
|
||||
}
|
||||
|
||||
func (box *TrackFragmentBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, box.TFHD, box.TRUN)
|
||||
}
|
||||
|
||||
func (box *MovieFragmentBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
r := bytes.NewReader(buf)
|
||||
for {
|
||||
b, err := ReadFrom(r)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
switch b := b.(type) {
|
||||
case *MovieFragmentHeaderBox:
|
||||
box.MFHD = b
|
||||
case *TrackFragmentBox:
|
||||
box.TRAFs = append(box.TRAFs, b)
|
||||
}
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (box *TrackFragmentBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
r := bytes.NewReader(buf)
|
||||
for {
|
||||
b, err := ReadFrom(r)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
switch b := b.(type) {
|
||||
case *TrackFragmentHeaderBox:
|
||||
box.TFHD = b
|
||||
case *TrackRunBox:
|
||||
box.TRUN = b
|
||||
}
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*MovieFragmentBox](TypeMOOF)
|
||||
RegisterBox[*TrackFragmentBox](TypeTRAF)
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type (
|
||||
MoovBox struct {
|
||||
BaseBox
|
||||
Tracks []*TrakBox
|
||||
// UDTA *UdtaBody
|
||||
MVHD *MovieHeaderBox
|
||||
MVEX *MovieExtendsBox
|
||||
}
|
||||
|
||||
EdtsBox struct {
|
||||
BaseBox
|
||||
Elst *EditListBox
|
||||
}
|
||||
)
|
||||
|
||||
func (m *MoovBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var boxes []IBox
|
||||
boxes = append(boxes, m.MVHD)
|
||||
for _, track := range m.Tracks {
|
||||
boxes = append(boxes, track)
|
||||
}
|
||||
return WriteTo(w, boxes...)
|
||||
}
|
||||
|
||||
func (m *MoovBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *TrakBox:
|
||||
m.Tracks = append(m.Tracks, box)
|
||||
case *MovieHeaderBox:
|
||||
m.MVHD = box
|
||||
case *MovieExtendsBox:
|
||||
m.MVEX = box
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (e *EdtsBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, e.Elst)
|
||||
}
|
||||
|
||||
func (e *EdtsBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *EditListBox:
|
||||
e.Elst = box
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*MoovBox](TypeMOOV)
|
||||
RegisterBox[*EdtsBox](TypeEDTS)
|
||||
}
|
||||
@@ -35,13 +35,6 @@ type ELSTEntry struct {
|
||||
MediaRateFraction int16
|
||||
}
|
||||
|
||||
type TrunEntry struct {
|
||||
SampleDuration uint32
|
||||
SampleSize uint32
|
||||
SampleFlags uint32
|
||||
SampleCompositionTimeOffset int32
|
||||
}
|
||||
|
||||
type SENC struct {
|
||||
Entrys []SencEntry
|
||||
}
|
||||
@@ -50,3 +43,11 @@ type FragEntry struct {
|
||||
Time uint64
|
||||
MoofOffset uint64
|
||||
}
|
||||
|
||||
type TFRAEntry struct {
|
||||
Time uint64
|
||||
MoofOffset uint64
|
||||
TrafNumber uint32
|
||||
TrunNumber uint32
|
||||
SampleNumber uint32
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class MovieExtendsBox extends Box('mvex') {
|
||||
// }
|
||||
|
||||
type MovieExtendsBox struct {
|
||||
BaseBox
|
||||
Trexs []*TrackExtendsBox
|
||||
}
|
||||
|
||||
func CreateMovieExtendsBox(trexs []*TrackExtendsBox) *MovieExtendsBox {
|
||||
size := uint32(BasicBoxLen)
|
||||
for _, trex := range trexs {
|
||||
size += trex.size
|
||||
}
|
||||
|
||||
return &MovieExtendsBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeMVEX,
|
||||
size: size,
|
||||
},
|
||||
Trexs: trexs,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *MovieExtendsBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
boxes := make([]IBox, len(box.Trexs))
|
||||
for i, trex := range box.Trexs {
|
||||
boxes[i] = trex
|
||||
}
|
||||
return WriteTo(w, boxes...)
|
||||
}
|
||||
|
||||
func (box *MovieExtendsBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
box.Trexs = make([]*TrackExtendsBox, 0)
|
||||
r := bytes.NewReader(buf)
|
||||
for {
|
||||
b, err := ReadFrom(r)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
if trex, ok := b.(*TrackExtendsBox); ok {
|
||||
box.Trexs = append(box.Trexs, trex)
|
||||
}
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*MovieExtendsBox](TypeMVEX)
|
||||
}
|
||||
+88
-101
@@ -6,7 +6,7 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// aligned(8) class MovieHeaderBox extends FullBox(‘mvhd’, version, 0) {
|
||||
// aligned(8) class MovieHeaderBox extends FullBox('mvhd', version, 0) {
|
||||
// if (version==1) {
|
||||
// unsigned int(64) creation_time;
|
||||
// unsigned int(64) modification_time;
|
||||
@@ -29,121 +29,108 @@ import (
|
||||
// }
|
||||
|
||||
type MovieHeaderBox struct {
|
||||
Creation_time uint64
|
||||
Modification_time uint64
|
||||
Timescale uint32
|
||||
Duration uint64
|
||||
Rate uint32
|
||||
Volume uint16
|
||||
Matrix [9]uint32
|
||||
Pre_defined [6]uint32
|
||||
Next_track_ID uint32
|
||||
FullBox
|
||||
CreationTime uint64
|
||||
ModificationTime uint64
|
||||
Timescale uint32
|
||||
Duration uint64
|
||||
Rate int32
|
||||
Volume int16
|
||||
Matrix [9]int32
|
||||
NextTrackID uint32
|
||||
}
|
||||
|
||||
func NewMovieHeaderBox() *MovieHeaderBox {
|
||||
// MP4/QuickTime epoch starts from Jan 1, 1904
|
||||
// Add offset between Unix epoch (1970) and QuickTime epoch (1904)
|
||||
const mp4Epoch = 2082844800 // seconds between 1904 and 1970
|
||||
now := uint64(time.Now().Unix() + mp4Epoch)
|
||||
func CreateMovieHeaderBox(nextTrackID uint32, duration uint32) *MovieHeaderBox {
|
||||
now := time.Now().Unix()
|
||||
return &MovieHeaderBox{
|
||||
Creation_time: now,
|
||||
Modification_time: now,
|
||||
Timescale: 1000,
|
||||
Rate: 0x00010000,
|
||||
Volume: 0x0100,
|
||||
Matrix: [9]uint32{0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000},
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeMVHD,
|
||||
size: uint32(FullBoxLen + 96),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
CreationTime: uint64(now),
|
||||
ModificationTime: uint64(now),
|
||||
Timescale: 1000,
|
||||
Duration: uint64(duration),
|
||||
Rate: 0x00010000,
|
||||
Volume: 0x0100,
|
||||
Matrix: [9]int32{0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000},
|
||||
NextTrackID: nextTrackID,
|
||||
}
|
||||
}
|
||||
|
||||
func (mvhd *MovieHeaderBox) Decode(r io.Reader, basebox *BasicBox) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
boxsize := 0
|
||||
if fullbox.Version == 0 {
|
||||
boxsize = 96
|
||||
func (box *MovieHeaderBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var nn int
|
||||
if box.Version == 1 {
|
||||
var tmp [108]byte
|
||||
binary.BigEndian.PutUint64(tmp[0:], box.CreationTime)
|
||||
binary.BigEndian.PutUint64(tmp[8:], box.ModificationTime)
|
||||
binary.BigEndian.PutUint32(tmp[16:], box.Timescale)
|
||||
binary.BigEndian.PutUint64(tmp[20:], box.Duration)
|
||||
binary.BigEndian.PutUint32(tmp[28:], uint32(box.Rate))
|
||||
binary.BigEndian.PutUint16(tmp[32:], uint16(box.Volume))
|
||||
offset := 34 + 8
|
||||
for i := 0; i < 9; i++ {
|
||||
binary.BigEndian.PutUint32(tmp[offset:], uint32(box.Matrix[i]))
|
||||
offset += 4
|
||||
}
|
||||
binary.BigEndian.PutUint32(tmp[104:], box.NextTrackID)
|
||||
nn, err = w.Write(tmp[:])
|
||||
n = int64(nn)
|
||||
} else {
|
||||
boxsize = 108
|
||||
var tmp [96]byte
|
||||
binary.BigEndian.PutUint32(tmp[0:], uint32(box.CreationTime))
|
||||
binary.BigEndian.PutUint32(tmp[4:], uint32(box.ModificationTime))
|
||||
binary.BigEndian.PutUint32(tmp[8:], box.Timescale)
|
||||
binary.BigEndian.PutUint32(tmp[12:], uint32(box.Duration))
|
||||
binary.BigEndian.PutUint32(tmp[16:], uint32(box.Rate))
|
||||
binary.BigEndian.PutUint16(tmp[20:], uint16(box.Volume))
|
||||
offset := 22 + 8
|
||||
for i := 0; i < 9; i++ {
|
||||
binary.BigEndian.PutUint32(tmp[offset:], uint32(box.Matrix[i]))
|
||||
offset += 4
|
||||
}
|
||||
binary.BigEndian.PutUint32(tmp[92:], box.NextTrackID)
|
||||
nn, err = w.Write(tmp[:])
|
||||
n = int64(nn)
|
||||
}
|
||||
buf := make([]byte, boxsize)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n := 0
|
||||
if fullbox.Version == 1 {
|
||||
mvhd.Creation_time = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
mvhd.Modification_time = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
mvhd.Timescale = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
mvhd.Duration = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
} else {
|
||||
mvhd.Creation_time = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
mvhd.Modification_time = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
mvhd.Timescale = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
mvhd.Duration = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
}
|
||||
mvhd.Rate = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
mvhd.Volume = binary.BigEndian.Uint16(buf[n:])
|
||||
n += 10
|
||||
|
||||
for i, _ := range mvhd.Matrix {
|
||||
mvhd.Matrix[i] = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
|
||||
for i := 0; i < 6; i++ {
|
||||
mvhd.Pre_defined[i] = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
mvhd.Next_track_ID = binary.BigEndian.Uint32(buf[n:])
|
||||
return n + 4 + offset, nil
|
||||
return
|
||||
}
|
||||
|
||||
func (mvhd *MovieHeaderBox) Encode() (int, []byte) {
|
||||
// Always use version 0 for better compatibility
|
||||
var fullbox = NewFullBox(TypeMVHD, 0)
|
||||
fullbox.Box.Size = FullBoxLen + 96 // version 0 size
|
||||
offset, buf := fullbox.Encode()
|
||||
func (box *MovieHeaderBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
var offset int
|
||||
if box.Version == 1 {
|
||||
box.CreationTime = binary.BigEndian.Uint64(buf[0:])
|
||||
box.ModificationTime = binary.BigEndian.Uint64(buf[8:])
|
||||
box.Timescale = binary.BigEndian.Uint32(buf[16:])
|
||||
box.Duration = binary.BigEndian.Uint64(buf[20:])
|
||||
offset = 28
|
||||
} else {
|
||||
box.CreationTime = uint64(binary.BigEndian.Uint32(buf[0:]))
|
||||
box.ModificationTime = uint64(binary.BigEndian.Uint32(buf[4:]))
|
||||
box.Timescale = binary.BigEndian.Uint32(buf[8:])
|
||||
box.Duration = uint64(binary.BigEndian.Uint32(buf[12:]))
|
||||
offset = 16
|
||||
}
|
||||
|
||||
// Version 0: all 32-bit values
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(mvhd.Creation_time))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(mvhd.Modification_time))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], mvhd.Timescale)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(mvhd.Duration))
|
||||
offset += 4
|
||||
box.Rate = int32(binary.BigEndian.Uint32(buf[offset:]))
|
||||
box.Volume = int16(binary.BigEndian.Uint16(buf[offset+4:]))
|
||||
// skip reserved: 2 + 8 bytes
|
||||
|
||||
binary.BigEndian.PutUint32(buf[offset:], mvhd.Rate)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint16(buf[offset:], mvhd.Volume)
|
||||
offset += 2
|
||||
offset += 10 // reserved
|
||||
|
||||
for _, matrix := range mvhd.Matrix {
|
||||
binary.BigEndian.PutUint32(buf[offset:], matrix)
|
||||
offset += 16
|
||||
for i := 0; i < 9; i++ {
|
||||
box.Matrix[i] = int32(binary.BigEndian.Uint32(buf[offset:]))
|
||||
offset += 4
|
||||
}
|
||||
// skip pre_defined: 24 bytes
|
||||
box.NextTrackID = binary.BigEndian.Uint32(buf[offset+24:])
|
||||
|
||||
offset += 24 // pre-defined
|
||||
binary.BigEndian.PutUint32(buf[offset:], mvhd.Next_track_ID)
|
||||
return offset + 4, buf
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func MakeMvhdBox(trackid uint32, duration uint32) []byte {
|
||||
mvhd := NewMovieHeaderBox()
|
||||
mvhd.Next_track_ID = trackid
|
||||
mvhd.Duration = uint64(duration)
|
||||
_, mvhdbox := mvhd.Encode()
|
||||
return mvhdbox
|
||||
func init() {
|
||||
RegisterBox[*MovieHeaderBox](TypeMVHD)
|
||||
}
|
||||
|
||||
+73
-18
@@ -1,6 +1,7 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
@@ -16,47 +17,101 @@ const (
|
||||
// PsshBox - Protection System Specific Header Box
|
||||
// Defined in ISO/IEC 23001-7 Section 8.1
|
||||
type PsshBox struct {
|
||||
FullBox
|
||||
SystemID [16]byte
|
||||
KIDs [][16]byte
|
||||
Data []byte
|
||||
}
|
||||
|
||||
func (pssh *PsshBox) Decode(r io.Reader, basebox *BasicBox) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
func CreatePsshBox(systemID [16]byte, kids [][16]byte, data []byte) *PsshBox {
|
||||
version := uint8(0)
|
||||
size := uint32(FullBoxLen + 16 + 4 + len(data))
|
||||
if len(kids) > 0 {
|
||||
version = 1
|
||||
size += 4 + uint32(len(kids)*16)
|
||||
}
|
||||
buf := make([]byte, basebox.Size-FullBoxLen)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
return &PsshBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypePSSH,
|
||||
size: size,
|
||||
},
|
||||
Version: version,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
SystemID: systemID,
|
||||
KIDs: kids,
|
||||
Data: data,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *PsshBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
buffers := make([][]byte, 0, 3+len(box.KIDs))
|
||||
buffers = append(buffers, box.SystemID[:])
|
||||
|
||||
if box.Version > 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(len(box.KIDs)))
|
||||
buffers = append(buffers, tmp[:])
|
||||
for _, kid := range box.KIDs {
|
||||
buffers = append(buffers, kid[:])
|
||||
}
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(len(box.Data)))
|
||||
buffers = append(buffers, tmp[:], box.Data)
|
||||
|
||||
nn, err := io.Copy(w, io.MultiReader(bytes.NewReader(box.SystemID[:]), bytes.NewReader(tmp[:]), bytes.NewReader(box.Data)))
|
||||
return int64(nn), err
|
||||
}
|
||||
|
||||
func (box *PsshBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 20 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
n := 0
|
||||
copy(pssh.SystemID[:], buf[n:n+16])
|
||||
copy(box.SystemID[:], buf[n:n+16])
|
||||
n += 16
|
||||
if fullbox.Version > 0 {
|
||||
if box.Version > 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
kidCount := binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
if len(buf) < n+int(kidCount)*16 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
for i := uint32(0); i < kidCount; i++ {
|
||||
var kid [16]byte
|
||||
copy(kid[:], buf[n:n+16])
|
||||
n += 16
|
||||
pssh.KIDs = append(pssh.KIDs, kid)
|
||||
box.KIDs = append(box.KIDs, kid)
|
||||
}
|
||||
}
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
dataLen := binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
pssh.Data = buf[n : n+int(dataLen)]
|
||||
return
|
||||
if len(buf) < n+int(dataLen) {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Data = buf[n : n+int(dataLen)]
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (pssh *PsshBox) IsWidevine() bool {
|
||||
return hex.EncodeToString(pssh.SystemID[:]) == UUIDWidevine
|
||||
func (box *PsshBox) IsWidevine() bool {
|
||||
return hex.EncodeToString(box.SystemID[:]) == UUIDWidevine
|
||||
}
|
||||
|
||||
func (pssh *PsshBox) IsPlayReady() bool {
|
||||
return hex.EncodeToString(pssh.SystemID[:]) == UUIDPlayReady
|
||||
func (box *PsshBox) IsPlayReady() bool {
|
||||
return hex.EncodeToString(box.SystemID[:]) == UUIDPlayReady
|
||||
}
|
||||
|
||||
func (pssh *PsshBox) IsFairPlay() bool {
|
||||
return hex.EncodeToString(pssh.SystemID[:]) == UUIDFairPlay
|
||||
func (box *PsshBox) IsFairPlay() bool {
|
||||
return hex.EncodeToString(box.SystemID[:]) == UUIDFairPlay
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*PsshBox](TypePSSH)
|
||||
}
|
||||
|
||||
+105
-26
@@ -7,40 +7,119 @@ import (
|
||||
|
||||
// SaioBox - Sample Auxiliary Information Offsets Box (saiz) (in stbl or traf box)
|
||||
type SaioBox struct {
|
||||
FullBox
|
||||
AuxInfoType string // Used for Common Encryption Scheme (4-bytes uint32 according to spec)
|
||||
AuxInfoTypeParameter uint32
|
||||
Offset []int64
|
||||
}
|
||||
|
||||
func (s *SaioBox) Decode(r io.Reader, size uint32) error {
|
||||
var fullbox FullBox
|
||||
if _, err := fullbox.Decode(r); err != nil {
|
||||
return err
|
||||
func CreateSaioBox(auxInfoType string, auxInfoTypeParameter uint32, offset []int64) *SaioBox {
|
||||
flags := uint32(0)
|
||||
size := uint32(FullBoxLen + 4) // base size + entry_count
|
||||
if len(auxInfoType) > 0 {
|
||||
flags |= 0x01
|
||||
size += 8 // auxInfoType(4) + auxInfoTypeParameter(4)
|
||||
}
|
||||
buf := make([]byte, size-12)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return err
|
||||
}
|
||||
var n int
|
||||
flags := uint32(fullbox.Flags[0])<<16 | uint32(fullbox.Flags[1])<<8 | uint32(fullbox.Flags[2])
|
||||
if flags&0x01 != 0 {
|
||||
s.AuxInfoType = string(buf[n : n+4])
|
||||
n += 4
|
||||
s.AuxInfoTypeParameter = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
entryCount := binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
if fullbox.Version == 0 {
|
||||
for i := uint32(0); i < entryCount; i++ {
|
||||
s.Offset = append(s.Offset, int64(binary.BigEndian.Uint32(buf[n:])))
|
||||
n += 4
|
||||
if len(offset) > 0 {
|
||||
if offset[0] > 0xFFFFFFFF {
|
||||
size += uint32(len(offset) * 8)
|
||||
} else {
|
||||
size += uint32(len(offset) * 4)
|
||||
}
|
||||
} else {
|
||||
for i := uint32(0); i < entryCount; i++ {
|
||||
s.Offset = append(s.Offset, int64(binary.BigEndian.Uint64(buf[n:])))
|
||||
}
|
||||
return &SaioBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSAIO,
|
||||
size: size,
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{byte(flags >> 16), byte(flags >> 8), byte(flags)},
|
||||
},
|
||||
AuxInfoType: auxInfoType,
|
||||
AuxInfoTypeParameter: auxInfoTypeParameter,
|
||||
Offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *SaioBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
|
||||
if flags&0x01 != 0 {
|
||||
copy(tmp[:4], []byte(box.AuxInfoType))
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint32(tmp[:4], box.AuxInfoTypeParameter)
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 8
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint32(tmp[:4], uint32(len(box.Offset)))
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 4
|
||||
|
||||
for _, offset := range box.Offset {
|
||||
if box.Version == 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:4], uint32(offset))
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 4
|
||||
} else {
|
||||
binary.BigEndian.PutUint64(tmp[:], uint64(offset))
|
||||
if _, err = w.Write(tmp[:]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 8
|
||||
}
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func (box *SaioBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
n := 0
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
if flags&0x01 != 0 {
|
||||
if len(buf) < n+8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.AuxInfoType = string(buf[n : n+4])
|
||||
n += 4
|
||||
box.AuxInfoTypeParameter = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
|
||||
entryCount := binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
box.Offset = make([]int64, entryCount)
|
||||
for i := uint32(0); i < entryCount; i++ {
|
||||
if box.Version == 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Offset[i] = int64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
} else {
|
||||
if len(buf) < n+8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Offset[i] = int64(binary.BigEndian.Uint64(buf[n:]))
|
||||
n += 8
|
||||
}
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*SaioBox](TypeSAIO)
|
||||
}
|
||||
|
||||
+92
-23
@@ -7,41 +7,110 @@ import (
|
||||
|
||||
// SaizBox - Sample Auxiliary Information Sizes Box (saiz) (in stbl or traf box)
|
||||
type SaizBox struct {
|
||||
FullBox
|
||||
AuxInfoType string // Used for Common Encryption Scheme (4-bytes uint32 according to spec)
|
||||
AuxInfoTypeParameter uint32
|
||||
DefaultSampleInfoSize uint8
|
||||
SampleCount uint32
|
||||
SampleInfo []byte
|
||||
DefaultSampleInfoSize uint8
|
||||
}
|
||||
|
||||
func (s *SaizBox) Decode(r io.Reader, size uint32) error {
|
||||
var fullbox FullBox
|
||||
if _, err := fullbox.Decode(r); err != nil {
|
||||
return err
|
||||
func CreateSaizBox(auxInfoType string, auxInfoTypeParameter uint32, defaultSampleInfoSize uint8, sampleInfo []byte) *SaizBox {
|
||||
flags := uint32(0)
|
||||
size := uint32(FullBoxLen + 5) // base size + defaultSampleInfoSize(1) + sampleCount(4)
|
||||
if len(auxInfoType) > 0 {
|
||||
flags |= 0x01
|
||||
size += 8 // auxInfoType(4) + auxInfoTypeParameter(4)
|
||||
}
|
||||
buf := make([]byte, size-12)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return err
|
||||
if defaultSampleInfoSize == 0 {
|
||||
size += uint32(len(sampleInfo))
|
||||
}
|
||||
var n int
|
||||
flags := uint32(fullbox.Flags[0])<<16 | uint32(fullbox.Flags[1])<<8 | uint32(fullbox.Flags[2])
|
||||
if flags&0x01 != 0 {
|
||||
s.AuxInfoType = string(buf[n : n+4])
|
||||
n += 4
|
||||
s.AuxInfoTypeParameter = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
return &SaizBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSAIZ,
|
||||
size: size,
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{byte(flags >> 16), byte(flags >> 8), byte(flags)},
|
||||
},
|
||||
AuxInfoType: auxInfoType,
|
||||
AuxInfoTypeParameter: auxInfoTypeParameter,
|
||||
DefaultSampleInfoSize: defaultSampleInfoSize,
|
||||
SampleCount: uint32(len(sampleInfo)),
|
||||
SampleInfo: sampleInfo,
|
||||
}
|
||||
s.DefaultSampleInfoSize = buf[n]
|
||||
n += 1
|
||||
}
|
||||
|
||||
s.SampleCount = binary.BigEndian.Uint32(buf[n:])
|
||||
func (box *SaizBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
|
||||
if flags&0x01 != 0 {
|
||||
copy(tmp[:4], []byte(box.AuxInfoType))
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint32(tmp[:4], box.AuxInfoTypeParameter)
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 8
|
||||
}
|
||||
|
||||
if _, err = w.Write([]byte{box.DefaultSampleInfoSize}); err != nil {
|
||||
return
|
||||
}
|
||||
n++
|
||||
|
||||
binary.BigEndian.PutUint32(tmp[:4], box.SampleCount)
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 4
|
||||
|
||||
if s.DefaultSampleInfoSize == 0 {
|
||||
for i := 0; i < int(s.SampleCount); i++ {
|
||||
s.SampleInfo = append(s.SampleInfo, buf[n])
|
||||
n += 1
|
||||
if box.DefaultSampleInfoSize == 0 && len(box.SampleInfo) > 0 {
|
||||
nn, err := w.Write(box.SampleInfo)
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
return nil
|
||||
return
|
||||
}
|
||||
|
||||
func (box *SaizBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 5 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
n := 0
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
if flags&0x01 != 0 {
|
||||
if len(buf) < n+8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.AuxInfoType = string(buf[n : n+4])
|
||||
n += 4
|
||||
box.AuxInfoTypeParameter = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
|
||||
box.DefaultSampleInfoSize = buf[n]
|
||||
n++
|
||||
|
||||
box.SampleCount = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
if box.DefaultSampleInfoSize == 0 {
|
||||
if len(buf) < n+int(box.SampleCount) {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.SampleInfo = make([]byte, box.SampleCount)
|
||||
copy(box.SampleInfo, buf[n:n+int(box.SampleCount)])
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*SaizBox](TypeSAIZ)
|
||||
}
|
||||
|
||||
+100
-30
@@ -11,49 +11,119 @@ const (
|
||||
|
||||
// SencBox - Sample Encryption Box (senc) (in trak or traf box)
|
||||
// See ISO/IEC 23001-7 Section 7.2 and CMAF specification
|
||||
// Full Box + SampleCount
|
||||
type SencBox struct {
|
||||
FullBox
|
||||
SampleCount uint32
|
||||
PerSampleIVSize uint32
|
||||
EntryList []SencEntry
|
||||
}
|
||||
|
||||
func (senc *SencBox) Decode(r io.Reader, size uint32, perSampleIVSize uint8) (offset int, err error) {
|
||||
var sencBox FullBox
|
||||
if offset, err = sencBox.Decode(r); err != nil {
|
||||
func CreateSencBox(perSampleIVSize uint32, entries []SencEntry, useSubSample bool) *SencBox {
|
||||
flags := uint32(0)
|
||||
if useSubSample {
|
||||
flags |= UseSubsampleEncryption
|
||||
}
|
||||
|
||||
size := uint32(FullBoxLen + 4) // base size + SampleCount
|
||||
for _, entry := range entries {
|
||||
size += uint32(len(entry.IV))
|
||||
if useSubSample {
|
||||
size += 2 // subsample count
|
||||
size += uint32(len(entry.SubSamples) * 6) // each subsample is 6 bytes
|
||||
}
|
||||
}
|
||||
|
||||
return &SencBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSENC,
|
||||
size: size,
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{byte(flags >> 16), byte(flags >> 8), byte(flags)},
|
||||
},
|
||||
SampleCount: uint32(len(entries)),
|
||||
PerSampleIVSize: perSampleIVSize,
|
||||
EntryList: entries,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *SencBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [6]byte
|
||||
binary.BigEndian.PutUint32(tmp[:4], box.SampleCount)
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
senc.PerSampleIVSize = uint32(perSampleIVSize)
|
||||
buf := make([]byte, size-12)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n := 0
|
||||
senc.SampleCount = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
sencFlags := uint32(sencBox.Flags[0])<<16 | uint32(sencBox.Flags[1])<<8 | uint32(sencBox.Flags[2])
|
||||
n = 4
|
||||
|
||||
senc.EntryList = make([]SencEntry, senc.SampleCount)
|
||||
for i := 0; i < int(senc.SampleCount); i++ {
|
||||
senc.EntryList[i].IV = buf[n : n+int(senc.PerSampleIVSize)]
|
||||
n += int(senc.PerSampleIVSize)
|
||||
|
||||
if sencFlags&UseSubsampleEncryption <= 0 {
|
||||
continue
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
for _, entry := range box.EntryList {
|
||||
if _, err = w.Write(entry.IV); err != nil {
|
||||
return
|
||||
}
|
||||
n += int64(len(entry.IV))
|
||||
|
||||
subsampleCount := binary.BigEndian.Uint16(buf[n:])
|
||||
n += 2
|
||||
|
||||
senc.EntryList[i].SubSamples = make([]SubSampleEntry, subsampleCount)
|
||||
for j := uint16(0); j < subsampleCount; j++ {
|
||||
senc.EntryList[i].SubSamples[j].BytesOfClearData = binary.BigEndian.Uint16(buf[n:])
|
||||
if flags&UseSubsampleEncryption != 0 {
|
||||
binary.BigEndian.PutUint16(tmp[:2], uint16(len(entry.SubSamples)))
|
||||
if _, err = w.Write(tmp[:2]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 2
|
||||
senc.EntryList[i].SubSamples[j].BytesOfProtectedData = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
for _, subsample := range entry.SubSamples {
|
||||
binary.BigEndian.PutUint16(tmp[:2], subsample.BytesOfClearData)
|
||||
binary.BigEndian.PutUint32(tmp[2:], subsample.BytesOfProtectedData)
|
||||
if _, err = w.Write(tmp[:]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 6
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
offset += n
|
||||
return
|
||||
}
|
||||
|
||||
func (box *SencBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
n := 0
|
||||
box.SampleCount = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
box.EntryList = make([]SencEntry, box.SampleCount)
|
||||
|
||||
for i := uint32(0); i < box.SampleCount; i++ {
|
||||
if len(buf) < n+int(box.PerSampleIVSize) {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.EntryList[i].IV = make([]byte, box.PerSampleIVSize)
|
||||
copy(box.EntryList[i].IV, buf[n:n+int(box.PerSampleIVSize)])
|
||||
n += int(box.PerSampleIVSize)
|
||||
|
||||
if flags&UseSubsampleEncryption != 0 {
|
||||
if len(buf) < n+2 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
subsampleCount := binary.BigEndian.Uint16(buf[n:])
|
||||
n += 2
|
||||
|
||||
if len(buf) < n+int(subsampleCount)*6 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.EntryList[i].SubSamples = make([]SubSampleEntry, subsampleCount)
|
||||
for j := uint16(0); j < subsampleCount; j++ {
|
||||
box.EntryList[i].SubSamples[j].BytesOfClearData = binary.BigEndian.Uint16(buf[n:])
|
||||
n += 2
|
||||
box.EntryList[i].SubSamples[j].BytesOfProtectedData = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
}
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*SencBox](TypeSENC)
|
||||
}
|
||||
|
||||
+145
-76
@@ -39,100 +39,169 @@ type SidxEntry struct {
|
||||
}
|
||||
|
||||
type SegmentIndexBox struct {
|
||||
Box *FullBox
|
||||
FullBox
|
||||
ReferenceID uint32
|
||||
TimeScale uint32
|
||||
EarliestPresentationTime uint64
|
||||
FirstOffset uint64
|
||||
ReferenceCount uint16
|
||||
Entrys []SidxEntry
|
||||
Entries []SidxEntry
|
||||
}
|
||||
|
||||
func NewSegmentIndexBox() *SegmentIndexBox {
|
||||
return &SegmentIndexBox{
|
||||
Box: NewFullBox(TypeSIDX, 1),
|
||||
func CreateSegmentIndexBox(referenceID uint32, timeScale uint32, earliestPresentationTime uint64, firstOffset uint64, entries []SidxEntry) *SegmentIndexBox {
|
||||
version := uint8(0)
|
||||
if earliestPresentationTime > 0xFFFFFFFF || firstOffset > 0xFFFFFFFF {
|
||||
version = 1
|
||||
}
|
||||
}
|
||||
|
||||
func (sidx *SegmentIndexBox) Size() uint64 {
|
||||
return sidx.Box.Size() + 28 + uint64(len(sidx.Entrys)*12)
|
||||
}
|
||||
|
||||
func (sidx *SegmentIndexBox) Decode(r io.Reader) (offset int, err error) {
|
||||
if offset, err = sidx.Box.Decode(r); err != nil {
|
||||
return
|
||||
}
|
||||
buf := make([]byte, sidx.Box.Box.Size-12)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
n := 0
|
||||
sidx.ReferenceID = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
sidx.TimeScale = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
if sidx.Box.Version == 0 {
|
||||
sidx.EarliestPresentationTime = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
sidx.FirstOffset = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
size := uint32(FullBoxLen + 12) // base size + referenceID(4) + timeScale(4) + reserved(2) + referenceCount(2)
|
||||
if version == 1 {
|
||||
size += 16 // earliestPresentationTime(8) + firstOffset(8)
|
||||
} else {
|
||||
sidx.EarliestPresentationTime = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
sidx.FirstOffset = binary.BigEndian.Uint64(buf[n:])
|
||||
size += 8 // earliestPresentationTime(4) + firstOffset(4)
|
||||
}
|
||||
size += uint32(len(entries) * 12) // each entry is 12 bytes
|
||||
|
||||
return &SegmentIndexBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSIDX,
|
||||
size: size,
|
||||
},
|
||||
Version: version,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
ReferenceID: referenceID,
|
||||
TimeScale: timeScale,
|
||||
EarliestPresentationTime: earliestPresentationTime,
|
||||
FirstOffset: firstOffset,
|
||||
ReferenceCount: uint16(len(entries)),
|
||||
Entries: entries,
|
||||
}
|
||||
}
|
||||
|
||||
func (box *SegmentIndexBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
binary.BigEndian.PutUint32(tmp[:4], box.ReferenceID)
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
n = 4
|
||||
|
||||
binary.BigEndian.PutUint32(tmp[:4], box.TimeScale)
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 4
|
||||
|
||||
if box.Version == 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:4], uint32(box.EarliestPresentationTime))
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint32(tmp[:4], uint32(box.FirstOffset))
|
||||
if _, err = w.Write(tmp[:4]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 8
|
||||
} else {
|
||||
binary.BigEndian.PutUint64(tmp[:], box.EarliestPresentationTime)
|
||||
if _, err = w.Write(tmp[:]); err != nil {
|
||||
return
|
||||
}
|
||||
binary.BigEndian.PutUint64(tmp[:], box.FirstOffset)
|
||||
if _, err = w.Write(tmp[:]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 16
|
||||
}
|
||||
|
||||
binary.BigEndian.PutUint16(tmp[:2], 0) // reserved
|
||||
if _, err = w.Write(tmp[:2]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 2
|
||||
sidx.ReferenceCount = binary.BigEndian.Uint16(buf[n:])
|
||||
n += 2
|
||||
sidx.Entrys = make([]SidxEntry, sidx.ReferenceCount)
|
||||
for i := 0; i < int(sidx.ReferenceCount); i++ {
|
||||
sidx.Entrys[i].ReferenceType = buf[n] >> 7
|
||||
buf[n] = buf[n] & 0x7F
|
||||
sidx.Entrys[i].ReferencedSize = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
sidx.Entrys[i].SubsegmentDuration = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
sidx.Entrys[i].StartsWithSAP = buf[n] >> 7
|
||||
sidx.Entrys[i].SAPType = buf[n] >> 4 & 0x07
|
||||
buf[n] = buf[n] & 0x0F
|
||||
sidx.Entrys[i].SAPDeltaTime = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
binary.BigEndian.PutUint16(tmp[:2], box.ReferenceCount)
|
||||
if _, err = w.Write(tmp[:2]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 2
|
||||
|
||||
for _, entry := range box.Entries {
|
||||
var entryBuf [12]byte
|
||||
entryBuf[0] = entry.ReferenceType << 7
|
||||
binary.BigEndian.PutUint32(entryBuf[:4], entry.ReferencedSize)
|
||||
entryBuf[0] &= 0x7F
|
||||
binary.BigEndian.PutUint32(entryBuf[4:], entry.SubsegmentDuration)
|
||||
entryBuf[8] = entry.StartsWithSAP << 7
|
||||
entryBuf[8] |= (entry.SAPType & 0x07) << 4
|
||||
binary.BigEndian.PutUint32(entryBuf[8:], entry.SAPDeltaTime)
|
||||
entryBuf[8] &= 0x0F
|
||||
|
||||
if _, err = w.Write(entryBuf[:]); err != nil {
|
||||
return
|
||||
}
|
||||
n += 12
|
||||
}
|
||||
offset += 4
|
||||
return
|
||||
}
|
||||
|
||||
func (sidx *SegmentIndexBox) Encode() (int, []byte) {
|
||||
sidx.Box.Box.Size = sidx.Size()
|
||||
offset, boxdata := sidx.Box.Encode()
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], sidx.ReferenceID)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], sidx.TimeScale)
|
||||
offset += 4
|
||||
if sidx.Box.Version == 0 {
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], uint32(sidx.EarliestPresentationTime))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], uint32(sidx.FirstOffset))
|
||||
offset += 4
|
||||
func (box *SegmentIndexBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
n := 0
|
||||
box.ReferenceID = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
box.TimeScale = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
if box.Version == 0 {
|
||||
if len(buf) < n+8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.EarliestPresentationTime = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
box.FirstOffset = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
} else {
|
||||
binary.BigEndian.PutUint64(boxdata[offset:], sidx.EarliestPresentationTime)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint64(boxdata[offset:], sidx.FirstOffset)
|
||||
offset += 8
|
||||
if len(buf) < n+16 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.EarliestPresentationTime = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
box.FirstOffset = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
}
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(boxdata[offset:], sidx.ReferenceCount)
|
||||
offset += 2
|
||||
for i := 0; i < int(sidx.ReferenceCount); i++ {
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], uint32(sidx.Entrys[i].ReferencedSize))
|
||||
boxdata[offset] = boxdata[offset]&0x7F | sidx.Entrys[i].ReferenceType<<7
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], sidx.Entrys[i].SubsegmentDuration)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], sidx.Entrys[i].SAPDeltaTime)
|
||||
boxdata[offset] = (boxdata[offset] & 0xF0) + sidx.Entrys[i].StartsWithSAP<<7 | (sidx.Entrys[i].SAPType&0x07)<<4
|
||||
offset += 4
|
||||
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
return offset, boxdata
|
||||
n += 2 // skip reserved
|
||||
box.ReferenceCount = binary.BigEndian.Uint16(buf[n:])
|
||||
n += 2
|
||||
|
||||
if len(buf) < n+int(box.ReferenceCount)*12 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Entries = make([]SidxEntry, box.ReferenceCount)
|
||||
for i := uint16(0); i < box.ReferenceCount; i++ {
|
||||
box.Entries[i].ReferenceType = buf[n] >> 7
|
||||
buf[n] = buf[n] & 0x7F
|
||||
box.Entries[i].ReferencedSize = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
box.Entries[i].SubsegmentDuration = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
box.Entries[i].StartsWithSAP = buf[n] >> 7
|
||||
box.Entries[i].SAPType = buf[n] >> 4 & 0x07
|
||||
buf[n] = buf[n] & 0x0F
|
||||
box.Entries[i].SAPDeltaTime = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*SegmentIndexBox](TypeSIDX)
|
||||
}
|
||||
|
||||
+32
-26
@@ -6,42 +6,48 @@ import (
|
||||
)
|
||||
|
||||
// aligned(8) class SoundMediaHeaderBox
|
||||
// extends FullBox(‘smhd’, version = 0, 0) {
|
||||
// extends FullBox('smhd', version = 0, 0) {
|
||||
// template int(16) balance = 0;
|
||||
// const unsigned int(16) reserved = 0;
|
||||
// }
|
||||
|
||||
type SoundMediaHeaderBox struct {
|
||||
FullBox
|
||||
Balance int16
|
||||
}
|
||||
|
||||
func NewSoundMediaHeaderBox() *SoundMediaHeaderBox {
|
||||
return &SoundMediaHeaderBox{}
|
||||
}
|
||||
|
||||
func (smhd *SoundMediaHeaderBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return 0, err
|
||||
func CreateSoundMediaHeaderBox() *SoundMediaHeaderBox {
|
||||
return &SoundMediaHeaderBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSMHD,
|
||||
size: uint32(FullBoxLen + 4),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
Balance: 0,
|
||||
}
|
||||
buf := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func (box *SoundMediaHeaderBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
binary.BigEndian.PutUint16(tmp[0:], uint16(box.Balance))
|
||||
// reserved already zeroed
|
||||
|
||||
nn, err := w.Write(tmp[:])
|
||||
n = int64(nn)
|
||||
return
|
||||
}
|
||||
|
||||
func (box *SoundMediaHeaderBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
smhd.Balance = int16(binary.BigEndian.Uint16(buf[:]))
|
||||
return 4, nil
|
||||
box.Balance = int16(binary.BigEndian.Uint16(buf[0:]))
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (smhd *SoundMediaHeaderBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeSMHD, 0)
|
||||
fullbox.Box.Size = FullBoxLen + 4
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint16(buf[offset:], uint16(smhd.Balance))
|
||||
return offset + 2, buf
|
||||
}
|
||||
|
||||
func MakeSmhdBox() []byte {
|
||||
smhd := NewSoundMediaHeaderBox()
|
||||
_, smhdbox := smhd.Encode()
|
||||
return smhdbox
|
||||
func init() {
|
||||
RegisterBox[*SoundMediaHeaderBox](TypeSMHD)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,64 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type SampleTable struct {
|
||||
STTS *TimeToSampleBox
|
||||
CTTS *CompositionOffsetBox
|
||||
STSC *SampleToChunkBox
|
||||
STSZ *SampleSizeBox
|
||||
STCO *ChunkOffsetBox
|
||||
STSS *SyncSampleBox
|
||||
STSD *STSDBox
|
||||
STTS *STTSBox
|
||||
CTTS *CTTSBox
|
||||
STSC *STSCBox
|
||||
STSZ *STSZBox
|
||||
STSS *STSSBox
|
||||
STCO *STCOBox
|
||||
}
|
||||
|
||||
type SampleTableBox struct {
|
||||
BaseBox
|
||||
SampleTable
|
||||
}
|
||||
|
||||
func (stbl *SampleTableBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, stbl.STSD, stbl.STTS, stbl.CTTS, stbl.STSC, stbl.STSZ, stbl.STSS, stbl.STCO)
|
||||
}
|
||||
|
||||
func (stbl *SampleTableBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
r := bytes.NewReader(buf)
|
||||
|
||||
for {
|
||||
box, err := ReadFrom(r)
|
||||
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
switch box := box.(type) {
|
||||
case *STSDBox:
|
||||
stbl.STSD = box
|
||||
|
||||
case *STTSBox:
|
||||
stbl.STTS = box
|
||||
case *CTTSBox:
|
||||
|
||||
stbl.CTTS = box
|
||||
case *STCOBox:
|
||||
stbl.STCO = box
|
||||
case *CO64Box:
|
||||
co64 := STCOBox(*box)
|
||||
stbl.STCO = &co64
|
||||
case *STSCBox:
|
||||
stbl.STSC = box
|
||||
|
||||
case *STSZBox:
|
||||
stbl.STSZ = box
|
||||
case *STSSBox:
|
||||
stbl.STSS = box
|
||||
}
|
||||
}
|
||||
return stbl, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*SampleTableBox](TypeSTBL)
|
||||
}
|
||||
|
||||
+94
-67
@@ -3,98 +3,125 @@ package box
|
||||
import (
|
||||
"encoding/binary"
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
// aligned(8) class ChunkOffsetBox
|
||||
// extends FullBox(‘stco’, version = 0, 0) {
|
||||
// extends FullBox('stco', version = 0, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// for (i=1; i <= entry_count; i++) {
|
||||
// unsigned int(32) chunk_offset;
|
||||
// }
|
||||
// }
|
||||
// aligned(8) class ChunkLargeOffsetBox
|
||||
// extends FullBox(‘co64’, version = 0, 0) {
|
||||
// extends FullBox('co64', version = 0, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// for (i=1; i <= entry_count; i++) {
|
||||
// unsigned int(64) chunk_offset;
|
||||
// }
|
||||
// }
|
||||
|
||||
type ChunkOffsetBox []uint64
|
||||
type STCOBox struct {
|
||||
FullBox
|
||||
Entries []uint64
|
||||
}
|
||||
|
||||
func (stco *ChunkOffsetBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
type CO64Box STCOBox
|
||||
|
||||
func CreateSTCOBox(entries []uint64) *STCOBox {
|
||||
return &STCOBox{
|
||||
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSTCO,
|
||||
size: uint32(FullBoxLen + 4 + len(entries)*4),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
|
||||
Entries: entries,
|
||||
}
|
||||
tmp := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, tmp); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func CreateCO64Box(entries []uint64) *CO64Box {
|
||||
return &CO64Box{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeCO64,
|
||||
size: uint32(FullBoxLen + 4 + len(entries)*8),
|
||||
},
|
||||
},
|
||||
|
||||
Entries: entries,
|
||||
}
|
||||
offset = 8
|
||||
l := binary.BigEndian.Uint32(tmp)
|
||||
*stco = make([]uint64, l)
|
||||
buf := make([]byte, l*4)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func (box *STCOBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
buf := make([]byte, 4+len(box.Entries)*4)
|
||||
|
||||
// Write entry count
|
||||
binary.BigEndian.PutUint32(buf[:4], uint32(len(box.Entries)))
|
||||
|
||||
// Write entries
|
||||
for i, chunkOffset := range box.Entries {
|
||||
binary.BigEndian.PutUint32(buf[4+i*4:], uint32(chunkOffset))
|
||||
}
|
||||
idx := 0
|
||||
for i := 0; i < int(l); i++ {
|
||||
(*stco)[i] = uint64(binary.BigEndian.Uint32(buf[idx:]))
|
||||
|
||||
_, err = w.Write(buf)
|
||||
return int64(len(buf)), err
|
||||
}
|
||||
|
||||
func (box *CO64Box) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
buffers := make(net.Buffers, 0, len(box.Entries)+1)
|
||||
|
||||
// Write entry count
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(len(box.Entries)))
|
||||
buffers = append(buffers, tmp[:])
|
||||
|
||||
// Write entries
|
||||
for _, chunkOffset := range box.Entries {
|
||||
binary.BigEndian.PutUint64(tmp[:], chunkOffset)
|
||||
buffers = append(buffers, tmp[:])
|
||||
}
|
||||
|
||||
return buffers.WriteTo(w)
|
||||
}
|
||||
|
||||
func (box *STCOBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
entryCount := binary.BigEndian.Uint32(buf[:4])
|
||||
box.Entries = make([]uint64, entryCount)
|
||||
|
||||
if len(buf) < 4+int(entryCount)*4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
idx := 4
|
||||
for i := 0; i < int(entryCount); i++ {
|
||||
box.Entries[i] = uint64(binary.BigEndian.Uint32(buf[idx:]))
|
||||
idx += 4
|
||||
}
|
||||
offset += idx
|
||||
return
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (stco ChunkOffsetBox) Encode() (int, []byte) {
|
||||
var fullbox *FullBox
|
||||
l := len(stco)
|
||||
if stco[l-1] > 0xFFFFFFFF {
|
||||
fullbox = NewFullBox(TypeCO64, 0)
|
||||
fullbox.Box.Size = FullBoxLen + 4 + 8*uint64(l)
|
||||
} else {
|
||||
fullbox = NewFullBox(TypeSTCO, 0)
|
||||
fullbox.Box.Size = FullBoxLen + 4 + 4*uint64(l)
|
||||
}
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(l))
|
||||
offset += 4
|
||||
for i := 0; i < int(l); i++ {
|
||||
if fullbox.Box.Type == TypeCO64 {
|
||||
binary.BigEndian.PutUint64(buf[offset:], uint64(stco[i]))
|
||||
offset += 8
|
||||
} else {
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(stco[i]))
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
return offset, buf
|
||||
}
|
||||
func (box *CO64Box) Unmarshal(buf []byte) (IBox, error) {
|
||||
entryCount := binary.BigEndian.Uint32(buf[:4])
|
||||
box.Entries = make([]uint64, entryCount)
|
||||
|
||||
type ChunkLargeOffsetBox ChunkOffsetBox
|
||||
if len(buf) < 4+int(entryCount)*8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
func (co64 *ChunkLargeOffsetBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
}
|
||||
tmp := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, tmp); err != nil {
|
||||
return
|
||||
}
|
||||
offset = 8
|
||||
l := binary.BigEndian.Uint32(tmp)
|
||||
*co64 = make([]uint64, l)
|
||||
buf := make([]byte, l*8)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
idx := 0
|
||||
for i := 0; i < int(l); i++ {
|
||||
(*co64)[i] = binary.BigEndian.Uint64(buf[idx:])
|
||||
idx := 4
|
||||
for i := 0; i < int(entryCount); i++ {
|
||||
box.Entries[i] = binary.BigEndian.Uint64(buf[idx:])
|
||||
idx += 8
|
||||
}
|
||||
offset += idx
|
||||
return
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*STCOBox](TypeSTCO)
|
||||
RegisterBox[*CO64Box](TypeCO64)
|
||||
}
|
||||
|
||||
+46
-44
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class SampleToChunkBox extends FullBox(‘stsc’, version = 0, 0) {
|
||||
// aligned(8) class SampleToChunkBox extends FullBox('stsc', version = 0, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// for (i=1; i <= entry_count; i++) {
|
||||
// unsigned int(32) first_chunk;
|
||||
@@ -14,59 +14,61 @@ import (
|
||||
// }
|
||||
// }
|
||||
|
||||
type SampleToChunkBox []STSCEntry
|
||||
|
||||
func NewSampleToChunkBox() *SampleToChunkBox {
|
||||
return &SampleToChunkBox{}
|
||||
type STSCBox struct {
|
||||
FullBox
|
||||
Entries []STSCEntry
|
||||
}
|
||||
|
||||
func (stsc SampleToChunkBox) Size() uint64 {
|
||||
return FullBoxLen + 4 + 12*uint64(len(stsc))
|
||||
func CreateSTSCBox(entries []STSCEntry) *STSCBox {
|
||||
return &STSCBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSTSC,
|
||||
size: uint32(FullBoxLen + 4 + len(entries)*12),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
Entries: entries,
|
||||
}
|
||||
}
|
||||
|
||||
func (stsc *SampleToChunkBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
func (box *STSCBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
buf := make([]byte, 4+len(box.Entries)*12)
|
||||
// Write entry count
|
||||
binary.BigEndian.PutUint32(buf[:4], uint32(len(box.Entries)))
|
||||
|
||||
// Write entries
|
||||
for i, entry := range box.Entries {
|
||||
binary.BigEndian.PutUint32(buf[4+i*12:], entry.FirstChunk)
|
||||
binary.BigEndian.PutUint32(buf[4+i*12+4:], entry.SamplesPerChunk)
|
||||
binary.BigEndian.PutUint32(buf[4+i*12+8:], entry.SampleDescriptionIndex)
|
||||
}
|
||||
tmp := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, tmp); err != nil {
|
||||
return
|
||||
|
||||
_, err = w.Write(buf)
|
||||
return int64(len(buf)), err
|
||||
}
|
||||
|
||||
func (box *STSCBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
entryCount := binary.BigEndian.Uint32(buf[:4])
|
||||
box.Entries = make([]STSCEntry, entryCount)
|
||||
|
||||
if len(buf) < 4+int(entryCount)*12 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
l := binary.BigEndian.Uint32(tmp)
|
||||
*stsc = make([]STSCEntry, l)
|
||||
buf := make([]byte, l*12)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
offset = 8
|
||||
idx := 0
|
||||
for i := 0; i < int(l); i++ {
|
||||
entry := &(*stsc)[i]
|
||||
entry.FirstChunk = binary.BigEndian.Uint32(buf[idx:])
|
||||
|
||||
idx := 4
|
||||
for i := 0; i < int(entryCount); i++ {
|
||||
box.Entries[i].FirstChunk = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
entry.SamplesPerChunk = binary.BigEndian.Uint32(buf[idx:])
|
||||
box.Entries[i].SamplesPerChunk = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
entry.SampleDescriptionIndex = binary.BigEndian.Uint32(buf[idx:])
|
||||
box.Entries[i].SampleDescriptionIndex = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
}
|
||||
offset += idx
|
||||
return
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (stsc SampleToChunkBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeSTSC, 0)
|
||||
fullbox.Box.Size = stsc.Size()
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(len(stsc)))
|
||||
offset += 4
|
||||
for _, entry := range stsc {
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.FirstChunk)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.SamplesPerChunk)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.SampleDescriptionIndex)
|
||||
offset += 4
|
||||
}
|
||||
return offset, buf
|
||||
func init() {
|
||||
RegisterBox[*STSCBox](TypeSTSC)
|
||||
}
|
||||
|
||||
+213
-162
@@ -1,6 +1,7 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"io"
|
||||
)
|
||||
@@ -11,39 +12,8 @@ import (
|
||||
// }
|
||||
|
||||
type SampleEntry struct {
|
||||
Type [4]byte
|
||||
data_reference_index uint16
|
||||
}
|
||||
|
||||
func NewSampleEntry(format [4]byte) *SampleEntry {
|
||||
return &SampleEntry{
|
||||
Type: format,
|
||||
data_reference_index: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *SampleEntry) Size() uint64 {
|
||||
return BasicBoxLen + 8
|
||||
}
|
||||
|
||||
func (entry *SampleEntry) Decode(r io.Reader) (offset int, err error) {
|
||||
|
||||
buf := make([]byte, 8)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
offset = 6
|
||||
entry.data_reference_index = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
return
|
||||
}
|
||||
|
||||
func (entry *SampleEntry) Encode(size uint64) (int, []byte) {
|
||||
offset, buf := (&BasicBox{Type: entry.Type, Size: size}).Encode()
|
||||
offset += 6
|
||||
binary.BigEndian.PutUint16(buf[offset:], entry.data_reference_index)
|
||||
offset += 2
|
||||
return offset, buf
|
||||
BaseBox
|
||||
DataReferenceIndex uint16
|
||||
}
|
||||
|
||||
// class HintSampleEntry() extends SampleEntry (protocol) {
|
||||
@@ -51,8 +21,9 @@ func (entry *SampleEntry) Encode(size uint64) (int, []byte) {
|
||||
// }
|
||||
|
||||
type HintSampleEntry struct {
|
||||
Entry *SampleEntry
|
||||
Data byte
|
||||
SampleEntry
|
||||
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// class AudioSampleEntry(codingname) extends SampleEntry (codingname){
|
||||
@@ -65,56 +36,87 @@ type HintSampleEntry struct {
|
||||
// }
|
||||
|
||||
type AudioSampleEntry struct {
|
||||
*SampleEntry
|
||||
SampleEntry
|
||||
Version uint16 // ffmpeg mov.c mov_parse_stsd_audio
|
||||
ChannelCount uint16
|
||||
SampleSize uint16
|
||||
Samplerate uint32
|
||||
ExtraData IBox
|
||||
}
|
||||
|
||||
func NewAudioSampleEntry(format [4]byte) *AudioSampleEntry {
|
||||
func (s *SampleEntry) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
binary.BigEndian.PutUint16(tmp[6:], s.DataReferenceIndex)
|
||||
_, err = w.Write(tmp[:])
|
||||
return 8, err
|
||||
}
|
||||
|
||||
func (s *SampleEntry) Unmarshal(buf []byte) {
|
||||
s.DataReferenceIndex = binary.BigEndian.Uint16(buf[6:])
|
||||
}
|
||||
|
||||
func CreateAudioSampleEntry(codecName BoxType, channelCount uint16, sampleSize uint16, samplerate uint32, extraData IBox) *AudioSampleEntry {
|
||||
size := 28 + BasicBoxLen
|
||||
if extraData != nil {
|
||||
size += int(extraData.Size())
|
||||
}
|
||||
return &AudioSampleEntry{
|
||||
SampleEntry: NewSampleEntry(format),
|
||||
SampleEntry: SampleEntry{
|
||||
BaseBox: BaseBox{
|
||||
typ: codecName,
|
||||
size: uint32(size),
|
||||
},
|
||||
DataReferenceIndex: 1,
|
||||
},
|
||||
Version: 0,
|
||||
ChannelCount: channelCount,
|
||||
SampleSize: sampleSize,
|
||||
Samplerate: samplerate,
|
||||
ExtraData: extraData,
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *AudioSampleEntry) Decode(r io.Reader) (offset int, err error) {
|
||||
if _, err = entry.SampleEntry.Decode(r); err != nil {
|
||||
func (audio *AudioSampleEntry) WriteTo(w io.Writer) (n int64, err error) {
|
||||
n, err = audio.SampleEntry.WriteTo(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := make([]byte, 20)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
var buf [20]byte
|
||||
binary.BigEndian.PutUint16(buf[8:], audio.Version)
|
||||
binary.BigEndian.PutUint16(buf[10:], audio.ChannelCount)
|
||||
binary.BigEndian.PutUint16(buf[12:], audio.SampleSize)
|
||||
binary.BigEndian.PutUint32(buf[16:], audio.Samplerate<<16)
|
||||
_, err = w.Write(buf[:])
|
||||
n += 20
|
||||
var nn int64
|
||||
if audio.ExtraData != nil {
|
||||
nn, err = WriteTo(w, audio.ExtraData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += nn
|
||||
}
|
||||
offset = 0
|
||||
entry.Version = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset = 8
|
||||
entry.ChannelCount = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
entry.SampleSize = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
offset += 4
|
||||
entry.Samplerate = binary.BigEndian.Uint32(buf[offset:])
|
||||
entry.Samplerate = entry.Samplerate >> 16
|
||||
offset += 4
|
||||
return
|
||||
|
||||
}
|
||||
|
||||
func (entry *AudioSampleEntry) Size() uint64 {
|
||||
return entry.SampleEntry.Size() + 20
|
||||
}
|
||||
func (audio *AudioSampleEntry) Unmarshal(buf []byte) (IBox, error) {
|
||||
audio.SampleEntry.Unmarshal(buf)
|
||||
buf = buf[8:]
|
||||
audio.Version = binary.BigEndian.Uint16(buf[0:])
|
||||
audio.ChannelCount = binary.BigEndian.Uint16(buf[8:])
|
||||
audio.SampleSize = binary.BigEndian.Uint16(buf[10:])
|
||||
|
||||
func (entry *AudioSampleEntry) Encode(size uint64) (int, []byte) {
|
||||
offset, buf := entry.SampleEntry.Encode(size)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint16(buf[offset:], entry.ChannelCount)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], entry.SampleSize)
|
||||
offset += 2
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.Samplerate<<16)
|
||||
offset += 4
|
||||
return offset, buf
|
||||
audio.Samplerate = binary.BigEndian.Uint32(buf[16:]) >> 16
|
||||
if len(buf) > 20 {
|
||||
box, err := ReadFrom(bytes.NewReader(buf[20:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
audio.ExtraData = box
|
||||
}
|
||||
|
||||
return audio, nil
|
||||
}
|
||||
|
||||
// class VisualSampleEntry(codingname) extends SampleEntry (codingname){
|
||||
@@ -136,71 +138,88 @@ func (entry *AudioSampleEntry) Encode(size uint64) (int, []byte) {
|
||||
// }
|
||||
|
||||
type VisualSampleEntry struct {
|
||||
*SampleEntry
|
||||
SampleEntry
|
||||
Width, Height uint16
|
||||
horizresolution, vertresolution uint32
|
||||
frame_count uint16
|
||||
compressorname [32]byte
|
||||
Horizresolution, Vertresolution uint32
|
||||
FrameCount uint16
|
||||
Compressorname [32]byte
|
||||
Depth uint16
|
||||
ExtraData IBox
|
||||
}
|
||||
|
||||
func NewVisualSampleEntry(format [4]byte) *VisualSampleEntry {
|
||||
func CreateVisualSampleEntry(codecName BoxType, width, height uint16, extraData IBox) *VisualSampleEntry {
|
||||
size := 78 + BasicBoxLen
|
||||
if extraData != nil {
|
||||
size += int(extraData.Size())
|
||||
}
|
||||
return &VisualSampleEntry{
|
||||
SampleEntry: NewSampleEntry(format),
|
||||
horizresolution: 0x00480000,
|
||||
vertresolution: 0x00480000,
|
||||
frame_count: 1,
|
||||
SampleEntry: SampleEntry{
|
||||
BaseBox: BaseBox{
|
||||
typ: codecName,
|
||||
size: uint32(size),
|
||||
},
|
||||
DataReferenceIndex: 1,
|
||||
},
|
||||
Width: width,
|
||||
Height: height,
|
||||
Horizresolution: 0x00480000,
|
||||
Vertresolution: 0x00480000,
|
||||
FrameCount: 1,
|
||||
Depth: 0x0018,
|
||||
ExtraData: extraData,
|
||||
}
|
||||
}
|
||||
|
||||
func (entry *VisualSampleEntry) Size() uint64 {
|
||||
return entry.SampleEntry.Size() + 70
|
||||
}
|
||||
|
||||
func (entry *VisualSampleEntry) Decode(r io.Reader) (offset int, err error) {
|
||||
if _, err = entry.SampleEntry.Decode(r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf := make([]byte, 70)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
func (visual *VisualSampleEntry) WriteTo(w io.Writer) (n int64, err error) {
|
||||
n, err = visual.SampleEntry.WriteTo(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
offset = 16
|
||||
entry.Width = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
entry.Height = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
entry.horizresolution = binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 4
|
||||
entry.vertresolution = binary.BigEndian.Uint32(buf[offset:])
|
||||
offset += 8
|
||||
entry.frame_count = binary.BigEndian.Uint16(buf[offset:])
|
||||
offset += 2
|
||||
copy(entry.compressorname[:], buf[offset:offset+32])
|
||||
offset += 32
|
||||
offset += 4
|
||||
|
||||
var buf [70]byte // 16(pre_defined) + 2(width) + 2(height) + 4(horiz) + 4(vert) + 4(reserved) + 2(frame) + 32(compressor) + 2(depth) + 2(pre_defined)
|
||||
binary.BigEndian.PutUint16(buf[16:], visual.Width)
|
||||
binary.BigEndian.PutUint16(buf[18:], visual.Height)
|
||||
binary.BigEndian.PutUint32(buf[20:], visual.Horizresolution)
|
||||
binary.BigEndian.PutUint32(buf[24:], visual.Vertresolution)
|
||||
binary.BigEndian.PutUint16(buf[32:], visual.FrameCount)
|
||||
copy(buf[34:66], visual.Compressorname[:])
|
||||
binary.BigEndian.PutUint16(buf[66:], visual.Depth)
|
||||
binary.BigEndian.PutUint16(buf[68:], 0xFFFF) // pre_defined = -1
|
||||
|
||||
_, err = w.Write(buf[:])
|
||||
n += 70
|
||||
var nn int64
|
||||
if visual.ExtraData != nil {
|
||||
nn, err = WriteTo(w, visual.ExtraData)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += nn
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (entry *VisualSampleEntry) Encode(size uint64) (int, []byte) {
|
||||
offset, buf := entry.SampleEntry.Encode(size)
|
||||
offset += 16
|
||||
binary.BigEndian.PutUint16(buf[offset:], entry.Width)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], entry.Height)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.horizresolution)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.vertresolution)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint16(buf[offset:], entry.frame_count)
|
||||
offset += 2
|
||||
copy(buf[offset:offset+32], entry.compressorname[:])
|
||||
offset += 32
|
||||
binary.BigEndian.PutUint16(buf[offset:], 0x0018)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], 0xFFFF)
|
||||
offset += 2
|
||||
return offset, buf
|
||||
func (visual *VisualSampleEntry) Unmarshal(buf []byte) (IBox, error) {
|
||||
visual.SampleEntry.Unmarshal(buf)
|
||||
buf = buf[24:] // Skip 8 bytes from SampleEntry + 16 bytes pre_defined
|
||||
visual.Width = binary.BigEndian.Uint16(buf[0:])
|
||||
visual.Height = binary.BigEndian.Uint16(buf[2:])
|
||||
|
||||
visual.Horizresolution = binary.BigEndian.Uint32(buf[4:])
|
||||
visual.Vertresolution = binary.BigEndian.Uint32(buf[8:])
|
||||
visual.FrameCount = binary.BigEndian.Uint16(buf[16:])
|
||||
copy(visual.Compressorname[:], buf[18:50])
|
||||
visual.Depth = binary.BigEndian.Uint16(buf[50:])
|
||||
|
||||
if len(buf) > 52 {
|
||||
box, err := ReadFrom(bytes.NewReader(buf[52:]))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
visual.ExtraData = box
|
||||
}
|
||||
|
||||
return visual, nil
|
||||
}
|
||||
|
||||
// aligned(8) class SampleDescriptionBox (unsigned int(32) handler_type) extends FullBox('stsd', 0, 0){
|
||||
@@ -208,16 +227,16 @@ func (entry *VisualSampleEntry) Encode(size uint64) (int, []byte) {
|
||||
// unsigned int(32) entry_count;
|
||||
// for (i = 1 ; i <= entry_count ; i++){
|
||||
// switch (handler_type){
|
||||
// case ‘soun’: // for audio tracks
|
||||
// case 'soun': // for audio tracks
|
||||
// AudioSampleEntry();
|
||||
// break;
|
||||
// case ‘vide’: // for video tracks
|
||||
// case 'vide': // for video tracks
|
||||
// VisualSampleEntry();
|
||||
// break;
|
||||
// case ‘hint’: // Hint track
|
||||
// case 'hint': // Hint track
|
||||
// HintSampleEntry();
|
||||
// break;
|
||||
// case ‘meta’: // Metadata track
|
||||
// case 'meta': // Metadata track
|
||||
// MetadataSampleEntry();
|
||||
// break;
|
||||
// }
|
||||
@@ -231,50 +250,82 @@ const (
|
||||
SAMPLE_VIDEO
|
||||
)
|
||||
|
||||
type SampleDescriptionBox uint32
|
||||
type STSDBox struct {
|
||||
FullBox
|
||||
Entries []IBox
|
||||
}
|
||||
|
||||
func (stsd *SampleDescriptionBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
func CreateSTSDBox(entries ...IBox) *STSDBox {
|
||||
childSize := 0
|
||||
for _, entry := range entries {
|
||||
childSize += int(entry.Size())
|
||||
}
|
||||
return &STSDBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSTSD,
|
||||
size: uint32(FullBoxLen + 4 + childSize),
|
||||
},
|
||||
},
|
||||
Entries: entries,
|
||||
}
|
||||
}
|
||||
|
||||
func (stsd *STSDBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
stsd.Entries = make([]IBox, 0, binary.BigEndian.Uint32(buf))
|
||||
r := bytes.NewReader(buf[4:])
|
||||
for {
|
||||
box, err := ReadFrom(r)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
stsd.Entries = append(stsd.Entries, box)
|
||||
}
|
||||
return stsd, nil
|
||||
}
|
||||
|
||||
func (stsd *STSDBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
var nn int64
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(len(stsd.Entries)))
|
||||
_, err = w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
buf := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
n += 4
|
||||
for _, entry := range stsd.Entries {
|
||||
nn, err = WriteTo(w, entry)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += nn
|
||||
}
|
||||
return int64(n), nil
|
||||
}
|
||||
|
||||
func (h *HintSampleEntry) Unmarshal(buf []byte) (IBox, error) {
|
||||
h.SampleEntry.Unmarshal(buf)
|
||||
h.Data = buf[8:]
|
||||
return h, nil
|
||||
}
|
||||
|
||||
func (h *HintSampleEntry) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var offset int64
|
||||
offset, err = h.SampleEntry.WriteTo(w)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
offset += 4
|
||||
*stsd = SampleDescriptionBox(binary.BigEndian.Uint32(buf))
|
||||
return
|
||||
_, err = w.Write(h.Data)
|
||||
return offset + int64(len(h.Data)), err
|
||||
}
|
||||
|
||||
func (entry SampleDescriptionBox) Encode(size uint64) (int, []byte) {
|
||||
fullbox := FullBox{Box: NewBasicBox(TypeSTSD)}
|
||||
fullbox.Box.Size = size
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(entry))
|
||||
offset += 4
|
||||
return offset, buf
|
||||
}
|
||||
|
||||
func MakeAvcCBox(extraData []byte) []byte {
|
||||
offset, boxdata := (&BasicBox{Type: TypeAVCC, Size: BasicBoxLen + uint64(len(extraData))}).Encode()
|
||||
copy(boxdata[offset:], extraData)
|
||||
return boxdata
|
||||
}
|
||||
|
||||
func MakeHvcCBox(extraData []byte) []byte {
|
||||
offset, boxdata := (&BasicBox{Type: TypeHVCC, Size: BasicBoxLen + uint64(len(extraData))}).Encode()
|
||||
copy(boxdata[offset:], extraData)
|
||||
return boxdata
|
||||
}
|
||||
|
||||
func MakeEsdsBox(tid uint32, cid MP4_CODEC_TYPE, extraData []byte) []byte {
|
||||
esd := makeESDescriptor(uint16(tid), cid, extraData)
|
||||
esds := FullBox{Box: NewBasicBox(TypeESDS), Version: 0}
|
||||
esds.Box.Size = esds.Size() + uint64(len(esd))
|
||||
offset, esdsBox := esds.Encode()
|
||||
copy(esdsBox[offset:], esd)
|
||||
return esdsBox
|
||||
func init() {
|
||||
RegisterBox[*STSDBox](TypeSTSD)
|
||||
RegisterBox[*AudioSampleEntry](TypeMP4A, TypeULAW, TypeALAW, TypeOPUS, TypeENCA)
|
||||
RegisterBox[*VisualSampleEntry](TypeAVC1, TypeHVC1, TypeHEV1, TypeENCV)
|
||||
RegisterBox[*HintSampleEntry](TypeHINT)
|
||||
RegisterBox[*DataBox](TypeAVCC, TypeHVCC)
|
||||
// RegisterBox[*MetadataSampleEntry](TypeMETA)
|
||||
}
|
||||
|
||||
//ffmpeg mov_write_wave_tag
|
||||
|
||||
+38
-32
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class SyncSampleBox extends FullBox(‘stss’, version = 0, 0) {
|
||||
// aligned(8) class SyncSampleBox extends FullBox('stss', version = 0, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// int i;
|
||||
// for (i=0; i < entry_count; i++) {
|
||||
@@ -13,41 +13,47 @@ import (
|
||||
// }
|
||||
// }
|
||||
|
||||
type SyncSampleBox []uint32
|
||||
|
||||
func (stss SyncSampleBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeSTSS, 0)
|
||||
fullbox.Box.Size = FullBoxLen + 4 + 4*uint64(len(stss))
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(len(stss)))
|
||||
offset += 4
|
||||
for _, sampleNumber := range stss {
|
||||
binary.BigEndian.PutUint32(buf[offset:], sampleNumber)
|
||||
offset += 4
|
||||
}
|
||||
return offset, buf
|
||||
type STSSBox struct {
|
||||
FullBox
|
||||
Entries []uint32
|
||||
}
|
||||
|
||||
func (stss *SyncSampleBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
func CreateSTSSBox(entries []uint32) *STSSBox {
|
||||
return &STSSBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSTSS,
|
||||
size: uint32(FullBoxLen + 4 + len(entries)*4),
|
||||
},
|
||||
},
|
||||
Entries: entries,
|
||||
}
|
||||
tmp := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, tmp); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func (box *STSSBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
buf := make([]byte, 4*(len(box.Entries)+1))
|
||||
// Write entry count
|
||||
binary.BigEndian.PutUint32(buf[:4], uint32(len(box.Entries)))
|
||||
|
||||
// Write entries
|
||||
for i, sampleNumber := range box.Entries {
|
||||
binary.BigEndian.PutUint32(buf[4+i*4:], sampleNumber)
|
||||
}
|
||||
offset = 8
|
||||
entryCount := binary.BigEndian.Uint32(tmp[:])
|
||||
buf := make([]byte, entryCount*4)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
}
|
||||
idx := 0
|
||||
for range entryCount {
|
||||
*stss = append(*stss, binary.BigEndian.Uint32(buf[idx:]))
|
||||
_, err = w.Write(buf)
|
||||
return int64(len(buf)), err
|
||||
}
|
||||
|
||||
func (box *STSSBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
entryCount := binary.BigEndian.Uint32(buf[:4])
|
||||
box.Entries = make([]uint32, entryCount)
|
||||
idx := 4
|
||||
for i := 0; i < int(entryCount); i++ {
|
||||
box.Entries[i] = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
}
|
||||
offset += idx
|
||||
return
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*STSSBox](TypeSTSS)
|
||||
}
|
||||
|
||||
+49
-44
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class SampleSizeBox extends FullBox(‘stsz’, version = 0, 0) {
|
||||
// aligned(8) class SampleSizeBox extends FullBox('stsz', version = 0, 0) {
|
||||
// unsigned int(32) sample_size;
|
||||
// unsigned int(32) sample_count;
|
||||
// if (sample_size==0) {
|
||||
@@ -15,61 +15,66 @@ import (
|
||||
// }
|
||||
// }
|
||||
|
||||
type SampleSizeBox struct {
|
||||
type STSZBox struct {
|
||||
FullBox
|
||||
SampleSize uint32
|
||||
SampleCount uint32
|
||||
EntrySizelist []uint32
|
||||
}
|
||||
|
||||
func (stsz *SampleSizeBox) Size() uint64 {
|
||||
if stsz.SampleSize == 0 {
|
||||
return FullBoxLen + 8 + 4*uint64(stsz.SampleCount)
|
||||
} else {
|
||||
return FullBoxLen + 8
|
||||
func CreateSTSZBox(sampleSize uint32, entrySizelist []uint32) *STSZBox {
|
||||
return &STSZBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeSTSZ,
|
||||
size: uint32(FullBoxLen + 8 + len(entrySizelist)*4),
|
||||
},
|
||||
},
|
||||
SampleSize: sampleSize,
|
||||
SampleCount: uint32(len(entrySizelist)),
|
||||
EntrySizelist: entrySizelist,
|
||||
}
|
||||
}
|
||||
|
||||
func (stsz *SampleSizeBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
}
|
||||
tmp := make([]byte, 8)
|
||||
if _, err = io.ReadFull(r, tmp); err != nil {
|
||||
return
|
||||
}
|
||||
offset = 12
|
||||
stsz.SampleSize = binary.BigEndian.Uint32(tmp[:])
|
||||
stsz.SampleCount = binary.BigEndian.Uint32(tmp[4:])
|
||||
if stsz.SampleSize == 0 {
|
||||
buf := make([]byte, stsz.SampleCount*4)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
func (box *STSZBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
buf := make([]byte, 8+len(box.EntrySizelist)*4)
|
||||
|
||||
// Write sample size
|
||||
binary.BigEndian.PutUint32(buf[:4], box.SampleSize)
|
||||
|
||||
// Write sample count
|
||||
binary.BigEndian.PutUint32(buf[4:8], box.SampleCount)
|
||||
|
||||
// Write entry sizes if sample size is 0
|
||||
if box.SampleSize == 0 {
|
||||
for i, size := range box.EntrySizelist {
|
||||
binary.BigEndian.PutUint32(buf[8+i*4:], size)
|
||||
}
|
||||
idx := 0
|
||||
stsz.EntrySizelist = make([]uint32, stsz.SampleCount)
|
||||
for i := 0; i < int(stsz.SampleCount); i++ {
|
||||
stsz.EntrySizelist[i] = binary.BigEndian.Uint32(buf[idx:])
|
||||
}
|
||||
|
||||
_, err = w.Write(buf)
|
||||
return int64(len(buf)), err
|
||||
}
|
||||
|
||||
func (box *STSZBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
box.SampleSize = binary.BigEndian.Uint32(buf[:4])
|
||||
box.SampleCount = binary.BigEndian.Uint32(buf[4:8])
|
||||
|
||||
if box.SampleSize == 0 {
|
||||
if len(buf) < 8+int(box.SampleCount)*4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.EntrySizelist = make([]uint32, box.SampleCount)
|
||||
idx := 8
|
||||
for i := 0; i < int(box.SampleCount); i++ {
|
||||
box.EntrySizelist[i] = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
}
|
||||
offset += idx
|
||||
}
|
||||
return
|
||||
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (stsz *SampleSizeBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeSTSZ, 0)
|
||||
fullbox.Box.Size = stsz.Size()
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], stsz.SampleSize)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], stsz.SampleCount)
|
||||
offset += 4
|
||||
if stsz.SampleSize == 0 {
|
||||
for i := 0; i < int(stsz.SampleCount); i++ {
|
||||
binary.BigEndian.PutUint32(buf[offset:], stsz.EntrySizelist[i])
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
return offset, buf
|
||||
func init() {
|
||||
RegisterBox[*STSZBox](TypeSTSZ)
|
||||
}
|
||||
|
||||
+47
-37
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class TimeToSampleBox extends FullBox(’stts’, version = 0, 0) {
|
||||
// aligned(8) class TimeToSampleBox extends FullBox('stts', version = 0, 0) {
|
||||
// unsigned int(32) entry_count;
|
||||
// int i;
|
||||
// for (i=0; i < entry_count; i++) {
|
||||
@@ -14,50 +14,60 @@ import (
|
||||
// }
|
||||
// }
|
||||
|
||||
type TimeToSampleBox []STTSEntry
|
||||
|
||||
func (stts TimeToSampleBox) Size() uint64 {
|
||||
return FullBoxLen + 4 + 8*uint64(len(stts))
|
||||
type STTSBox struct {
|
||||
FullBox
|
||||
Entries []STTSEntry
|
||||
}
|
||||
|
||||
func (stts *TimeToSampleBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
func CreateSTTSBox(entries []STTSEntry) *STTSBox {
|
||||
return &STTSBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
|
||||
typ: TypeSTTS,
|
||||
size: uint32(FullBoxLen + 4 + len(entries)*8),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
|
||||
Entries: entries,
|
||||
}
|
||||
entryCountBuf := make([]byte, 4)
|
||||
if _, err = io.ReadFull(r, entryCountBuf); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
func (box *STTSBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
buf := make([]byte, 4+len(box.Entries)*8)
|
||||
// Write entry count
|
||||
binary.BigEndian.PutUint32(buf[:4], uint32(len(box.Entries)))
|
||||
|
||||
// Write entries
|
||||
for i, entry := range box.Entries {
|
||||
binary.BigEndian.PutUint32(buf[4+i*8:], entry.SampleCount)
|
||||
binary.BigEndian.PutUint32(buf[4+i*8+4:], entry.SampleDelta)
|
||||
}
|
||||
offset = 8
|
||||
l := binary.BigEndian.Uint32(entryCountBuf)
|
||||
*stts = make([]STTSEntry, l)
|
||||
buf := make([]byte, l*8)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
|
||||
_, err = w.Write(buf)
|
||||
return int64(len(buf)), err
|
||||
}
|
||||
|
||||
func (box *STTSBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
entryCount := binary.BigEndian.Uint32(buf[:4])
|
||||
box.Entries = make([]STTSEntry, entryCount)
|
||||
|
||||
if len(buf) < 4+int(entryCount)*8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
idx := 0
|
||||
for i := 0; i < int(l); i++ {
|
||||
(*stts)[i].SampleCount = binary.BigEndian.Uint32(buf[idx:])
|
||||
|
||||
idx := 4
|
||||
for i := 0; i < int(entryCount); i++ {
|
||||
box.Entries[i].SampleCount = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
(*stts)[i].SampleDelta = binary.BigEndian.Uint32(buf[idx:])
|
||||
box.Entries[i].SampleDelta = binary.BigEndian.Uint32(buf[idx:])
|
||||
idx += 4
|
||||
}
|
||||
offset += idx
|
||||
return
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (stts TimeToSampleBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeSTTS, 0)
|
||||
fullbox.Box.Size = stts.Size()
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(len(stts)))
|
||||
offset += 4
|
||||
for _, entry := range stts {
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.SampleCount)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], entry.SampleDelta)
|
||||
offset += 4
|
||||
}
|
||||
return offset, buf
|
||||
func init() {
|
||||
RegisterBox[*STTSBox](TypeSTTS)
|
||||
}
|
||||
|
||||
+41
-36
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class TrackFragmentBaseMediaDecodeTimeBox extends FullBox(‘tfdt’, version, 0) {
|
||||
// aligned(8) class TrackFragmentBaseMediaDecodeTimeBox extends FullBox('tfdt', version, 0) {
|
||||
// if (version==1) {
|
||||
// unsigned int(64) baseMediaDecodeTime;
|
||||
// } else { // version==0
|
||||
@@ -14,53 +14,58 @@ import (
|
||||
// }
|
||||
|
||||
type TrackFragmentBaseMediaDecodeTimeBox struct {
|
||||
FullBox
|
||||
BaseMediaDecodeTime uint64
|
||||
version uint8
|
||||
}
|
||||
|
||||
func NewTrackFragmentBaseMediaDecodeTimeBox(fragStart uint64) *TrackFragmentBaseMediaDecodeTimeBox {
|
||||
func CreateTrackFragmentBaseMediaDecodeTimeBox(baseMediaDecodeTime uint64) *TrackFragmentBaseMediaDecodeTimeBox {
|
||||
version := uint8(0)
|
||||
if fragStart > 0xFFFFFFFF {
|
||||
size := uint32(FullBoxLen + 4)
|
||||
if baseMediaDecodeTime > 0xFFFFFFFF {
|
||||
version = 1
|
||||
size = uint32(FullBoxLen + 8)
|
||||
}
|
||||
return &TrackFragmentBaseMediaDecodeTimeBox{
|
||||
version: version,
|
||||
BaseMediaDecodeTime: fragStart,
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeTFDT,
|
||||
size: size,
|
||||
},
|
||||
Version: version,
|
||||
Flags: [3]byte{0, 0, 0},
|
||||
},
|
||||
BaseMediaDecodeTime: baseMediaDecodeTime,
|
||||
}
|
||||
}
|
||||
|
||||
func (tfdt *TrackFragmentBaseMediaDecodeTimeBox) Size() uint64 {
|
||||
if tfdt.version == 1 {
|
||||
return FullBoxLen + 8 // 8 bytes for base_media_decode_time
|
||||
}
|
||||
return FullBoxLen + 4 // 4 bytes for base_media_decode_time
|
||||
}
|
||||
|
||||
func (tfdt *TrackFragmentBaseMediaDecodeTimeBox) Decode(r io.Reader, size uint32) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
buf := make([]byte, size-12)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if fullbox.Version == 1 {
|
||||
tfdt.BaseMediaDecodeTime = binary.BigEndian.Uint64(buf)
|
||||
offset += 8
|
||||
func (box *TrackFragmentBaseMediaDecodeTimeBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
if box.Version == 1 {
|
||||
binary.BigEndian.PutUint64(tmp[:], box.BaseMediaDecodeTime)
|
||||
nn, err := w.Write(tmp[:8])
|
||||
return int64(nn), err
|
||||
} else {
|
||||
tfdt.BaseMediaDecodeTime = uint64(binary.BigEndian.Uint32(buf))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(box.BaseMediaDecodeTime))
|
||||
nn, err := w.Write(tmp[:4])
|
||||
return int64(nn), err
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (tfdt *TrackFragmentBaseMediaDecodeTimeBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeTFDT, 1)
|
||||
tfdt.version = fullbox.Version
|
||||
fullbox.Box.Size = tfdt.Size()
|
||||
offset, boxdata := fullbox.Encode()
|
||||
binary.BigEndian.PutUint64(boxdata[offset:], tfdt.BaseMediaDecodeTime)
|
||||
return offset + 8, boxdata
|
||||
func (box *TrackFragmentBaseMediaDecodeTimeBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if box.Version == 1 {
|
||||
if len(buf) < 8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.BaseMediaDecodeTime = binary.BigEndian.Uint64(buf[:8])
|
||||
} else {
|
||||
if len(buf) < 4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.BaseMediaDecodeTime = uint64(binary.BigEndian.Uint32(buf[:4]))
|
||||
}
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*TrackFragmentBaseMediaDecodeTimeBox](TypeTFDT)
|
||||
}
|
||||
|
||||
+129
-84
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class TrackFragmentHeaderBox extends FullBox(‘tfhd’, 0, tf_flags){
|
||||
// aligned(8) class TrackFragmentHeaderBox extends FullBox('tfhd', 0, tf_flags){
|
||||
// unsigned int(32) track_ID;
|
||||
// // all the following are optional fields
|
||||
// unsigned int(64) base_data_offset;
|
||||
@@ -38,7 +38,8 @@ const (
|
||||
)
|
||||
|
||||
type TrackFragmentHeaderBox struct {
|
||||
Track_ID uint32
|
||||
FullBox
|
||||
TrackID uint32
|
||||
BaseDataOffset uint64
|
||||
SampleDescriptionIndex uint32
|
||||
DefaultSampleDuration uint32
|
||||
@@ -46,107 +47,151 @@ type TrackFragmentHeaderBox struct {
|
||||
DefaultSampleFlags uint32
|
||||
}
|
||||
|
||||
func NewTrackFragmentHeaderBox(trackid uint32) *TrackFragmentHeaderBox {
|
||||
func CreateTrackFragmentHeaderBox(trackID uint32, flags uint32) *TrackFragmentHeaderBox {
|
||||
size := uint32(FullBoxLen + 4) // base size + track_ID
|
||||
if flags&TF_FLAG_BASE_DATA_OFFSET_PRESENT != 0 {
|
||||
size += 8
|
||||
}
|
||||
if flags&TF_FLAG_SAMPLE_DESCRIPTION_INDEX_PRESENT != 0 {
|
||||
size += 4
|
||||
}
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_DURATION_PRESENT != 0 {
|
||||
size += 4
|
||||
}
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT != 0 {
|
||||
size += 4
|
||||
}
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_FLAGS_PRESENT != 0 {
|
||||
size += 4
|
||||
}
|
||||
|
||||
return &TrackFragmentHeaderBox{
|
||||
Track_ID: trackid,
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeTFHD,
|
||||
size: size,
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{byte(flags >> 16), byte(flags >> 8), byte(flags)},
|
||||
},
|
||||
TrackID: trackID,
|
||||
SampleDescriptionIndex: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (tfhd *TrackFragmentHeaderBox) Size(thfdFlags uint32) uint64 {
|
||||
n := uint64(FullBoxLen)
|
||||
n += 4
|
||||
if thfdFlags&TF_FLAG_BASE_DATA_OFFSET_PRESENT > 0 && thfdFlags&TF_FLAG_DEFAULT_BASE_IS_MOOF == 0 {
|
||||
n += 8
|
||||
func (box *TrackFragmentHeaderBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
binary.BigEndian.PutUint32(tmp[:], box.TrackID)
|
||||
nn, err := w.Write(tmp[:4])
|
||||
if err != nil {
|
||||
return int64(nn), err
|
||||
}
|
||||
if thfdFlags&TF_FLAG_SAMPLE_DESCRIPTION_INDEX_PRESENT > 0 {
|
||||
n += 4
|
||||
}
|
||||
if thfdFlags&TF_FLAG_DEFAULT_SAMPLE_DURATION_PRESENT > 0 {
|
||||
n += 4
|
||||
}
|
||||
if thfdFlags&TF_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT > 0 {
|
||||
n += 4
|
||||
}
|
||||
if thfdFlags&TF_FLAG_DEFAULT_SAMPLE_FLAGS_PRESENT > 0 {
|
||||
n += 4
|
||||
}
|
||||
return n
|
||||
}
|
||||
n = int64(nn)
|
||||
|
||||
func (tfhd *TrackFragmentHeaderBox) Decode(r io.Reader, size uint32, moofOffset uint64) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
}
|
||||
buf := make([]byte, size-12)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n := 0
|
||||
tfhd.Track_ID = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
tfhdFlags := uint32(fullbox.Flags[0])<<16 | uint32(fullbox.Flags[1])<<8 | uint32(fullbox.Flags[2])
|
||||
if tfhdFlags&uint32(TF_FLAG_BASE_DATA_OFFSET_PRESENT) > 0 {
|
||||
tfhd.BaseDataOffset = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
}
|
||||
if tfhdFlags&uint32(TF_FLAG_DEFAULT_BASE_IS_MOOF) > 0 {
|
||||
tfhd.BaseDataOffset = moofOffset
|
||||
} else {
|
||||
//TODO,In some cases, it is wrong
|
||||
tfhd.BaseDataOffset = moofOffset
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
|
||||
if flags&TF_FLAG_BASE_DATA_OFFSET_PRESENT != 0 {
|
||||
binary.BigEndian.PutUint64(tmp[:], box.BaseDataOffset)
|
||||
nn, err = w.Write(tmp[:8])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
|
||||
if tfhdFlags&uint32(TF_FLAG_SAMPLE_DESCRIPTION_INDEX_PRESENT) > 0 {
|
||||
tfhd.SampleDescriptionIndex = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
if flags&TF_FLAG_SAMPLE_DESCRIPTION_INDEX_PRESENT != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], box.SampleDescriptionIndex)
|
||||
nn, err = w.Write(tmp[:4])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
if tfhdFlags&uint32(TF_FLAG_DEFAULT_SAMPLE_DURATION_PRESENT) > 0 {
|
||||
tfhd.DefaultSampleDuration = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_DURATION_PRESENT != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], box.DefaultSampleDuration)
|
||||
nn, err = w.Write(tmp[:4])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
if tfhdFlags&uint32(TF_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT) > 0 {
|
||||
tfhd.DefaultSampleSize = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], box.DefaultSampleSize)
|
||||
nn, err = w.Write(tmp[:4])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
if tfhdFlags&uint32(TF_FLAG_DEFAULT_SAMPLE_FLAGS_PRESENT) > 0 {
|
||||
tfhd.DefaultSampleFlags = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_FLAGS_PRESENT != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], box.DefaultSampleFlags)
|
||||
nn, err = w.Write(tmp[:4])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
offset += n
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (tfhd *TrackFragmentHeaderBox) Encode(tFfFlags uint32) (int, []byte) {
|
||||
fullbox := NewFullBox(TypeTFHD, 0)
|
||||
fullbox.Flags[0] = byte(tFfFlags >> 16)
|
||||
fullbox.Flags[1] = byte(tFfFlags >> 8)
|
||||
fullbox.Flags[2] = byte(tFfFlags)
|
||||
fullbox.Box.Size = tfhd.Size(tFfFlags)
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], tfhd.Track_ID)
|
||||
offset += 4
|
||||
thfdFlags := uint32(fullbox.Flags[0])<<16 | uint32(fullbox.Flags[1])<<8 | uint32(fullbox.Flags[2])
|
||||
if thfdFlags&uint32(TF_FLAG_BASE_DATA_OFFSET_PRESENT) > 0 && thfdFlags&uint32(TF_FLAG_DEFAULT_BASE_IS_MOOF) == 0 {
|
||||
binary.BigEndian.PutUint64(buf[offset:], tfhd.BaseDataOffset)
|
||||
offset += 8
|
||||
func (box *TrackFragmentHeaderBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
if thfdFlags&uint32(TF_FLAG_SAMPLE_DESCRIPTION_INDEX_PRESENT) > 0 {
|
||||
binary.BigEndian.PutUint32(buf[offset:], tfhd.SampleDescriptionIndex)
|
||||
offset += 4
|
||||
|
||||
n := 0
|
||||
box.TrackID = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
|
||||
if flags&TF_FLAG_BASE_DATA_OFFSET_PRESENT != 0 {
|
||||
if len(buf) < n+8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.BaseDataOffset = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
}
|
||||
if thfdFlags&uint32(TF_FLAG_DEFAULT_SAMPLE_DURATION_PRESENT) > 0 {
|
||||
binary.BigEndian.PutUint32(buf[offset:], tfhd.DefaultSampleDuration)
|
||||
offset += 4
|
||||
|
||||
if flags&TF_FLAG_SAMPLE_DESCRIPTION_INDEX_PRESENT != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.SampleDescriptionIndex = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
if thfdFlags&uint32(TF_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT) > 0 {
|
||||
binary.BigEndian.PutUint32(buf[offset:], tfhd.DefaultSampleSize)
|
||||
offset += 4
|
||||
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_DURATION_PRESENT != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.DefaultSampleDuration = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
if thfdFlags&uint32(TF_FLAG_DEFAULT_SAMPLE_FLAGS_PRESENT) > 0 {
|
||||
binary.BigEndian.PutUint32(buf[offset:], tfhd.DefaultSampleFlags)
|
||||
offset += 4
|
||||
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_SIZE_PRESENT != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.DefaultSampleSize = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
return offset, buf
|
||||
|
||||
if flags&TF_FLAG_DEFAULT_SAMPLE_FLAGS_PRESENT != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.DefaultSampleFlags = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*TrackFragmentHeaderBox](TypeTFHD)
|
||||
}
|
||||
|
||||
+157
-80
@@ -28,109 +28,186 @@ import (
|
||||
// }
|
||||
|
||||
type TrackFragmentRandomAccessBox struct {
|
||||
Box *FullBox
|
||||
FullBox
|
||||
TrackID uint32
|
||||
LengthSizeOfTrafNum uint8
|
||||
LengthSizeOfTrunNum uint8
|
||||
LengthSizeOfSampleNum uint8
|
||||
FragEntrys []FragEntry
|
||||
Entries []TFRAEntry
|
||||
}
|
||||
|
||||
func NewTrackFragmentRandomAccessBox(trackid uint32) *TrackFragmentRandomAccessBox {
|
||||
func CreateTrackFragmentRandomAccessBox(trackID uint32, entries []TFRAEntry) *TrackFragmentRandomAccessBox {
|
||||
return &TrackFragmentRandomAccessBox{
|
||||
Box: NewFullBox(TypeTFRA, 1),
|
||||
TrackID: trackid,
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeTFRA,
|
||||
size: uint32(FullBoxLen + 8 + 4 + len(entries)*(3+8)),
|
||||
},
|
||||
},
|
||||
TrackID: trackID,
|
||||
Entries: entries,
|
||||
}
|
||||
}
|
||||
|
||||
func (tfra *TrackFragmentRandomAccessBox) Size() uint64 {
|
||||
entrySize := 0
|
||||
if tfra.Box.Version == 1 {
|
||||
entrySize = 16 // 8 bytes for time + 8 bytes for moof_offset
|
||||
} else {
|
||||
entrySize = 8 // 4 bytes for time + 4 bytes for moof_offset
|
||||
}
|
||||
// Add size for traf_number, trun_number, and sample_number
|
||||
entrySize += int(tfra.LengthSizeOfTrafNum + tfra.LengthSizeOfTrunNum + tfra.LengthSizeOfSampleNum + 3)
|
||||
return tfra.Box.Size() + 12 + uint64(len(tfra.FragEntrys)*entrySize) // 12 = 4(track_id) + 4(reserved) + 4(number_of_entry)
|
||||
}
|
||||
|
||||
func (tfra *TrackFragmentRandomAccessBox) Decode(r io.Reader) (offset int, err error) {
|
||||
if offset, err = tfra.Box.Decode(r); err != nil {
|
||||
func (box *TrackFragmentRandomAccessBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [12]byte
|
||||
binary.BigEndian.PutUint32(tmp[:4], box.TrackID)
|
||||
tmp[7] = box.LengthSizeOfTrafNum<<6 | box.LengthSizeOfTrunNum<<4 | box.LengthSizeOfSampleNum<<2
|
||||
binary.BigEndian.PutUint32(tmp[8:], uint32(len(box.Entries)))
|
||||
nn, err := w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n = int64(nn)
|
||||
|
||||
needSize := tfra.Box.Box.Size - 12
|
||||
buf := make([]byte, needSize)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n := 0
|
||||
tfra.TrackID = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
tfra.LengthSizeOfTrafNum = (buf[n+3] >> 4) & 0x03
|
||||
tfra.LengthSizeOfTrunNum = (buf[n+3] >> 2) & 0x03
|
||||
tfra.LengthSizeOfSampleNum = buf[n+3] & 0x03
|
||||
n += 4
|
||||
tfra.FragEntrys = make([]FragEntry, binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
for i := range tfra.FragEntrys {
|
||||
frag := &tfra.FragEntrys[i]
|
||||
if tfra.Box.Version == 1 {
|
||||
frag.Time = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
frag.MoofOffset = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
for _, entry := range box.Entries {
|
||||
if box.Version == 1 {
|
||||
binary.BigEndian.PutUint64(tmp[:], entry.Time)
|
||||
nn, err = w.Write(tmp[:8])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
|
||||
binary.BigEndian.PutUint64(tmp[:], entry.MoofOffset)
|
||||
nn, err = w.Write(tmp[:8])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
} else {
|
||||
frag.Time = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
frag.MoofOffset = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
binary.BigEndian.PutUint32(tmp[:4], uint32(entry.Time))
|
||||
binary.BigEndian.PutUint32(tmp[4:8], uint32(entry.MoofOffset))
|
||||
nn, err = w.Write(tmp[:8])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
n += int(tfra.LengthSizeOfTrafNum + tfra.LengthSizeOfTrunNum + tfra.LengthSizeOfSampleNum + 3)
|
||||
|
||||
trafSize := box.LengthSizeOfTrafNum + 1
|
||||
trunSize := box.LengthSizeOfTrunNum + 1
|
||||
sampleSize := box.LengthSizeOfSampleNum + 1
|
||||
|
||||
switch trafSize {
|
||||
case 4:
|
||||
binary.BigEndian.PutUint32(tmp[:], entry.TrafNumber)
|
||||
case 3:
|
||||
binary.BigEndian.PutUint32(tmp[:], entry.TrafNumber&0x00FFFFFF)
|
||||
case 2:
|
||||
binary.BigEndian.PutUint16(tmp[:], uint16(entry.TrafNumber))
|
||||
case 1:
|
||||
tmp[0] = uint8(entry.TrafNumber)
|
||||
}
|
||||
switch trunSize {
|
||||
case 4:
|
||||
binary.BigEndian.PutUint32(tmp[trafSize:], entry.TrunNumber)
|
||||
case 3:
|
||||
binary.BigEndian.PutUint32(tmp[trafSize:], entry.TrunNumber&0x00FFFFFF)
|
||||
case 2:
|
||||
binary.BigEndian.PutUint16(tmp[trafSize:], uint16(entry.TrunNumber))
|
||||
case 1:
|
||||
tmp[trafSize] = uint8(entry.TrunNumber)
|
||||
}
|
||||
|
||||
switch sampleSize {
|
||||
case 4:
|
||||
binary.BigEndian.PutUint32(tmp[trafSize+trunSize:], entry.SampleNumber)
|
||||
case 3:
|
||||
binary.BigEndian.PutUint32(tmp[trafSize+trunSize:], entry.SampleNumber&0x00FFFFFF)
|
||||
case 2:
|
||||
binary.BigEndian.PutUint16(tmp[trafSize+trunSize:], uint16(entry.SampleNumber))
|
||||
case 1:
|
||||
tmp[trafSize+trunSize] = uint8(entry.SampleNumber)
|
||||
}
|
||||
nn, err = w.Write(tmp[:trafSize+trunSize+sampleSize])
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
offset += 4
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (tfra *TrackFragmentRandomAccessBox) Encode() (int, []byte) {
|
||||
tfra.Box.Box.Size = tfra.Size()
|
||||
offset, boxdata := tfra.Box.Encode()
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], tfra.TrackID)
|
||||
offset += 4
|
||||
// Pack length size fields into the reserved uint32
|
||||
lengthSizeFlags := uint32(tfra.LengthSizeOfTrafNum&0x03)<<4 |
|
||||
uint32(tfra.LengthSizeOfTrunNum&0x03)<<2 |
|
||||
uint32(tfra.LengthSizeOfSampleNum&0x03)
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], lengthSizeFlags)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], uint32(len(tfra.FragEntrys)))
|
||||
offset += 4
|
||||
for _, frag := range tfra.FragEntrys {
|
||||
if tfra.Box.Version == 1 {
|
||||
binary.BigEndian.PutUint64(boxdata[offset:], frag.Time)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint64(boxdata[offset:], frag.MoofOffset)
|
||||
offset += 8
|
||||
func (box *TrackFragmentRandomAccessBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
|
||||
box.TrackID = binary.BigEndian.Uint32(buf[:4])
|
||||
flags := buf[7]
|
||||
box.LengthSizeOfTrafNum = (flags >> 6) & 0x03
|
||||
box.LengthSizeOfTrunNum = (flags >> 4) & 0x03
|
||||
box.LengthSizeOfSampleNum = (flags >> 2) & 0x03
|
||||
entryCount := binary.BigEndian.Uint32(buf[8:])
|
||||
|
||||
n := 12
|
||||
box.Entries = make([]TFRAEntry, entryCount)
|
||||
for i := uint32(0); i < entryCount; i++ {
|
||||
if box.Version == 1 {
|
||||
if len(buf) < n+16 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Entries[i].Time = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
box.Entries[i].MoofOffset = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
} else {
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], uint32(frag.Time))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(boxdata[offset:], uint32(frag.MoofOffset))
|
||||
offset += 4
|
||||
if len(buf) < n+8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Entries[i].Time = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
box.Entries[i].MoofOffset = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
}
|
||||
// Write traf_number, trun_number, and sample_number based on length sizes
|
||||
for i := uint8(0); i < tfra.LengthSizeOfTrafNum+1; i++ {
|
||||
boxdata[offset] = 1
|
||||
offset++
|
||||
|
||||
trafSize := box.LengthSizeOfTrafNum + 1
|
||||
trunSize := box.LengthSizeOfTrunNum + 1
|
||||
sampleSize := box.LengthSizeOfSampleNum + 1
|
||||
|
||||
if len(buf) < n+int(trafSize+trunSize+sampleSize) {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
for i := uint8(0); i < tfra.LengthSizeOfTrunNum+1; i++ {
|
||||
boxdata[offset] = 1
|
||||
offset++
|
||||
|
||||
switch trafSize {
|
||||
case 4:
|
||||
box.Entries[i].TrafNumber = binary.BigEndian.Uint32(buf[n:])
|
||||
case 3:
|
||||
box.Entries[i].TrafNumber = binary.BigEndian.Uint32(buf[n:]) & 0x00FFFFFF
|
||||
case 2:
|
||||
box.Entries[i].TrafNumber = uint32(binary.BigEndian.Uint16(buf[n:]))
|
||||
case 1:
|
||||
box.Entries[i].TrafNumber = uint32(buf[n])
|
||||
}
|
||||
for i := uint8(0); i < tfra.LengthSizeOfSampleNum+1; i++ {
|
||||
boxdata[offset] = 1
|
||||
offset++
|
||||
n += int(trafSize)
|
||||
|
||||
switch trunSize {
|
||||
case 4:
|
||||
box.Entries[i].TrunNumber = binary.BigEndian.Uint32(buf[n:])
|
||||
case 3:
|
||||
box.Entries[i].TrunNumber = binary.BigEndian.Uint32(buf[n:]) & 0x00FFFFFF
|
||||
case 2:
|
||||
box.Entries[i].TrunNumber = uint32(binary.BigEndian.Uint16(buf[n:]))
|
||||
case 1:
|
||||
box.Entries[i].TrunNumber = uint32(buf[n])
|
||||
}
|
||||
n += int(trunSize)
|
||||
|
||||
switch sampleSize {
|
||||
case 4:
|
||||
box.Entries[i].SampleNumber = binary.BigEndian.Uint32(buf[n:])
|
||||
case 3:
|
||||
box.Entries[i].SampleNumber = binary.BigEndian.Uint32(buf[n:]) & 0x00FFFFFF
|
||||
case 2:
|
||||
box.Entries[i].SampleNumber = uint32(binary.BigEndian.Uint16(buf[n:]))
|
||||
case 1:
|
||||
box.Entries[i].SampleNumber = uint32(buf[n])
|
||||
}
|
||||
n += int(sampleSize)
|
||||
}
|
||||
return offset, boxdata
|
||||
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*TrackFragmentRandomAccessBox](TypeTFRA)
|
||||
}
|
||||
|
||||
+105
-99
@@ -9,7 +9,7 @@ import (
|
||||
)
|
||||
|
||||
// aligned(8) class TrackHeaderBox
|
||||
// extends FullBox(‘tkhd’, version, flags){
|
||||
// extends FullBox('tkhd', version, flags){
|
||||
// if (version==1) {
|
||||
// unsigned int(64) creation_time;
|
||||
// unsigned int(64) modification_time;
|
||||
@@ -36,118 +36,124 @@ import (
|
||||
// }
|
||||
|
||||
type TrackHeaderBox struct {
|
||||
Creation_time uint64
|
||||
Modification_time uint64
|
||||
Track_ID uint32
|
||||
Duration uint64
|
||||
Layer uint16
|
||||
Alternate_group uint16
|
||||
Volume uint16
|
||||
Matrix [9]uint32
|
||||
Width uint32
|
||||
Height uint32
|
||||
FullBox
|
||||
CreationTime uint64
|
||||
ModificationTime uint64
|
||||
TrackID uint32
|
||||
Duration uint64
|
||||
Layer uint16
|
||||
AlternateGroup uint16
|
||||
Volume uint16
|
||||
Matrix [9]uint32
|
||||
Width uint32
|
||||
Height uint32
|
||||
}
|
||||
|
||||
func NewTrackHeaderBox() *TrackHeaderBox {
|
||||
func CreateTrackHeaderBox(trackID uint32, duration uint64, width, height uint32) *TrackHeaderBox {
|
||||
_, offset := time.Now().Zone()
|
||||
now := uint64(time.Now().Unix() + int64(offset) + 0x7C25B080)
|
||||
version := util.Conditional[uint8](duration > 0xFFFFFFFF, 1, 0)
|
||||
|
||||
return &TrackHeaderBox{
|
||||
Creation_time: uint64(time.Now().Unix() + int64(offset) + 0x7C25B080),
|
||||
Modification_time: uint64(time.Now().Unix() + int64(offset) + 0x7C25B080),
|
||||
Layer: 0,
|
||||
Alternate_group: 0,
|
||||
Matrix: [9]uint32{0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000},
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeTKHD,
|
||||
size: util.Conditional[uint32](version == 1, 92, 80) + FullBoxLen,
|
||||
},
|
||||
Version: version,
|
||||
Flags: [3]byte{0, 0, 3}, // Track_enabled | Track_in_movie
|
||||
},
|
||||
CreationTime: now,
|
||||
ModificationTime: now,
|
||||
TrackID: trackID,
|
||||
Duration: duration,
|
||||
Layer: 0,
|
||||
AlternateGroup: 0,
|
||||
Matrix: [9]uint32{0x00010000, 0, 0, 0, 0x00010000, 0, 0, 0, 0x40000000},
|
||||
Width: width,
|
||||
Height: height,
|
||||
}
|
||||
}
|
||||
|
||||
func (tkhd *TrackHeaderBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
boxsize := 0
|
||||
if fullbox.Version == 0 {
|
||||
boxsize = 80
|
||||
func (box *TrackHeaderBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var data []byte
|
||||
|
||||
if box.Version == 1 {
|
||||
data = make([]byte, 92)
|
||||
binary.BigEndian.PutUint64(data[0:], box.CreationTime)
|
||||
binary.BigEndian.PutUint64(data[8:], box.ModificationTime)
|
||||
binary.BigEndian.PutUint32(data[16:], box.TrackID)
|
||||
binary.BigEndian.PutUint64(data[24:], box.Duration)
|
||||
// 32-40 reserved
|
||||
} else {
|
||||
boxsize = 92
|
||||
data = make([]byte, 80)
|
||||
binary.BigEndian.PutUint32(data[0:], uint32(box.CreationTime))
|
||||
binary.BigEndian.PutUint32(data[4:], uint32(box.ModificationTime))
|
||||
binary.BigEndian.PutUint32(data[8:], box.TrackID)
|
||||
binary.BigEndian.PutUint32(data[16:], uint32(box.Duration))
|
||||
// 20-28 reserved
|
||||
}
|
||||
buf := make([]byte, boxsize)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
|
||||
offset := util.Conditional[int](box.Version == 1, 32, 20)
|
||||
// 8 bytes reserved already zeroed
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint16(data[offset:], box.Layer)
|
||||
binary.BigEndian.PutUint16(data[offset+2:], box.AlternateGroup)
|
||||
binary.BigEndian.PutUint16(data[offset+4:], box.Volume)
|
||||
// 2 bytes reserved already zeroed
|
||||
offset += 8
|
||||
|
||||
for i, m := range box.Matrix {
|
||||
binary.BigEndian.PutUint32(data[offset+i*4:], m)
|
||||
}
|
||||
n := 0
|
||||
if fullbox.Version == 1 {
|
||||
tkhd.Creation_time = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
tkhd.Modification_time = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
tkhd.Track_ID = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 8
|
||||
tkhd.Duration = binary.BigEndian.Uint64(buf[n:])
|
||||
n += 8
|
||||
} else {
|
||||
tkhd.Creation_time = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
tkhd.Modification_time = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
tkhd.Track_ID = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 8
|
||||
tkhd.Duration = uint64(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
}
|
||||
n += 8
|
||||
tkhd.Layer = binary.BigEndian.Uint16(buf[n:])
|
||||
n += 2
|
||||
tkhd.Alternate_group = binary.BigEndian.Uint16(buf[n:])
|
||||
n += 2
|
||||
tkhd.Volume = binary.BigEndian.Uint16(buf[n:])
|
||||
n += 4
|
||||
for i := 0; i < 9; i++ {
|
||||
tkhd.Matrix[i] = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
tkhd.Width = binary.BigEndian.Uint32(buf[n:])
|
||||
tkhd.Height = binary.BigEndian.Uint32(buf[n+4:])
|
||||
offset += n + 8
|
||||
offset += 36
|
||||
|
||||
binary.BigEndian.PutUint32(data[offset:], box.Width)
|
||||
binary.BigEndian.PutUint32(data[offset+4:], box.Height)
|
||||
|
||||
nn, err := w.Write(data)
|
||||
n = int64(nn)
|
||||
return
|
||||
}
|
||||
|
||||
func (tkhd *TrackHeaderBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeTKHD, util.Conditional[uint8](tkhd.Duration > 0xFFFFFFFF, 1, 0))
|
||||
fullbox.Flags[2] = 0x03 //Track_enabled | Track_in_movie
|
||||
fullbox.Box.Size = util.Conditional[uint64](fullbox.Version == 1, 92, 80) + FullBoxLen
|
||||
offset, buf := fullbox.Encode()
|
||||
if fullbox.Version == 1 {
|
||||
binary.BigEndian.PutUint64(buf[offset:], tkhd.Creation_time)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint64(buf[offset:], tkhd.Creation_time)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint32(buf[offset:], tkhd.Track_ID)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint64(buf[offset:], tkhd.Duration)
|
||||
offset += 8
|
||||
func (box *TrackHeaderBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if box.Version == 1 {
|
||||
if len(buf) < 92 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.CreationTime = binary.BigEndian.Uint64(buf[0:])
|
||||
box.ModificationTime = binary.BigEndian.Uint64(buf[8:])
|
||||
box.TrackID = binary.BigEndian.Uint32(buf[16:])
|
||||
box.Duration = binary.BigEndian.Uint64(buf[24:])
|
||||
buf = buf[32:]
|
||||
} else {
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(tkhd.Creation_time))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(tkhd.Creation_time))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], tkhd.Track_ID)
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(tkhd.Duration))
|
||||
offset += 4
|
||||
if len(buf) < 80 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.CreationTime = uint64(binary.BigEndian.Uint32(buf[0:]))
|
||||
box.ModificationTime = uint64(binary.BigEndian.Uint32(buf[4:]))
|
||||
box.TrackID = binary.BigEndian.Uint32(buf[8:])
|
||||
box.Duration = uint64(binary.BigEndian.Uint32(buf[16:]))
|
||||
buf = buf[20:]
|
||||
}
|
||||
offset += 8
|
||||
binary.BigEndian.PutUint16(buf[offset:], tkhd.Layer)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], tkhd.Alternate_group)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], tkhd.Volume)
|
||||
offset += 4
|
||||
for i, _ := range tkhd.Matrix {
|
||||
binary.BigEndian.PutUint32(buf[offset:], tkhd.Matrix[i])
|
||||
offset += 4
|
||||
|
||||
buf = buf[8:] // skip reserved
|
||||
box.Layer = binary.BigEndian.Uint16(buf[0:])
|
||||
box.AlternateGroup = binary.BigEndian.Uint16(buf[2:])
|
||||
box.Volume = binary.BigEndian.Uint16(buf[4:])
|
||||
buf = buf[8:] // skip reserved
|
||||
|
||||
for i := 0; i < 9; i++ {
|
||||
box.Matrix[i] = binary.BigEndian.Uint32(buf[i*4:])
|
||||
}
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(tkhd.Width))
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(tkhd.Height))
|
||||
return offset + 4, buf
|
||||
buf = buf[36:]
|
||||
|
||||
box.Width = binary.BigEndian.Uint32(buf[0:])
|
||||
box.Height = binary.BigEndian.Uint32(buf[4:])
|
||||
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*TrackHeaderBox](TypeTKHD)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
package box
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
)
|
||||
|
||||
type TrakBox struct {
|
||||
BaseBox
|
||||
MDIA *MdiaBox
|
||||
EDTS *EdtsBox
|
||||
TKHD *TrackHeaderBox
|
||||
}
|
||||
|
||||
func CreateTrakBox(mdia *MdiaBox, edts *EdtsBox, tkhd *TrackHeaderBox) *TrakBox {
|
||||
size := uint32(BasicBoxLen)
|
||||
if mdia != nil {
|
||||
size += mdia.size
|
||||
}
|
||||
if edts != nil {
|
||||
size += edts.size
|
||||
}
|
||||
if tkhd != nil {
|
||||
size += tkhd.size
|
||||
}
|
||||
return &TrakBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeTRAK,
|
||||
size: size,
|
||||
},
|
||||
MDIA: mdia,
|
||||
EDTS: edts,
|
||||
TKHD: tkhd,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *TrakBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
return WriteTo(w, t.MDIA, t.EDTS, t.TKHD)
|
||||
}
|
||||
|
||||
func (t *TrakBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
for {
|
||||
b, err := ReadFrom(bytes.NewReader(buf))
|
||||
if err != nil {
|
||||
return t, err
|
||||
}
|
||||
switch box := b.(type) {
|
||||
case *MdiaBox:
|
||||
t.MDIA = box
|
||||
case *EdtsBox:
|
||||
t.EDTS = box
|
||||
case *TrackHeaderBox:
|
||||
t.TKHD = box
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ParseSamples parses the sample table and builds the sample list
|
||||
func (t *TrakBox) ParseSamples() (samplelist []Sample) {
|
||||
stbl := t.MDIA.MINF.STBL
|
||||
var chunkOffsets []uint64
|
||||
if stbl.STCO != nil {
|
||||
chunkOffsets = stbl.STCO.Entries
|
||||
}
|
||||
|
||||
var sampleToChunks []STSCEntry
|
||||
if stbl.STSC != nil {
|
||||
sampleToChunks = stbl.STSC.Entries
|
||||
}
|
||||
|
||||
var sampleCount uint32
|
||||
if stbl.STSZ != nil {
|
||||
sampleCount = stbl.STSZ.SampleCount
|
||||
}
|
||||
|
||||
samplelist = make([]Sample, sampleCount)
|
||||
iterator := 0
|
||||
|
||||
for i, chunk := range sampleToChunks {
|
||||
var samplesInChunk uint32
|
||||
if i < len(sampleToChunks)-1 {
|
||||
samplesInChunk = sampleToChunks[i+1].FirstChunk - chunk.FirstChunk
|
||||
} else {
|
||||
samplesInChunk = uint32(len(chunkOffsets)) - chunk.FirstChunk + 1
|
||||
}
|
||||
|
||||
for j := uint32(0); j < samplesInChunk; j++ {
|
||||
chunkIndex := chunk.FirstChunk - 1 + j
|
||||
if chunkIndex >= uint32(len(chunkOffsets)) {
|
||||
break
|
||||
}
|
||||
|
||||
for k := uint32(0); k < chunk.SamplesPerChunk; k++ {
|
||||
if iterator >= len(samplelist) {
|
||||
break
|
||||
}
|
||||
|
||||
sample := &samplelist[iterator]
|
||||
if stbl.STSZ != nil {
|
||||
if stbl.STSZ.SampleSize != 0 {
|
||||
sample.Size = int(stbl.STSZ.SampleSize)
|
||||
} else {
|
||||
sample.Size = int(stbl.STSZ.EntrySizelist[iterator])
|
||||
}
|
||||
}
|
||||
|
||||
sample.Offset = int64(chunkOffsets[chunkIndex])
|
||||
if k > 0 {
|
||||
sample.Offset = samplelist[iterator-1].Offset + int64(samplelist[iterator-1].Size)
|
||||
}
|
||||
|
||||
iterator++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process STTS entries for timestamps
|
||||
if stbl.STTS != nil {
|
||||
sampleIndex := 0
|
||||
timestamp := uint64(0)
|
||||
|
||||
for _, entry := range stbl.STTS.Entries {
|
||||
for i := uint32(0); i < entry.SampleCount; i++ {
|
||||
if sampleIndex >= len(samplelist) {
|
||||
break
|
||||
}
|
||||
samplelist[sampleIndex].DTS = timestamp
|
||||
samplelist[sampleIndex].PTS = timestamp
|
||||
timestamp += uint64(entry.SampleDelta)
|
||||
sampleIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Process CTTS entries for presentation timestamps
|
||||
if stbl.CTTS != nil {
|
||||
sampleIndex := 0
|
||||
for _, entry := range stbl.CTTS.Entries {
|
||||
for i := uint32(0); i < entry.SampleCount; i++ {
|
||||
if sampleIndex >= len(samplelist) {
|
||||
break
|
||||
}
|
||||
samplelist[sampleIndex].PTS = samplelist[sampleIndex].DTS + uint64(entry.SampleOffset)
|
||||
sampleIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
return samplelist
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterBox[*TrakBox](TypeTRAK)
|
||||
}
|
||||
+33
-40
@@ -5,7 +5,7 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// aligned(8) class TrackExtendsBox extends FullBox(‘trex’, 0, 0){
|
||||
// aligned(8) class TrackExtendsBox extends FullBox('trex', 0, 0){
|
||||
// unsigned int(32) track_ID;
|
||||
// unsigned int(32) default_sample_description_index;
|
||||
// unsigned int(32) default_sample_duration;
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
// }
|
||||
|
||||
type TrackExtendsBox struct {
|
||||
Box *FullBox
|
||||
FullBox
|
||||
TrackID uint32
|
||||
DefaultSampleDescriptionIndex uint32
|
||||
DefaultSampleDuration uint32
|
||||
@@ -22,52 +22,45 @@ type TrackExtendsBox struct {
|
||||
DefaultSampleFlags uint32
|
||||
}
|
||||
|
||||
func NewTrackExtendsBox(track uint32) *TrackExtendsBox {
|
||||
func CreateTrackExtendsBox(trackID uint32) *TrackExtendsBox {
|
||||
return &TrackExtendsBox{
|
||||
Box: NewFullBox(TypeTREX, 0),
|
||||
TrackID: track,
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeTREX,
|
||||
size: uint32(FullBoxLen + 20),
|
||||
},
|
||||
},
|
||||
TrackID: trackID,
|
||||
}
|
||||
}
|
||||
|
||||
func (trex *TrackExtendsBox) Size() uint64 {
|
||||
return trex.Box.Size() + 20
|
||||
func (box *TrackExtendsBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [20]byte
|
||||
binary.BigEndian.PutUint32(tmp[0:], box.TrackID)
|
||||
binary.BigEndian.PutUint32(tmp[4:], box.DefaultSampleDescriptionIndex)
|
||||
binary.BigEndian.PutUint32(tmp[8:], box.DefaultSampleDuration)
|
||||
binary.BigEndian.PutUint32(tmp[12:], box.DefaultSampleSize)
|
||||
binary.BigEndian.PutUint32(tmp[16:], box.DefaultSampleFlags)
|
||||
|
||||
nn, err := w.Write(tmp[:])
|
||||
n = int64(nn)
|
||||
return
|
||||
}
|
||||
|
||||
func (trex *TrackExtendsBox) Decode(r io.Reader) (offset int, err error) {
|
||||
if offset, err = trex.Box.Decode(r); err != nil {
|
||||
return 0, err
|
||||
func (box *TrackExtendsBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 20 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
buf := make([]byte, 20)
|
||||
if _, err := io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
n := 0
|
||||
trex.TrackID = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
trex.DefaultSampleDescriptionIndex = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
trex.DefaultSampleDuration = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
trex.DefaultSampleSize = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
trex.DefaultSampleFlags = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
return offset + 20, nil
|
||||
box.TrackID = binary.BigEndian.Uint32(buf[0:])
|
||||
box.DefaultSampleDescriptionIndex = binary.BigEndian.Uint32(buf[4:])
|
||||
box.DefaultSampleDuration = binary.BigEndian.Uint32(buf[8:])
|
||||
box.DefaultSampleSize = binary.BigEndian.Uint32(buf[12:])
|
||||
box.DefaultSampleFlags = binary.BigEndian.Uint32(buf[16:])
|
||||
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (trex *TrackExtendsBox) Encode() (int, []byte) {
|
||||
trex.Box.Box.Size = trex.Size()
|
||||
offset, buf := trex.Box.Encode()
|
||||
binary.BigEndian.PutUint32(buf[offset:], trex.TrackID)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], trex.DefaultSampleDescriptionIndex)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], trex.DefaultSampleDuration)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], trex.DefaultSampleSize)
|
||||
offset += 4
|
||||
binary.BigEndian.PutUint32(buf[offset:], trex.DefaultSampleFlags)
|
||||
offset += 4
|
||||
return offset, buf
|
||||
func init() {
|
||||
RegisterBox[*TrackExtendsBox](TypeTREX)
|
||||
}
|
||||
|
||||
+158
-118
@@ -35,160 +35,200 @@ const (
|
||||
TR_FLAG_DATA_SAMPLE_COMPOSITION_TIME uint32 = 0x000800
|
||||
)
|
||||
|
||||
type TrunEntry struct {
|
||||
SampleDuration uint32
|
||||
SampleSize uint32
|
||||
SampleFlags uint32
|
||||
SampleCompositionTimeOffset int32
|
||||
}
|
||||
|
||||
type TrackRunBox struct {
|
||||
FullBox
|
||||
SampleCount uint32
|
||||
Dataoffset int32
|
||||
DataOffset int32
|
||||
FirstSampleFlags uint32
|
||||
EntryList []TrunEntry
|
||||
Entries []TrunEntry
|
||||
}
|
||||
|
||||
func NewTrackRunBox() *TrackRunBox {
|
||||
return &TrackRunBox{}
|
||||
}
|
||||
func CreateTrackRunBox(flags uint32, sampleCount uint32) *TrackRunBox {
|
||||
size := uint32(FullBoxLen + 4) // base size + sample_count
|
||||
|
||||
func (trun *TrackRunBox) Size(trunFlags uint32) uint64 {
|
||||
size := uint64(8) // box header
|
||||
size += 4 // version and flags
|
||||
size += 4 // sample count
|
||||
|
||||
// data offset is always present if flag is set
|
||||
if trunFlags&TR_FLAG_DATA_OFFSET != 0 {
|
||||
if flags&TR_FLAG_DATA_OFFSET != 0 {
|
||||
size += 4
|
||||
}
|
||||
if flags&TR_FLAG_DATA_FIRST_SAMPLE_FLAGS != 0 {
|
||||
size += 4
|
||||
}
|
||||
|
||||
// first sample flags is present if flag is set
|
||||
if trunFlags&TR_FLAG_DATA_FIRST_SAMPLE_FLAGS != 0 {
|
||||
size += 4
|
||||
entrySize := uint32(0)
|
||||
if flags&TR_FLAG_DATA_SAMPLE_DURATION != 0 {
|
||||
entrySize += 4
|
||||
}
|
||||
if flags&TR_FLAG_DATA_SAMPLE_SIZE != 0 {
|
||||
entrySize += 4
|
||||
}
|
||||
if flags&TR_FLAG_DATA_SAMPLE_FLAGS != 0 {
|
||||
entrySize += 4
|
||||
}
|
||||
if flags&TR_FLAG_DATA_SAMPLE_COMPOSITION_TIME != 0 {
|
||||
entrySize += 4
|
||||
}
|
||||
|
||||
// calculate size for each sample entry
|
||||
for i := 0; i < int(trun.SampleCount); i++ {
|
||||
// sample duration is present if flag is set
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_DURATION != 0 {
|
||||
size += 4
|
||||
}
|
||||
// sample size is present if flag is set
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_SIZE != 0 {
|
||||
size += 4
|
||||
}
|
||||
// sample flags is present if flag is set
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_FLAGS != 0 {
|
||||
size += 4
|
||||
}
|
||||
// sample composition time offset is present if flag is set
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_COMPOSITION_TIME != 0 {
|
||||
size += 4
|
||||
}
|
||||
size += entrySize * sampleCount
|
||||
|
||||
return &TrackRunBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeTRUN,
|
||||
size: size,
|
||||
},
|
||||
Version: 1, // Use version 1 for signed composition time offsets
|
||||
Flags: [3]byte{byte(flags >> 16), byte(flags >> 8), byte(flags)},
|
||||
},
|
||||
SampleCount: sampleCount,
|
||||
Entries: make([]TrunEntry, sampleCount),
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (trun *TrackRunBox) Decode(r io.Reader, size uint32, dataOffset int32) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if offset, err = fullbox.Decode(r); err != nil {
|
||||
return
|
||||
func (box *TrackRunBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [4]byte
|
||||
binary.BigEndian.PutUint32(tmp[:], box.SampleCount)
|
||||
nn, err := w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return int64(nn), err
|
||||
}
|
||||
buf := make([]byte, size-12)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return
|
||||
n = int64(nn)
|
||||
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
|
||||
if flags&TR_FLAG_DATA_OFFSET != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(box.DataOffset))
|
||||
nn, err = w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
|
||||
if flags&TR_FLAG_DATA_FIRST_SAMPLE_FLAGS != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], box.FirstSampleFlags)
|
||||
nn, err = w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
|
||||
for i := uint32(0); i < box.SampleCount; i++ {
|
||||
if flags&TR_FLAG_DATA_SAMPLE_DURATION != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], box.Entries[i].SampleDuration)
|
||||
nn, err = w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
|
||||
if flags&TR_FLAG_DATA_SAMPLE_SIZE != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], box.Entries[i].SampleSize)
|
||||
nn, err = w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
|
||||
if flags&TR_FLAG_DATA_SAMPLE_FLAGS != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], box.Entries[i].SampleFlags)
|
||||
nn, err = w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
|
||||
if flags&TR_FLAG_DATA_SAMPLE_COMPOSITION_TIME != 0 {
|
||||
binary.BigEndian.PutUint32(tmp[:], uint32(box.Entries[i].SampleCompositionTimeOffset))
|
||||
nn, err = w.Write(tmp[:])
|
||||
if err != nil {
|
||||
return n + int64(nn), err
|
||||
}
|
||||
n += int64(nn)
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (box *TrackRunBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
|
||||
n := 0
|
||||
trun.SampleCount = binary.BigEndian.Uint32(buf[n:])
|
||||
box.SampleCount = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
|
||||
trunFlags := uint32(fullbox.Flags[0])<<16 | uint32(fullbox.Flags[1])<<8 | uint32(fullbox.Flags[2])
|
||||
flags := uint32(box.Flags[0])<<16 | uint32(box.Flags[1])<<8 | uint32(box.Flags[2])
|
||||
|
||||
if trunFlags&TR_FLAG_DATA_OFFSET != 0 {
|
||||
trun.Dataoffset = int32(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
} else {
|
||||
trun.Dataoffset = dataOffset
|
||||
}
|
||||
|
||||
if trunFlags&TR_FLAG_DATA_FIRST_SAMPLE_FLAGS != 0 {
|
||||
trun.FirstSampleFlags = binary.BigEndian.Uint32(buf[n:])
|
||||
if flags&TR_FLAG_DATA_OFFSET != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.DataOffset = int32(binary.BigEndian.Uint32(buf[n:]))
|
||||
n += 4
|
||||
}
|
||||
|
||||
trun.EntryList = make([]TrunEntry, trun.SampleCount)
|
||||
for i := 0; i < int(trun.SampleCount); i++ {
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_DURATION != 0 {
|
||||
trun.EntryList[i].SampleDuration = binary.BigEndian.Uint32(buf[n:])
|
||||
if flags&TR_FLAG_DATA_FIRST_SAMPLE_FLAGS != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.FirstSampleFlags = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
|
||||
box.Entries = make([]TrunEntry, box.SampleCount)
|
||||
for i := uint32(0); i < box.SampleCount; i++ {
|
||||
if flags&TR_FLAG_DATA_SAMPLE_DURATION != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Entries[i].SampleDuration = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_SIZE != 0 {
|
||||
trun.EntryList[i].SampleSize = binary.BigEndian.Uint32(buf[n:])
|
||||
|
||||
if flags&TR_FLAG_DATA_SAMPLE_SIZE != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Entries[i].SampleSize = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_FLAGS != 0 {
|
||||
trun.EntryList[i].SampleFlags = binary.BigEndian.Uint32(buf[n:])
|
||||
|
||||
if flags&TR_FLAG_DATA_SAMPLE_FLAGS != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Entries[i].SampleFlags = binary.BigEndian.Uint32(buf[n:])
|
||||
n += 4
|
||||
}
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_COMPOSITION_TIME != 0 {
|
||||
if fullbox.Version == 0 {
|
||||
trun.EntryList[i].SampleCompositionTimeOffset = int32(binary.BigEndian.Uint32(buf[n:]))
|
||||
|
||||
if flags&TR_FLAG_DATA_SAMPLE_COMPOSITION_TIME != 0 {
|
||||
if len(buf) < n+4 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
if box.Version == 0 {
|
||||
box.Entries[i].SampleCompositionTimeOffset = int32(binary.BigEndian.Uint32(buf[n:]))
|
||||
} else {
|
||||
trun.EntryList[i].SampleCompositionTimeOffset = int32(binary.BigEndian.Uint32(buf[n:]))
|
||||
box.Entries[i].SampleCompositionTimeOffset = int32(binary.BigEndian.Uint32(buf[n:]))
|
||||
}
|
||||
n += 4
|
||||
}
|
||||
}
|
||||
|
||||
offset += n
|
||||
return
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func (trun *TrackRunBox) Encode(trunFlags uint32) (int, []byte) {
|
||||
// Always use version 1 for signed composition time offsets
|
||||
fullbox := NewFullBox(TypeTRUN, 1)
|
||||
fullbox.Box.Size = trun.Size(trunFlags)
|
||||
fullbox.Flags[0] = byte(trunFlags >> 16)
|
||||
fullbox.Flags[1] = byte(trunFlags >> 8)
|
||||
fullbox.Flags[2] = byte(trunFlags)
|
||||
offset, buf := fullbox.Encode()
|
||||
|
||||
// Write sample count
|
||||
binary.BigEndian.PutUint32(buf[offset:], trun.SampleCount)
|
||||
offset += 4
|
||||
|
||||
// Write data offset if present
|
||||
if trunFlags&TR_FLAG_DATA_OFFSET != 0 {
|
||||
// Write data offset as int32
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(trun.Dataoffset))
|
||||
offset += 4
|
||||
}
|
||||
|
||||
// Write first sample flags if present
|
||||
if trunFlags&TR_FLAG_DATA_FIRST_SAMPLE_FLAGS != 0 {
|
||||
binary.BigEndian.PutUint32(buf[offset:], trun.FirstSampleFlags)
|
||||
offset += 4
|
||||
}
|
||||
|
||||
// Write sample entries in the correct order
|
||||
for i := 0; i < int(trun.SampleCount); i++ {
|
||||
// Write sample duration if present
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_DURATION != 0 {
|
||||
binary.BigEndian.PutUint32(buf[offset:], trun.EntryList[i].SampleDuration)
|
||||
offset += 4
|
||||
}
|
||||
// Write sample size if present
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_SIZE != 0 {
|
||||
binary.BigEndian.PutUint32(buf[offset:], trun.EntryList[i].SampleSize)
|
||||
offset += 4
|
||||
}
|
||||
// Write sample flags if present
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_FLAGS != 0 {
|
||||
binary.BigEndian.PutUint32(buf[offset:], trun.EntryList[i].SampleFlags)
|
||||
offset += 4
|
||||
}
|
||||
// Write sample composition time offset if present
|
||||
if trunFlags&TR_FLAG_DATA_SAMPLE_COMPOSITION_TIME != 0 {
|
||||
// Version 1 uses signed int32 for composition time offset
|
||||
binary.BigEndian.PutUint32(buf[offset:], uint32(trun.EntryList[i].SampleCompositionTimeOffset))
|
||||
offset += 4
|
||||
}
|
||||
}
|
||||
|
||||
return offset, buf
|
||||
func init() {
|
||||
RegisterBox[*TrackRunBox](TypeTRUN)
|
||||
}
|
||||
|
||||
+33
-37
@@ -5,65 +5,61 @@ import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// Box Types: ‘vmhd’, ‘smhd’, ’hmhd’, ‘nmhd’
|
||||
// Container: Media Information Box (‘minf’)
|
||||
// Box Types: 'vmhd', 'smhd', 'hmhd', 'nmhd'
|
||||
// Container: Media Information Box ('minf')
|
||||
// Mandatory: Yes
|
||||
// Quantity: Exactly one specific media header shall be present
|
||||
|
||||
// aligned(8) class VideoMediaHeaderBox
|
||||
// extends FullBox(‘vmhd’, version = 0, 1) {
|
||||
// extends FullBox('vmhd', version = 0, 1) {
|
||||
// template unsigned int(16) graphicsmode = 0; // copy, see below template
|
||||
// unsigned int(16)[3] opcolor = {0, 0, 0};
|
||||
// }
|
||||
|
||||
type VideoMediaHeaderBox struct {
|
||||
FullBox
|
||||
Graphicsmode uint16
|
||||
Opcolor [3]uint16
|
||||
}
|
||||
|
||||
func NewVideoMediaHeaderBox() *VideoMediaHeaderBox {
|
||||
func CreateVideoMediaHeaderBox() *VideoMediaHeaderBox {
|
||||
return &VideoMediaHeaderBox{
|
||||
FullBox: FullBox{
|
||||
BaseBox: BaseBox{
|
||||
typ: TypeVMHD,
|
||||
size: uint32(FullBoxLen + 8),
|
||||
},
|
||||
Version: 0,
|
||||
Flags: [3]byte{0, 0, 1}, // Flags = 1
|
||||
},
|
||||
Graphicsmode: 0,
|
||||
Opcolor: [3]uint16{0, 0, 0},
|
||||
}
|
||||
}
|
||||
|
||||
func (vmhd *VideoMediaHeaderBox) Decode(r io.Reader) (offset int, err error) {
|
||||
var fullbox FullBox
|
||||
if _, err = fullbox.Decode(r); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
buf := make([]byte, 8)
|
||||
if _, err = io.ReadFull(r, buf); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
offset = 0
|
||||
vmhd.Graphicsmode = binary.BigEndian.Uint16(buf[offset:])
|
||||
vmhd.Opcolor[0] = binary.BigEndian.Uint16(buf[offset+2:])
|
||||
vmhd.Opcolor[1] = binary.BigEndian.Uint16(buf[offset+4:])
|
||||
vmhd.Opcolor[2] = binary.BigEndian.Uint16(buf[offset+6:])
|
||||
offset += 8
|
||||
func (box *VideoMediaHeaderBox) WriteTo(w io.Writer) (n int64, err error) {
|
||||
var tmp [8]byte
|
||||
binary.BigEndian.PutUint16(tmp[0:], box.Graphicsmode)
|
||||
binary.BigEndian.PutUint16(tmp[2:], box.Opcolor[0])
|
||||
binary.BigEndian.PutUint16(tmp[4:], box.Opcolor[1])
|
||||
binary.BigEndian.PutUint16(tmp[6:], box.Opcolor[2])
|
||||
|
||||
nn, err := w.Write(tmp[:])
|
||||
n = int64(nn)
|
||||
return
|
||||
}
|
||||
|
||||
func (vmhd *VideoMediaHeaderBox) Encode() (int, []byte) {
|
||||
fullbox := NewFullBox(TypeVMHD, 0)
|
||||
fullbox.Box.Size = FullBoxLen + 8
|
||||
fullbox.Flags[2] = 1
|
||||
offset, buf := fullbox.Encode()
|
||||
binary.BigEndian.PutUint16(buf[offset:], vmhd.Graphicsmode)
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], vmhd.Opcolor[0])
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], vmhd.Opcolor[1])
|
||||
offset += 2
|
||||
binary.BigEndian.PutUint16(buf[offset:], vmhd.Opcolor[2])
|
||||
offset += 2
|
||||
return offset, buf
|
||||
func (box *VideoMediaHeaderBox) Unmarshal(buf []byte) (IBox, error) {
|
||||
if len(buf) < 8 {
|
||||
return nil, io.ErrShortBuffer
|
||||
}
|
||||
box.Graphicsmode = binary.BigEndian.Uint16(buf[0:])
|
||||
box.Opcolor[0] = binary.BigEndian.Uint16(buf[2:])
|
||||
box.Opcolor[1] = binary.BigEndian.Uint16(buf[4:])
|
||||
box.Opcolor[2] = binary.BigEndian.Uint16(buf[6:])
|
||||
return box, nil
|
||||
}
|
||||
|
||||
func MakeVmhdBox() []byte {
|
||||
vmhd := NewVideoMediaHeaderBox()
|
||||
_, vmhdbox := vmhd.Encode()
|
||||
return vmhdbox
|
||||
func init() {
|
||||
RegisterBox[*VideoMediaHeaderBox](TypeVMHD)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user