160 lines
4.0 KiB
Go
160 lines
4.0 KiB
Go
package mp4
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"io"
|
|
"math"
|
|
)
|
|
|
|
type Context struct {
|
|
// IsQuickTimeCompatible represents whether ftyp.compatible_brands contains "qt ".
|
|
IsQuickTimeCompatible bool
|
|
|
|
// UnderWave represents whether current box is under the wave box.
|
|
UnderWave bool
|
|
|
|
// UnderIlst represents whether current box is under the ilst box.
|
|
UnderIlst bool
|
|
|
|
// UnderIlstMeta represents whether current box is under the metadata box under the ilst box.
|
|
UnderIlstMeta bool
|
|
|
|
// UnderIlstFreeMeta represents whether current box is under "----" box.
|
|
UnderIlstFreeMeta bool
|
|
|
|
// UnderUdta represents whether current box is under the udta box.
|
|
UnderUdta bool
|
|
}
|
|
|
|
// BoxInfo has common infomations of box
|
|
type BoxInfo struct {
|
|
// Offset specifies an offset of the box in a file.
|
|
Offset uint64
|
|
|
|
// Size specifies size(bytes) of box.
|
|
Size uint64
|
|
|
|
// HeaderSize specifies size(bytes) of common fields which are defined as "Box" class member at ISO/IEC 14496-12.
|
|
HeaderSize uint64
|
|
|
|
// Type specifies box type which is represented by 4 characters.
|
|
Type BoxType
|
|
|
|
// ExtendToEOF is set true when Box.size is zero. It means that end of box equals to end of file.
|
|
ExtendToEOF bool
|
|
|
|
// Context would be set by ReadBoxStructure, not ReadBoxInfo.
|
|
Context
|
|
}
|
|
|
|
func (bi *BoxInfo) IsSupportedType() bool {
|
|
return bi.Type.IsSupported(bi.Context)
|
|
}
|
|
|
|
const (
|
|
SmallHeaderSize = 8
|
|
LargeHeaderSize = 16
|
|
)
|
|
|
|
// WriteBoxInfo writes common fields which are defined as "Box" class member at ISO/IEC 14496-12.
|
|
// This function ignores bi.Offset and returns BoxInfo which contains real Offset and recalculated Size/HeaderSize.
|
|
func WriteBoxInfo(w io.WriteSeeker, bi *BoxInfo) (*BoxInfo, error) {
|
|
offset, err := w.Seek(0, io.SeekCurrent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var data []byte
|
|
if bi.ExtendToEOF {
|
|
data = make([]byte, SmallHeaderSize)
|
|
} else if bi.Size <= math.MaxUint32 && bi.HeaderSize != LargeHeaderSize {
|
|
data = make([]byte, SmallHeaderSize)
|
|
binary.BigEndian.PutUint32(data, uint32(bi.Size))
|
|
} else {
|
|
data = make([]byte, LargeHeaderSize)
|
|
binary.BigEndian.PutUint32(data, 1)
|
|
binary.BigEndian.PutUint64(data[SmallHeaderSize:], bi.Size)
|
|
}
|
|
data[4] = bi.Type[0]
|
|
data[5] = bi.Type[1]
|
|
data[6] = bi.Type[2]
|
|
data[7] = bi.Type[3]
|
|
|
|
if _, err := w.Write(data); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &BoxInfo{
|
|
Offset: uint64(offset),
|
|
Size: bi.Size - bi.HeaderSize + uint64(len(data)),
|
|
HeaderSize: uint64(len(data)),
|
|
Type: bi.Type,
|
|
ExtendToEOF: bi.ExtendToEOF,
|
|
}, nil
|
|
}
|
|
|
|
// ReadBoxInfo reads common fields which are defined as "Box" class member at ISO/IEC 14496-12.
|
|
func ReadBoxInfo(r io.ReadSeeker) (*BoxInfo, error) {
|
|
offset, err := r.Seek(0, io.SeekCurrent)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
bi := &BoxInfo{
|
|
Offset: uint64(offset),
|
|
}
|
|
|
|
// read 8 bytes
|
|
buf := bytes.NewBuffer(make([]byte, 0, SmallHeaderSize))
|
|
if _, err := io.CopyN(buf, r, SmallHeaderSize); err != nil {
|
|
return nil, err
|
|
}
|
|
bi.HeaderSize += SmallHeaderSize
|
|
|
|
// pick size and type
|
|
data := buf.Bytes()
|
|
bi.Size = uint64(binary.BigEndian.Uint32(data))
|
|
bi.Type = BoxType{data[4], data[5], data[6], data[7]}
|
|
|
|
if bi.Size == 0 {
|
|
// box extends to end of file
|
|
offsetEOF, err := r.Seek(0, io.SeekEnd)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
bi.Size = uint64(offsetEOF) - bi.Offset
|
|
bi.ExtendToEOF = true
|
|
if _, err := bi.SeekToPayload(r); err != nil {
|
|
return nil, err
|
|
}
|
|
} else if bi.Size == 1 {
|
|
// read more 8 bytes
|
|
buf.Reset()
|
|
if _, err := io.CopyN(buf, r, LargeHeaderSize-SmallHeaderSize); err != nil {
|
|
return nil, err
|
|
}
|
|
bi.HeaderSize += LargeHeaderSize - SmallHeaderSize
|
|
bi.Size = binary.BigEndian.Uint64(buf.Bytes())
|
|
}
|
|
|
|
if bi.Size == 0 {
|
|
return nil, fmt.Errorf("invalid size")
|
|
}
|
|
|
|
return bi, nil
|
|
}
|
|
|
|
func (bi *BoxInfo) SeekToStart(s io.Seeker) (int64, error) {
|
|
return s.Seek(int64(bi.Offset), io.SeekStart)
|
|
}
|
|
|
|
func (bi *BoxInfo) SeekToPayload(s io.Seeker) (int64, error) {
|
|
return s.Seek(int64(bi.Offset+bi.HeaderSize), io.SeekStart)
|
|
}
|
|
|
|
func (bi *BoxInfo) SeekToEnd(s io.Seeker) (int64, error) {
|
|
return s.Seek(int64(bi.Offset+bi.Size), io.SeekStart)
|
|
}
|