Skip to content

Commit

Permalink
1.自动删除最旧的录像文件的功能
Browse files Browse the repository at this point in the history
2.增加字段用于区分record对象是自动的普通录像还是事件录像
3.修复自动录像时,订阅却订阅失败时会生成小文件的问题
  • Loading branch information
pggiroro committed Oct 2, 2024
1 parent b15c776 commit 9b33cbd
Show file tree
Hide file tree
Showing 13 changed files with 264 additions and 100 deletions.
34 changes: 18 additions & 16 deletions entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,24 @@ import "time"
// mysql数据库eventrecord表
type EventRecord struct {
Id uint `json:"id" desc:"自增长id" gorm:"primaryKey;autoIncrement"`
StreamPath string `json:"streamPath" desc:"流路径" gorm:"type:varchar(255)"`
EventId string `json:"eventId" desc:"事件编号" gorm:"type:varchar(255)"`
RecordMode string `json:"recordMode" desc:"事件类型,0=连续录像模式,1=事件录像模式" gorm:"type:varchar(255)"`
EventName string `json:"eventName" desc:"事件名称" gorm:"type:varchar(255)"`
BeforeDuration string `json:"beforeDuration" desc:"事件前缓存时长" gorm:"type:varchar(255);"`
AfterDuration string `json:"afterDuration" desc:"事件后缓存时长" gorm:"type:varchar(255)"`
CreateTime string `json:"createTime" desc:"录像时间" gorm:"type:varchar(255)"`
StartTime string `json:"startTime" desc:"录像开始时间" gorm:"type:varchar(255)"`
EndTime string `json:"endTime" desc:"录像结束时间" gorm:"type:varchar(255)"`
Filepath string `json:"filePath" desc:"录像文件物理路径" gorm:"type:varchar(255)"`
Urlpath string `json:"urlPath" desc:"录像文件下载URL路径" gorm:"type:varchar(255)"`
IsDelete string `json:"isDelete" desc:"是否删除,0表示正常,1表示删除,默认0" gorm:"type:varchar(255);default:'0'"`
UserId string `json:"useId" desc:"用户id" gorm:"type:varchar(255)"`
Filename string `json:"fileName" desc:"文件名" gorm:"type:varchar(255)"`
Fragment string `json:"fragment" desc:"切片大小" gorm:"type:varchar(255)"`
EventDesc string `json:"eventDesc" desc:"事件描述" gorm:"type:varchar(255)"`
StreamPath string `json:"streamPath" desc:"流路径" gorm:"type:varchar(255);comment:流路径"`
EventId string `json:"eventId" desc:"事件编号" gorm:"type:varchar(255);comment:事件编号"`
RecordMode string `json:"recordMode" desc:"事件类型,0=连续录像模式,1=事件录像模式" gorm:"type:varchar(255);comment:事件类型,0=连续录像模式,1=事件录像模式"`
EventName string `json:"eventName" desc:"事件名称" gorm:"type:varchar(255);comment:事件名称"`
BeforeDuration string `json:"beforeDuration" desc:"事件前缓存时长" gorm:"type:varchar(255);comment:事件前缓存时长"`
AfterDuration string `json:"afterDuration" desc:"事件后缓存时长" gorm:"type:varchar(255);comment:事件后缓存时长"`
CreateTime string `json:"createTime" desc:"录像时间" gorm:"type:varchar(255);comment:录像时间"`
StartTime string `json:"startTime" desc:"录像开始时间" gorm:"type:varchar(255);comment:录像开始时间"`
EndTime string `json:"endTime" desc:"录像结束时间" gorm:"type:varchar(255);comment:录像结束时间"`
Filepath string `json:"filePath" desc:"录像文件物理路径" gorm:"type:varchar(255);comment:录像文件物理路径"`
Urlpath string `json:"urlPath" desc:"录像文件下载URL路径" gorm:"type:varchar(255);comment:录像文件下载URL路径"`
IsDelete string `json:"isDelete" desc:"是否删除,0表示正常,1表示删除,默认0" gorm:"type:varchar(255);default:'0';comment:是否删除,0表示正常,1表示删除,默认0"`
UserId string `json:"useId" desc:"用户id" gorm:"type:varchar(255);comment:用户id"`
Filename string `json:"fileName" desc:"文件名" gorm:"type:varchar(255);comment:文件名"`
Fragment string `json:"fragment" desc:"切片大小" gorm:"type:varchar(255);comment:切片大小;default:'0'"`
EventDesc string `json:"eventDesc" desc:"事件描述" gorm:"type:varchar(255);comment:事件描述"`
Type string `json:"type" desc:"录像文件类型" gorm:"type:varchar(255);comment:录像文件类型,flv,mp4,raw,fmp4,hls"`
EventLevel string `json:"eventLevel" desc:"事件级别" gorm:"type:varchar(255);comment:事件级别,0表示重要事件,无法删除且表示无需自动删除,1表示非重要事件,达到自动删除时间后,自动删除;default:'1'"`
}

