read.go (4563B)
1 package mp4 2 3 import ( 4 "errors" 5 "fmt" 6 "io" 7 ) 8 9 type BoxPath []BoxType 10 11 func (lhs BoxPath) compareWith(rhs BoxPath) (forwardMatch bool, match bool) { 12 if len(lhs) > len(rhs) { 13 return false, false 14 } 15 for i := 0; i < len(lhs); i++ { 16 if !lhs[i].MatchWith(rhs[i]) { 17 return false, false 18 } 19 } 20 if len(lhs) < len(rhs) { 21 return true, false 22 } 23 return false, true 24 } 25 26 type ReadHandle struct { 27 Params []interface{} 28 BoxInfo BoxInfo 29 Path BoxPath 30 ReadPayload func() (box IBox, n uint64, err error) 31 ReadData func(io.Writer) (n uint64, err error) 32 Expand func(params ...interface{}) (vals []interface{}, err error) 33 } 34 35 type ReadHandler func(handle *ReadHandle) (val interface{}, err error) 36 37 func ReadBoxStructure(r io.ReadSeeker, handler ReadHandler, params ...interface{}) ([]interface{}, error) { 38 if _, err := r.Seek(0, io.SeekStart); err != nil { 39 return nil, err 40 } 41 return readBoxStructure(r, 0, true, nil, Context{}, handler, params) 42 } 43 44 func ReadBoxStructureFromInternal(r io.ReadSeeker, bi *BoxInfo, handler ReadHandler, params ...interface{}) (interface{}, error) { 45 return readBoxStructureFromInternal(r, bi, nil, handler, params) 46 } 47 48 func readBoxStructureFromInternal(r io.ReadSeeker, bi *BoxInfo, path BoxPath, handler ReadHandler, params []interface{}) (interface{}, error) { 49 if _, err := bi.SeekToPayload(r); err != nil { 50 return nil, err 51 } 52 53 // check comatible-brands 54 if len(path) == 0 && bi.Type == BoxTypeFtyp() { 55 var ftyp Ftyp 56 if _, err := Unmarshal(r, bi.Size-bi.HeaderSize, &ftyp, bi.Context); err != nil { 57 return nil, err 58 } 59 if ftyp.HasCompatibleBrand(BrandQT()) { 60 bi.IsQuickTimeCompatible = true 61 } 62 if _, err := bi.SeekToPayload(r); err != nil { 63 return nil, err 64 } 65 } 66 67 ctx := bi.Context 68 if bi.Type == BoxTypeWave() { 69 ctx.UnderWave = true 70 } else if bi.Type == BoxTypeIlst() { 71 ctx.UnderIlst = true 72 } else if bi.UnderIlst && !bi.UnderIlstMeta && IsIlstMetaBoxType(bi.Type) { 73 ctx.UnderIlstMeta = true 74 if bi.Type == StrToBoxType("----") { 75 ctx.UnderIlstFreeMeta = true 76 } 77 } else if bi.Type == BoxTypeUdta() { 78 ctx.UnderUdta = true 79 } 80 81 newPath := make(BoxPath, len(path)+1) 82 copy(newPath, path) 83 newPath[len(path)] = bi.Type 84 85 h := &ReadHandle{ 86 Params: params, 87 BoxInfo: *bi, 88 Path: newPath, 89 } 90 91 var childrenOffset uint64 92 93 h.ReadPayload = func() (IBox, uint64, error) { 94 if _, err := bi.SeekToPayload(r); err != nil { 95 return nil, 0, err 96 } 97 98 box, n, err := UnmarshalAny(r, bi.Type, bi.Size-bi.HeaderSize, bi.Context) 99 if err != nil { 100 return nil, 0, err 101 } 102 childrenOffset = bi.Offset + bi.HeaderSize + n 103 return box, n, nil 104 } 105 106 h.ReadData = func(w io.Writer) (uint64, error) { 107 if _, err := bi.SeekToPayload(r); err != nil { 108 return 0, err 109 } 110 111 size := bi.Size - bi.HeaderSize 112 if _, err := io.CopyN(w, r, int64(size)); err != nil { 113 return 0, err 114 } 115 return size, nil 116 } 117 118 h.Expand = func(params ...interface{}) ([]interface{}, error) { 119 if childrenOffset == 0 { 120 if _, err := bi.SeekToPayload(r); err != nil { 121 return nil, err 122 } 123 124 _, n, err := UnmarshalAny(r, bi.Type, bi.Size-bi.HeaderSize, bi.Context) 125 if err != nil { 126 return nil, err 127 } 128 childrenOffset = bi.Offset + bi.HeaderSize + n 129 } else { 130 if _, err := r.Seek(int64(childrenOffset), io.SeekStart); err != nil { 131 return nil, err 132 } 133 } 134 135 childrenSize := bi.Offset + bi.Size - childrenOffset 136 return readBoxStructure(r, childrenSize, false, newPath, ctx, handler, params) 137 } 138 139 if val, err := handler(h); err != nil { 140 return nil, err 141 } else if _, err := bi.SeekToEnd(r); err != nil { 142 return nil, err 143 } else { 144 return val, nil 145 } 146 } 147 148 func readBoxStructure(r io.ReadSeeker, totalSize uint64, isRoot bool, path BoxPath, ctx Context, handler ReadHandler, params []interface{}) ([]interface{}, error) { 149 vals := make([]interface{}, 0, 8) 150 151 for isRoot || totalSize >= SmallHeaderSize { 152 bi, err := ReadBoxInfo(r) 153 if isRoot && err == io.EOF { 154 return vals, nil 155 } else if err != nil { 156 return nil, err 157 } 158 159 if !isRoot && bi.Size > totalSize { 160 return nil, fmt.Errorf("too large box size: type=%s, size=%d, actualBufSize=%d", bi.Type.String(), bi.Size, totalSize) 161 } 162 totalSize -= bi.Size 163 164 bi.Context = ctx 165 166 val, err := readBoxStructureFromInternal(r, bi, path, handler, params) 167 if err != nil { 168 return nil, err 169 } 170 vals = append(vals, val) 171 172 if bi.IsQuickTimeCompatible { 173 ctx.IsQuickTimeCompatible = true 174 } 175 } 176 177 if totalSize != 0 && !ctx.IsQuickTimeCompatible { 178 return nil, errors.New("Unexpected EOF") 179 } 180 181 return vals, nil 182 }