-
Notifications
You must be signed in to change notification settings - Fork 86
/
main.go
127 lines (107 loc) · 3.23 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
package main
import (
"errors"
"flag"
"fmt"
"os"
"path"
"github.com/Eyevinn/mp4ff/mp4"
)
const (
appName = "segmenter"
)
var usg = `%s segments a progressive mp4 file into init and media segments.
The output is either single-track segments, or muxed multi-track segments.
With the -lazy mode, mdat is read and written lazily. The lazy write
is only for single-track segments, to provide a comparison with the multi-track
implementation.
There should be at most one audio and one video track in the input.
The output files will be named as
init segments: <output>_a.mp4 and <output>_v.mp4
media segments: <output>_a_<n>.m4s and <output>_v_<n>.m4s where n >= 1
or init.mp4 and media_<n>.m4s
Codecs supported are AVC and HEVC for video and AAC and AC-3 for audio.
Usage of %s:
`
type options struct {
chunkDurMS uint64
multipex bool
lazy bool
verbose bool
}
func parseOptions(fs *flag.FlagSet, args []string) (*options, error) {
fs.Usage = func() {
fmt.Fprintf(os.Stderr, usg, appName, appName)
fmt.Fprintf(os.Stderr, "\n%s [options] infile outfilePrefix\n\noptions:\n", appName)
fs.PrintDefaults()
}
opts := options{}
fs.Uint64Var(&opts.chunkDurMS, "d", 0, "Required: segment duration (milliseconds). The segments will start at syncSamples with decoded time >= n*segDur")
fs.BoolVar(&opts.multipex, "m", false, "Output multiplexed segments")
fs.BoolVar(&opts.lazy, "lazy", false, "Read/write mdat lazily")
fs.BoolVar(&opts.verbose, "v", false, "Verbose output")
err := fs.Parse(args[1:])
return &opts, err
}
func main() {
if err := run(os.Args, "."); err != nil {
fmt.Fprintf(os.Stderr, "error: %v\n", err)
os.Exit(1)
}
}
func run(args []string, outDir string) error {
fs := flag.NewFlagSet(appName, flag.ContinueOnError)
o, err := parseOptions(fs, args)
if err != nil {
if errors.Is(err, flag.ErrHelp) {
return nil
}
return err
}
if len(fs.Args()) != 2 {
fs.Usage()
return fmt.Errorf("infile and outfilePrefix must be set")
}
if o.chunkDurMS == 0 {
fs.Usage()
return fmt.Errorf("segment duration must be set (and positive)")
}
ifd, err := os.Open(fs.Arg(0))
if err != nil {
return fmt.Errorf("error opening file: %w", err)
}
defer ifd.Close()
outfilePrefix := path.Join(outDir, fs.Arg(1))
var parsedMp4 *mp4.File
if o.lazy {
parsedMp4, err = mp4.DecodeFile(ifd, mp4.WithDecodeMode(mp4.DecModeLazyMdat))
} else {
parsedMp4, err = mp4.DecodeFile(ifd)
}
if err != nil {
return fmt.Errorf("error decoding file: %w", err)
}
segmenter, err := NewSegmenter(parsedMp4)
if err != nil {
return fmt.Errorf("error creating segmenter: %w", err)
}
syncTimescale, segmentStarts := getSegmentStartsFromVideo(parsedMp4, uint32(o.chunkDurMS))
fmt.Printf("segment starts in timescale %d: %v\n", syncTimescale, segmentStarts)
err = segmenter.SetTargetSegmentation(syncTimescale, segmentStarts)
if err != nil {
return fmt.Errorf("error setting target segmentation: %w", err)
}
if o.multipex {
err = makeMultiTrackSegments(segmenter, parsedMp4, ifd, outfilePrefix)
} else {
if o.lazy {
err = makeSingleTrackSegmentsLazyWrite(segmenter, parsedMp4, ifd, outfilePrefix)
} else {
err = makeSingleTrackSegments(segmenter, parsedMp4, nil, outfilePrefix)
}
}
if err != nil {
return err
}
return nil
}