-
Notifications
You must be signed in to change notification settings - Fork 89
/
Copy pathmain.go
145 lines (135 loc) · 3.74 KB
/
main.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
package main
import (
"fmt"
"io"
"os"
"path"
"github.com/Eyevinn/mp4ff/bits"
"github.com/Eyevinn/mp4ff/mp4"
)
func main() {
if err := run("."); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
func run(outDir string) error {
trackIDs := []uint32{1, 2}
initSegFiles := []string{"testdata/V300/init.mp4", "testdata/A48/init.mp4"}
combinedInitSeg, err := combineInitSegments(initSegFiles, trackIDs)
if err != nil {
return err
}
err = writeSeg(combinedInitSeg, path.Join(outDir, "combined-init.mp4"))
if err != nil {
return err
}
mediaSegFiles := []string{"testdata/V300/1.m4s", "testdata/A48/1.m4s"}
combinedMediaSeg, err := combineMediaSegments(mediaSegFiles, trackIDs)
if err != nil {
return err
}
return writeSeg(combinedMediaSeg, path.Join(outDir, "combined-1.m4s"))
}
func combineInitSegments(files []string, newTrackIDs []uint32) (*mp4.InitSegment, error) {
var combinedInit *mp4.InitSegment
for i := 0; i < len(files); i++ {
data, err := os.ReadFile(files[i])
if err != nil {
return nil, fmt.Errorf("failed to read init segment: %w", err)
}
sr := bits.NewFixedSliceReader(data)
f, err := mp4.DecodeFileSR(sr)
if err != nil {
return nil, fmt.Errorf("failed to decode init segment: %w", err)
}
init := f.Init
if len(init.Moov.Traks) != 1 {
return nil, fmt.Errorf("expected exactly one track per init file")
}
init.Moov.Trak.Tkhd.TrackID = newTrackIDs[i]
if init.Moov.Mvex != nil && init.Moov.Mvex.Trex != nil {
init.Moov.Mvex.Trex.TrackID = newTrackIDs[i]
}
if i == 0 {
combinedInit = init
} else {
combinedInit.Moov.AddChild(init.Moov.Trak)
if init.Moov.Mvex != nil {
if init.Moov.Mvex.Trex != nil {
combinedInit.Moov.Mvex.AddChild(init.Moov.Mvex.Trex)
}
if init.Moov.Mvex.Mehd != nil {
combinedInit.Moov.Mvex.AddChild(init.Moov.Mvex.Mehd)
}
}
}
}
return combinedInit, nil
}
func combineMediaSegments(files []string, newTrackIDs []uint32) (*mp4.MediaSegment, error) {
var combinedSeg *mp4.MediaSegment
var outFrag *mp4.Fragment
for i := 0; i < len(files); i++ {
data, err := os.ReadFile(files[i])
if err != nil {
return nil, fmt.Errorf("failed to read media segment: %w", err)
}
sr := bits.NewFixedSliceReader(data)
f, err := mp4.DecodeFileSR(sr)
if err != nil {
return nil, fmt.Errorf("failed to decode media segment: %w", err)
}
if len(f.Segments) != 1 {
return nil, fmt.Errorf("expected exactly one media segment per file")
}
seg := f.Segments[0]
if i == 0 {
if seg.Styp != nil {
combinedSeg = mp4.NewMediaSegmentWithStyp(seg.Styp)
} else {
combinedSeg = mp4.NewMediaSegmentWithoutStyp()
}
}
if len(seg.Fragments) != 1 {
return nil, fmt.Errorf("expected exactly one fragment per media segment")
}
frag := seg.Fragments[0]
if len(frag.Moof.Trafs) != 1 {
return nil, fmt.Errorf("expected exactly one traf per fragment")
}
if i == 0 {
seqNr := frag.Moof.Mfhd.SequenceNumber
outFrag, err = mp4.CreateMultiTrackFragment(seqNr, newTrackIDs)
if err != nil {
return nil, fmt.Errorf("failed to create fragment: %w", err)
}
combinedSeg.AddFragment(outFrag)
}
var trex *mp4.TrexBox = nil // Here we should have the trex from the corresponding init segment
fss, err := frag.GetFullSamples(trex)
if err != nil {
return nil, fmt.Errorf("failed to get full samples: %w", err)
}
for _, fs := range fss {
outFrag.AddFullSampleToTrack(fs, newTrackIDs[i])
}
}
return combinedSeg, nil
}
func writeSeg(seg encoder, filename string) error {
ofh, err := os.Create(filename)
if err != nil {
return err
}
defer ofh.Close()
err = seg.Encode(ofh)
if err != nil {
return err
}
fmt.Printf("wrote %s\n", filename)
return nil
}
type encoder interface {
Encode(w io.Writer) error
}