//// TableName 返回自定义的表名
Expand Down
2 changes: 1 addition & 1 deletion exception.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ func SendToThirdPartyAPI(exception *Exception) {
fmt.Println("Error marshalling exception:", err)
return
}
err = mysqldb.Create(&exception).Error
err = db.Create(&exception).Error
if err != nil {
fmt.Println("异常数据插入数据库失败:", err)
return
Expand Down
59 changes: 46 additions & 13 deletions flv.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"io"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
Expand All @@ -26,6 +25,22 @@ type FLVRecorder struct {
timer *time.Timer
stopCh chan struct{}
mu sync.Mutex
RecordMode
}

func (r *FLVRecorder) SetId(streamPath string) {
r.ID = fmt.Sprintf("%s/flv/%s", streamPath, r.GetRecordModeString(r.RecordMode))
}

func (r *FLVRecorder) GetRecordModeString(mode RecordMode) string {
switch mode {
case EventMode:
return "eventmode"
case OrdinaryMode:
return "ordinarymode"
default:
return ""
}
}

// Goroutine 等待定时器停止录像
Expand Down Expand Up @@ -84,21 +99,22 @@ func (r *FLVRecorder) UpdateTimeout(timeout time.Duration) {
r.resetTimer(timeout)
}

func NewFLVRecorder() (r *FLVRecorder) {
func NewFLVRecorder(mode RecordMode) (r *FLVRecorder) {
r = &FLVRecorder{
stopCh: make(chan struct{}),
stopCh: make(chan struct{}),
RecordMode: mode,
}
r.Record = RecordPluginConfig.Flv
return r
}

func (r *FLVRecorder) Start(streamPath string) (err error) {
r.ID = streamPath + "/flv"
r.ID = fmt.Sprintf("%s/flv/%s", streamPath, r.GetRecordModeString(r.RecordMode))
return r.start(r, streamPath, SUBTYPE_FLV)
}

func (r *FLVRecorder) StartWithFileName(streamPath string, fileName string) error {
r.ID = fmt.Sprintf("%s/flv/%s", streamPath, fileName)
r.ID = fmt.Sprintf("%s/flv/%s", streamPath, r.GetRecordModeString(r.RecordMode))
return r.start(r, streamPath, SUBTYPE_FLV)
}

Expand Down Expand Up @@ -223,14 +239,14 @@ func (r *FLVRecorder) OnEvent(event any) {
}
case VideoFrame:
if r.VideoReader.Value.IFrame {
go func() { //将视频关键帧的数据存入sqlite数据库中
var flvKeyfram = &FLVKeyframe{FLVFileName: r.Path + "/" + strings.ReplaceAll(r.filePath, "\\", "/"), FrameOffset: r.Offset, FrameAbstime: r.VideoReader.AbsTime}
sqlitedb.Create(flvKeyfram)
}()
r.Info("这是关键帧,且取到了r.filePath是" + r.Path + r.filePath)
r.Info("这是关键帧,且取到了r.VideoReader.AbsTime是" + strconv.FormatUint(uint64(r.VideoReader.AbsTime), 10))
r.Info("这是关键帧,且取到了r.Offset是" + strconv.Itoa(int(r.Offset)))
r.Info("这是关键帧,且取到了r.Offset是" + r.Stream.Path)
//go func() { //将视频关键帧的数据存入sqlite数据库中
// var flvKeyfram = &FLVKeyframe{FLVFileName: r.Path + "/" + strings.ReplaceAll(r.filePath, "\\", "/"), FrameOffset: r.Offset, FrameAbstime: r.VideoReader.AbsTime}
// db.Create(flvKeyfram)
//}()
//r.Info("这是关键帧,且取到了r.filePath是" + r.Path + r.filePath)
//r.Info("这是关键帧,且取到了r.VideoReader.AbsTime是" + strconv.FormatUint(uint64(r.VideoReader.AbsTime), 10))
//r.Info("这是关键帧,且取到了r.Offset是" + strconv.Itoa(int(r.Offset)))
//r.Info("这是关键帧,且取到了r.Offset是" + r.Stream.Path)
}
case FLVFrame:
check := false
Expand Down Expand Up @@ -283,8 +299,25 @@ func (r *FLVRecorder) OnEvent(event any) {
func (r *FLVRecorder) Close() error {
if r.File != nil {
if !r.append {
go func() {
if r.RecordMode == OrdinaryMode {
startTime := time.Now().Add(-time.Duration(r.duration) * time.Millisecond).Format("2006-01-02 15:04:05")
endTime := time.Now().Format("2006-01-02 15:04:05")
fileName := r.FileName
if r.FileName == "" {
fileName = strings.ReplaceAll(r.Stream.Path, "/", "-") + "-" + time.Now().Format("2006-01-02-15-04-05")
}
filepath := RecordPluginConfig.Flv.Path + "/" + r.Stream.Path + "/" + fileName + r.Ext //录像文件存入的完整路径(相对路径)
eventRecord := EventRecord{StreamPath: r.Stream.Path, RecordMode: "0", BeforeDuration: "0",
AfterDuration: fmt.Sprintf("%.0f", r.Fragment.Seconds()), CreateTime: startTime, StartTime: startTime,
EndTime: endTime, Filepath: filepath, Filename: fileName + r.Ext, Urlpath: "record/" + strings.ReplaceAll(r.filePath, "\\", "/"), Fragment: fmt.Sprintf("%.0f", r.Fragment.Seconds()), Type: "flv"}
err = db.Omit("id", "isDelete").Create(&eventRecord).Error
}
}()
plugin.Info("====into close append false===recordid is===" + r.ID + "====record type is " + r.GetRecordModeString(r.RecordMode) + "====starttime is " + time.Now().Add(-time.Duration(r.duration)*time.Millisecond).Format("2006-01-02 15:04:05"))
go r.writeMetaData(r.File, r.duration)
} else {
plugin.Info("====into close append true===recordid is===" + r.ID + "====record type is " + r.GetRecordModeString(r.RecordMode))
return r.File.Close()
}
}
Expand Down
10 changes: 10 additions & 0 deletions fmp4.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,16 @@ type FMP4Recorder struct {
ftyp *mp4.FtypBox
}

func (r *FMP4Recorder) SetId(string) {
//TODO implement me
panic("implement me")
}

func (r *FMP4Recorder) GetRecordModeString(mode RecordMode) string {
//TODO implement me
panic("implement me")
}

func (r *FMP4Recorder) StartWithDynamicTimeout(streamPath, fileName string, timeout time.Duration) error {
//TODO implement me
panic("implement me")
Expand Down
10 changes: 10 additions & 0 deletions hls.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ type HLSRecorder struct {
MemoryTs
}

func (h *HLSRecorder) SetId(string) {
//TODO implement me
panic("implement me")
}

func (h *HLSRecorder) GetRecordModeString(mode RecordMode) string {
//TODO implement me
panic("implement me")
}

func (h *HLSRecorder) StartWithDynamicTimeout(streamPath, fileName string, timeout time.Duration) error {
//TODO implement me
panic("implement me")
Expand Down
97 changes: 70 additions & 27 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,33 +3,38 @@ package record
import (
_ "embed"
"errors"
"fmt"
"gorm.io/gorm"
"io"
"net"
"sync"

. "m7s.live/engine/v4"
"m7s.live/engine/v4/codec"
"m7s.live/engine/v4/config"
"m7s.live/engine/v4/util"
"net"
"os"
"sync"
"time"
)

type RecordConfig struct {
config.Subscribe
config.HTTP
Flv Record `desc:"flv录制配置"`
Mp4 Record `desc:"mp4录制配置"`
Fmp4 Record `desc:"fmp4录制配置"`
Hls Record `desc:"hls录制配置"`
Raw Record `desc:"视频裸流录制配置"`
RawAudio Record `desc:"音频裸流录制配置"`
recordings sync.Map
beforeDuration int `desc:"事件前缓存时长"`
afterDuration int `desc:"事件后缓存时长"`
MysqlDSN string `desc:"mysql数据库连接字符串"`
ExceptionPostUrl string `desc:"第三方异常上报地址"`
SqliteDbPath string `desc:"sqlite数据库路径"`
DiskMaxPercent float64 `desc:"硬盘使用百分之上限值,超过后报警"`
LocalIp string `desc:"本机IP"`
Flv Record `desc:"flv录制配置"`
Mp4 Record `desc:"mp4录制配置"`
Fmp4 Record `desc:"fmp4录制配置"`
Hls Record `desc:"hls录制配置"`
Raw Record `desc:"视频裸流录制配置"`
RawAudio Record `desc:"音频裸流录制配置"`
recordings sync.Map
beforeDuration int `desc:"事件前缓存时长"`
afterDuration int `desc:"事件后缓存时长"`
MysqlDSN string `desc:"mysql数据库连接字符串"`
ExceptionPostUrl string `desc:"第三方异常上报地址"`
SqliteDbPath string `desc:"sqlite数据库路径"`
DiskMaxPercent float64 `desc:"硬盘使用百分之上限值,超过后报警"`
LocalIp string `desc:"本机IP"`
RecordFileExpireDays int `desc:"录像自动删除的天数,0或未设置表示不自动删除"`
RecordPathNotShowStreamPath bool `desc:"录像路径中是否包含streamPath,默认true"`
}

//go:embed default.yaml
Expand Down Expand Up @@ -61,17 +66,20 @@ var RecordPluginConfig = &RecordConfig{
Path: "record/raw",
Ext: ".", // 默认aac扩展名为.aac,pcma扩展名为.pcma,pcmu扩展名为.pcmu
},
beforeDuration: 30,
afterDuration: 30,
MysqlDSN: "",
ExceptionPostUrl: "http://www.163.com",
SqliteDbPath: "./sqlite.db",
DiskMaxPercent: 80.00,
LocalIp: getLocalIP(),
beforeDuration: 30,
afterDuration: 30,
MysqlDSN: "",
ExceptionPostUrl: "http://www.163.com",
SqliteDbPath: "./sqlite.db",
DiskMaxPercent: 80.00,
LocalIp: getLocalIP(),
RecordFileExpireDays: 0,
RecordPathNotShowStreamPath: true,
}

var plugin = InstallPlugin(RecordPluginConfig, defaultYaml)
var exceptionChannel = make(chan *Exception)
var db *gorm.DB

func (conf *RecordConfig) OnEvent(event any) {
switch v := event.(type) {
Expand All @@ -87,10 +95,45 @@ func (conf *RecordConfig) OnEvent(event any) {
}()
if conf.MysqlDSN == "" {
plugin.Info("sqliteDb filepath is" + conf.SqliteDbPath)
initSqliteDB(conf.SqliteDbPath)
db = initSqliteDB(conf.SqliteDbPath)
} else {
plugin.Info("mysqlDSN is" + conf.MysqlDSN)
initMysqlDB(conf.MysqlDSN)
db = initMysqlDB(conf.MysqlDSN)
}

if conf.RecordFileExpireDays > 0 { //当有设置录像文件自动删除时间时,则开始运行录像自动删除的进程
//主要逻辑为
//搜索event_records表中event_level值为1的(非重要)数据,并将其create_time与当前时间比对,大于RecordFileExpireDays则进行删除,数据库标记is_delete为1,磁盘上删除录像文件
go func() {
for {
var eventRecords []EventRecord
expireTime := time.Now().AddDate(0, 0, -conf.RecordFileExpireDays)
// 创建包含查询条件的 EventRecord 对象
queryRecord := EventRecord{
EventLevel: "1", // 查询条件:event_level = 1
}
fmt.Printf(" Create Time: %s\n", expireTime.Format("2006-01-02 15:04:05"))
err = db.Where(&queryRecord).Where("create_time < ?", expireTime).Find(&eventRecords).Error
if err == nil {
if len(eventRecords) > 0 {
for _, record := range eventRecords {
fmt.Printf("ID: %d, Create Time: %s,filepath is %s\n", record.Id, record.CreateTime, record.Filepath)
err = os.Remove(record.Filepath)
if err != nil {
fmt.Println("error is " + err.Error())
}
err = db.Delete(record).Error
if err != nil {
fmt.Println("error is " + err.Error())
}
}
}
}

// 等待 1 分钟后继续执行
<-time.After(1 * time.Minute)
}
}()
}
conf.Flv.Init()
conf.Mp4.Init()
Expand All @@ -101,7 +144,7 @@ func (conf *RecordConfig) OnEvent(event any) {
case SEpublish:
streamPath := v.Target.Path
if conf.Flv.NeedRecord(streamPath) {
go NewFLVRecorder().Start(streamPath)
go NewFLVRecorder(OrdinaryMode).Start(streamPath)
}
if conf.Mp4.NeedRecord(streamPath) {
go NewMP4Recorder().Start(streamPath)
Expand Down
22 changes: 22 additions & 0 deletions mp4.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package record

import (
"net"
"os"
"path/filepath"
"time"

"github.com/yapingcat/gomedia/go-mp4"
Expand All @@ -18,6 +20,16 @@ type MP4Recorder struct {
audioId uint32
}

func (r *MP4Recorder) SetId(string) {
//TODO implement me
panic("implement me")
}

func (r *MP4Recorder) GetRecordModeString(mode RecordMode) string {
//TODO implement me
panic("implement me")
}

func (r *MP4Recorder) StartWithDynamicTimeout(streamPath, fileName string, timeout time.Duration) error {
//TODO implement me
panic("implement me")
Expand Down Expand Up @@ -54,7 +66,17 @@ func (r *MP4Recorder) Close() (err error) {
r.Info("mp4 write trailer", zap.Error(err))
}
err = r.File.Close()
if !isWrifeFrame {
fullPath := filepath.Join(r.Path, "/", r.filePath)
go func() {
err = os.Remove(fullPath)
if err != nil {
r.Info("未写入帧,文件为空,直接删除,删除结果为=======" + err.Error())
}
}()
}
}
isWrifeFrame = false
return
}
func (r *MP4Recorder) setTracks() {
Expand Down
Loading

0 comments on commit 9b33cbd

Please sign in to comment.