/
record.go
188 lines (178 loc) · 5.5 KB
/
record.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
package routers
import (
"bytes"
"log"
"math"
"os"
"os/exec"
"path/filepath"
"regexp"
"strconv"
"strings"
"time"
"github.com/gin-gonic/gin"
"github.com/onedss/onedss/utils"
)
/**
* @apiDefine record 录像
*/
/**
* @apiDefine fileInfo
* @apiSuccess (200) {String} duration 格式化好的录像时长
* @apiSuccess (200) {Number} durationMillis 录像时长,毫秒为单位
* @apiSuccess (200) {String} path 录像文件的相对路径,其绝对路径为:http[s]://host:port/record/[path]。
* @apiSuccess (200) {String} folder 录像文件夹,录像文件夹以推流路径命名。
*/
/**
* @api {get} /api/v1/record/folders 获取所有录像文件夹
* @apiGroup record
* @apiName RecordFolders
* @apiParam {Number} [start] 分页开始,从零开始
* @apiParam {Number} [limit] 分页大小
* @apiParam {String} [sort] 排序字段
* @apiParam {String=ascending,descending} [order] 排序顺序
* @apiParam {String} [q] 查询参数
* @apiSuccess (200) {Number} total 总数
* @apiSuccess (200) {Array} rows 文件夹列表
* @apiSuccess (200) {String} rows.folder 录像文件夹名称
*/
func (h *APIHandler) RecordFolders(c *gin.Context) {
mp4Path := utils.Conf().Section("rtsp").Key("m3u8_dir_path").MustString("")
form := utils.NewPageForm()
if err := c.Bind(form); err != nil {
log.Printf("record folder bind err:%v", err)
return
}
var files = make([]interface{}, 0)
if mp4Path != "" {
visit := func(files *[]interface{}) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == mp4Path {
return nil
}
if !info.IsDir() {
return nil
}
*files = append(*files, map[string]interface{}{"folder": info.Name()})
return filepath.SkipDir
}
}
err := filepath.Walk(mp4Path, visit(&files))
if err != nil {
log.Printf("Query RecordFolders err:%v", err)
}
}
pr := utils.NewPageResult(files)
if form.Sort != "" {
pr.Sort(form.Sort, form.Order)
}
pr.Slice(form.Start, form.Limit)
c.IndentedJSON(200, pr)
}
/**
* @api {get} /api/v1/record/files 获取所有录像文件
* @apiGroup record
* @apiName RecordFiles
* @apiParam {Number} folder 录像文件所在的文件夹
* @apiParam {Number} [start] 分页开始,从零开始
* @apiParam {Number} [limit] 分页大小
* @apiParam {String} [sort] 排序字段
* @apiParam {String=ascending,descending} [order] 排序顺序
* @apiParam {String} [q] 查询参数
* @apiSuccess (200) {Number} total 总数
* @apiSuccess (200) {Array} rows 文件列表
* @apiSuccess (200) {String} rows.duration 格式化好的录像时长
* @apiSuccess (200) {Number} rows.durationMillis 录像时长,毫秒为单位
* @apiSuccess (200) {String} rows.path 录像文件的相对路径,录像文件为m3u8格式,将其放到video标签中便可直接播放。其绝对路径为:http[s]://host:port/record/[path]。
*/
func (h *APIHandler) RecordFiles(c *gin.Context) {
type Form struct {
utils.PageForm
Folder string `form:"folder" binding:"required"`
StartAt int `form:"beginUTCSecond"`
StopAt int `form:"endUTCSecond"`
}
var form = Form{}
form.Limit = math.MaxUint32
err := c.Bind(&form)
if err != nil {
log.Printf("record file bind err:%v", err)
return
}
files := make([]interface{}, 0)
mp4Path := utils.Conf().Section("rtsp").Key("m3u8_dir_path").MustString("")
if mp4Path != "" {
ffmpeg_path := utils.Conf().Section("rtsp").Key("ffmpeg_path").MustString("")
ffmpeg_folder, executable := filepath.Split(ffmpeg_path)
split := strings.Split(executable, ".")
suffix := ""
if len(split) > 1 {
suffix = split[1]
}
ffprobe := ffmpeg_folder + "ffprobe" + suffix
folder := filepath.Join(mp4Path, form.Folder)
visit := func(files *[]interface{}) filepath.WalkFunc {
return func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if path == folder {
return nil
}
if info.IsDir() {
return nil
}
if info.Size() == 0 {
return nil
}
if info.Name() == ".DS_Store" {
return nil
}
if !strings.HasSuffix(strings.ToLower(info.Name()), ".m3u8") && !strings.HasSuffix(strings.ToLower(info.Name()), ".ts") {
return nil
}
cmd := exec.Command(ffprobe, "-i", path)
cmdOutput := &bytes.Buffer{}
//cmd.Stdout = cmdOutput
cmd.Stderr = cmdOutput
err = cmd.Run()
bytes := cmdOutput.Bytes()
output := string(bytes)
//log.Printf("%v result:%v", cmd, output)
var average = regexp.MustCompile(`Duration: ((\d+):(\d+):(\d+).(\d+))`)
result := average.FindStringSubmatch(output)
duration := time.Duration(0)
durationStr := ""
if len(result) > 0 {
durationStr = result[1]
h, _ := strconv.Atoi(result[2])
duration += time.Duration(h) * time.Hour
m, _ := strconv.Atoi(result[3])
duration += time.Duration(m) * time.Minute
s, _ := strconv.Atoi(result[4])
duration += time.Duration(s) * time.Second
millis, _ := strconv.Atoi(result[5])
duration += time.Duration(millis) * time.Millisecond
}
*files = append(*files, map[string]interface{}{
"path": path[len(mp4Path):],
"durationMillis": duration / time.Millisecond,
"duration": durationStr})
return nil
}
}
err = filepath.Walk(folder, visit(&files))
if err != nil {
log.Printf("Query RecordFolders err:%v", err)
}
}
pr := utils.NewPageResult(files)
if form.Sort != "" {
pr.Sort(form.Sort, form.Order)
}
pr.Slice(form.Start, form.Limit)
c.IndentedJSON(200, pr)
}