• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Eyevinn / mp4ff / 11744569652

08 Nov 2024 03:06PM UTC coverage: 77.972% (+11.1%) from 66.885%
11744569652

push

github

tobbee
fix: ExtractInitProtectData was broken for video

4 of 6 new or added lines in 1 file covered. (66.67%)

474 existing lines in 38 files now uncovered.

15231 of 19534 relevant lines covered (77.97%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

77.02
/examples/segmenter/segment.go
1
package main
2

3
import (
4
        "fmt"
5
        "io"
6
        "os"
7

8
        "github.com/Eyevinn/mp4ff/mp4"
9
)
10

11
func makeSingleTrackSegments(segmenter *Segmenter, parsedMp4 *mp4.File, rs io.ReadSeeker, outFilePath string) error {
1✔
12
        fileNameMap := map[string]string{"video": "_v", "audio": "_a"}
1✔
13
        inits, err := segmenter.MakeInitSegments()
1✔
14
        if err != nil {
1✔
15
                return err
×
16
        }
×
17
        for _, init := range inits {
2✔
18
                trackID := init.Moov.Trak.Tkhd.TrackID
1✔
19
                outPath := fmt.Sprintf("%s%s%d_init.mp4", outFilePath, fileNameMap[init.GetMediaType()], trackID)
1✔
20
                err = mp4.WriteToFile(init, outPath)
1✔
21
                if err != nil {
1✔
22
                        return err
×
23
                }
×
24
                fmt.Printf("Generated %s\n", outPath)
1✔
25
        }
26

27
        segNr := 1
1✔
28
        for {
2✔
29
                for _, tr := range segmenter.tracks {
2✔
30
                        mediaType := tr.trackType
1✔
31
                        startSampleNr, endSampleNr := tr.segments[segNr-1].startNr, tr.segments[segNr-1].endNr
1✔
32
                        fmt.Printf("%s: %d-%d\n", tr.trackType, startSampleNr, endSampleNr)
1✔
33
                        fullSamples, err := segmenter.GetFullSamplesForInterval(parsedMp4, tr, startSampleNr, endSampleNr, rs)
1✔
34
                        if len(fullSamples) == 0 {
1✔
35
                                fmt.Printf("No more samples for %s\n", mediaType)
×
36
                                continue
×
37
                        }
38
                        if err != nil {
1✔
39
                                return err
×
40
                        }
×
41
                        seg := mp4.NewMediaSegment()
1✔
42
                        frag, err := mp4.CreateFragment(uint32(segNr), tr.trackID)
1✔
43
                        if err != nil {
1✔
44
                                return err
×
45
                        }
×
46
                        seg.AddFragment(frag)
1✔
47

1✔
48
                        for _, fullSample := range fullSamples {
2✔
49
                                err = frag.AddFullSampleToTrack(fullSample, tr.trackID)
1✔
50
                                if err != nil {
1✔
51
                                        return err
×
52
                                }
×
53
                        }
54
                        outPath := fmt.Sprintf("%s%s%d_%d.m4s", outFilePath, fileNameMap[mediaType], tr.trackID, segNr)
1✔
55
                        err = mp4.WriteToFile(seg, outPath)
1✔
56
                        if err != nil {
1✔
57
                                return err
×
58
                        }
×
59
                        fmt.Printf("Generated %s\n", outPath)
1✔
60
                }
61
                segNr++
1✔
62
                if segNr > segmenter.nrSegs {
2✔
63
                        break
1✔
64
                }
65
        }
66
        return nil
1✔
67
}
68

69
func makeSingleTrackSegmentsLazyWrite(segmenter *Segmenter, parsedMp4 *mp4.File, rs io.ReadSeeker, outFilePath string) error {
1✔
70
        fileNameMap := map[string]string{"video": "_v", "audio": "_a"}
1✔
71
        inits, err := segmenter.MakeInitSegments()
1✔
72
        if err != nil {
1✔
73
                return err
×
74
        }
×
75
        for _, init := range inits {
2✔
76
                trackID := init.Moov.Trak.Tkhd.TrackID
1✔
77
                outPath := fmt.Sprintf("%s%s%d_init.mp4", outFilePath, fileNameMap[init.GetMediaType()], trackID)
1✔
78
                err = mp4.WriteToFile(init, outPath)
1✔
79
                if err != nil {
1✔
80
                        return err
×
81
                }
×
82
                fmt.Printf("Generated %s\n", outPath)
1✔
83
        }
84

85
        segNr := 1
1✔
86
        for {
2✔
87
                for _, tr := range segmenter.tracks {
2✔
88
                        mediaType := tr.trackType
1✔
89
                        startSampleNr, endSampleNr := tr.segments[segNr-1].startNr, tr.segments[segNr-1].endNr
1✔
90
                        fmt.Printf("%s: %d-%d\n", tr.trackType, startSampleNr, endSampleNr)
1✔
91
                        samples, err := segmenter.GetSamplesForInterval(parsedMp4, tr.inTrak, startSampleNr, endSampleNr)
1✔
92
                        if len(samples) == 0 {
1✔
93
                                fmt.Printf("No more samples for %s\n", mediaType)
×
94
                                continue
×
95
                        }
96
                        if err != nil {
1✔
97
                                return err
×
98
                        }
×
99
                        seg := mp4.NewMediaSegment()
1✔
100
                        frag, err := mp4.CreateFragment(uint32(segNr), tr.trackID)
1✔
101
                        if err != nil {
1✔
102
                                return err
×
103
                        }
×
104
                        seg.AddFragment(frag)
1✔
105
                        baseMediaDecodeTime, _ := tr.inTrak.Mdia.Minf.Stbl.Stts.GetDecodeTime(startSampleNr)
1✔
106
                        for _, sample := range samples {
2✔
107
                                err = frag.AddSampleToTrack(sample, tr.trackID, baseMediaDecodeTime)
1✔
108
                                if err != nil {
1✔
109
                                        return err
×
110
                                }
×
111
                        }
112
                        outPath := fmt.Sprintf("%s%s%d_%d.m4s", outFilePath, fileNameMap[mediaType], tr.trackID, segNr)
1✔
113
                        ofh, err := os.Create(outPath)
1✔
114
                        if err != nil {
1✔
115
                                return err
×
116
                        }
×
117
                        defer ofh.Close()
1✔
118
                        err = seg.Encode(ofh)
1✔
119
                        if err != nil {
1✔
120
                                return err
×
121
                        }
×
122
                        // Also write media data
123
                        err = copyMediaData(tr.inTrak, startSampleNr, endSampleNr, rs, ofh)
1✔
124
                        if err != nil {
1✔
125
                                return err
×
126
                        }
×
127
                        fmt.Printf("Generated %s\n", outPath)
1✔
128
                }
129
                segNr++
1✔
130
                if segNr > segmenter.nrSegs {
2✔
131
                        break
1✔
132
                }
133
        }
134
        return nil
1✔
135
}
136

137
func makeMultiTrackSegments(segmenter *Segmenter, parsedMp4 *mp4.File, rs io.ReadSeeker, outFilePath string) error {
1✔
138
        init, err := segmenter.MakeMuxedInitSegment()
1✔
139
        if err != nil {
1✔
140
                return err
×
141
        }
×
142
        outPath := fmt.Sprintf("%s_init.mp4", outFilePath)
1✔
143
        err = mp4.WriteToFile(init, outPath)
1✔
144
        if err != nil {
1✔
145
                return err
×
146
        }
×
147
        fmt.Printf("Generated %s\n", outPath)
1✔
148
        var trackIDs []uint32
1✔
149
        for _, trak := range init.Moov.Traks {
2✔
150
                trackIDs = append(trackIDs, trak.Tkhd.TrackID)
1✔
151
        }
1✔
152

153
        segNr := 1
1✔
154
        for {
2✔
155
                seg := mp4.NewMediaSegment()
1✔
156
                frag, err := mp4.CreateMultiTrackFragment(uint32(segNr), trackIDs)
1✔
157
                if err != nil {
1✔
158
                        return err
×
159
                }
×
160
                seg.AddFragment(frag)
1✔
161

1✔
162
                for _, tr := range segmenter.tracks {
2✔
163
                        startSampleNr, endSampleNr := tr.segments[segNr-1].startNr, tr.segments[segNr-1].endNr
1✔
164
                        fmt.Printf("%s: %d-%d\n", tr.trackType, startSampleNr, endSampleNr)
1✔
165
                        fullSamples, err := segmenter.GetFullSamplesForInterval(parsedMp4, tr, startSampleNr, endSampleNr, rs)
1✔
166
                        if len(fullSamples) == 0 {
1✔
167
                                continue
×
168
                        }
169
                        if err != nil {
1✔
170
                                return err
×
171
                        }
×
172
                        for _, sample := range fullSamples {
2✔
173
                                err = frag.AddFullSampleToTrack(sample, tr.trackID)
1✔
174
                                if err != nil {
1✔
175
                                        return err
×
176
                                }
×
177
                        }
178
                }
179
                outPath := fmt.Sprintf("%s_media_%d.m4s", outFilePath, segNr)
1✔
180
                err = mp4.WriteToFile(seg, outPath)
1✔
181
                if err != nil {
1✔
182
                        return err
×
183
                }
×
184
                fmt.Printf("Generated %s\n", outPath)
1✔
185
                segNr++
1✔
186
                if segNr > segmenter.nrSegs {
2✔
187
                        break
1✔
188
                }
189
        }
190
        return nil
1✔
191
}
192

193
type syncPoint struct {
194
        sampleNr   uint32
195
        decodeTime uint64
196
        presTime   uint64
197
}
198

199
func getSegmentStartsFromVideo(parsedMp4 *mp4.File, segDurMS uint32) (timeScale uint32, syncPoints []syncPoint) {
1✔
200
        var refTrak *mp4.TrakBox
1✔
201
        for _, trak := range parsedMp4.Moov.Traks {
2✔
202
                hdlrType := trak.Mdia.Hdlr.HandlerType
1✔
203
                if hdlrType == "vide" {
2✔
204
                        refTrak = trak
1✔
205
                        break
1✔
206
                }
207
        }
208
        if refTrak == nil {
1✔
UNCOV
209
                panic("Cannot handle case with no video track yet")
×
210
        }
211
        timeScale = refTrak.Mdia.Mdhd.Timescale
1✔
212
        stts := refTrak.Mdia.Minf.Stbl.Stts
1✔
213
        stss := refTrak.Mdia.Minf.Stbl.Stss
1✔
214
        ctts := refTrak.Mdia.Minf.Stbl.Ctts
1✔
215
        syncPoints = make([]syncPoint, 0, stss.EntryCount())
1✔
216
        var segmentStep uint32 = uint32(uint64(segDurMS) * uint64(timeScale) / 1000)
1✔
217
        var nextSegmentStart uint32 = 0
1✔
218
        for _, sampleNr := range stss.SampleNumber {
2✔
219
                decodeTime, _ := stts.GetDecodeTime(sampleNr)
1✔
220
                presTime := int64(decodeTime)
1✔
221
                if ctts != nil {
2✔
222
                        presTime += int64(ctts.GetCompositionTimeOffset(sampleNr))
1✔
223
                }
1✔
224
                if presTime >= int64(nextSegmentStart) {
2✔
225
                        syncPoints = append(syncPoints, syncPoint{sampleNr, decodeTime, uint64(presTime)})
1✔
226
                        nextSegmentStart += segmentStep
1✔
227
                }
1✔
228
        }
229
        return timeScale, syncPoints
1✔
230
}
231

232
type sampleInterval struct {
233
        startNr uint32
234
        endNr   uint32 // included in interval
235
}
236

237
func getSegmentIntervals(syncTimescale uint32, syncPoints []syncPoint, trak *mp4.TrakBox) ([]sampleInterval, error) {
1✔
238
        totNrSamples := trak.Mdia.Minf.Stbl.Stsz.SampleNumber
1✔
239
        var startSampleNr uint32 = 1
1✔
240
        var nextStartSampleNr uint32 = 0
1✔
241
        var endSampleNr uint32
1✔
242
        var err error
1✔
243

1✔
244
        sampleIntervals := make([]sampleInterval, len(syncPoints))
1✔
245

1✔
246
        for i := range syncPoints {
2✔
247
                if nextStartSampleNr != 0 {
2✔
248
                        startSampleNr = nextStartSampleNr
1✔
249
                }
1✔
250
                if i == len(syncPoints)-1 {
2✔
251
                        endSampleNr = totNrSamples - 1
1✔
252
                } else {
2✔
253
                        nextSyncStart := syncPoints[i+1].decodeTime
1✔
254
                        nextStartTime := nextSyncStart * uint64(trak.Mdia.Mdhd.Timescale) / uint64(syncTimescale)
1✔
255
                        nextStartSampleNr, err = trak.Mdia.Minf.Stbl.Stts.GetSampleNrAtTime(nextStartTime)
1✔
256
                        if err != nil {
1✔
257
                                return nil, err
×
258
                        }
×
259
                        endSampleNr = nextStartSampleNr - 1
1✔
260
                }
261
                sampleIntervals[i] = sampleInterval{startSampleNr, endSampleNr}
1✔
262
        }
263
        fmt.Printf("Sample intervals: %v\n", sampleIntervals)
1✔
264
        return sampleIntervals, nil
1✔
265
}
266

267
func copyMediaData(trak *mp4.TrakBox, startSampleNr, endSampleNr uint32, rs io.ReadSeeker, w io.Writer) error {
1✔
268
        stbl := trak.Mdia.Minf.Stbl
1✔
269
        chunks, err := stbl.Stsc.GetContainingChunks(startSampleNr, endSampleNr)
1✔
270
        if err != nil {
1✔
271
                return err
×
272
        }
×
273
        var offset uint64
1✔
274
        var startNr, endNr uint32
1✔
275
        for i, chunk := range chunks {
2✔
276
                if stbl.Co64 != nil {
1✔
277
                        offset = stbl.Co64.ChunkOffset[chunk.ChunkNr-1]
×
278
                } else if stbl.Stco != nil {
2✔
279
                        offset = uint64(stbl.Stco.ChunkOffset[chunk.ChunkNr-1])
1✔
280
                }
1✔
281
                startNr = chunk.StartSampleNr
1✔
282
                endNr = startNr + chunk.NrSamples - 1
1✔
283
                if i == 0 {
2✔
284
                        for sNr := chunk.StartSampleNr; sNr < startSampleNr; sNr++ {
2✔
285
                                offset += uint64(stbl.Stsz.GetSampleSize(int(sNr)))
1✔
286
                        }
1✔
287
                        startNr = startSampleNr
1✔
288
                }
289

290
                if i == len(chunks)-1 {
2✔
291
                        endNr = endSampleNr
1✔
292
                }
1✔
293
                var size int64
1✔
294
                for sNr := startNr; sNr <= endNr; sNr++ {
2✔
295
                        size += int64(stbl.Stsz.GetSampleSize(int(sNr)))
1✔
296
                }
1✔
297
                _, err := rs.Seek(int64(offset), io.SeekStart)
1✔
298
                if err != nil {
1✔
299
                        return err
×
300
                }
×
301
                n, err := io.CopyN(w, rs, size)
1✔
302
                if err != nil {
1✔
303
                        return err
×
304
                }
×
305
                if n != size {
1✔
306
                        return fmt.Errorf("copied %d bytes instead of %d", n, size)
×
307
                }
×
308
        }
309

310
        return nil
1✔
311
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc