GoToSocial/vendor/github.com/abema/go-mp4/box_info.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)
}