-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathffmpeg.go
161 lines (139 loc) · 3.55 KB
/
ffmpeg.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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
package radigo
import (
"context"
"fmt"
"io"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
)
type ffmpeg struct {
*exec.Cmd
}
func newFfmpeg(ctx context.Context) (*ffmpeg, error) {
cmdPath, err := exec.LookPath("ffmpeg")
if err != nil {
return nil, err
}
return &ffmpeg{exec.CommandContext(
ctx,
cmdPath,
)}, nil
}
func (f *ffmpeg) setDir(dir string) {
f.Dir = dir
}
func (f *ffmpeg) setArgs(args ...string) {
f.Args = append(f.Args, args...)
}
func (f *ffmpeg) setInput(input string) {
f.setArgs("-i", input)
}
func (f *ffmpeg) run(output string) error {
f.setArgs(output)
return f.Run()
}
func (f *ffmpeg) start(output string) error {
f.setArgs(output)
return f.Start()
}
func (f *ffmpeg) wait() error {
return f.Wait()
}
func (f *ffmpeg) stdinPipe() (io.WriteCloser, error) {
return f.StdinPipe()
}
func (f *ffmpeg) stderrPipe() (io.ReadCloser, error) {
return f.StderrPipe()
}
// ConvertAACtoMP3 converts an aac file to a mp3 file.
func ConvertAACtoMP3(ctx context.Context, input, output string) error {
f, err := newFfmpeg(ctx)
if err != nil {
return err
}
f.setInput(input)
f.setArgs(
"-c:a", "libmp3lame",
"-ac", "2",
"-q:a", "2",
"-y", // overwrite the output file without asking
)
// TODO: Collect log
return f.run(output)
}
// ConcatAACFilesFromList concatenates files from the list of resources.
func ConcatAACFilesFromList(ctx context.Context, resourcesDir string) (string, error) {
files, err := ioutil.ReadDir(resourcesDir)
if err != nil {
return "", err
}
allFilePaths := []string{}
for _, f := range files {
p := filepath.Join(resourcesDir, f.Name())
allFilePaths = append(allFilePaths, p)
}
concatedFile := filepath.Join(resourcesDir, "concated.aac")
if err := ConcatAACFilesAll(ctx, allFilePaths, resourcesDir, concatedFile); err != nil {
return "", err
}
return concatedFile, nil
}
// ConcatAACFiles concatenate files of the same type.
func ConcatAACFilesAll(ctx context.Context, files []string, resourcesDir string, output string) error {
// input is a path to a file which lists all the aac files.
// it may include a lot of aac file and exceed max number of file descriptor.
oneConcatNum := 100
if len(files) > oneConcatNum {
reducedFiles := files[:oneConcatNum]
restFiles := files[oneConcatNum:]
// reducedFiles -> reducedFiles[0]
tmpOutputFile, err := ioutil.TempFile(resourcesDir, "tmp-concatenated-*.aac")
if err != nil {
fmt.Println("Failed to call ioutil.TempFile")
return err
}
err = ConcatAACFiles(ctx, reducedFiles, resourcesDir, tmpOutputFile.Name())
if err != nil {
fmt.Println("Failed to ConcatAACFiles")
return err
}
err = ConcatAACFilesAll(ctx, append([]string{tmpOutputFile.Name()}, restFiles...), resourcesDir, output)
defer os.Remove(tmpOutputFile.Name())
return err
} else {
return ConcatAACFiles(ctx, files, resourcesDir, output)
}
}
func ConcatAACFiles(ctx context.Context, input []string, resourcesDir string, output string) error {
listFile, err0 := ioutil.TempFile(resourcesDir, "aac_resources")
if err0 != nil {
return err0
}
defer os.Remove(listFile.Name())
for _, f := range input {
p := fmt.Sprintf("file '%s'\n", f)
if _, err := listFile.WriteString(p); err != nil {
return err
}
}
f, err := newFfmpeg(ctx)
if err != nil {
return err
}
f.setArgs(
"-f", "concat",
"-safe", "0",
"-y",
)
f.setInput(listFile.Name())
f.setArgs("-c", "copy")
// TODO: Collect log
err = f.run(output)
// Remove the intermediate files right after they are concatenated into one file
for _, f := range input {
defer os.Remove(f)
}
return err
}