github.com/bluenviron/mediacommon@v1.9.3/pkg/formats/fmp4/parts.go (about) 1 package fmp4 2 3 import ( 4 "bytes" 5 "fmt" 6 "io" 7 8 "github.com/abema/go-mp4" 9 ) 10 11 // Parts is a sequence of fMP4 parts. 12 type Parts []*Part 13 14 // Unmarshal decodes one or more fMP4 parts. 15 func (ps *Parts) Unmarshal(byts []byte) error { 16 type readState int 17 18 const ( 19 waitingMoof readState = iota 20 waitingMfhd 21 waitingTraf 22 waitingTfdtTfhdTrun 23 ) 24 25 state := waitingMoof 26 var curPart *Part 27 var moofOffset uint64 28 var curTrack *PartTrack 29 var tfdt *mp4.Tfdt 30 var tfhd *mp4.Tfhd 31 32 _, err := mp4.ReadBoxStructure(bytes.NewReader(byts), func(h *mp4.ReadHandle) (interface{}, error) { 33 if h.BoxInfo.IsSupportedType() { 34 switch h.BoxInfo.Type.String() { 35 case "moof": 36 if state != waitingMoof { 37 return nil, fmt.Errorf("unexpected moof") 38 } 39 40 curPart = &Part{} 41 *ps = append(*ps, curPart) 42 moofOffset = h.BoxInfo.Offset 43 state = waitingMfhd 44 return h.Expand() 45 46 case "mfhd": 47 if state != waitingMfhd { 48 return nil, fmt.Errorf("unexpected mfhd") 49 } 50 51 box, _, err := h.ReadPayload() 52 if err != nil { 53 return nil, err 54 } 55 mfhd := box.(*mp4.Mfhd) 56 57 curPart.SequenceNumber = mfhd.SequenceNumber 58 state = waitingTraf 59 60 case "traf": 61 if state != waitingTraf && state != waitingTfdtTfhdTrun { 62 return nil, fmt.Errorf("unexpected traf") 63 } 64 65 if curTrack != nil { 66 if tfdt == nil || tfhd == nil || curTrack.Samples == nil { 67 return nil, fmt.Errorf("parse error") 68 } 69 } 70 71 curTrack = &PartTrack{} 72 curPart.Tracks = append(curPart.Tracks, curTrack) 73 tfdt = nil 74 tfhd = nil 75 state = waitingTfdtTfhdTrun 76 return h.Expand() 77 78 case "tfhd": 79 if state != waitingTfdtTfhdTrun || tfhd != nil { 80 return nil, fmt.Errorf("unexpected tfhd") 81 } 82 83 box, _, err := h.ReadPayload() 84 if err != nil { 85 return nil, err 86 } 87 tfhd = box.(*mp4.Tfhd) 88 89 curTrack.ID = int(tfhd.TrackID) 90 91 case "tfdt": 92 if state != waitingTfdtTfhdTrun || tfdt != nil { 93 return nil, fmt.Errorf("unexpected tfdt") 94 } 95 96 box, _, err := h.ReadPayload() 97 if err != nil { 98 return nil, err 99 } 100 tfdt = box.(*mp4.Tfdt) 101 102 if tfdt.FullBox.Version != 1 { 103 return nil, fmt.Errorf("unsupported tfdt version") 104 } 105 106 curTrack.BaseTime = tfdt.BaseMediaDecodeTimeV1 107 108 case "trun": 109 if state != waitingTfdtTfhdTrun || tfhd == nil { 110 return nil, fmt.Errorf("unexpected trun") 111 } 112 113 box, _, err := h.ReadPayload() 114 if err != nil { 115 return nil, err 116 } 117 trun := box.(*mp4.Trun) 118 119 trunFlags := uint16(trun.Flags[1])<<8 | uint16(trun.Flags[2]) 120 if (trunFlags & trunFlagDataOffsetPreset) == 0 { 121 return nil, fmt.Errorf("unsupported flags") 122 } 123 124 existing := len(curTrack.Samples) 125 tmp := make([]*PartSample, existing+len(trun.Entries)) 126 copy(tmp, curTrack.Samples) 127 curTrack.Samples = tmp 128 129 pos := uint64(trun.DataOffset) + moofOffset 130 if uint64(len(byts)) < pos { 131 return nil, fmt.Errorf("invalid data_offset / moof_offset") 132 } 133 134 ptr := byts[pos:] 135 136 for i, e := range trun.Entries { 137 s := &PartSample{} 138 139 if (trunFlags & trunFlagSampleDurationPresent) != 0 { 140 s.Duration = e.SampleDuration 141 } else { 142 s.Duration = tfhd.DefaultSampleDuration 143 } 144 145 s.PTSOffset = e.SampleCompositionTimeOffsetV1 146 147 var sampleFlags uint32 148 if (trunFlags & trunFlagSampleFlagsPresent) != 0 { 149 sampleFlags = e.SampleFlags 150 } else { 151 sampleFlags = tfhd.DefaultSampleFlags 152 } 153 s.IsNonSyncSample = ((sampleFlags & sampleFlagIsNonSyncSample) != 0) 154 155 var size uint32 156 if (trunFlags & trunFlagSampleSizePresent) != 0 { 157 size = e.SampleSize 158 } else { 159 size = tfhd.DefaultSampleSize 160 } 161 162 if len(ptr) < int(size) { 163 return nil, fmt.Errorf("invalid sample size") 164 } 165 166 s.Payload = ptr[:size] 167 ptr = ptr[size:] 168 169 curTrack.Samples[existing+i] = s 170 } 171 172 case "mdat": 173 if state != waitingTraf && state != waitingTfdtTfhdTrun { 174 return nil, fmt.Errorf("unexpected mdat") 175 } 176 177 if curTrack != nil { 178 if tfdt == nil || tfhd == nil || curTrack.Samples == nil { 179 return nil, fmt.Errorf("parse error") 180 } 181 } 182 183 state = waitingMoof 184 } 185 } 186 187 return nil, nil 188 }) 189 if err != nil { 190 return err 191 } 192 193 if state != waitingMoof { 194 return fmt.Errorf("decode error") 195 } 196 197 return nil 198 } 199 200 // Marshal encodes a one or more fMP4 part. 201 func (ps *Parts) Marshal(w io.WriteSeeker) error { 202 for _, p := range *ps { 203 err := p.Marshal(w) 204 if err != nil { 205 return err 206 } 207 } 208 return nil 209 }