Commit 92a25393f38d9877aeefca3bcc0f6574b7dff6d4

Authored by 648540858
1 parent 5a75381a

基于新的云端录像实现页面功能

src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -291,7 +291,7 @@ public class ZLMHttpHookListener {
291 291 }
292 292 // 如果是录像下载就设置视频间隔十秒
293 293 if (ssrcTransactionForAll.get(0).getType() == InviteSessionType.DOWNLOAD) {
294   - result.setMp4_max_second(10);
  294 + result.setMp4_max_second(30);
295 295 result.setEnable_mp4(true);
296 296 }
297 297 }
... ...
src/main/java/com/genersoft/iot/vmp/service/ICloudRecordService.java
... ... @@ -18,7 +18,7 @@ public interface ICloudRecordService {
18 18 /**
19 19 * 分页回去云端录像列表
20 20 */
21   - PageInfo<CloudRecordItem> getList(int page, int count, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
  21 + PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems);
22 22  
23 23 /**
24 24 * 根据hook消息增加一条记录
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/CloudRecordServiceImpl.java
... ... @@ -50,7 +50,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
50 50 private VideoStreamSessionManager streamSession;
51 51  
52 52 @Override
53   - public PageInfo<CloudRecordItem> getList(int page, int count, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {
  53 + public PageInfo<CloudRecordItem> getList(int page, int count, String query, String app, String stream, String startTime, String endTime, List<MediaServerItem> mediaServerItems) {
54 54 // 开始时间和结束时间在数据库中都是以秒为单位的
55 55 Long startTimeStamp = null;
56 56 Long endTimeStamp = null;
... ... @@ -69,7 +69,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
69 69  
70 70 }
71 71 PageHelper.startPage(page, count);
72   - List<CloudRecordItem> all = cloudRecordServiceMapper.getList(app, stream, startTimeStamp, endTimeStamp,
  72 + List<CloudRecordItem> all = cloudRecordServiceMapper.getList(query, app, stream, startTimeStamp, endTimeStamp,
73 73 null, mediaServerItems);
74 74 return new PageInfo<>(all);
75 75 }
... ... @@ -85,7 +85,7 @@ public class CloudRecordServiceImpl implements ICloudRecordService {
85 85 }
86 86 long startTimeStamp = startDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
87 87 long endTimeStamp = endDate.atStartOfDay().toInstant(ZoneOffset.ofHours(8)).getEpochSecond();
88   - List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(app, stream, startTimeStamp,
  88 + List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, app, stream, startTimeStamp,
89 89 endTimeStamp, null, mediaServerItems);
90 90 if (cloudRecordItemList.isEmpty()) {
91 91 return new ArrayList<>();
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -765,7 +765,7 @@ public class PlayServiceImpl implements IPlayService {
765 765 }
766 766  
767 767 // 为了支持多个数据库,这里不能使用求和函数来直接获取总数了
768   - List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList("rtp", inviteInfo.getStream(), null, null, ssrcTransaction.getCallId(), null);
  768 + List<CloudRecordItem> cloudRecordItemList = cloudRecordServiceMapper.getList(null, "rtp", inviteInfo.getStream(), null, null, ssrcTransaction.getCallId(), null);
769 769  
770 770 if (cloudRecordItemList.isEmpty()) {
771 771 logger.warn("[获取下载进度],未找到下载视频信息");
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/CloudRecordServiceMapper.java
... ... @@ -44,6 +44,7 @@ public interface CloudRecordServiceMapper {
44 44 "select * " +
45 45 " from wvp_cloud_record " +
46 46 " where 0 = 0" +
  47 + " <if test='query != null'> AND (app LIKE concat('%',#{query},'%') OR stream LIKE concat('%',#{query},'%') )</if> " +
47 48 " <if test= 'app != null '> and app=#{app}</if>" +
48 49 " <if test= 'stream != null '> and stream=#{stream}</if>" +
49 50 " <if test= 'startTimeStamp != null '> and end_time &gt;= #{startTimeStamp}</if>" +
... ... @@ -52,8 +53,10 @@ public interface CloudRecordServiceMapper {
52 53 " <if test= 'mediaServerItemList != null ' > and media_server_id in " +
53 54 " <foreach collection='mediaServerItemList' item='item' open='(' separator=',' close=')' > #{item.id}</foreach>" +
54 55 " </if>" +
  56 + " order by start_time DESC" +
  57 +
55 58 " </script>")
56   - List<CloudRecordItem> getList(@Param("app") String app, @Param("stream") String stream,
  59 + List<CloudRecordItem> getList(@Param("query") String query, @Param("app") String app, @Param("stream") String stream,
57 60 @Param("startTimeStamp")Long startTimeStamp, @Param("endTimeStamp")Long endTimeStamp,
58 61 @Param("callId")String callId, List<MediaServerItem> mediaServerItemList);
59 62  
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java
... ... @@ -105,20 +105,22 @@ public class CloudRecordController {
105 105 @ResponseBody
106 106 @GetMapping("/list")
107 107 @Operation(summary = "分页查询云端录像")
108   - @Parameter(name = "app", description = "应用名", required = true)
109   - @Parameter(name = "stream", description = "流ID", required = true)
  108 + @Parameter(name = "query", description = "检索内容", required = false)
  109 + @Parameter(name = "app", description = "应用名", required = false)
  110 + @Parameter(name = "stream", description = "流ID", required = false)
110 111 @Parameter(name = "page", description = "当前页", required = false)
111 112 @Parameter(name = "count", description = "每页查询数量", required = false)
112   - @Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = true)
113   - @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = true)
  113 + @Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = false)
  114 + @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false)
114 115 @Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部流媒体", required = false)
115 116 public PageInfo<CloudRecordItem> openRtpServer(
116   - @RequestParam String app,
117   - @RequestParam String stream,
  117 + @RequestParam(required = false) String query,
  118 + @RequestParam(required = false) String app,
  119 + @RequestParam(required = false) String stream,
118 120 @RequestParam int page,
119 121 @RequestParam int count,
120   - @RequestParam String startTime,
121   - @RequestParam String endTime,
  122 + @RequestParam(required = false) String startTime,
  123 + @RequestParam(required = false) String endTime,
122 124 @RequestParam(required = false) String mediaServerId
123 125  
124 126 ) {
... ... @@ -139,7 +141,22 @@ public class CloudRecordController {
139 141 if (mediaServerItems.isEmpty()) {
140 142 throw new ControllerException(ErrorCode.ERROR100.getCode(), "当前无流媒体");
141 143 }
142   - return cloudRecordService.getList(page, count, app, stream, startTime, endTime, mediaServerItems);
  144 + if (query != null && ObjectUtils.isEmpty(query.trim())) {
  145 + query = null;
  146 + }
  147 + if (app != null && ObjectUtils.isEmpty(app.trim())) {
  148 + app = null;
  149 + }
  150 + if (stream != null && ObjectUtils.isEmpty(stream.trim())) {
  151 + stream = null;
  152 + }
  153 + if (startTime != null && ObjectUtils.isEmpty(startTime.trim())) {
  154 + startTime = null;
  155 + }
  156 + if (endTime != null && ObjectUtils.isEmpty(endTime.trim())) {
  157 + endTime = null;
  158 + }
  159 + return cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServerItems);
143 160 }
144 161  
145 162 @ResponseBody
... ...
web_src/src/components/CloudRecord.vue
1 1 <template>
2   - <div id="app" style="width: 100%">
  2 + <div id="app" style="width: 100%">
3 3 <div class="page-header">
4 4 <div class="page-title">
5 5 <el-page-header v-if="recordDetail" @back="backToList" content="云端录像"></el-page-header>
... ... @@ -7,44 +7,82 @@
7 7 </div>
8 8  
9 9 <div class="page-header-btn">
  10 + 搜索:
  11 + <el-input @input="search" style="margin-right: 1rem; width: auto;" size="mini" placeholder="关键字"
  12 + prefix-icon="el-icon-search" v-model="search" clearable></el-input>
  13 + 开始时间:
  14 + <el-date-picker
  15 + v-model="startTime"
  16 + type="datetime"
  17 + placeholder="选择日期时间">
  18 + </el-date-picker>
  19 + 结束时间:
  20 + <el-date-picker
  21 + v-model="endTime"
  22 + type="datetime"
  23 + placeholder="选择日期时间">
  24 + </el-date-picker>
10 25 节点选择:
11   - <el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;" v-model="mediaServerId" placeholder="请选择" :disabled="recordDetail">
  26 + <el-select size="mini" @change="chooseMediaChange" style="width: 16rem; margin-right: 1rem;"
  27 + v-model="mediaServerId" placeholder="请选择" :disabled="recordDetail">
12 28 <el-option
13   - v-for="item in mediaServerList"
14   - :key="item.id"
15   - :label="item.id"
16   - :value="item.id">
  29 + v-for="item in mediaServerList"
  30 + :key="item.id"
  31 + :label="item.id"
  32 + :value="item.id">
17 33 </el-option>
18 34 </el-select>
19   - <el-button v-if="!recordDetail" icon="el-icon-refresh-right" circle size="mini" :loading="loading" @click="getRecordList()"></el-button>
  35 + <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord()">批量删除</el-button>
  36 + <el-button v-if="!recordDetail" icon="el-icon-refresh-right" circle size="mini" :loading="loading"
  37 + @click="getRecordList()"></el-button>
20 38 </div>
21 39 </div>
22 40 <div v-if="!recordDetail">
23 41  
24 42 <!--设备列表-->
25 43 <el-table :data="recordList" style="width: 100%" :height="winHeight">
26   - <el-table-column prop="app" label="应用名" >
  44 + <el-table-column prop="app" label="应用名">
27 45 </el-table-column>
28   - <el-table-column prop="stream" label="流ID" >
  46 + <el-table-column prop="stream" label="流ID" width="380">
29 47 </el-table-column>
30   - <el-table-column prop="time" label="时间" >
  48 + <el-table-column label="开始时间">
  49 + <template slot-scope="scope">
  50 + {{formatTimeStamp(scope.row.startTime)}}
  51 + </template>
  52 + </el-table-column>
  53 + <el-table-column label="结束时间">
  54 + <template slot-scope="scope">
  55 + {{formatTimeStamp(scope.row.endTime)}}
  56 + </template>
  57 + </el-table-column>
  58 + <el-table-column label="时长">
  59 + <template slot-scope="scope">
  60 + <el-tag>{{formatTime(scope.row.timeLen)}}</el-tag>
  61 + </template>
  62 + </el-table-column>
  63 + <el-table-column prop="fileName" label="文件名称">
31 64 </el-table-column>
32   - <el-table-column label="操作" width="360" fixed="right">
  65 + <el-table-column prop="mediaServerId" label="流媒体">
  66 + </el-table-column>
  67 + <el-table-column label="操作" width="200" fixed="right">
33 68 <template slot-scope="scope">
34   - <el-button size="medium" icon="el-icon-folder-opened" type="text" @click="showRecordDetail(scope.row)">查看</el-button>
35   - <!-- <el-button size="mini" icon="el-icon-delete" type="danger" @click="deleteRecord(scope.row)">删除</el-button>-->
  69 + <el-button size="medium" icon="el-icon-video-play" type="text" @click="showRecordDetail(scope.row)">播放
  70 + </el-button>
  71 + <el-button size="medium" icon="el-icon-delete" type="text" style="color: #f56c6c"
  72 + @click="deleteRecord(scope.row)">删除
  73 + </el-button>
36 74 </template>
37 75 </el-table-column>
38 76 </el-table>
39 77 <el-pagination
40   - style="float: right"
41   - @size-change="handleSizeChange"
42   - @current-change="currentChange"
43   - :current-page="currentPage"
44   - :page-size="count"
45   - :page-sizes="[15, 25, 35, 50]"
46   - layout="total, sizes, prev, pager, next"
47   - :total="total">
  78 + style="float: right"
  79 + @size-change="handleSizeChange"
  80 + @current-change="currentChange"
  81 + :current-page="currentPage"
  82 + :page-size="count"
  83 + :page-sizes="[15, 25, 35, 50]"
  84 + layout="total, sizes, prev, pager, next"
  85 + :total="total">
48 86 </el-pagination>
49 87 </div>
50 88  
... ... @@ -52,156 +90,173 @@
52 90 </template>
53 91  
54 92 <script>
55   - import uiHeader from '../layout/UiHeader.vue'
56   - import MediaServer from './service/MediaServer'
57   - export default {
58   - name: 'app',
59   - components: {
60   - uiHeader
61   - },
62   - data() {
63   - return {
64   - mediaServerList: [], // 滅体节点列表
65   - mediaServerId: null, // 媒体服务
66   - mediaServerPath: null, // 媒体服务地址
67   - recordList: [], // 设备列表
68   - chooseRecord: null, // 媒体服务
  93 +import uiHeader from '../layout/UiHeader.vue'
  94 +import MediaServer from './service/MediaServer'
  95 +import moment from 'moment'
69 96  
70   - updateLooper: 0, //数据刷新轮训标志
71   - winHeight: window.innerHeight - 250,
72   - currentPage:1,
73   - count:15,
74   - total:0,
75   - loading: false,
76   - mediaServerObj : new MediaServer(),
77   - recordDetail: false
  97 +export default {
  98 + name: 'app',
  99 + components: {
  100 + uiHeader
  101 + },
  102 + data() {
  103 + return {
  104 + search: '',
  105 + startTime: '',
  106 + endTime: '',
  107 + mediaServerList: [], // 滅体节点列表
  108 + mediaServerId: null, // 媒体服务
  109 + mediaServerPath: null, // 媒体服务地址
  110 + recordList: [], // 设备列表
  111 + chooseRecord: null, // 媒体服务
78 112  
79   - };
80   - },
81   - computed: {
  113 + updateLooper: 0, //数据刷新轮训标志
  114 + winHeight: window.innerHeight - 250,
  115 + currentPage: 1,
  116 + count: 15,
  117 + total: 0,
  118 + loading: false,
  119 + mediaServerObj: new MediaServer(),
  120 + recordDetail: false
82 121  
83   - },
84   - mounted() {
85   - this.initData();
86   - },
87   - destroyed() {
88   - // this.$destroy('videojs');
89   - },
90   - methods: {
91   - initData: function() {
92   - // 获取媒体节点列表
93   - this.getMediaServerList();
94   - // this.getRecordList();
95   - },
96   - currentChange: function(val){
97   - this.currentPage = val;
98   - this.getRecordList();
99   - },
100   - handleSizeChange: function(val){
101   - this.count = val;
102   - this.getRecordList();
103   - },
104   - getMediaServerList: function (){
105   - let that = this;
106   - that.mediaServerObj.getOnlineMediaServerList((data)=>{
107   - that.mediaServerList = data.data;
108   - if (that.mediaServerList.length > 0) {
109   - that.mediaServerId = that.mediaServerList[0].id
110   - that.setMediaServerPath(that.mediaServerId);
111   - that.getRecordList();
112   - }
113   - })
114   - },
115   - setMediaServerPath: function (serverId) {
116   - let that = this;
117   - let i;
118   - for (i = 0; i < that.mediaServerList.length; i++) {
119   - if (serverId === that.mediaServerList[i].id) {
120   - break;
121   - }
  122 + };
  123 + },
  124 + computed: {},
  125 + mounted() {
  126 + this.initData();
  127 + },
  128 + destroyed() {
  129 + // this.$destroy('videojs');
  130 + },
  131 + methods: {
  132 + initData: function () {
  133 + // 获取媒体节点列表
  134 + this.getMediaServerList();
  135 + // this.getRecordList();
  136 + },
  137 + currentChange: function (val) {
  138 + this.currentPage = val;
  139 + this.getRecordList();
  140 + },
  141 + handleSizeChange: function (val) {
  142 + this.count = val;
  143 + this.getRecordList();
  144 + },
  145 + getMediaServerList: function () {
  146 + let that = this;
  147 + that.mediaServerObj.getOnlineMediaServerList((data) => {
  148 + that.mediaServerList = data.data;
  149 + if (that.mediaServerList.length > 0) {
  150 + that.mediaServerId = that.mediaServerList[0].id
  151 + that.setMediaServerPath(that.mediaServerId);
  152 + that.getRecordList();
  153 + }
  154 + })
  155 + },
  156 + setMediaServerPath: function (serverId) {
  157 + let that = this;
  158 + let i;
  159 + for (i = 0; i < that.mediaServerList.length; i++) {
  160 + if (serverId === that.mediaServerList[i].id) {
  161 + break;
  162 + }
  163 + }
  164 + let port = that.mediaServerList[i].httpPort;
  165 + if (location.protocol === "https:" && that.mediaServerList[i].httpSSlPort) {
  166 + port = that.mediaServerList[i].httpSSlPort
  167 + }
  168 + that.mediaServerPath = location.protocol + "//" + that.mediaServerList[i].streamIp + ":" + port
  169 + console.log(that.mediaServerPath)
  170 + },
  171 + getRecordList: function () {
  172 + this.$axios({
  173 + method: 'get',
  174 + url: `/api/cloud/record/list`,
  175 + params: {
  176 + app: '',
  177 + stream: '',
  178 + query: this.search,
  179 + startTime: this.startTime,
  180 + endTime: this.endTime,
  181 + mediaServerId: this.mediaServerId,
  182 + page: this.currentPage,
  183 + count: this.count
  184 + }
  185 + }).then((res) => {
  186 + console.log(res)
  187 + if (res.data.code === 0) {
  188 + this.total = res.data.data.total;
  189 + this.recordList = res.data.data.list;
  190 + }
  191 + this.loading = false;
  192 + }).catch((error) => {
  193 + console.log(error);
  194 + this.loading = false;
  195 + });
  196 + },
  197 + backToList() {
  198 + this.recordDetail = false;
  199 + },
  200 + chooseMediaChange(val) {
  201 + console.log(val)
  202 + this.total = 0;
  203 + this.recordList = [];
  204 + this.setMediaServerPath(val);
  205 + this.getRecordList();
  206 + },
  207 + showRecordDetail(row) {
  208 + this.recordDetail = true;
  209 + this.chooseRecord = row;
  210 + // 查询是否存在录像
  211 + // this.$axios({
  212 + // method: 'delete',
  213 + // url:`/record_proxy/api/record/delete`,
  214 + // params: {
  215 + // page: this.currentPage,
  216 + // count: this.count
  217 + // }
  218 + // }).then((res) => {
  219 + // console.log(res)
  220 + // this.total = res.data.data.total;
  221 + // this.recordList = res.data.data.list;
  222 + // }).catch(function (error) {
  223 + // console.log(error);
  224 + // });
  225 + this.$router.push(`/cloudRecordDetail/${row.app}/${row.stream}`)
  226 + },
  227 + deleteRecord() {
  228 + // TODO
  229 + let that = this;
  230 + this.$axios({
  231 + method: 'delete',
  232 + url: `/record_proxy/api/record/delete`,
  233 + params: {
  234 + page: that.currentPage,
  235 + count: that.count
122 236 }
123   - let port = that.mediaServerList[i].httpPort;
124   - if (location.protocol === "https:" && that.mediaServerList[i].httpSSlPort) {
125   - port = that.mediaServerList[i].httpSSlPort
  237 + }).then(function (res) {
  238 + console.log(res)
  239 + if (res.data.code === 0) {
  240 + that.total = res.data.data.total;
  241 + that.recordList = res.data.data.list;
126 242 }
127   - that.mediaServerPath = location.protocol + "//" + that.mediaServerList[i].streamIp + ":" + port
128   - console.log(that.mediaServerPath)
129   - },
130   - getRecordList: function (){
131   - let that = this;
132   - this.$axios({
133   - method: 'get',
134   - url:`/record_proxy/${that.mediaServerId}/api/record/list`,
135   - params: {
136   - page: that.currentPage,
137   - count: that.count
138   - }
139   - }).then(function (res) {
140   - console.log(res)
141   - if (res.data.code === 0) {
142   - that.total = res.data.data.total;
143   - that.recordList = res.data.data.list;
144   - }
145   - that.loading = false;
146   - }).catch(function (error) {
147   - console.log(error);
148   - that.loading = false;
149   - });
150   - },
151   - backToList(){
152   - this.recordDetail= false;
153   - },
154   - chooseMediaChange(val){
155   - console.log(val)
156   - this.total = 0;
157   - this.recordList = [];
158   - this.setMediaServerPath(val);
159   - this.getRecordList();
160   - },
161   - showRecordDetail(row){
162   - this.recordDetail = true;
163   - this.chooseRecord = row;
164   - // 查询是否存在录像
165   - // this.$axios({
166   - // method: 'delete',
167   - // url:`/record_proxy/api/record/delete`,
168   - // params: {
169   - // page: this.currentPage,
170   - // count: this.count
171   - // }
172   - // }).then((res) => {
173   - // console.log(res)
174   - // this.total = res.data.data.total;
175   - // this.recordList = res.data.data.list;
176   - // }).catch(function (error) {
177   - // console.log(error);
178   - // });
179   - this.$router.push(`/cloudRecordDetail/${row.app}/${row.stream}`)
180   - },
181   - deleteRecord(){
182   - // TODO
183   - let that = this;
184   - this.$axios({
185   - method: 'delete',
186   - url:`/record_proxy/api/record/delete`,
187   - params: {
188   - page: that.currentPage,
189   - count: that.count
190   - }
191   - }).then(function (res) {
192   - console.log(res)
193   - if (res.data.code === 0) {
194   - that.total = res.data.data.total;
195   - that.recordList = res.data.data.list;
196   - }
197   - }).catch(function (error) {
198   - console.log(error);
199   - });
200   - },
  243 + }).catch(function (error) {
  244 + console.log(error);
  245 + });
  246 + },
  247 + formatTime(time) {
  248 + const h = parseInt(time / 3600)
  249 + const minute = parseInt(time / 60 % 60)
  250 + const second = Math.ceil(time % 60)
201 251  
  252 + return (h > 0 ? h + `小时` : '') + (minute > 0 ? minute + '分' : '') + second + '秒'
  253 + },
  254 + formatTimeStamp(time) {
  255 + return moment.unix(time).format('yyyy-MM-DD HH:mm:ss')
  256 + }
202 257  
203   - }
204   - };
  258 + }
  259 +};
205 260 </script>
206 261  
207 262 <style>
... ...
web_src/src/components/dialog/recordDownload.vue
... ... @@ -27,7 +27,7 @@ export default {
27 27 },
28 28 data() {
29 29 return {
30   - title: "四倍速下载中...",
  30 + title: "下载中...",
31 31 deviceId: "",
32 32 channelId: "",
33 33 app: "",
... ...