-
Notifications
You must be signed in to change notification settings - Fork 0
/
video.go
205 lines (186 loc) · 5.29 KB
/
video.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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
package main
import (
"context"
"fmt"
"net/http"
"net/url"
"os"
"os/exec"
"path/filepath"
"strings"
"time"
)
type PexelsVideoResponse struct {
Page int `json:"page"`
PerPage int `json:"per_page"`
TotalResults int `json:"total_results"`
Url string `json:"url"`
Videos []struct {
Id int `json:"id"`
Width int `json:"width"`
Height int `json:"height"`
Url string `json:"url"`
Image string `json:"image"`
Duration int `json:"duration"`
User struct {
Id int `json:"id"`
Name string `json:"name"`
Url string `json:"url"`
} `json:"user"`
VideoFiles []struct {
Id int `json:"id"`
Quality string `json:"quality"`
FileType string `json:"file_type"`
Width int `json:"width"`
Height int `json:"height"`
Link string `json:"link"`
} `json:"video_files"`
VideoPictures []struct {
Id int `json:"id"`
Picture string `json:"picture"`
Nr int `json:"nr"`
} `json:"video_pictures"`
} `json:"videos"`
}
/*
SearchVideosInPexels 检索视频
term: 检索条件
limit: 检索数量
minDuration: 视频最短时间
return
videoUrls: 检索到的视频url slices
*/
func SearchVideosInPexels(ctx context.Context, term string, limit int, minDuration int) (videoUrls []string, err error) {
var (
param = url.Values{}
header = make(http.Header)
response PexelsVideoResponse
videoRatio int
)
param.Add("query", term)
param.Add("per_page", fmt.Sprint(limit))
token := os.Getenv("PEXELS_API_KEY")
if len(token) < 0 {
return nil, fmt.Errorf("PEXELS_API_KEY not set")
}
header.Add("Authorization", token)
request, err := BuildRequest(ctx, http.MethodGet, "https://api.pexels.com/videos/search", nil, header, param)
if err != nil {
return
}
err = SendRequest(request, &http.Client{}, &response)
if err != nil {
return
}
for _, video := range response.Videos {
if video.Duration < minDuration {
// 过滤不满足时长的视频
continue
}
tmpLinkUrl := ""
for _, linkFile := range video.VideoFiles {
// 检查是否满足下赞路径
//if !strings.Contains(linkFile.Link, ".com/external") {
// continue
//}
// 只保留分辨率更高的视频
if linkFile.Width*linkFile.Height < videoRatio {
continue
}
tmpLinkUrl = linkFile.Link
videoRatio = linkFile.Width * linkFile.Height
if tmpLinkUrl != "" {
videoUrls = append(videoUrls, tmpLinkUrl)
}
}
}
return
}
/*
CombinedVideo 使用ffmpeg将多个视频合成为一个
videos: 要合成的视频的本地地址
return
combinedVideoUrl 合成后视频的本地地址
example:
ffmpeg -i a.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts 1.ts
ffmpeg -i b.mp4 -c copy -bsf:v h264_mp4toannexb -f mpegts 2.ts
ffmpeg -i "concat:1.ts|2.ts" -c copy -bsf:a aac_adtstoasc -movflags +faststart ts.mp4
*/
func CombinedVideo(ctx context.Context, videos []string) (combinedVideoUrl string, err error) {
//mp4->ts merge ts ts->mp4
var (
tsDir = "./tmp/ts/"
combinedVideoDir = "./tmp/combinedVideo/"
tsList []string
)
for _, video := range videos {
tmpTs := fmt.Sprintf("%s%d.ts", tsDir, time.Now().UnixNano())
err = os.MkdirAll(filepath.Dir(tmpTs), os.ModePerm)
if err != nil {
return
}
// mp4转ts
cmd := fmt.Sprintf("ffmpeg -i %s -c copy -bsf:v h264_mp4toannexb -f mpegts %s", video, tmpTs)
command := exec.Command("/bin/bash", "-c", cmd)
_, err = command.CombinedOutput()
if err != nil {
return
}
tsList = append(tsList, tmpTs)
}
// 合并所有的ts 并转为mp4
allTs := strings.Join(tsList, "|")
// mp4转ts
combinedVideoUrl = fmt.Sprintf("%s%d.mp4", combinedVideoDir, time.Now().UnixNano())
err = os.MkdirAll(filepath.Dir(combinedVideoUrl), os.ModePerm)
if err != nil {
return
}
cmd := fmt.Sprintf(`ffmpeg -i "concat:%s" -c copy -bsf:a aac_adtstoasc -movflags +faststart %s`, allTs, combinedVideoUrl)
command := exec.Command("/bin/bash", "-c", cmd)
_, err = command.CombinedOutput()
if err != nil {
return
}
return
}
/*
MixAllInfoForVideo 将音频字幕视频融合
CombinedVideo 合并视频路径
mergeAudio 合并音频路径
subtitle 字幕路径
return
finalVideo 最终视频
*/
func MixAllInfoForVideo(ctx context.Context, CombinedVideo string, mergeAudio string, subtitle string) (finalVideo string, err error) {
var (
tmpVideoWithAudio = "./tmp/videoWithAudio/"
finalVideoPath = "./tmp/finalVideo/"
)
// 合并视频和音频
CombinedVideoAndAudio := fmt.Sprintf("%s%d.mp4", tmpVideoWithAudio, time.Now().UnixNano())
err = os.MkdirAll(filepath.Dir(CombinedVideoAndAudio), os.ModePerm)
if err != nil {
return
}
cmd := fmt.Sprintf(`ffmpeg -i %s -i %s -vcodec copy -acodec copy %s`, CombinedVideo, mergeAudio, CombinedVideoAndAudio)
command := exec.Command("/bin/bash", "-c", cmd)
_, err = command.CombinedOutput()
if err != nil {
return
}
// 添加字幕
finalVideo = fmt.Sprintf("%s%d.mp4", finalVideoPath, time.Now().UnixNano())
err = os.MkdirAll(filepath.Dir(finalVideo), os.ModePerm)
if err != nil {
return
}
cmd = fmt.Sprintf(`ffmpeg -i %s -strict -2 -vf \
subtitles=%s:force_style='Fontsize=15\,FontName=FZYBKSJW--GB1-0' -qscale:v 3 %s`, CombinedVideoAndAudio, subtitle, finalVideo)
command = exec.Command("/bin/bash", "-c", cmd)
_, err = command.CombinedOutput()
if err != nil {
return
}
return
}