-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathconvert.go
154 lines (127 loc) · 4.94 KB
/
convert.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
package moshpit
import (
"errors"
"fmt"
"math"
"path/filepath"
"strconv"
"golang.org/x/net/context"
)
// ConvertToAvi uses ffmpeg to convert the input file
// into a mute AVI file for datamoshing.
// The encoding quality of the output file is determined
// by the quality parameter, with 0.0 being the lowest
// and 1.0 being highest possible quality setting.
// If iFrameIndices is not nil, automatic I-Frame generation
// is disabled and I-Frames are placed at the given frame indices.
// The encoding progress is frequently written to the
// progress channel as a value between 0.0 and 1.0.
// The error channel is closed when processing is finished.
func ConvertToAvi(ctx context.Context, ffmpegPath string,
ffmpegLogPath string, inputFile string, outputFile string, quality float64,
iFrameIndices []uint64, progressChan chan<- float64,
errorChan chan<- error) {
if filepath.Ext(outputFile) != ".avi" {
errorChan <- errors.New("output file must have the .avi extension")
close(errorChan)
return
}
if quality < 0 || quality > 1 {
errorChan <- errors.New("quality setting must be a value between 0 and 1")
close(errorChan)
return
}
// the ffmpeg quality setting ranges from 0 to 31,
// with 0 being the best quality.
ffmpegQuality := uint64(math.Round(31.0 * (1 - quality)))
// construct ffmpeg arguments
args := []string{
"-i", inputFile,
// disable audio to avoid audio frames
// messing with the datamoshing to perform
"-an",
// set output quality to desired value
"-q", strconv.FormatUint(ffmpegQuality, 10),
// force overwrite the output file
// to avoid the command line prompt before execution
"-y",
}
if iFrameIndices != nil {
// disable automatic I-Frame generation by setting the
// requested I-Frame interval to the maximum possible value
args = append(args, "-g", strconv.FormatInt(math.MaxInt32, 10))
// experimental mode is required for keyframe intervals larger than 600
// https://github.com/FFmpeg/FFmpeg/blob/c1b282dc74d801f943db282e89f7729e90897669/libavcodec/mpegvideo_enc.c#L371
args = append(args, "-strict", "experimental")
// construct force_key_frames expression
// that sets I-Frames at the specified frame indices.
// For each desired keyframe index, it checks
// whether the current frame index n is equal to it,
// and adds the results together, which is equivalent
// to a logical or.
expr := "expr:"
for i, frame := range iFrameIndices {
if i != 0 {
expr += "+"
}
expr += fmt.Sprintf("eq(n,%d)", frame)
}
args = append(args, "-force_key_frames", expr)
}
// append output file as last argument
args = append(args, outputFile)
runFFmpeg(ctx, ffmpegPath, args, ffmpegLogPath, progressChan, nil, errorChan)
}
// ConvertToMp4 uses ffmpeg to convert the input file
// into an MP4 file, taking the audio stream from another file.
// If the sound file path is empty, no audio is added to the output file.
// The encoding quality of the output file is determined
// by the quality parameter, with 0.0 being the lowest
// and 1.0 being highest possible quality setting.
// The encoding progress is frequently written to the
// progress channel as a value between 0.0 and 1.0.
// Any errors encountered are sent to the error channel.
// The error channel is closed when processing is finished.
func ConvertToMp4(ctx context.Context, ffmpegPath string,
ffmpegLogPath string, aviFile string, soundFile string,
outputFile string, quality float64,
progressChan chan<- float64, errorChan chan<- error) {
if filepath.Ext(outputFile) != ".mp4" {
errorChan <- errors.New("output file must have the .mp4 extension")
close(errorChan)
return
}
if quality < 0 || quality > 1 {
errorChan <- errors.New("quality setting must be a value between 0 and 1")
close(errorChan)
return
}
// the ffmpeg quality setting ranges from 0 to 31,
// with 0 being the best quality.
ffmpegQuality := uint64(math.Round(31.0 * (1 - quality)))
// construct ffmpeg arguments
args := []string{
"-i", aviFile,
}
if soundFile != "" {
args = append(args, "-i", soundFile)
// take the video stream from the input file,
// and the audio stream from the sound file
// makeworld: Question mark was added to support videos with
// no audio stream. See this issue: https://github.com/CrushedPixel/moshpit/issues/1
args = append(args, "-map", "0:v:0", "-map", "1:a:0?")
// the mp4 format requires the aac format for audio streams.
// use a high bitrate to ensure high-quality audio
args = append(args, "-c:a", "aac", "-b:a", "320k")
}
// set output quality to desired value
args = append(args, "-q", strconv.FormatUint(ffmpegQuality, 10))
// speed up encoding at the cost of a larger file size
args = append(args, "-preset", "ultrafast")
// force overwrite the output file
// to avoid the command line prompt before execution
args = append(args, "-y")
// append output file as last argument
args = append(args, outputFile)
runFFmpeg(ctx, ffmpegPath, args, ffmpegLogPath, progressChan, nil, errorChan)
}