PlaybackTimebox.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <template>
  2. <div class="">
  3. <div class="box box-primary">
  4. <div class="box-header">
  5. <h4 class="text-primary text-center">设备录像时间轴({{devid}}-{{channel}})</h4>
  6. </div>
  7. <div class="box-body">
  8. <form class="form-inline">
  9. <div class="form-group form-group-sm">
  10. <button type="button" class="btn btn-sm btn-primary" @click.prevent="$router.go(-1)">
  11. <i class="fa fa-chevron-left"></i> 返回
  12. </button>
  13. </div>
  14. <div class="form-group pull-right">
  15. <div class="input-group input-group-sm">
  16. <DatePicker class="form-control" @update:day="updateDay" :day="day" ref="datePicker"></DatePicker>
  17. <div class="input-group-btn">
  18. <button type="button" class="btn btn-sm btn-default" @click.prevent="showDatePicker">
  19. <i class="fa fa-calendar"></i>
  20. </button>
  21. <router-link :to="`/devices/playback/list/${this.devid}/${this.channel}/${this.day}`" replace class="btn btn-default btn-sm">
  22. <i class="fa fa-hand-o-right"></i> 列表视图
  23. </router-link>
  24. </div>
  25. </div>
  26. </div>
  27. </form>
  28. <br>
  29. <div class="clearfix"></div>
  30. <LivePlayer live muted :videoUrl="videoUrl" :currentTime="currentTime" @ended="onVideoEnd" @timeupdate="onVideoTimeUpdate"
  31. v-loading="videoLoading" element-loading-text="加载中" element-loading-background="#000"
  32. style="margin:0 auto; max-width:700px;">
  33. </LivePlayer>
  34. <div class="text-center text-gray" v-if="isDemoUser(serverInfo, userInfo)">
  35. <br>
  36. 提示: 演示系统限制匿名登录播放时间, 若需测试长时间播放, 请<a target="_blank" href="//www.liveqing.com/docs/download/LiveGBS.html">下载使用</a>
  37. </div>
  38. <br>
  39. <br>
  40. <TimeRule :videos="videos" @timeChange="onTimeChange" ref="timeRule" v-loading="loading"></TimeRule>
  41. <div class="clearfix"></div>
  42. <br>
  43. </div>
  44. </div>
  45. </div>
  46. </template>
  47. <script>
  48. import _ from "lodash";
  49. import moment from "moment";
  50. import DatePicker from "components/DatePicker.vue";
  51. import TimeRule from 'components/TimeRule.vue'
  52. import LivePlayer from '@liveqing/liveplayer'
  53. import { mapState } from "vuex";
  54. export default {
  55. props: {
  56. devid: {
  57. type: String,
  58. default: ""
  59. },
  60. channel: {
  61. type: String,
  62. default: ""
  63. },
  64. mode: {
  65. type: String,
  66. default: "timebox"
  67. },
  68. day: {
  69. type: String,
  70. default: () => moment().format("YYYYMMDD")
  71. }
  72. },
  73. data() {
  74. return {
  75. timerange: [
  76. moment(this.day, "YYYYMMDD").startOf('hour').toDate(),
  77. moment(this.day, "YYYYMMDD").startOf('hour').toDate()
  78. ],
  79. videoLoading: false,
  80. loading: false,
  81. records: [],
  82. currentTime: null,
  83. videos: [],
  84. video: null,
  85. videoUrl: "",
  86. streamID: "",
  87. touchTimer: 0
  88. };
  89. },
  90. computed: {
  91. ...mapState(['userInfo', 'serverInfo']),
  92. },
  93. watch: {
  94. day: function(newVal, oldVal) {
  95. this.timerange = [
  96. moment(this.day, "YYYYMMDD").startOf('hour').toDate(),
  97. moment(this.day, "YYYYMMDD").startOf('hour').toDate()
  98. ]
  99. },
  100. video: function(newVal, oldVal) {
  101. if(newVal && newVal != oldVal) {
  102. this.startPlayback();
  103. } else {
  104. this.stopPlayback();
  105. }
  106. }
  107. },
  108. components: {
  109. DatePicker, LivePlayer, TimeRule
  110. },
  111. methods: {
  112. keyDown(e) {
  113. if(e.keyCode == 27) {
  114. this.$el.querySelector('.fa-chevron-left').click();
  115. }
  116. },
  117. isMobile() {
  118. return videojs.browser.IS_IOS || videojs.browser.IS_ANDROID;
  119. },
  120. showDatePicker() {
  121. $(this.$refs.datePicker.$el).focus();
  122. },
  123. updateDay(day) {
  124. this.$router.replace(`/devices/playback/${this.mode}/${this.devid}/${this.channel}/${day}`);
  125. },
  126. nextTimeRange() {
  127. var end = moment(this.day, "YYYYMMDD").add(24, 'hours');
  128. var now = moment().startOf("second");
  129. if(end.isAfter(now, "second")) {
  130. end = now;
  131. }
  132. var r1 = moment(this.timerange[1]);
  133. if(r1.isSameOrAfter(end, "second")){
  134. return false;
  135. }
  136. var r2 = moment(this.timerange[1]).add(6, 'hours');
  137. if(r2.isAfter(end)) {
  138. r2 = end;
  139. }
  140. if(r2.startOf("minute").isSameOrBefore(r1.startOf("minute"), "second")) {
  141. return false;
  142. }
  143. this.timerange = [r1.toDate(), r2.toDate()];
  144. return true;
  145. },
  146. getRecords(refresh) {
  147. if(refresh) {
  148. this.loading = true;
  149. this.records = [];
  150. }
  151. if(!this.nextTimeRange()){
  152. this.videos = this.records;
  153. this.loading = false;
  154. return
  155. }
  156. $.ajax("/api/v1/playback/recordlist", {
  157. type: 'get',
  158. global: false,
  159. data: {
  160. timeout: 5,
  161. serial: this.devid,
  162. code: this.channel,
  163. starttime: moment(this.timerange[0]).format("YYYY-MM-DDTHH:mm:ss"),
  164. endtime: moment(this.timerange[1]).format("YYYY-MM-DDTHH:mm:ss")
  165. }
  166. }).then(ret => {
  167. var items = ret.RecordList || [];
  168. this.records = this.records.concat(items.filter(item => {
  169. if(!item || !item.StartTime || !item.EndTime) {
  170. return false;
  171. }
  172. return true;
  173. }));
  174. }).always(() => {
  175. this.$nextTick(() => {
  176. this.getRecords(false);
  177. })
  178. });
  179. },
  180. formatName(row, col, cell) {
  181. if (cell) return cell;
  182. return "-";
  183. },
  184. stopPlayback() {
  185. return new Promise((resolve, reject) => {
  186. if(this.touchTimer) {
  187. clearInterval(this.touchTimer);
  188. this.touchTimer = 0;
  189. }
  190. if(!this.streamID) {
  191. resolve();
  192. return
  193. }
  194. $.get("/api/v1/playback/stop", {
  195. streamid: this.streamID
  196. }).always(() => {
  197. this.streamID = "";
  198. this.videoUrl = "";
  199. resolve();
  200. })
  201. })
  202. },
  203. beforeUnload(event) {
  204. this.stopPlayback();
  205. event.preventDefault();
  206. event.returnValue = '';
  207. },
  208. async startPlayback() {
  209. await this.stopPlayback();
  210. if(!this.video) return;
  211. this.videoLoading = true;
  212. $.get("/api/v1/playback/start", {
  213. serial: this.devid,
  214. code: this.channel,
  215. starttime: this.video.StartTime,
  216. endtime: this.video.EndTime
  217. }).then(streamInfo => {
  218. var videoUrl = this.isMobile() ? streamInfo.HLS : streamInfo.RTMP;
  219. if(this.flvSupported()) {
  220. if(streamInfo.WS_FLV && !this.isIE()) {
  221. videoUrl = streamInfo.WS_FLV;
  222. } else if(streamInfo.FLV) {
  223. videoUrl = streamInfo.FLV;
  224. }
  225. }
  226. this.streamID = streamInfo.StreamID;
  227. this.videoUrl = videoUrl;
  228. // no need since v1.2
  229. // this.touchTimer = setInterval(() => {
  230. // this.touchPlayback()
  231. // }, 15000);
  232. }).always(() => {
  233. this.videoLoading = false;
  234. });
  235. },
  236. touchPlayback() {
  237. if(!this.streamID) return;
  238. $.get("/api/v1/playback/touch",{
  239. streamid: this.streamID
  240. })
  241. },
  242. onTimeChange(video) {
  243. this.video = video;
  244. },
  245. onVideoEnd() {
  246. },
  247. onVideoTimeUpdate() {
  248. }
  249. },
  250. mounted() {
  251. let mmt = moment();
  252. let n = mmt.diff(mmt.clone().startOf('day'), 'minutes');
  253. n -= 10;
  254. if(n < 0) n = 0;
  255. this.$refs.timeRule.clickMinute(n);
  256. this.getRecords(true);
  257. $(window).on("beforeunload", this.beforeUnload);
  258. },
  259. beforeDestroy() {
  260. $(window).off("beforeunload", this.beforeUnload);
  261. this.stopPlayback();
  262. },
  263. beforeRouteLeave(to, from, next) {
  264. this.stopPlayback();
  265. next();
  266. },
  267. beforeRouteUpdate(to, from, next) {
  268. this.stopPlayback();
  269. next();
  270. this.$nextTick(() => {
  271. this.getRecords(true);
  272. })
  273. }
  274. };
  275. </script>