in progress

This commit is contained in:
langhuihui
2025-02-08 17:36:28 +08:00
committed by dexter
parent 180e766a24
commit f4923d9df6
50 changed files with 4250 additions and 3157 deletions
+240 -94
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
-105
View File
@@ -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
}
+132
View File
@@ -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)
}
+33 -1
View File
@@ -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)
}
+5 -19
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+71
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+85
View File
@@ -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)
}
+70
View File
@@ -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)
}
+8 -7
View File
@@ -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
}
+56
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+60 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}
+153
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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)
}