gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

box_info.go (4068B)


      1 package mp4
      2 
      3 import (
      4 	"bytes"
      5 	"encoding/binary"
      6 	"io"
      7 	"math"
      8 )
      9 
     10 type Context struct {
     11 	// IsQuickTimeCompatible represents whether ftyp.compatible_brands contains "qt  ".
     12 	IsQuickTimeCompatible bool
     13 
     14 	// UnderWave represents whether current box is under the wave box.
     15 	UnderWave bool
     16 
     17 	// UnderIlst represents whether current box is under the ilst box.
     18 	UnderIlst bool
     19 
     20 	// UnderIlstMeta represents whether current box is under the metadata box under the ilst box.
     21 	UnderIlstMeta bool
     22 
     23 	// UnderIlstFreeMeta represents whether current box is under "----" box.
     24 	UnderIlstFreeMeta bool
     25 
     26 	// UnderUdta represents whether current box is under the udta box.
     27 	UnderUdta bool
     28 }
     29 
     30 // BoxInfo has common infomations of box
     31 type BoxInfo struct {
     32 	// Offset specifies an offset of the box in a file.
     33 	Offset uint64
     34 
     35 	// Size specifies size(bytes) of box.
     36 	Size uint64
     37 
     38 	// HeaderSize specifies size(bytes) of common fields which are defined as "Box" class member at ISO/IEC 14496-12.
     39 	HeaderSize uint64
     40 
     41 	// Type specifies box type which is represented by 4 characters.
     42 	Type BoxType
     43 
     44 	// ExtendToEOF is set true when Box.size is zero. It means that end of box equals to end of file.
     45 	ExtendToEOF bool
     46 
     47 	// Context would be set by ReadBoxStructure, not ReadBoxInfo.
     48 	Context
     49 }
     50 
     51 func (bi *BoxInfo) IsSupportedType() bool {
     52 	return bi.Type.IsSupported(bi.Context)
     53 }
     54 
     55 const (
     56 	SmallHeaderSize = 8
     57 	LargeHeaderSize = 16
     58 )
     59 
     60 // WriteBoxInfo writes common fields which are defined as "Box" class member at ISO/IEC 14496-12.
     61 // This function ignores bi.Offset and returns BoxInfo which contains real Offset and recalculated Size/HeaderSize.
     62 func WriteBoxInfo(w io.WriteSeeker, bi *BoxInfo) (*BoxInfo, error) {
     63 	offset, err := w.Seek(0, io.SeekCurrent)
     64 	if err != nil {
     65 		return nil, err
     66 	}
     67 
     68 	var data []byte
     69 	if bi.ExtendToEOF {
     70 		data = make([]byte, SmallHeaderSize)
     71 	} else if bi.Size <= math.MaxUint32 && bi.HeaderSize != LargeHeaderSize {
     72 		data = make([]byte, SmallHeaderSize)
     73 		binary.BigEndian.PutUint32(data, uint32(bi.Size))
     74 	} else {
     75 		data = make([]byte, LargeHeaderSize)
     76 		binary.BigEndian.PutUint32(data, 1)
     77 		binary.BigEndian.PutUint64(data[SmallHeaderSize:], bi.Size)
     78 	}
     79 	data[4] = bi.Type[0]
     80 	data[5] = bi.Type[1]
     81 	data[6] = bi.Type[2]
     82 	data[7] = bi.Type[3]
     83 
     84 	if _, err := w.Write(data); err != nil {
     85 		return nil, err
     86 	}
     87 
     88 	return &BoxInfo{
     89 		Offset:      uint64(offset),
     90 		Size:        bi.Size - bi.HeaderSize + uint64(len(data)),
     91 		HeaderSize:  uint64(len(data)),
     92 		Type:        bi.Type,
     93 		ExtendToEOF: bi.ExtendToEOF,
     94 	}, nil
     95 }
     96 
     97 // ReadBoxInfo reads common fields which are defined as "Box" class member at ISO/IEC 14496-12.
     98 func ReadBoxInfo(r io.ReadSeeker) (*BoxInfo, error) {
     99 	offset, err := r.Seek(0, io.SeekCurrent)
    100 	if err != nil {
    101 		return nil, err
    102 	}
    103 
    104 	bi := &BoxInfo{
    105 		Offset: uint64(offset),
    106 	}
    107 
    108 	// read 8 bytes
    109 	buf := bytes.NewBuffer(make([]byte, 0, SmallHeaderSize))
    110 	if _, err := io.CopyN(buf, r, SmallHeaderSize); err != nil {
    111 		return nil, err
    112 	}
    113 	bi.HeaderSize += SmallHeaderSize
    114 
    115 	// pick size and type
    116 	data := buf.Bytes()
    117 	bi.Size = uint64(binary.BigEndian.Uint32(data))
    118 	bi.Type = BoxType{data[4], data[5], data[6], data[7]}
    119 
    120 	if bi.Size == 0 {
    121 		// box extends to end of file
    122 		offsetEOF, err := r.Seek(0, io.SeekEnd)
    123 		if err != nil {
    124 			return nil, err
    125 		}
    126 		bi.Size = uint64(offsetEOF) - bi.Offset
    127 		bi.ExtendToEOF = true
    128 		if _, err := bi.SeekToPayload(r); err != nil {
    129 			return nil, err
    130 		}
    131 
    132 	} else if bi.Size == 1 {
    133 		// read more 8 bytes
    134 		buf.Reset()
    135 		if _, err := io.CopyN(buf, r, LargeHeaderSize-SmallHeaderSize); err != nil {
    136 			return nil, err
    137 		}
    138 		bi.HeaderSize += LargeHeaderSize - SmallHeaderSize
    139 		bi.Size = binary.BigEndian.Uint64(buf.Bytes())
    140 	}
    141 
    142 	return bi, nil
    143 }
    144 
    145 func (bi *BoxInfo) SeekToStart(s io.Seeker) (int64, error) {
    146 	return s.Seek(int64(bi.Offset), io.SeekStart)
    147 }
    148 
    149 func (bi *BoxInfo) SeekToPayload(s io.Seeker) (int64, error) {
    150 	return s.Seek(int64(bi.Offset+bi.HeaderSize), io.SeekStart)
    151 }
    152 
    153 func (bi *BoxInfo) SeekToEnd(s io.Seeker) (int64, error) {
    154 	return s.Seek(int64(bi.Offset+bi.Size), io.SeekStart)
    155 }