|
|
|
package routers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"math"
|
|
|
|
"net/http"
|
|
|
|
"os"
|
|
|
|
"os/exec"
|
|
|
|
"path/filepath"
|
|
|
|
"regexp"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/EasyDarwin/EasyDarwin/rtsp"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
"github.com/penggy/EasyGoLib/utils"
|
|
|
|
)
|
|
|
|
|
|
|
|
func (h *APIHandler) StreamStart(c *gin.Context) {
|
|
|
|
type Form struct {
|
|
|
|
URL string `form:"url" binding:"required"`
|
|
|
|
CustomPath string `form:"customPath"`
|
|
|
|
IdleTimeout int `form:"idleTimeout"`
|
|
|
|
HeartbeatInterval int `form:"heartbeatInterval"`
|
|
|
|
}
|
|
|
|
var form Form
|
|
|
|
err := c.Bind(&form)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Pull to push err:%v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
client, err := rtsp.NewRTSPClient(rtsp.GetServer(), form.URL, int64(form.HeartbeatInterval)*1000)
|
|
|
|
if err != nil {
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
if form.CustomPath != "" && !strings.HasPrefix(form.CustomPath, "/") {
|
|
|
|
form.CustomPath = "/" + form.CustomPath
|
|
|
|
}
|
|
|
|
client.CustomPath = form.CustomPath
|
|
|
|
|
|
|
|
pusher := rtsp.NewClientPusher(client)
|
|
|
|
if rtsp.GetServer().GetPusher(pusher.Path()) != nil {
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, fmt.Sprintf("Path %s already exists", client.Path))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
err = client.Start(time.Duration(form.IdleTimeout) * time.Second)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Pull stream err :%v", err)
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, fmt.Sprintf("Pull stream err: %v", err))
|
|
|
|
return
|
|
|
|
}
|
|
|
|
log.Printf("Pull to push %v success ", form)
|
|
|
|
rtsp.GetServer().AddPusher(pusher)
|
|
|
|
c.IndentedJSON(200, pusher.ID())
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *APIHandler) StreamStop(c *gin.Context) {
|
|
|
|
type Form struct {
|
|
|
|
ID string `form:"id" binding:"required"`
|
|
|
|
}
|
|
|
|
var form Form
|
|
|
|
err := c.Bind(&form)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("stop pull to push err:%v", err)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
pushers := rtsp.GetServer().GetPushers()
|
|
|
|
for _, v := range pushers {
|
|
|
|
if v.ID() == form.ID {
|
|
|
|
v.Stop()
|
|
|
|
c.IndentedJSON(200, "OK")
|
|
|
|
log.Printf("Stop %v success ", v)
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
c.AbortWithStatusJSON(http.StatusBadRequest, fmt.Sprintf("Pusher[%s] not found", form.ID))
|
|
|
|
}
|
|
|
|
|
|
|
|
func (h *APIHandler) RecordFolders(c *gin.Context) {
|
|
|
|
mp4Path := utils.Conf().Section("rtsp").Key("mp4_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)
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
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("mp4_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
|
|
|
|
}
|
|
|
|
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{}{
|
|
|
|
"name": info.Name(),
|
|
|
|
"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)
|
|
|
|
}
|