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 }