videoList.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. <template>
  2. <div id="app">
  3. <div style="background-color: #FFFFFF; margin-bottom: 1rem; position: relative; padding: 0.5rem; text-align: left;">
  4. <span style="font-size: 1rem; font-weight: bold;">视频列表</span>
  5. <div style="position: absolute; right: 1rem; top: 0.3rem;">
  6. <el-button icon="el-icon-refresh-right" circle size="mini" @click="getVideoList()"></el-button>
  7. </div>
  8. </div>
  9. <div class="videoList">
  10. <div class="video-item" v-for="(item, index) in videoList">
  11. <!-- <video class="video-js vjs-default-skin vjs-big-play-centered" :id="genVideoId(index, item)" style="width: 15rem; height: 10rem;" controls>
  12. </video> -->
  13. <img class="video-item-img" :src="getSnap(item)" @click="showVideo(item)" />
  14. <div class="video-item-title">
  15. {{ item.stream }}
  16. <el-button icon="el-icon-search" circle size="mini" style="float: right;" @click="showVideoInfo(item)"></el-button>
  17. </div>
  18. </div>
  19. </div>
  20. <el-dialog title="视频播放" :visible.sync="showVideoDialog" :destroy-on-close="true">
  21. <iframe :src="getPlayerPath" ref="videoRender" style="width:100%; height:35rem;" frameborder="no" border="0"
  22. marginwidth="0" marginheight="0" scrolling="no" allowtransparency="yes"></iframe>
  23. <div id="shared" style="text-align: right;">
  24. <el-tabs>
  25. <el-tab-pane label="媒体流信息" name="media">
  26. <div style="margin-bottom: 0.5rem;">
  27. <el-button type="primary" size="small" @click="playRecord(true,'')">播放</el-button>
  28. <el-button type="primary" size="small" @click="startRecord()">录制</el-button>
  29. <el-button type="primary" size="small" @click="stopRecord()">停止录制</el-button>
  30. </div>
  31. <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  32. <span style="width: 5rem; line-height: 2.5rem; text-align: right;">播放地址:</span>
  33. <el-input v-model="getPlayerShared.sharedUrl" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedUrl)"></el-input>
  34. </div>
  35. <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  36. <span style="width: 5rem; line-height: 2.5rem; text-align: right;">iframe:</span>
  37. <el-input v-model="getPlayerShared.sharedIframe" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedIframe)"></el-input>
  38. </div>
  39. <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  40. <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span>
  41. <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true" v-on:click.native="copySharedInfo(getPlayerShared.sharedRtmp)"></el-input>
  42. </div>
  43. </el-tab-pane>
  44. <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
  45. <el-tab-pane label="录像查询" name="second">
  46. <el-table :data="videoHistory.searchHistoryResult" style="width: 100%">
  47. <el-table-column label="文件" prop="path" width="500">
  48. </el-table-column>
  49. <el-table-column align="right" width="300">
  50. <template slot="header" slot-scope="scope">
  51. <el-date-picker v-model="videoHistory.searchHistoryParam" type="date" placeholder="选择日期" @change="recordList()">
  52. </el-date-picker>
  53. </template>
  54. </el-table-column>
  55. <el-table-column label="">
  56. <template slot-scope="scope">
  57. <el-button type="primary" size="mini" @click="playRecord(false,scope.row)">播放</el-button>
  58. </template>
  59. </el-table-column>
  60. </el-table>
  61. </el-tab-pane>
  62. </el-tabs>
  63. </div>
  64. </el-dialog>
  65. </div>
  66. </template>
  67. <script>
  68. export default {
  69. name: 'app',
  70. components: {},
  71. data() {
  72. return {
  73. videoUrl: '',
  74. showVideoDialog: false, //控制在线监控实时流窗口的开关
  75. videoList: [],
  76. videoComponentList: [],
  77. currentPlayerInfo: {}, //当前播放对象
  78. updateLooper: 0, //数据刷新轮训标志
  79. videoHistory: {
  80. searchHistoryParam: "",
  81. searchHistoryResult: [], //媒体流历史记录搜索结果
  82. }
  83. };
  84. },
  85. computed: {
  86. getPlayerPath: function() {
  87. if (!this.showVideoDialog) { //防止出现流断开不彻底的问题
  88. return '';
  89. } else {
  90. return '/video/video.html?url=' + encodeURIComponent(this.videoUrl);
  91. }
  92. },
  93. getPlayerShared: function() {
  94. let info = {
  95. sharedUrl: window.location.host + '/video/video.html?url=' + this.videoUrl,
  96. sharedIframe: '<iframe src="' + window.location.host + '/video/video.html?url=' + this.videoUrl + '"></iframe>',
  97. sharedRtmp: this.videoUrl
  98. };
  99. return info;
  100. },
  101. },
  102. mounted() {
  103. this.initData();
  104. this.updateLooper = setInterval(this.initData, 10000);
  105. },
  106. destroyed() {
  107. this.$destroy('videojs');
  108. clearTimeout(this.updateLooper);
  109. },
  110. methods: {
  111. initData: function() {
  112. this.getVideoList();
  113. },
  114. getVideoList: function() {
  115. let that = this;
  116. this.$axios({
  117. method: 'get',
  118. url: this.$global.genApiUrl('/getMediaList') + '&schema=rtmp'
  119. }).then(function(res) {
  120. if (res.data.code == 0) {
  121. that.videoList = res.data.data;
  122. }
  123. });
  124. },
  125. showVideoInfo: function(videoData) {
  126. let msg = '所属应用:' + videoData.app + ' 数据流类型:' + videoData.schema + ' 流名称:' + videoData.stream + ' 观看人数:' +
  127. videoData.readerCount;
  128. this.$alert(msg, '视频信息', {
  129. confirmButtonText: '确定'
  130. });
  131. },
  132. showVideo: function(streamInfo) {
  133. this.showVideoDialog = true;
  134. this.videoUrl = this.$global.baseMediaUrl + streamInfo.app + '/' + streamInfo.stream + ".flv?st="+new Date().getTime();
  135. this.currentPlayerInfo = streamInfo;
  136. },
  137. copySharedInfo: function(data) {
  138. console.log('复制内容:' + data);
  139. let _this = this;
  140. this.$copyText(data).then(
  141. function(e) {
  142. _this.$message({
  143. showClose: true,
  144. message: '复制成功',
  145. type: 'success'
  146. });
  147. },
  148. function(e) {
  149. _this.$message({
  150. showClose: true,
  151. message: '复制失败,请手动复制',
  152. type: 'error'
  153. });
  154. }
  155. );
  156. },
  157. startRecord: function() {
  158. let that = this;
  159. let streamInfo = this.currentPlayerInfo;
  160. let startURL = this.$global.genApiUrl('/startRecord') + '&type=1&vhost=' + streamInfo.vhost + "&app=" + streamInfo
  161. .app + "&stream=" + streamInfo.stream;
  162. console.log(startURL);
  163. this.$axios({
  164. method: 'get',
  165. url: startURL
  166. }).then(function(res) {
  167. console.log(JSON.stringify(res.data));
  168. if (res.data.code == 0 && res.data.result) {
  169. that.$message({
  170. showClose: true,
  171. message: '开始录制',
  172. type: 'success'
  173. });
  174. } else {
  175. that.$message({
  176. showClose: true,
  177. message: res.data.msg,
  178. type: 'error'
  179. });
  180. }
  181. });
  182. },
  183. stopRecord: function() {
  184. let that = this;
  185. let streamInfo = this.currentPlayerInfo;
  186. let stopURL = this.$global.genApiUrl('/stopRecord') + '&type=1&vhost=' + streamInfo.vhost + "&app=" + streamInfo.app +
  187. "&stream=" + streamInfo.stream;
  188. console.log(stopURL);
  189. this.$axios({
  190. method: 'get',
  191. url: stopURL
  192. }).then(function(res) {
  193. console.log(JSON.stringify(res.data));
  194. if (res.data.code == 0 && res.data.result) {
  195. that.$message({
  196. showClose: true,
  197. message: '结束录制',
  198. type: 'success'
  199. });
  200. } else {
  201. that.$message({
  202. showClose: true,
  203. message: res.data.msg,
  204. type: 'error'
  205. });
  206. }
  207. });
  208. },
  209. recordList: function() {
  210. let that = this;
  211. let streamInfo = this.currentPlayerInfo;
  212. var date = this.videoHistory.searchHistoryParam;
  213. var year = date.getFullYear();
  214. var month = date.getMonth() + 1;
  215. var day = date.getDate();
  216. if (month < 10) {
  217. month = "0" + month;
  218. }
  219. if (day < 10) {
  220. day = "0" + day;
  221. }
  222. let nowDate = year + "-" + month + "-" + day;
  223. let stopURL = this.$global.genApiUrl('/getMp4RecordFile') + '&vhost=' + streamInfo.vhost + "&app=" + streamInfo.app +
  224. "&stream=" + streamInfo.stream + '&period=' + nowDate;
  225. console.log(stopURL);
  226. this.$axios({
  227. method: 'get',
  228. url: stopURL
  229. }).then(function(res) {
  230. console.log(JSON.stringify(res.data));
  231. if (res.data.code == 0 && res.data) {
  232. let mp4files = res.data.data.paths;
  233. let rootPath = res.data.data.rootPath;
  234. let weburltemp = rootPath.substr(rootPath.indexOf("record"));
  235. let weburl = "http://" + that.$global.serverip + "/" + weburltemp;
  236. for (let i in mp4files) {
  237. let fullMp4file = weburl + mp4files[i]
  238. console.log(fullMp4file);
  239. that.videoHistory.searchHistoryResult.push({
  240. "path": fullMp4file
  241. });
  242. };
  243. that.$message({
  244. showClose: true,
  245. message: '列表获取成功',
  246. type: 'success'
  247. });
  248. } else {
  249. that.$message({
  250. showClose: true,
  251. message: res.data.msg,
  252. type: 'error'
  253. });
  254. }
  255. });
  256. },
  257. getSnap: function(streamInfo) {
  258. let videoUrl = this.$global.baseMediaUrl + streamInfo.app + '/' + streamInfo.stream + ".flv";
  259. console.log("videoUrl:\t" + videoUrl);
  260. let snapUrl = videoUrl.replace(/^ws/, "http");
  261. console.log('snapUrl:\t' + snapUrl);
  262. let fullSnapRequest = this.$global.genApiUrl('/getSnap') + '&timeout_sec=10&expire_sec=30&url=' + snapUrl + "&st=" +
  263. new Date().getTime();
  264. console.log('fullSnapRequest:\t' + fullSnapRequest);
  265. return fullSnapRequest;
  266. },
  267. playRecord: function(isBackLive,rowData) {
  268. console.log("当前行:" + JSON.stringify(rowData));
  269. this.$refs.videoRender.contentWindow.postMessage({
  270. cmd: 'switchUrl',
  271. params: isBackLive?{"path":this.videoUrl,"live":isBackLive}:{"path":rowData.path,"live":isBackLive}
  272. }, '*')
  273. }
  274. }
  275. };
  276. </script>
  277. <style>
  278. .videoList {
  279. display: flex;
  280. flex-wrap: wrap;
  281. align-content: flex-start;
  282. }
  283. .video-item {
  284. position: relative;
  285. width: 15rem;
  286. height: 10rem;
  287. margin-right: 1rem;
  288. background-color: #000000;
  289. }
  290. .video-item-img {
  291. position: absolute;
  292. top: 0;
  293. bottom: 0;
  294. left: 0;
  295. right: 0;
  296. margin: auto;
  297. width: 100%;
  298. height: 100%;
  299. }
  300. .video-item-img:after {
  301. content: "";
  302. display: inline-block;
  303. position: absolute;
  304. z-index: 2;
  305. top: 0;
  306. bottom: 0;
  307. left: 0;
  308. right: 0;
  309. margin: auto;
  310. width: 3rem;
  311. height: 3rem;
  312. background-image: url("../assets/loading.png");
  313. background-size: cover;
  314. background-color: #000000;
  315. }
  316. .video-item-title {
  317. position: absolute;
  318. bottom: 0;
  319. color: #000000;
  320. background-color: #ffffff;
  321. line-height: 1.5rem;
  322. padding: 0.3rem;
  323. width: 14.4rem;
  324. }
  325. </style>