Chinaunix首页 | 论坛 | 博客
  • 博客访问: 59105
  • 博文数量: 22
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 232
  • 用 户 组: 普通用户
  • 注册时间: 2020-11-30 14:45
个人简介

专注流媒体、视频物联网、云计算、大数据开发

文章分类

全部博文(22)

文章存档

2021年(18)

2020年(4)

我的朋友

分类: 架构设计与优化

2021-03-09 17:42:52

随着视频智能安防监控系统的不断普及,安防监控平台在各行各业的项目中也得到了充分利用。未来AI智能将会成为安防监控的主导方向,TSINGSEE青犀视频根据行业需求,不断提升现有产品的适应能力,将进一步推动智能安防监控系统的发展。

目前EasyNVR作为TSINGSEE青犀视频开发的稳定可靠的智能安防监控平台,具备视频采集、直播、转码、分发等能力,其中在录像功能方面,不仅可以调取录像视频直接回放,还可以将录像文件通过接口调用下载,但目前下载和播放的都是mp4格式的视频文件。

部分项目团队对EasyNVR的录像接口调用功能提出了新的需求,即需要在指定时间段录像播放及下载接口返回在线的m3u8格式的视频,在线观看。

EasyNVR的录像文件就是m3u8格式的文件,存储在服务器中,调用指定时间段录像播放及下载接口时,EasyNVR将录像文件利用了ffmpeg转成了mp4,所以下载和播放的都是mp4格式的视频。

实现该项目的需求,我们可以定位到EasyNVR接口中找到指定时间段录像播放及下载接口具体逻辑处。首先将接口改变下,需要添加个字段判断获取m3u8格式的视频还是mp4的视频,这个字段为file,这个新接口的file字段不能为空。参考代码如下:


点击(此处)折叠或打开

  1. /**
  2.  * @api {get} /api/v2/record/video/:operate/:file/:id/:starttime/:endtime 指定时间段录像播放及下载
  3.  * @apiGroup record
  4.  * @apiParam {String=play,download} operate 调用操作 play:播放 download下载
  5.  * @apiParam {String=mp4,m3u8} file 文件类别 m3u8:(只提供在线地址) mp4:支持
  6.  * @apiParam {String} id 通道号
  7.  * @apiParam {String} starttime 开始时间, YYYYMMDDHHmmss
  8.  * @apiParam {String} endtime 结束时间, YYYYMMDDHHmmss
  9.  * @apiSuccessExample 播放示例
  10.  * 播放mp4
  11.  * http://localhost:10800/api/v2/record/video/play/mp4/1/20180911101139/20180911101248
  12.  * 播放m3u8
  13.  * http://localhost:10800/api/v2/record/video/play/m3u8/1/20180911101139/20180911101248
  14.  * @apiErrorExample 下载示例 (不支持下载m3u8)
  15.  * 下载mp4
  16.  * http://localhost:10800/api/v2/record/video/download/mp4/1/20180911101139/20180911101248
  17.  */
  18. func (h *APIHandler) VideoFileMP4(c *gin.Context) {
  19.    operate := c.Param("operate")
  20.    fileType := strings.ToLower(c.Param("file"))
  21.    if fileType == "" {
  22.       c.AbortWithStatusJSON(http.StatusBadRequest, "文件类别不能为空")
  23.       return
  24.    }
  25.    var err error
  26.    id := c.Param("id")
  27.    if id == "" {
  28.       c.AbortWithStatusJSON(http.StatusBadRequest, "未指定通道号")
  29.       return
  30.    }
  31.    streamID := channels.StreamIDString(id)
  32.    starttime := c.Param("starttime")
  33.    endtime := c.Param("endtime")
  34.    if matched, err := regexp.MatchString(`^\d{14}$`, starttime); !matched || err != nil {
  35.       c.AbortWithStatusJSON(http.StatusBadRequest, "开始时间格式不合法,正确格式:YYYYMMDDHHmmss")
  36.       return
  37.    }
  38.    if matched, err := regexp.MatchString(`^\d{14}$`, endtime); !matched || err != nil {
  39.       c.AbortWithStatusJSON(http.StatusBadRequest, "结束时间格式不合法,正确格式:YYYYMMDDHHmmss")
  40.       return
  41.    }
  42.    start := ff.StrYYYYMMDDHHmmssToTime(starttime)
  43.    end := ff.StrYYYYMMDDHHmmssToTime(endtime)
  44.    if start.After(end) {
  45.       c.AbortWithStatusJSON(http.StatusBadRequest, "开始时间要小于结束时间")
  46.       return
  47.    }
  48.    if end.Unix()-start.Unix() > 10800 {
  49.       c.AbortWithStatusJSON(http.StatusBadRequest, "最大播放/下载录像间隔是3小时!")
  50.       return
  51.    }
  52.    videoID := fmt.Sprintf(`%s_%s_%s`, id, starttime, endtime)
  53.    recordCachePath := dss.RecordDir()
  54.  
  55.    if operate == "play" && fileType == "m3u8" {
  56.       resPath, ok, _ := ff.GetRecordM3U8File(id, start, end, videoID, false)
  57.       if ok != "Complete" {
  58.          c.AbortWithStatusJSON(http.StatusBadRequest, "获取录像错误!")
  59.          return
  60.       }
  61.       c.JSON(http.StatusOK, resPath)
  62.       return
  63.    }
  64.  
  65.    mp4Path := filepath.Join(recordCachePath, fmt.Sprintf(`Stream_%s.mp4`, videoID))
  66.    if utils.Exist(mp4Path) {
  67.       //存在直接返回
  68.       ff.RecordCacheTime[videoID] = ff.RecordCacheTimeStruct{
  69.          Time: time.Now(),
  70.          Path: mp4Path}
  71.       ff.OperFile(c, operate, mp4Path, videoID)
  72.       return
  73.    }
  74.    //判断任务是否在处理
  75.    if _, OK := ff.RecordCacheTime[videoID]; OK { //存在任务
  76.       c.AbortWithStatusJSON(http.StatusBadRequest, "合成中")
  77.       return
  78.    }

阅读(1743) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~