修改读写buffer大小

change read/write buffer size
完善apidoc
complete apidoc
更改录像文件格式,由mp4改为hls格式
change the recorded file format from mp4 to hls
pull/132/head
macbookpro 2018-12-02 17:52:02 +08:00
parent 02ee38a14c
commit 4a830c8f9f
6 changed files with 92 additions and 22 deletions

View File

@ -5,8 +5,9 @@ default_password=admin
[rtsp]
port=554
timeout=28800
gop_cache_enable=1
save_stream_to_mp4=0
ffmpeg_path=/Users/jiaozebo/Downloads/ffmpeg-20180719-9cb3d8f-macos64-shared/bin/ffmpeg
mp4_dir_path=/Users/jiaozebo/Downloads/EasyDarwinGoMP4
timeout=28800; rtsp 超时时间包括RTSP建立连接与数据收发。
gop_cache_enable=1; 是否使能gop cache。如果使能服务器会缓存最后一个I帧以及其后的非I帧以提高播放速度。但是可能在高并发的情况下带来内存压力。
save_stream_to_local=1; 是否使能推送的同事进行本地存储,使能后则可以进行录像查询与回放。
ffmpeg_path=~/Downloads/ffmpeg-20180719-9cb3d8f-macos64-shared/bin/ffmpeg;easydarwin使用ffmpeg工具来进行存储。这里表示ffmpeg的可执行程序的路径
m3u8_dir_path=~/Downloads/EasyDarwinGoM3u8;本地存储所将要保存的根目录。如果不存在,程序会尝试创建该目录。
ts_duration_second=600;切片文件时长。本地存储时将以该时间段为标准来生成ts文件单位秒

View File

@ -145,7 +145,7 @@ func Init() (err error) {
{
mp4Path := utils.Conf().Section("rtsp").Key("mp4_dir_path").MustString("")
mp4Path := utils.Conf().Section("rtsp").Key("m3u8_dir_path").MustString("")
if len(mp4Path) != 0 {
Router.Use(static.Serve("/record", static.LocalFile(mp4Path, true)))
}

View File

@ -19,6 +19,20 @@ import (
"github.com/penggy/EasyGoLib/utils"
)
/**
* @apiDefine pull
*/
/**
* @api {get} /api/vi/stream/start
* @apiGroup pull
* @apiName StreamStart
* @apiParam {String} url RTSP
* @apiParam {String} [customPath] PATH
* @apiParam {Number} [IdleTimeout]
* @apiParam {Number} [HeartbeatInterval] 0OPTION
* @apiSuccess (200) {String} ID IDID
*/
func (h *APIHandler) StreamStart(c *gin.Context) {
type Form struct {
URL string `form:"url" binding:"required"`
@ -58,6 +72,13 @@ func (h *APIHandler) StreamStart(c *gin.Context) {
c.IndentedJSON(200, pusher.ID())
}
/**
* @api {get} /api/vi/stream/stop
* @apiGroup pull
* @apiName StreamStop
* @apiParam {String} id ID
* @apiUse simpleSuccess
*/
func (h *APIHandler) StreamStop(c *gin.Context) {
type Form struct {
ID string `form:"id" binding:"required"`
@ -80,8 +101,33 @@ func (h *APIHandler) StreamStop(c *gin.Context) {
c.AbortWithStatusJSON(http.StatusBadRequest, fmt.Sprintf("Pusher[%s] not found", form.ID))
}
/**
* @apiDefine record 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/vi/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("mp4_dir_path").MustString("")
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)
@ -118,6 +164,22 @@ func (h *APIHandler) RecordFolders(c *gin.Context) {
}
/**
* @api {get} /api/vi/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 ,m3u8video便http[s]://host:port/record/[path]。
*/
func (h *APIHandler) RecordFiles(c *gin.Context) {
type Form struct {
utils.PageForm
@ -134,7 +196,7 @@ func (h *APIHandler) RecordFiles(c *gin.Context) {
}
files := make([]interface{}, 0)
mp4Path := utils.Conf().Section("rtsp").Key("mp4_dir_path").MustString("")
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)
@ -162,6 +224,9 @@ func (h *APIHandler) RecordFiles(c *gin.Context) {
if info.Name() == ".DS_Store" {
return nil
}
if !strings.HasSuffix(info.Name(), ".m3u8") {
return filepath.SkipDir
}
cmd := exec.Command(ffprobe, "-i", path)
cmdOutput := &bytes.Buffer{}
//cmd.Stdout = cmdOutput
@ -185,8 +250,9 @@ func (h *APIHandler) RecordFiles(c *gin.Context) {
millis, _ := strconv.Atoi(result[5])
duration += time.Duration(millis) * time.Millisecond
}
*files = append(*files, map[string]interface{}{
"name": info.Name(),
"path": path[len(mp4Path):],
"durationMillis": duration / time.Millisecond,
"duration": durationStr})
return nil

View File

@ -119,7 +119,7 @@ func (client *RTSPClient) Start(timeout time.Duration) error {
}
client.Conn = conn
networkBuffer := utils.Conf().Section("rtsp").Key("network_buffer").MustInt(1048576)
networkBuffer := utils.Conf().Section("rtsp").Key("network_buffer").MustInt(204800)
timeoutConn := RichConn{
conn,

View File

@ -2,16 +2,16 @@ package rtsp
import (
"fmt"
"github.com/penggy/EasyGoLib/utils"
"log"
"net"
"os"
"os/exec"
"path"
"strconv"
"sync"
"syscall"
"time"
"github.com/penggy/EasyGoLib/utils"
)
type Server struct {
@ -49,14 +49,15 @@ func (server *Server) Start() (err error) {
return
}
localRecord := utils.Conf().Section("rtsp").Key("save_stream_to_mp4").MustInt(0)
localRecord := utils.Conf().Section("rtsp").Key("save_stream_to_local").MustInt(0)
ffmpeg := utils.Conf().Section("rtsp").Key("ffmpeg_path").MustString("")
mp4Path := utils.Conf().Section("rtsp").Key("mp4_dir_path").MustString("")
m3u8_dir_path := utils.Conf().Section("rtsp").Key("m3u8_dir_path").MustString("")
ts_duration_second := utils.Conf().Section("rtsp").Key("ts_duration_second").MustInt(10 * 60)
SaveStreamToLocal := false
if (len(ffmpeg) > 0) && localRecord > 0 && len(mp4Path) > 0 {
err := utils.EnsureDir(mp4Path)
if (len(ffmpeg) > 0) && localRecord > 0 && len(m3u8_dir_path) > 0 {
err := utils.EnsureDir(m3u8_dir_path)
if err != nil {
log.Printf("Create mp4_dir_path[%s] err:%v.", mp4Path, err)
log.Printf("Create m3u8_dir_path[%s] err:%v.", m3u8_dir_path, err)
} else {
SaveStreamToLocal = true
}
@ -75,14 +76,16 @@ func (server *Server) Start() (err error) {
case pusher, addChnOk = <-server.addPusherCh:
if SaveStreamToLocal {
if addChnOk {
dir := path.Join(mp4Path, pusher.Path())
dir := path.Join(m3u8_dir_path, pusher.Path(), time.Now().Format("20060102150405"))
err := utils.EnsureDir(dir)
if err != nil {
log.Printf("EnsureDir:[%s] err:%v.", dir, err)
continue
}
path := path.Join(dir, fmt.Sprintf("%s.mp4", time.Now().Format("20060102150405")))
cmd := exec.Command(ffmpeg, "-i", pusher.URL(), "-c:v", "copy", "-c:a", "copy", path)
path := path.Join(dir, fmt.Sprintf("out.m3u8"))
// ffmpeg -i ~/Downloads/720p.mp4 -s 640x360 -g 15 -c:a aac -hls_time 5 -hls_list_size 0 record.m3u8
cmd := exec.Command(ffmpeg, "-fflags", "genpts", "-rtsp_transport", "tcp", "-i", pusher.URL(), "-c:v", "copy", "-hls_time", strconv.Itoa(ts_duration_second), "-hls_list_size", "0", path)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err = cmd.Start()
@ -90,7 +93,7 @@ func (server *Server) Start() (err error) {
log.Printf("Start ffmpeg err:%v", err)
}
pusher2ffmpegMap[pusher] = cmd
log.Printf("add ffmpeg to pull stream from pusher[%v]", pusher)
log.Printf("add ffmpeg [%v] to pull stream from pusher[%v]", cmd, pusher)
} else {
log.Printf("addPusherChan closed")
}

View File

@ -128,7 +128,7 @@ func (session *Session) String() string {
}
func NewSession(server *Server, conn net.Conn) *Session {
networkBuffer := utils.Conf().Section("rtsp").Key("network_buffer").MustInt(1048576)
networkBuffer := utils.Conf().Section("rtsp").Key("network_buffer").MustInt(204800)
timeoutMillis := utils.Conf().Section("rtsp").Key("timeout").MustInt(0)
timeoutTCPConn := &RichConn{conn, time.Duration(timeoutMillis) * time.Millisecond}