Commit c226deacff2135751247d2e31d7e12c8b6db5314

Authored by 648540858
1 parent dea44dcd

修复语音对讲发送页

src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -45,6 +45,8 @@ import gov.nist.javax.sip.message.SIPResponse;
45 45 import org.slf4j.Logger;
46 46 import org.slf4j.LoggerFactory;
47 47 import org.springframework.beans.factory.annotation.Autowired;
  48 +import org.springframework.beans.factory.annotation.Qualifier;
  49 +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
48 50 import org.springframework.stereotype.Service;
49 51 import org.springframework.util.ObjectUtils;
50 52  
... ... @@ -993,7 +995,7 @@ public class PlayServiceImpl implements IPlayService {
993 995 logger.warn("开启语音广播的时候未找到通道: {}", channelId);
994 996 return null;
995 997 }
996   - MediaServerItem mediaServerItem = mediaServerService.getMediaServerForMinimumLoad();
  998 + MediaServerItem mediaServerItem = mediaServerService.getMediaServerForMinimumLoad(null);
997 999 String app = "broadcast";
998 1000 // TODO 从sip user agent中判断是什么品牌设备,大华默认使用talk模式,其他使用broadcast模式
999 1001 // String app = "talk";
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
... ... @@ -271,7 +271,7 @@ public class PlayController {
271 271  
272 272 @GetMapping("/1111")
273 273 public void broadcastApi1() {
274   - MediaServerItem defaultMediaServer = mediaServerService.getMediaServerForMinimumLoad();
  274 + MediaServerItem defaultMediaServer = mediaServerService.getMediaServerForMinimumLoad(null);
275 275 Device device = storager.queryVideoDevice("34020000001320090001");
276 276 playService.talk(defaultMediaServer, device, "34020000001370000001", null, null, null);
277 277  
... ...
web_src/src/components/dialog/devicePlayer.vue
1 1 <template>
2   -<div id="devicePlayer" v-loading="isLoging">
  2 + <div id="devicePlayer" v-loading="isLoging">
3 3  
4 4 <el-dialog title="视频播放" top="0" :close-on-click-modal="false" :visible.sync="showVideoDialog" @close="close()">
5 5 <div style="width: 100%; height: 100%">
6   - <el-tabs type="card" :stretch="true" v-model="activePlayer" @tab-click="changePlayer" v-if="Object.keys(this.player).length > 1">
  6 + <el-tabs type="card" :stretch="true" v-model="activePlayer" @tab-click="changePlayer"
  7 + v-if="Object.keys(this.player).length > 1">
7 8 <el-tab-pane label="Jessibuca" name="jessibuca">
8   - <jessibucaPlayer v-if="activePlayer === 'jessibuca'" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer>
  9 + <jessibucaPlayer v-if="activePlayer === 'jessibuca'" ref="jessibuca" :visible.sync="showVideoDialog"
  10 + :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px"
  11 + :hasAudio="hasAudio" fluent autoplay live></jessibucaPlayer>
9 12 </el-tab-pane>
10 13 <el-tab-pane label="WebRTC" name="webRTC">
11   - <rtc-player v-if="activePlayer === 'webRTC'" ref="webRTC" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player>
  14 + <rtc-player v-if="activePlayer === 'webRTC'" ref="webRTC" :visible.sync="showVideoDialog"
  15 + :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px"
  16 + :hasAudio="hasAudio" fluent autoplay live></rtc-player>
12 17 </el-tab-pane>
13 18 <el-tab-pane label="h265web">h265web敬请期待</el-tab-pane>
14 19 <el-tab-pane label="wsPlayer">wsPlayer 敬请期待</el-tab-pane>
15 20 </el-tabs>
16   - <jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></jessibucaPlayer>
17   - <rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca" :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError" height="100px" :hasAudio="hasAudio" fluent autoplay live ></rtc-player>
  21 + <jessibucaPlayer v-if="Object.keys(this.player).length == 1 && this.player.jessibuca" ref="jessibuca"
  22 + :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError"
  23 + height="100px" :hasAudio="hasAudio" fluent autoplay live></jessibucaPlayer>
  24 + <rtc-player v-if="Object.keys(this.player).length == 1 && this.player.webRTC" ref="jessibuca"
  25 + :visible.sync="showVideoDialog" :videoUrl="videoUrl" :error="videoError" :message="videoError"
  26 + height="100px" :hasAudio="hasAudio" fluent autoplay live></rtc-player>
18 27  
19 28 </div>
20   - <div id="shared" style="text-align: right; margin-top: 1rem;">
21   - <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick" >
22   - <el-tab-pane label="实时视频" name="media">
23   - <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
24   - <span style="width: 5rem; line-height: 2.5rem; text-align: right;">播放地址:</span>
25   - <el-input v-model="getPlayerShared.sharedUrl" :disabled="true" >
26   - <template slot="append">
27   - <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedUrl" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
28   - </template>
29   - </el-input>
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;">iframe:</span>
33   - <el-input v-model="getPlayerShared.sharedIframe" :disabled="true" >
34   - <template slot="append">
35   - <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedIframe" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
36   - </template>
37   - </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" >
42   - <el-button slot="append" icon="el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedRtmp" @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button>
43   - <el-dropdown slot="prepend" v-if="streamInfo" trigger="click" @command="copyUrl">
44   - <el-button >
45   - 更多地址<i class="el-icon-arrow-down el-icon--right"></i>
46   - </el-button>
47   - <el-dropdown-menu slot="dropdown" >
48   - <el-dropdown-item v-if="streamInfo.flv" :command="streamInfo.flv">
49   - <el-tag >FLV:</el-tag>
50   - <span>{{ streamInfo.flv }}</span>
51   - </el-dropdown-item>
52   - <el-dropdown-item v-if="streamInfo.https_flv" :command="streamInfo.https_flv">
53   - <el-tag >FLV(https):</el-tag>
54   - <span>{{ streamInfo.https_flv }}</span>
55   - </el-dropdown-item>
56   - <el-dropdown-item v-if="streamInfo.ws_flv" :command="streamInfo.ws_flv">
57   - <el-tag >FLV(ws):</el-tag>
58   - <span >{{ streamInfo.ws_flv }}</span>
59   - </el-dropdown-item>
60   - <el-dropdown-item v-if="streamInfo.wss_flv" :command="streamInfo.wss_flv">
61   - <el-tag >FLV(wss):</el-tag>
62   - <span>{{ streamInfo.wss_flv }}</span>
63   - </el-dropdown-item>
64   - <el-dropdown-item v-if="streamInfo.fmp4" :command="streamInfo.fmp4">
65   - <el-tag >FMP4:</el-tag>
66   - <span>{{ streamInfo.fmp4 }}</span>
67   - </el-dropdown-item>
68   - <el-dropdown-item v-if="streamInfo.https_fmp4" :command="streamInfo.https_fmp4">
69   - <el-tag >FMP4(https):</el-tag>
70   - <span>{{ streamInfo.https_fmp4 }}</span>
71   - </el-dropdown-item>
72   - <el-dropdown-item v-if="streamInfo.ws_fmp4" :command="streamInfo.ws_fmp4">
73   - <el-tag >FMP4(ws):</el-tag>
74   - <span>{{ streamInfo.ws_fmp4 }}</span>
75   - </el-dropdown-item>
76   - <el-dropdown-item v-if="streamInfo.wss_fmp4" :command="streamInfo.wss_fmp4">
77   - <el-tag >FMP4(wss):</el-tag>
78   - <span>{{ streamInfo.wss_fmp4 }}</span>
79   - </el-dropdown-item>
80   - <el-dropdown-item v-if="streamInfo.hls" :command="streamInfo.hls">
81   - <el-tag>HLS:</el-tag>
82   - <span>{{ streamInfo.hls }}</span>
83   - </el-dropdown-item>
84   - <el-dropdown-item v-if="streamInfo.https_hls" :command="streamInfo.https_hls">
85   - <el-tag >HLS(https):</el-tag>
86   - <span>{{ streamInfo.https_hls }}</span>
87   - </el-dropdown-item>
88   - <el-dropdown-item v-if="streamInfo.ws_hls" :command="streamInfo.ws_hls">
89   - <el-tag >HLS(ws):</el-tag>
90   - <span>{{ streamInfo.ws_hls }}</span>
91   - </el-dropdown-item>
92   - <el-dropdown-item v-if="streamInfo.wss_hls" :command="streamInfo.wss_hls">
93   - <el-tag >HLS(wss):</el-tag>
94   - <span>{{ streamInfo.wss_hls }}</span>
95   - </el-dropdown-item>
96   - <el-dropdown-item v-if="streamInfo.ts" :command="streamInfo.ts">
97   - <el-tag>TS:</el-tag>
98   - <span>{{ streamInfo.ts }}</span>
99   - </el-dropdown-item>
100   - <el-dropdown-item v-if="streamInfo.https_ts" :command="streamInfo.https_ts">
101   - <el-tag>TS(https):</el-tag>
102   - <span>{{ streamInfo.https_ts }}</span>
103   - </el-dropdown-item>
104   - <el-dropdown-item v-if="streamInfo.ws_ts" :command="streamInfo.ws_ts">
105   - <el-tag>TS(ws):</el-tag>
106   - <span>{{ streamInfo.ws_ts }}</span>
107   - </el-dropdown-item>
108   - <el-dropdown-item v-if="streamInfo.wss_ts" :command="streamInfo.wss_ts">
109   - <el-tag>TS(wss):</el-tag>
110   - <span>{{ streamInfo.wss_ts }}</span>
111   - </el-dropdown-item>
112   - <el-dropdown-item v-if="streamInfo.rtc" :command="streamInfo.rtc">
113   - <el-tag >RTC:</el-tag>
114   - <span>{{ streamInfo.rtc }}</span>
115   - </el-dropdown-item>
116   - <el-dropdown-item v-if="streamInfo.rtcs" :command="streamInfo.rtcs">
117   - <el-tag >RTCS:</el-tag>
118   - <span>{{ streamInfo.rtcs }}</span>
119   - </el-dropdown-item>
120   - <el-dropdown-item v-if="streamInfo.rtmp" :command="streamInfo.rtmp">
121   - <el-tag >RTMP:</el-tag>
122   - <span>{{ streamInfo.rtmp }}</span>
123   - </el-dropdown-item>
124   - <el-dropdown-item v-if="streamInfo.rtmps" :command="streamInfo.rtmps">
125   - <el-tag >RTMPS:</el-tag>
126   - <span>{{ streamInfo.rtmps }}</span>
127   - </el-dropdown-item>
128   - <el-dropdown-item v-if="streamInfo.rtsp" :command="streamInfo.rtsp">
129   - <el-tag >RTSP:</el-tag>
130   - <span>{{ streamInfo.rtsp }}</span>
131   - </el-dropdown-item>
132   - <el-dropdown-item v-if="streamInfo.rtsps" :command="streamInfo.rtsps">
133   - <el-tag >RTSPS:</el-tag>
134   - <span>{{ streamInfo.rtsps }}</span>
135   - </el-dropdown-item>
136   - </el-dropdown-menu>
137   - </el-dropdown>
138   - </el-input>
139   -
140   - </div>
141   - </el-tab-pane>
142   - <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
143   - <!--遥控界面-->
144   - <el-tab-pane label="云台控制" name="control" v-if="showPtz">
145   - <div style="display: flex; justify-content: left;">
146   - <div class="control-wrapper">
147   - <div class="control-btn control-top" @mousedown="ptzCamera('up')" @mouseup="ptzCamera('stop')">
148   - <i class="el-icon-caret-top"></i>
149   - <div class="control-inner-btn control-inner"></div>
150   - </div>
151   - <div class="control-btn control-left" @mousedown="ptzCamera('left')" @mouseup="ptzCamera('stop')">
152   - <i class="el-icon-caret-left"></i>
153   - <div class="control-inner-btn control-inner"></div>
154   - </div>
155   - <div class="control-btn control-bottom" @mousedown="ptzCamera('down')" @mouseup="ptzCamera('stop')">
156   - <i class="el-icon-caret-bottom"></i>
157   - <div class="control-inner-btn control-inner"></div>
158   - </div>
159   - <div class="control-btn control-right" @mousedown="ptzCamera('right')" @mouseup="ptzCamera('stop')">
160   - <i class="el-icon-caret-right"></i>
161   - <div class="control-inner-btn control-inner"></div>
162   - </div>
163   - <div class="control-round">
164   - <div class="control-round-inner"><i class="fa fa-pause-circle"></i></div>
165   - </div>
166   - <div style="position: absolute; left: 7.25rem; top: 1.25rem" @mousedown="ptzCamera('zoomin')" @mouseup="ptzCamera('stop')"><i class="el-icon-zoom-in control-zoom-btn" style="font-size: 1.875rem;"></i></div>
167   - <div style="position: absolute; left: 7.25rem; top: 3.25rem; font-size: 1.875rem;" @mousedown="ptzCamera('zoomout')" @mouseup="ptzCamera('stop')"><i class="el-icon-zoom-out control-zoom-btn"></i></div>
168   - <div class="contro-speed" style="position: absolute; left: 4px; top: 7rem; width: 9rem;">
169   - <el-slider v-model="controSpeed" :max="255"></el-slider>
170   - </div>
171   - </div>
172   -
173   - <div class="control-panel">
174   - <el-button-group>
175   - <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center" size="medium">预置位编号</el-tag>
176   - <el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini" v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1" :max="255"></el-input-number>
177   - <el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="presetPosition(129, presetPos)">设置</el-button>
178   - <el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary" icon="el-icon-place" @click="presetPosition(130, presetPos)">调用</el-button>
179   - <el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">删除</el-button>
180   - <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">巡航速度</el-tag>
181   - <el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
182   - <el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置</el-button>
183   - <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center" size="medium">停留时间</el-tag>
184   - <el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini" v-model="cruisingTime" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
185   - <el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini" icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置</el-button>
186   - <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center" size="medium">巡航组编号</el-tag>
187   - <el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini" v-model="cruisingGroup" controls-position="right" :precision="0" :min="0" :max="255"></el-input-number>
188   - <el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点</el-button>
189   - <el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点</el-button>
190   - <el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini" icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组</el-button>
191   - <el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航</el-button>
192   - <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center" size="medium">扫描速度</el-tag>
193   - <el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini" v-model="scanSpeed" controls-position="right" :precision="0" :min="1" :max="4095"></el-input-number>
194   - <el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini" icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置</el-button>
195   - <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center" size="medium">扫描组编号</el-tag>
196   - <el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini" v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0" :max="255"></el-input-number>
197   - <el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界</el-button>
198   - <el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini" icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界</el-button>
199   - <el-button style="position: absolute; left: 27rem; top: 7rem; width: 5rem" size="mini" type="primary" icon="el-icon-video-camera-solid" @click="setCommand(137, scanGroup, 0)">扫描</el-button>
200   - <el-button style="position: absolute; left: 27rem; top: 9rem; width: 5rem" size="mini" type="danger" icon="el-icon-switch-button" @click="ptzCamera('stop')">停止</el-button>
201   - </el-button-group>
202   - </div>
203   - </div>
204   - </el-tab-pane>
205   - <el-tab-pane label="编码信息" name="codec" v-loading="tracksLoading">
206   - <p>
207   - 无法播放或者没有声音?&nbsp&nbsp&nbsp试一试&nbsp
208   - <el-button size="mini" type="primary" v-if="!coverPlaying" @click="coverPlay">转码播放</el-button>
209   - <el-button size="mini" type="danger" v-if="coverPlaying" @click="convertStopClick">停止转码</el-button>
210   - </p>
211   - <div class="trank" >
212   - <p v-if="tracksNotLoaded" style="text-align: center;padding-top: 3rem;">暂无数据</p>
213   - <div v-for="(item, index) in tracks" style="width: 50%; float: left" loading>
214   - <span >流 {{index}}</span>
215   - <div class="trankInfo" v-if="item.codec_type == 0">
216   - <p>格式: {{item.codec_id_name}}</p>
217   - <p>类型: 视频</p>
218   - <p>分辨率: {{item.width}} x {{item.height}}</p>
219   - <p>帧率: {{item.fps}}</p>
220   - </div>
221   - <div class="trankInfo" v-if="item.codec_type == 1">
222   - <p>格式: {{item.codec_id_name}}</p>
223   - <p>类型: 音频</p>
224   - <p>采样位数: {{item.sample_bit}}</p>
225   - <p>采样率: {{item.sample_rate}}</p>
226   - </div>
227   - </div>
228   -
229   - </div>
230   -
231   - </el-tab-pane>
232   - <el-tab-pane label="语音对讲" name="broadcast" >
233   - <div class="trank" style="text-align: center;">
234   - <el-button @click="broadcastStatusClick()" :type="getBroadcastStatus()" :disabled="broadcastStatus === -2" circle icon="el-icon-microphone" style="font-size: 32px; padding: 24px;margin-top: 24px;"/>
235   - <p>
236   - <span v-if="broadcastStatus === -2">正在释放资源</span>
237   - <span v-if="broadcastStatus === -1">点击开始对讲</span>
238   - <span v-if="broadcastStatus === 0">等待接通中...</span>
239   - <span v-if="broadcastStatus === 1">请说话</span>
240   - </p>
241   -
  29 + <div id="shared" style="text-align: right; margin-top: 1rem;">
  30 + <el-tabs v-model="tabActiveName" @tab-click="tabHandleClick">
  31 + <el-tab-pane label="实时视频" name="media">
  32 + <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  33 + <span style="width: 5rem; line-height: 2.5rem; text-align: right;">播放地址:</span>
  34 + <el-input v-model="getPlayerShared.sharedUrl" :disabled="true">
  35 + <template slot="append">
  36 + <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedUrl"
  37 + @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
  38 + </template>
  39 + </el-input>
  40 + </div>
  41 + <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  42 + <span style="width: 5rem; line-height: 2.5rem; text-align: right;">iframe:</span>
  43 + <el-input v-model="getPlayerShared.sharedIframe" :disabled="true">
  44 + <template slot="append">
  45 + <i class="cpoy-btn el-icon-document-copy" title="点击拷贝" v-clipboard="getPlayerShared.sharedIframe"
  46 + @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></i>
  47 + </template>
  48 + </el-input>
  49 + </div>
  50 + <div style="display: flex; margin-bottom: 0.5rem; height: 2.5rem;">
  51 + <span style="width: 5rem; line-height: 2.5rem; text-align: right;">资源地址:</span>
  52 + <el-input v-model="getPlayerShared.sharedRtmp" :disabled="true">
  53 + <el-button slot="append" icon="el-icon-document-copy" title="点击拷贝"
  54 + v-clipboard="getPlayerShared.sharedRtmp"
  55 + @success="$message({type:'success', message:'成功拷贝到粘贴板'})"></el-button>
  56 + <el-dropdown slot="prepend" v-if="streamInfo" trigger="click" @command="copyUrl">
  57 + <el-button>
  58 + 更多地址<i class="el-icon-arrow-down el-icon--right"></i>
  59 + </el-button>
  60 + <el-dropdown-menu slot="dropdown">
  61 + <el-dropdown-item v-if="streamInfo.flv" :command="streamInfo.flv">
  62 + <el-tag>FLV:</el-tag>
  63 + <span>{{ streamInfo.flv }}</span>
  64 + </el-dropdown-item>
  65 + <el-dropdown-item v-if="streamInfo.https_flv" :command="streamInfo.https_flv">
  66 + <el-tag>FLV(https):</el-tag>
  67 + <span>{{ streamInfo.https_flv }}</span>
  68 + </el-dropdown-item>
  69 + <el-dropdown-item v-if="streamInfo.ws_flv" :command="streamInfo.ws_flv">
  70 + <el-tag>FLV(ws):</el-tag>
  71 + <span>{{ streamInfo.ws_flv }}</span>
  72 + </el-dropdown-item>
  73 + <el-dropdown-item v-if="streamInfo.wss_flv" :command="streamInfo.wss_flv">
  74 + <el-tag>FLV(wss):</el-tag>
  75 + <span>{{ streamInfo.wss_flv }}</span>
  76 + </el-dropdown-item>
  77 + <el-dropdown-item v-if="streamInfo.fmp4" :command="streamInfo.fmp4">
  78 + <el-tag>FMP4:</el-tag>
  79 + <span>{{ streamInfo.fmp4 }}</span>
  80 + </el-dropdown-item>
  81 + <el-dropdown-item v-if="streamInfo.https_fmp4" :command="streamInfo.https_fmp4">
  82 + <el-tag>FMP4(https):</el-tag>
  83 + <span>{{ streamInfo.https_fmp4 }}</span>
  84 + </el-dropdown-item>
  85 + <el-dropdown-item v-if="streamInfo.ws_fmp4" :command="streamInfo.ws_fmp4">
  86 + <el-tag>FMP4(ws):</el-tag>
  87 + <span>{{ streamInfo.ws_fmp4 }}</span>
  88 + </el-dropdown-item>
  89 + <el-dropdown-item v-if="streamInfo.wss_fmp4" :command="streamInfo.wss_fmp4">
  90 + <el-tag>FMP4(wss):</el-tag>
  91 + <span>{{ streamInfo.wss_fmp4 }}</span>
  92 + </el-dropdown-item>
  93 + <el-dropdown-item v-if="streamInfo.hls" :command="streamInfo.hls">
  94 + <el-tag>HLS:</el-tag>
  95 + <span>{{ streamInfo.hls }}</span>
  96 + </el-dropdown-item>
  97 + <el-dropdown-item v-if="streamInfo.https_hls" :command="streamInfo.https_hls">
  98 + <el-tag>HLS(https):</el-tag>
  99 + <span>{{ streamInfo.https_hls }}</span>
  100 + </el-dropdown-item>
  101 + <el-dropdown-item v-if="streamInfo.ws_hls" :command="streamInfo.ws_hls">
  102 + <el-tag>HLS(ws):</el-tag>
  103 + <span>{{ streamInfo.ws_hls }}</span>
  104 + </el-dropdown-item>
  105 + <el-dropdown-item v-if="streamInfo.wss_hls" :command="streamInfo.wss_hls">
  106 + <el-tag>HLS(wss):</el-tag>
  107 + <span>{{ streamInfo.wss_hls }}</span>
  108 + </el-dropdown-item>
  109 + <el-dropdown-item v-if="streamInfo.ts" :command="streamInfo.ts">
  110 + <el-tag>TS:</el-tag>
  111 + <span>{{ streamInfo.ts }}</span>
  112 + </el-dropdown-item>
  113 + <el-dropdown-item v-if="streamInfo.https_ts" :command="streamInfo.https_ts">
  114 + <el-tag>TS(https):</el-tag>
  115 + <span>{{ streamInfo.https_ts }}</span>
  116 + </el-dropdown-item>
  117 + <el-dropdown-item v-if="streamInfo.ws_ts" :command="streamInfo.ws_ts">
  118 + <el-tag>TS(ws):</el-tag>
  119 + <span>{{ streamInfo.ws_ts }}</span>
  120 + </el-dropdown-item>
  121 + <el-dropdown-item v-if="streamInfo.wss_ts" :command="streamInfo.wss_ts">
  122 + <el-tag>TS(wss):</el-tag>
  123 + <span>{{ streamInfo.wss_ts }}</span>
  124 + </el-dropdown-item>
  125 + <el-dropdown-item v-if="streamInfo.rtc" :command="streamInfo.rtc">
  126 + <el-tag>RTC:</el-tag>
  127 + <span>{{ streamInfo.rtc }}</span>
  128 + </el-dropdown-item>
  129 + <el-dropdown-item v-if="streamInfo.rtcs" :command="streamInfo.rtcs">
  130 + <el-tag>RTCS:</el-tag>
  131 + <span>{{ streamInfo.rtcs }}</span>
  132 + </el-dropdown-item>
  133 + <el-dropdown-item v-if="streamInfo.rtmp" :command="streamInfo.rtmp">
  134 + <el-tag>RTMP:</el-tag>
  135 + <span>{{ streamInfo.rtmp }}</span>
  136 + </el-dropdown-item>
  137 + <el-dropdown-item v-if="streamInfo.rtmps" :command="streamInfo.rtmps">
  138 + <el-tag>RTMPS:</el-tag>
  139 + <span>{{ streamInfo.rtmps }}</span>
  140 + </el-dropdown-item>
  141 + <el-dropdown-item v-if="streamInfo.rtsp" :command="streamInfo.rtsp">
  142 + <el-tag>RTSP:</el-tag>
  143 + <span>{{ streamInfo.rtsp }}</span>
  144 + </el-dropdown-item>
  145 + <el-dropdown-item v-if="streamInfo.rtsps" :command="streamInfo.rtsps">
  146 + <el-tag>RTSPS:</el-tag>
  147 + <span>{{ streamInfo.rtsps }}</span>
  148 + </el-dropdown-item>
  149 + </el-dropdown-menu>
  150 + </el-dropdown>
  151 + </el-input>
  152 +
  153 + </div>
  154 + </el-tab-pane>
  155 + <!--{"code":0,"data":{"paths":["22-29-30.mp4"],"rootPath":"/home/kkkkk/Documents/ZLMediaKit/release/linux/Debug/www/record/hls/kkkkk/2020-05-11/"}}-->
  156 + <!--遥控界面-->
  157 + <el-tab-pane label="云台控制" name="control" v-if="showPtz">
  158 + <div style="display: flex; justify-content: left;">
  159 + <div class="control-wrapper">
  160 + <div class="control-btn control-top" @mousedown="ptzCamera('up')" @mouseup="ptzCamera('stop')">
  161 + <i class="el-icon-caret-top"></i>
  162 + <div class="control-inner-btn control-inner"></div>
  163 + </div>
  164 + <div class="control-btn control-left" @mousedown="ptzCamera('left')" @mouseup="ptzCamera('stop')">
  165 + <i class="el-icon-caret-left"></i>
  166 + <div class="control-inner-btn control-inner"></div>
  167 + </div>
  168 + <div class="control-btn control-bottom" @mousedown="ptzCamera('down')" @mouseup="ptzCamera('stop')">
  169 + <i class="el-icon-caret-bottom"></i>
  170 + <div class="control-inner-btn control-inner"></div>
  171 + </div>
  172 + <div class="control-btn control-right" @mousedown="ptzCamera('right')" @mouseup="ptzCamera('stop')">
  173 + <i class="el-icon-caret-right"></i>
  174 + <div class="control-inner-btn control-inner"></div>
  175 + </div>
  176 + <div class="control-round">
  177 + <div class="control-round-inner"><i class="fa fa-pause-circle"></i></div>
  178 + </div>
  179 + <div style="position: absolute; left: 7.25rem; top: 1.25rem" @mousedown="ptzCamera('zoomin')"
  180 + @mouseup="ptzCamera('stop')"><i class="el-icon-zoom-in control-zoom-btn"
  181 + style="font-size: 1.875rem;"></i></div>
  182 + <div style="position: absolute; left: 7.25rem; top: 3.25rem; font-size: 1.875rem;"
  183 + @mousedown="ptzCamera('zoomout')" @mouseup="ptzCamera('stop')"><i
  184 + class="el-icon-zoom-out control-zoom-btn"></i></div>
  185 + <div class="contro-speed" style="position: absolute; left: 4px; top: 7rem; width: 9rem;">
  186 + <el-slider v-model="controSpeed" :max="255"></el-slider>
242 187 </div>
243   - </el-tab-pane>
  188 + </div>
  189 +
  190 + <div class="control-panel">
  191 + <el-button-group>
  192 + <el-tag style="position :absolute; left: 0rem; top: 0rem; width: 5rem; text-align: center"
  193 + size="medium">预置位编号
  194 + </el-tag>
  195 + <el-input-number style="position: absolute; left: 5rem; top: 0rem; width: 6rem" size="mini"
  196 + v-model="presetPos" controls-position="right" :precision="0" :step="1" :min="1"
  197 + :max="255"></el-input-number>
  198 + <el-button style="position: absolute; left: 11rem; top: 0rem; width: 5rem" size="mini"
  199 + icon="el-icon-add-location" @click="presetPosition(129, presetPos)">设置
  200 + </el-button>
  201 + <el-button style="position: absolute; left: 27rem; top: 0rem; width: 5rem" size="mini" type="primary"
  202 + icon="el-icon-place" @click="presetPosition(130, presetPos)">调用
  203 + </el-button>
  204 + <el-button style="position: absolute; left: 16rem; top: 0rem; width: 5rem" size="mini"
  205 + icon="el-icon-delete-location" @click="presetPosition(131, presetPos)">删除
  206 + </el-button>
  207 + <el-tag style="position :absolute; left: 0rem; top: 2.5rem; width: 5rem; text-align: center"
  208 + size="medium">巡航速度
  209 + </el-tag>
  210 + <el-input-number style="position: absolute; left: 5rem; top: 2.5rem; width: 6rem" size="mini"
  211 + v-model="cruisingSpeed" controls-position="right" :precision="0" :min="1"
  212 + :max="4095"></el-input-number>
  213 + <el-button style="position: absolute; left: 11rem; top: 2.5rem; width: 5rem" size="mini"
  214 + icon="el-icon-loading" @click="setSpeedOrTime(134, cruisingGroup, cruisingSpeed)">设置
  215 + </el-button>
  216 + <el-tag style="position :absolute; left: 16rem; top: 2.5rem; width: 5rem; text-align: center"
  217 + size="medium">停留时间
  218 + </el-tag>
  219 + <el-input-number style="position: absolute; left: 21rem; top: 2.5rem; width: 6rem" size="mini"
  220 + v-model="cruisingTime" controls-position="right" :precision="0" :min="1"
  221 + :max="4095"></el-input-number>
  222 + <el-button style="position: absolute; left: 27rem; top: 2.5rem; width: 5rem" size="mini"
  223 + icon="el-icon-timer" @click="setSpeedOrTime(135, cruisingGroup, cruisingTime)">设置
  224 + </el-button>
  225 + <el-tag style="position :absolute; left: 0rem; top: 4.5rem; width: 5rem; text-align: center"
  226 + size="medium">巡航组编号
  227 + </el-tag>
  228 + <el-input-number style="position: absolute; left: 5rem; top: 4.5rem; width: 6rem" size="mini"
  229 + v-model="cruisingGroup" controls-position="right" :precision="0" :min="0"
  230 + :max="255"></el-input-number>
  231 + <el-button style="position: absolute; left: 11rem; top: 4.5rem; width: 5rem" size="mini"
  232 + icon="el-icon-add-location" @click="setCommand(132, cruisingGroup, presetPos)">添加点
  233 + </el-button>
  234 + <el-button style="position: absolute; left: 16rem; top: 4.5rem; width: 5rem" size="mini"
  235 + icon="el-icon-delete-location" @click="setCommand(133, cruisingGroup, presetPos)">删除点
  236 + </el-button>
  237 + <el-button style="position: absolute; left: 21rem; top: 4.5rem; width: 5rem" size="mini"
  238 + icon="el-icon-delete" @click="setCommand(133, cruisingGroup, 0)">删除组
  239 + </el-button>
  240 + <el-button style="position: absolute; left: 27rem; top: 5rem; width: 5rem" size="mini" type="primary"
  241 + icon="el-icon-video-camera-solid" @click="setCommand(136, cruisingGroup, 0)">巡航
  242 + </el-button>
  243 + <el-tag style="position :absolute; left: 0rem; top: 7rem; width: 5rem; text-align: center"
  244 + size="medium">扫描速度
  245 + </el-tag>
  246 + <el-input-number style="position: absolute; left: 5rem; top: 7rem; width: 6rem" size="mini"
  247 + v-model="scanSpeed" controls-position="right" :precision="0" :min="1"
  248 + :max="4095"></el-input-number>
  249 + <el-button style="position: absolute; left: 11rem; top: 7rem; width: 5rem" size="mini"
  250 + icon="el-icon-loading" @click="setSpeedOrTime(138, scanGroup, scanSpeed)">设置
  251 + </el-button>
  252 + <el-tag style="position :absolute; left: 0rem; top: 9rem; width: 5rem; text-align: center"
  253 + size="medium">扫描组编号
  254 + </el-tag>
  255 + <el-input-number style="position: absolute; left: 5rem; top: 9rem; width: 6rem" size="mini"
  256 + v-model="scanGroup" controls-position="right" :precision="0" :step="1" :min="0"
  257 + :max="255"></el-input-number>
  258 + <el-button style="position: absolute; left: 11rem; top: 9rem; width: 5rem" size="mini"
  259 + icon="el-icon-d-arrow-left" @click="setCommand(137, scanGroup, 1)">左边界
  260 + </el-button>
  261 + <el-button style="position: absolute; left: 16rem; top: 9rem; width: 5rem" size="mini"
  262 + icon="el-icon-d-arrow-right" @click="setCommand(137, scanGroup, 2)">右边界
  263 + </el-button>
  264 + <el-button style="position: absolute; left: 27rem; top: 7rem; width: 5rem" size="mini" type="primary"
  265 + icon="el-icon-video-camera-solid" @click="setCommand(137, scanGroup, 0)">扫描
  266 + </el-button>
  267 + <el-button style="position: absolute; left: 27rem; top: 9rem; width: 5rem" size="mini" type="danger"
  268 + icon="el-icon-switch-button" @click="ptzCamera('stop')">停止
  269 + </el-button>
  270 + </el-button-group>
  271 + </div>
  272 + </div>
  273 + </el-tab-pane>
  274 + <el-tab-pane label="编码信息" name="codec" v-loading="tracksLoading">
  275 + <p>
  276 + 无法播放或者没有声音?&nbsp&nbsp&nbsp试一试&nbsp
  277 + <el-button size="mini" type="primary" v-if="!coverPlaying" @click="coverPlay">转码播放</el-button>
  278 + <el-button size="mini" type="danger" v-if="coverPlaying" @click="convertStopClick">停止转码</el-button>
  279 + </p>
  280 + <div class="trank">
  281 + <p v-if="tracksNotLoaded" style="text-align: center;padding-top: 3rem;">暂无数据</p>
  282 + <div v-for="(item, index) in tracks" style="width: 50%; float: left" loading>
  283 + <span>流 {{ index }}</span>
  284 + <div class="trankInfo" v-if="item.codec_type == 0">
  285 + <p>格式: {{ item.codec_id_name }}</p>
  286 + <p>类型: 视频</p>
  287 + <p>分辨率: {{ item.width }} x {{ item.height }}</p>
  288 + <p>帧率: {{ item.fps }}</p>
  289 + </div>
  290 + <div class="trankInfo" v-if="item.codec_type == 1">
  291 + <p>格式: {{ item.codec_id_name }}</p>
  292 + <p>类型: 音频</p>
  293 + <p>采样位数: {{ item.sample_bit }}</p>
  294 + <p>采样率: {{ item.sample_rate }}</p>
  295 + </div>
  296 + </div>
  297 +
  298 + </div>
  299 +
  300 + </el-tab-pane>
  301 + <el-tab-pane label="语音对讲" name="broadcast">
  302 + <div class="trank" style="text-align: center;">
  303 + <el-button @click="broadcastStatusClick()" :type="getBroadcastStatus()" :disabled="broadcastStatus === -2"
  304 + circle icon="el-icon-microphone" style="font-size: 32px; padding: 24px;margin-top: 24px;"/>
  305 + <p>
  306 + <span v-if="broadcastStatus === -2">正在释放资源</span>
  307 + <span v-if="broadcastStatus === -1">点击开始对讲</span>
  308 + <span v-if="broadcastStatus === 0">等待接通中...</span>
  309 + <span v-if="broadcastStatus === 1">请说话</span>
  310 + </p>
  311 +
  312 + </div>
  313 + </el-tab-pane>
244 314  
245   - </el-tabs>
246   - </div>
  315 + </el-tabs>
  316 + </div>
247 317 </el-dialog>
248   -</div>
  318 + </div>
249 319 </template>
250 320  
251 321 <script>
... ... @@ -253,561 +323,620 @@ import rtcPlayer from &#39;../dialog/rtcPlayer.vue&#39;
253 323 import LivePlayer from '@liveqing/liveplayer'
254 324 import crypto from 'crypto'
255 325 import jessibucaPlayer from '../common/jessibuca.vue'
  326 +
256 327 export default {
257   - name: 'devicePlayer',
258   - props: {},
259   - components: {
260   - LivePlayer, jessibucaPlayer, rtcPlayer,
  328 + name: 'devicePlayer',
  329 + props: {},
  330 + components: {
  331 + LivePlayer, jessibucaPlayer, rtcPlayer,
  332 + },
  333 + computed: {
  334 + getPlayerShared: function () {
  335 + return {
  336 + sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl),
  337 + sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>',
  338 + sharedRtmp: this.videoUrl
  339 + };
  340 + }
  341 + },
  342 + created() {
  343 + console.log("created")
  344 + console.log(this.player)
  345 + this.broadcastStatus = -1;
  346 + if (Object.keys(this.player).length === 1) {
  347 + this.activePlayer = Object.keys(this.player)[0]
  348 + }
  349 + },
  350 + data() {
  351 + return {
  352 + video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4',
  353 + videoUrl: '',
  354 + activePlayer: "jessibuca",
  355 + // 如何你只是用一种播放器,直接注释掉不用的部分即可
  356 + player: {
  357 + jessibuca: ["ws_flv", "wss_flv"],
  358 + webRTC: ["rtc", "rtcs"],
  359 + },
  360 + showVideoDialog: false,
  361 + streamId: '',
  362 + app: '',
  363 + mediaServerId: '',
  364 + convertKey: '',
  365 + deviceId: '',
  366 + channelId: '',
  367 + tabActiveName: 'media',
  368 + hasAudio: false,
  369 + loadingRecords: false,
  370 + recordsLoading: false,
  371 + isLoging: false,
  372 + controSpeed: 30,
  373 + timeVal: 0,
  374 + timeMin: 0,
  375 + timeMax: 1440,
  376 + presetPos: 1,
  377 + cruisingSpeed: 100,
  378 + cruisingTime: 5,
  379 + cruisingGroup: 0,
  380 + scanSpeed: 100,
  381 + scanGroup: 0,
  382 + tracks: [],
  383 + coverPlaying: false,
  384 + tracksLoading: false,
  385 + showPtz: true,
  386 + showRrecord: true,
  387 + tracksNotLoaded: false,
  388 + sliderTime: 0,
  389 + seekTime: 0,
  390 + recordStartTime: 0,
  391 + showTimeText: "00:00:00",
  392 + streamInfo: null,
  393 + broadcastRtc: null,
  394 + broadcastStatus: -1, // -2 正在释放资源 -1 默认状态 0 等待接通 1 接通成功
  395 + };
  396 + },
  397 + methods: {
  398 + tabHandleClick: function (tab, event) {
  399 + console.log(tab)
  400 + var that = this;
  401 + that.tracks = [];
  402 + that.tracksLoading = true;
  403 + that.tracksNotLoaded = false;
  404 + if (tab.name === "codec") {
  405 + this.$axios({
  406 + method: 'get',
  407 + url: '/zlm/' + this.mediaServerId + '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtsp&app=' + this.app + '&stream=' + this.streamId
  408 + }).then(function (res) {
  409 + that.tracksLoading = false;
  410 + if (res.data.code == 0 && res.data.tracks) {
  411 + that.tracks = res.data.tracks;
  412 + } else {
  413 + that.tracksNotLoaded = true;
  414 + that.$message({
  415 + showClose: true,
  416 + message: '获取编码信息失败,',
  417 + type: 'warning'
  418 + });
  419 + }
  420 + }).catch(function (e) {
  421 + });
  422 + }
261 423 },
262   - computed: {
263   - getPlayerShared: function () {
264   - return {
265   - sharedUrl: window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl),
266   - sharedIframe: '<iframe src="' + window.location.origin + '/#/play/wasm/' + encodeURIComponent(this.videoUrl) + '"></iframe>',
267   - sharedRtmp: this.videoUrl
268   - };
269   - }
  424 + changePlayer: function (tab) {
  425 + console.log(this.player[tab.name][0])
  426 + this.activePlayer = tab.name;
  427 + this.videoUrl = this.getUrlByStreamInfo()
  428 + console.log(this.videoUrl)
270 429 },
271   - created() {
272   - console.log("created")
273   - console.log(this.player)
274   - this.broadcastStatus = -1;
275   - if (Object.keys(this.player).length === 1) {
276   - this.activePlayer = Object.keys(this.player)[0]
  430 + openDialog: function (tab, deviceId, channelId, param) {
  431 + if (this.showVideoDialog) {
  432 + return;
  433 + }
  434 + this.tabActiveName = tab;
  435 + this.channelId = channelId;
  436 + this.deviceId = deviceId;
  437 + this.streamId = "";
  438 + this.mediaServerId = "";
  439 + this.app = "";
  440 + this.videoUrl = ""
  441 + if (!!this.$refs[this.activePlayer]) {
  442 + this.$refs[this.activePlayer].pause();
  443 + }
  444 + switch (tab) {
  445 + case "media":
  446 + this.play(param.streamInfo, param.hasAudio)
  447 + break;
  448 + case "streamPlay":
  449 + this.tabActiveName = "media";
  450 + this.showRrecord = false;
  451 + this.showPtz = false;
  452 + this.play(param.streamInfo, param.hasAudio)
  453 + break;
  454 + case "control":
  455 + break;
277 456 }
278 457 },
279   - data() {
280   - return {
281   - video: 'http://lndxyj.iqilu.com/public/upload/2019/10/14/8c001ea0c09cdc59a57829dabc8010fa.mp4',
282   - videoUrl: '',
283   - activePlayer: "jessibuca",
284   - // 如何你只是用一种播放器,直接注释掉不用的部分即可
285   - player: {
286   - jessibuca : ["ws_flv", "wss_flv"],
287   - webRTC: ["rtc", "rtcs"],
288   - },
289   - showVideoDialog: false,
290   - streamId: '',
291   - app : '',
292   - mediaServerId : '',
293   - convertKey: '',
294   - deviceId: '',
295   - channelId: '',
296   - tabActiveName: 'media',
297   - hasAudio: false,
298   - loadingRecords: false,
299   - recordsLoading: false,
300   - isLoging: false,
301   - controSpeed: 30,
302   - timeVal: 0,
303   - timeMin: 0,
304   - timeMax: 1440,
305   - presetPos: 1,
306   - cruisingSpeed: 100,
307   - cruisingTime: 5,
308   - cruisingGroup: 0,
309   - scanSpeed: 100,
310   - scanGroup: 0,
311   - tracks: [],
312   - coverPlaying:false,
313   - tracksLoading: false,
314   - showPtz: true,
315   - showRrecord: true,
316   - tracksNotLoaded: false,
317   - sliderTime: 0,
318   - seekTime: 0,
319   - recordStartTime: 0,
320   - showTimeText: "00:00:00",
321   - streamInfo: null,
322   - broadcastRtc: null,
323   - broadcastStatus: -1, // -2 正在释放资源 -1 默认状态 0 等待接通 1 接通成功
324   - };
  458 + play: function (streamInfo, hasAudio) {
  459 + this.streamInfo = streamInfo;
  460 + this.hasAudio = hasAudio;
  461 + this.isLoging = false;
  462 + // this.videoUrl = streamInfo.rtc;
  463 + this.videoUrl = this.getUrlByStreamInfo();
  464 + this.streamId = streamInfo.stream;
  465 + this.app = streamInfo.app;
  466 + this.mediaServerId = streamInfo.mediaServerId;
  467 + this.playFromStreamInfo(false, streamInfo)
325 468 },
326   - methods: {
327   - tabHandleClick: function(tab, event) {
328   - console.log(tab)
329   - var that = this;
330   - that.tracks = [];
331   - that.tracksLoading = true;
332   - that.tracksNotLoaded = false;
333   - if (tab.name === "codec") {
334   - this.$axios({
335   - method: 'get',
336   - url: '/zlm/' +this.mediaServerId+ '/index/api/getMediaInfo?vhost=__defaultVhost__&schema=rtsp&app='+ this.app +'&stream='+ this.streamId
337   - }).then(function (res) {
338   - that.tracksLoading = false;
339   - if (res.data.code == 0 && res.data.tracks) {
340   - that.tracks = res.data.tracks;
341   - }else{
342   - that.tracksNotLoaded = true;
343   - that.$message({
344   - showClose: true,
345   - message: '获取编码信息失败,',
346   - type: 'warning'
347   - });
348   - }
349   - }).catch(function (e) {});
350   - }
351   - },
352   - changePlayer: function (tab) {
353   - console.log(this.player[tab.name][0])
354   - this.activePlayer = tab.name;
355   - this.videoUrl = this.getUrlByStreamInfo()
356   - console.log(this.videoUrl)
357   - },
358   - openDialog: function (tab, deviceId, channelId, param) {
359   - if (this.showVideoDialog) {
360   - return;
361   - }
362   - this.tabActiveName = tab;
363   - this.channelId = channelId;
364   - this.deviceId = deviceId;
365   - this.streamId = "";
366   - this.mediaServerId = "";
367   - this.app = "";
368   - this.videoUrl = ""
369   - if (!!this.$refs[this.activePlayer]) {
370   - this.$refs[this.activePlayer].pause();
371   - }
372   - switch (tab) {
373   - case "media":
374   - this.play(param.streamInfo, param.hasAudio)
375   - break;
376   - case "streamPlay":
377   - this.tabActiveName = "media";
378   - this.showRrecord = false;
379   - this.showPtz = false;
380   - this.play(param.streamInfo, param.hasAudio)
381   - break;
382   - case "control":
383   - break;
384   - }
385   - },
386   - play: function (streamInfo, hasAudio) {
387   - this.streamInfo = streamInfo;
388   - this.hasAudio = hasAudio;
389   - this.isLoging = false;
390   - // this.videoUrl = streamInfo.rtc;
391   - this.videoUrl = this.getUrlByStreamInfo();
392   - this.streamId = streamInfo.stream;
393   - this.app = streamInfo.app;
394   - this.mediaServerId = streamInfo.mediaServerId;
395   - this.playFromStreamInfo(false, streamInfo)
396   - },
397   - getUrlByStreamInfo(){
398   - console.log(this.streamInfo)
399   - if (location.protocol === "https:") {
400   - this.videoUrl = this.streamInfo[this.player[this.activePlayer][1]]
401   - }else {
402   - this.videoUrl = this.streamInfo[this.player[this.activePlayer][0]]
403   - }
404   - return this.videoUrl;
  469 + getUrlByStreamInfo() {
  470 + console.log(this.streamInfo)
  471 + if (location.protocol === "https:") {
  472 + this.videoUrl = this.streamInfo[this.player[this.activePlayer][1]]
  473 + } else {
  474 + this.videoUrl = this.streamInfo[this.player[this.activePlayer][0]]
  475 + }
  476 + return this.videoUrl;
405 477  
406   - },
407   - coverPlay: function () {
408   - var that = this;
409   - this.coverPlaying = true;
410   - this.$refs[this.activePlayer].pause()
411   - that.$axios({
412   - method: 'post',
413   - url: '/api/play/convert/' + that.streamId
414   - }).then(function (res) {
415   - if (res.data.code === 0) {
416   - that.convertKey = res.data.key;
417   - setTimeout(()=>{
418   - that.isLoging = false;
419   - that.playFromStreamInfo(false, res.data.data);
420   - }, 2000)
421   - } else {
422   - that.isLoging = false;
423   - that.coverPlaying = false;
424   - that.$message({
425   - showClose: true,
426   - message: '转码失败',
427   - type: 'error'
428   - });
429   - }
430   - }).catch(function (e) {
431   - console.log(e)
432   - that.coverPlaying = false;
433   - that.$message({
434   - showClose: true,
435   - message: '播放错误',
436   - type: 'error'
437   - });
438   - });
439   - },
440   - convertStopClick: function() {
441   - this.convertStop(()=>{
442   - this.$refs[this.activePlayer].play(this.videoUrl)
443   - });
444   - },
445   - convertStop: function(callback) {
446   - var that = this;
447   - that.$refs.videoPlayer.pause()
448   - this.$axios({
449   - method: 'post',
450   - url: '/api/play/convertStop/' + this.convertKey
451   - }).then(function (res) {
452   - if (res.data.code == 0) {
453   - console.log(res.data.msg)
454   - }else {
455   - console.error(res.data.msg)
456   - }
457   - if (callback )callback();
458   - }).catch(function (e) {});
459   - that.coverPlaying = false;
460   - that.convertKey = "";
461   - // if (callback )callback();
462   - },
  478 + },
  479 + coverPlay: function () {
  480 + var that = this;
  481 + this.coverPlaying = true;
  482 + this.$refs[this.activePlayer].pause()
  483 + that.$axios({
  484 + method: 'post',
  485 + url: '/api/play/convert/' + that.streamId
  486 + }).then(function (res) {
  487 + if (res.data.code === 0) {
  488 + that.convertKey = res.data.key;
  489 + setTimeout(() => {
  490 + that.isLoging = false;
  491 + that.playFromStreamInfo(false, res.data.data);
  492 + }, 2000)
  493 + } else {
  494 + that.isLoging = false;
  495 + that.coverPlaying = false;
  496 + that.$message({
  497 + showClose: true,
  498 + message: '转码失败',
  499 + type: 'error'
  500 + });
  501 + }
  502 + }).catch(function (e) {
  503 + console.log(e)
  504 + that.coverPlaying = false;
  505 + that.$message({
  506 + showClose: true,
  507 + message: '播放错误',
  508 + type: 'error'
  509 + });
  510 + });
  511 + },
  512 + convertStopClick: function () {
  513 + this.convertStop(() => {
  514 + this.$refs[this.activePlayer].play(this.videoUrl)
  515 + });
  516 + },
  517 + convertStop: function (callback) {
  518 + var that = this;
  519 + that.$refs.videoPlayer.pause()
  520 + this.$axios({
  521 + method: 'post',
  522 + url: '/api/play/convertStop/' + this.convertKey
  523 + }).then(function (res) {
  524 + if (res.data.code == 0) {
  525 + console.log(res.data.msg)
  526 + } else {
  527 + console.error(res.data.msg)
  528 + }
  529 + if (callback) callback();
  530 + }).catch(function (e) {
  531 + });
  532 + that.coverPlaying = false;
  533 + that.convertKey = "";
  534 + // if (callback )callback();
  535 + },
463 536  
464   - playFromStreamInfo: function (realHasAudio, streamInfo) {
465   - this.showVideoDialog = true;
466   - this.hasaudio = realHasAudio && this.hasaudio;
467   - this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo))
468   - },
469   - close: function () {
470   - console.log('关闭视频');
471   - if (!!this.$refs[this.activePlayer]){
472   - this.$refs[this.activePlayer].pause();
473   - }
474   - this.videoUrl = '';
475   - this.coverPlaying = false;
476   - this.showVideoDialog = false;
477   - if (this.convertKey != '') {
478   - this.convertStop();
479   - }
480   - this.convertKey = ''
481   - this.stopBroadcast()
482   - },
  537 + playFromStreamInfo: function (realHasAudio, streamInfo) {
  538 + this.showVideoDialog = true;
  539 + this.hasaudio = realHasAudio && this.hasaudio;
  540 + this.$refs[this.activePlayer].play(this.getUrlByStreamInfo(streamInfo))
  541 + },
  542 + close: function () {
  543 + console.log('关闭视频');
  544 + if (!!this.$refs[this.activePlayer]) {
  545 + this.$refs[this.activePlayer].pause();
  546 + }
  547 + this.videoUrl = '';
  548 + this.coverPlaying = false;
  549 + this.showVideoDialog = false;
  550 + if (this.convertKey != '') {
  551 + this.convertStop();
  552 + }
  553 + this.convertKey = ''
  554 + this.stopBroadcast()
  555 + },
483 556  
484   - copySharedInfo: function (data) {
485   - console.log('复制内容:' + data);
486   - this.coverPlaying = false;
487   - this.tracks = []
488   - let _this = this;
489   - this.$copyText(data).then(
490   - function (e) {
491   - _this.$message({
492   - showClose: true,
493   - message: '复制成功',
494   - type: 'success'
495   - });
496   - },
497   - function (e) {
498   - _this.$message({
499   - showClose: true,
500   - message: '复制失败,请手动复制',
501   - type: 'error'
502   - });
503   - }
504   - );
505   - },
506   - ptzCamera: function (command) {
507   - console.log('云台控制:' + command);
508   - let that = this;
509   - this.$axios({
510   - method: 'post',
511   - url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.controSpeed
512   - }).then(function (res) {});
513   - },
514   - //////////////////////播放器事件处理//////////////////////////
515   - videoError: function (e) {
516   - console.log("播放器错误:" + JSON.stringify(e));
517   - },
518   - presetPosition: function (cmdCode, presetPos) {
519   - console.log('预置位控制:' + this.presetPos + ' : 0x' + cmdCode.toString(16));
520   - let that = this;
521   - this.$axios({
522   - method: 'post',
523   - url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=0&parameter2=' + presetPos + '&combindCode2=0'
524   - }).then(function (res) {});
525   - },
526   - setSpeedOrTime: function (cmdCode, groupNum, parameter) {
527   - let that = this;
528   - let parameter2 = parameter % 256;
529   - let combindCode2 = Math.floor(parameter / 256) * 16;
530   - console.log('前端控制:0x' + cmdCode.toString(16) + ' 0x' + groupNum.toString(16) + ' 0x' + parameter2.toString(16) + ' 0x' + combindCode2.toString(16));
531   - this.$axios({
532   - method: 'post',
533   - url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=' + groupNum + '&parameter2=' + parameter2 + '&combindCode2=' + combindCode2
534   - }).then(function (res) {});
535   - },
536   - setCommand: function (cmdCode, groupNum, parameter) {
537   - let that = this;
538   - console.log('前端控制:0x' + cmdCode.toString(16) + ' 0x' + groupNum.toString(16) + ' 0x' + parameter.toString(16) + ' 0x0');
539   - this.$axios({
540   - method: 'post',
541   - url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=' + groupNum + '&parameter2=' + parameter + '&combindCode2=0'
542   - }).then(function (res) {});
  557 + copySharedInfo: function (data) {
  558 + console.log('复制内容:' + data);
  559 + this.coverPlaying = false;
  560 + this.tracks = []
  561 + let _this = this;
  562 + this.$copyText(data).then(
  563 + function (e) {
  564 + _this.$message({
  565 + showClose: true,
  566 + message: '复制成功',
  567 + type: 'success'
  568 + });
543 569 },
544   - copyUrl: function (dropdownItem){
545   - console.log(dropdownItem)
546   - this.$copyText(dropdownItem).then((e)=> {
547   - this.$message.success("成功拷贝到粘贴板");
548   - }, (e)=> {
  570 + function (e) {
  571 + _this.$message({
  572 + showClose: true,
  573 + message: '复制失败,请手动复制',
  574 + type: 'error'
  575 + });
  576 + }
  577 + );
  578 + },
  579 + ptzCamera: function (command) {
  580 + console.log('云台控制:' + command);
  581 + let that = this;
  582 + this.$axios({
  583 + method: 'post',
  584 + url: '/api/ptz/control/' + this.deviceId + '/' + this.channelId + '?command=' + command + '&horizonSpeed=' + this.controSpeed + '&verticalSpeed=' + this.controSpeed + '&zoomSpeed=' + this.controSpeed
  585 + }).then(function (res) {
  586 + });
  587 + },
  588 + //////////////////////播放器事件处理//////////////////////////
  589 + videoError: function (e) {
  590 + console.log("播放器错误:" + JSON.stringify(e));
  591 + },
  592 + presetPosition: function (cmdCode, presetPos) {
  593 + console.log('预置位控制:' + this.presetPos + ' : 0x' + cmdCode.toString(16));
  594 + let that = this;
  595 + this.$axios({
  596 + method: 'post',
  597 + url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=0&parameter2=' + presetPos + '&combindCode2=0'
  598 + }).then(function (res) {
  599 + });
  600 + },
  601 + setSpeedOrTime: function (cmdCode, groupNum, parameter) {
  602 + let that = this;
  603 + let parameter2 = parameter % 256;
  604 + let combindCode2 = Math.floor(parameter / 256) * 16;
  605 + console.log('前端控制:0x' + cmdCode.toString(16) + ' 0x' + groupNum.toString(16) + ' 0x' + parameter2.toString(16) + ' 0x' + combindCode2.toString(16));
  606 + this.$axios({
  607 + method: 'post',
  608 + url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=' + groupNum + '&parameter2=' + parameter2 + '&combindCode2=' + combindCode2
  609 + }).then(function (res) {
  610 + });
  611 + },
  612 + setCommand: function (cmdCode, groupNum, parameter) {
  613 + let that = this;
  614 + console.log('前端控制:0x' + cmdCode.toString(16) + ' 0x' + groupNum.toString(16) + ' 0x' + parameter.toString(16) + ' 0x0');
  615 + this.$axios({
  616 + method: 'post',
  617 + url: '/api/ptz/front_end_command/' + this.deviceId + '/' + this.channelId + '?cmdCode=' + cmdCode + '&parameter1=' + groupNum + '&parameter2=' + parameter + '&combindCode2=0'
  618 + }).then(function (res) {
  619 + });
  620 + },
  621 + copyUrl: function (dropdownItem) {
  622 + console.log(dropdownItem)
  623 + this.$copyText(dropdownItem).then((e) => {
  624 + this.$message.success("成功拷贝到粘贴板");
  625 + }, (e) => {
549 626  
550   - })
551   - },
  627 + })
  628 + },
  629 + startBroadcast(url) {
  630 + // 获取推流鉴权Key
  631 + this.$axios({
  632 + method: 'post',
  633 + url: '/api/user/userInfo',
  634 + }).then((res) => {
  635 + if (res.data.code !== 0) {
  636 + this.$message({
  637 + showClose: true,
  638 + message: "获取推流鉴权Key失败",
  639 + type: "error",
  640 + });
  641 + this.broadcastStatus = -1;
  642 + } else {
  643 + let pushKey = res.data.data.pushKey;
  644 + // 获取推流鉴权KEY
  645 + url += "&sign=" + crypto.createHash('md5').update(pushKey, "utf8").digest('hex')
  646 + console.log("开始语音对讲: " + url)
  647 + this.broadcastRtc = new ZLMRTCClient.Endpoint({
  648 + debug: true, // 是否打印日志
  649 + zlmsdpUrl: url, //流地址
  650 + simulecast: false,
  651 + useCamera: false,
  652 + audioEnable: true,
  653 + videoEnable: false,
  654 + recvOnly: false,
  655 + })
  656 +
  657 + // webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,(e)=>{//获取到了远端流,可以播放
  658 + // console.error('播放成功',e.streams)
  659 + // this.broadcastStatus = 1;
  660 + // });
  661 + //
  662 + // webrtcPlayer.on(ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,(s)=>{// 获取到了本地流
  663 + // this.broadcastStatus = 1;
  664 + // // document.getElementById('selfVideo').srcObject=s;
  665 + // // this.eventcallbacK("LOCAL STREAM", "获取到了本地流")
  666 + // });
  667 +
  668 + this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_NOT_SUPPORT, (e) => {// 获取到了本地流
  669 + console.error('不支持webrtc', e)
  670 + this.$message({
  671 + showClose: true,
  672 + message: '不支持webrtc, 无法进行语音对讲',
  673 + type: 'error'
  674 + });
  675 + this.broadcastStatus = -1;
  676 + });
552 677  
  678 + this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, (e) => {// ICE 协商出错
  679 + console.error('ICE 协商出错')
  680 + this.$message({
  681 + showClose: true,
  682 + message: 'ICE 协商出错',
  683 + type: 'error'
  684 + });
  685 + this.broadcastStatus = -1;
  686 + });
553 687  
554   - this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,(e)=>{// ICE 协商出错
555   - console.error('ICE 协商出错')
556   - this.$message({
557   - showClose: true,
558   - message: 'ICE 协商出错',
559   - type: 'error'
560   - });
561   - this.broadcastStatus = -1;
562   - });
563   -
564   - this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,(e)=>{// offer anwser 交换失败
565   - console.error('offer anwser 交换失败',e)
566   - this.$message({
567   - showClose: true,
568   - message: 'offer anwser 交换失败' + e,
569   - type: 'error'
570   - });
571   - this.broadcastStatus = -1;
572   - });
573   - this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE,(e)=>{// offer anwser 交换失败
574   - console.log('状态改变',e)
575   - if (e === "connecting") {
576   - this.broadcastStatus = 0;
577   - }else if (e === "connected") {
578   - this.broadcastStatus = 1;
579   - }else if (e === "disconnected") {
580   - this.broadcastStatus = -1;
581   - }
582   - });
583   - this.broadcastRtc.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED,(e)=>{// offer anwser 交换失败
584   - console.log('捕获流失败',e)
585   - this.$message({
586   - showClose: true,
587   - message: '捕获流失败' + e,
588   - type: 'error'
589   - });
590   - this.broadcastStatus = -1;
591   - });
  688 + this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, (e) => {// offer anwser 交换失败
  689 + console.error('offer anwser 交换失败', e)
  690 + this.$message({
  691 + showClose: true,
  692 + message: 'offer anwser 交换失败' + e,
  693 + type: 'error'
  694 + });
  695 + this.broadcastStatus = -1;
  696 + });
  697 + this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, (e) => {// offer anwser 交换失败
  698 + console.log('状态改变', e)
  699 + if (e === "connecting") {
  700 + this.broadcastStatus = 0;
  701 + } else if (e === "connected") {
  702 + this.broadcastStatus = 1;
  703 + } else if (e === "disconnected") {
  704 + this.broadcastStatus = -1;
592 705 }
593   - }).catch((e) => {
  706 + });
  707 + this.broadcastRtc.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, (e) => {// offer anwser 交换失败
  708 + console.log('捕获流失败', e)
594 709 this.$message({
595 710 showClose: true,
596   - message: e,
  711 + message: '捕获流失败' + e,
597 712 type: 'error'
598 713 });
599 714 this.broadcastStatus = -1;
600 715 });
  716 + }
  717 + }).catch((e) => {
  718 + this.$message({
  719 + showClose: true,
  720 + message: e,
  721 + type: 'error'
  722 + });
  723 + this.broadcastStatus = -1;
  724 + });
601 725  
602 726  
603   - },
604   - stopBroadcast(){
605   - this.broadcastRtc.close();
606   - this.broadcastStatus = -1;
607   - this.$axios({
608   - method: 'get',
609   - url: '/api/play/broadcast/stop/' + this.deviceId + '/' + this.channelId
610   - }).then( (res)=> {
611   - if (res.data.code == 0) {
612   - // this.broadcastStatus = -1;
613   - // this.broadcastRtc.close()
614   - }else {
615   - this.$message({
616   - showClose: true,
617   - message: res.data.msg,
618   - type: "error",
619   - });
620   - }
  727 + },
  728 + stopBroadcast() {
  729 + this.broadcastRtc.close();
  730 + this.broadcastStatus = -1;
  731 + this.$axios({
  732 + method: 'get',
  733 + url: '/api/play/broadcast/stop/' + this.deviceId + '/' + this.channelId
  734 + }).then((res) => {
  735 + if (res.data.code == 0) {
  736 + // this.broadcastStatus = -1;
  737 + // this.broadcastRtc.close()
  738 + } else {
  739 + this.$message({
  740 + showClose: true,
  741 + message: res.data.msg,
  742 + type: "error",
621 743 });
622 744 }
  745 + });
623 746 }
  747 + }
624 748 };
625 749 </script>
626 750  
627 751 <style>
628 752 .control-wrapper {
629   - position: relative;
630   - width: 6.25rem;
631   - height: 6.25rem;
632   - max-width: 6.25rem;
633   - max-height: 6.25rem;
634   - border-radius: 100%;
635   - margin-top: 1.5rem;
636   - margin-left: 0.5rem;
637   - float: left;
  753 + position: relative;
  754 + width: 6.25rem;
  755 + height: 6.25rem;
  756 + max-width: 6.25rem;
  757 + max-height: 6.25rem;
  758 + border-radius: 100%;
  759 + margin-top: 1.5rem;
  760 + margin-left: 0.5rem;
  761 + float: left;
638 762 }
639 763  
640 764 .control-panel {
641   - position: relative;
642   - top: 0;
643   - left: 5rem;
644   - height: 11rem;
645   - max-height: 11rem;
  765 + position: relative;
  766 + top: 0;
  767 + left: 5rem;
  768 + height: 11rem;
  769 + max-height: 11rem;
646 770 }
647 771  
648 772 .control-btn {
649   - display: flex;
650   - justify-content: center;
651   - position: absolute;
652   - width: 44%;
653   - height: 44%;
654   - border-radius: 5px;
655   - border: 1px solid #78aee4;
656   - box-sizing: border-box;
657   - transition: all 0.3s linear;
  773 + display: flex;
  774 + justify-content: center;
  775 + position: absolute;
  776 + width: 44%;
  777 + height: 44%;
  778 + border-radius: 5px;
  779 + border: 1px solid #78aee4;
  780 + box-sizing: border-box;
  781 + transition: all 0.3s linear;
658 782 }
  783 +
659 784 .control-btn:hover {
660   - cursor:pointer
  785 + cursor: pointer
661 786 }
662 787  
663 788 .control-btn i {
664   - font-size: 20px;
665   - color: #78aee4;
666   - display: flex;
667   - justify-content: center;
668   - align-items: center;
  789 + font-size: 20px;
  790 + color: #78aee4;
  791 + display: flex;
  792 + justify-content: center;
  793 + align-items: center;
669 794 }
  795 +
670 796 .control-btn i:hover {
671   - cursor:pointer
  797 + cursor: pointer
672 798 }
  799 +
673 800 .control-zoom-btn:hover {
674   - cursor:pointer
  801 + cursor: pointer
675 802 }
676 803  
677 804 .control-round {
678   - position: absolute;
679   - top: 21%;
680   - left: 21%;
681   - width: 58%;
682   - height: 58%;
683   - background: #fff;
684   - border-radius: 100%;
  805 + position: absolute;
  806 + top: 21%;
  807 + left: 21%;
  808 + width: 58%;
  809 + height: 58%;
  810 + background: #fff;
  811 + border-radius: 100%;
685 812 }
686 813  
687 814 .control-round-inner {
688   - position: absolute;
689   - left: 13%;
690   - top: 13%;
691   - display: flex;
692   - justify-content: center;
693   - align-items: center;
694   - width: 70%;
695   - height: 70%;
696   - font-size: 40px;
697   - color: #78aee4;
698   - border: 1px solid #78aee4;
699   - border-radius: 100%;
700   - transition: all 0.3s linear;
  815 + position: absolute;
  816 + left: 13%;
  817 + top: 13%;
  818 + display: flex;
  819 + justify-content: center;
  820 + align-items: center;
  821 + width: 70%;
  822 + height: 70%;
  823 + font-size: 40px;
  824 + color: #78aee4;
  825 + border: 1px solid #78aee4;
  826 + border-radius: 100%;
  827 + transition: all 0.3s linear;
701 828 }
702 829  
703 830 .control-inner-btn {
704   - position: absolute;
705   - width: 60%;
706   - height: 60%;
707   - background: #fafafa;
  831 + position: absolute;
  832 + width: 60%;
  833 + height: 60%;
  834 + background: #fafafa;
708 835 }
709 836  
710 837 .control-top {
711   - top: -8%;
712   - left: 27%;
713   - transform: rotate(-45deg);
714   - border-radius: 5px 100% 5px 0;
  838 + top: -8%;
  839 + left: 27%;
  840 + transform: rotate(-45deg);
  841 + border-radius: 5px 100% 5px 0;
715 842 }
716 843  
717 844 .control-top i {
718   - transform: rotate(45deg);
719   - border-radius: 5px 100% 5px 0;
  845 + transform: rotate(45deg);
  846 + border-radius: 5px 100% 5px 0;
720 847 }
721 848  
722 849 .control-top .control-inner {
723   - left: -1px;
724   - bottom: 0;
725   - border-top: 1px solid #78aee4;
726   - border-right: 1px solid #78aee4;
727   - border-radius: 0 100% 0 0;
  850 + left: -1px;
  851 + bottom: 0;
  852 + border-top: 1px solid #78aee4;
  853 + border-right: 1px solid #78aee4;
  854 + border-radius: 0 100% 0 0;
728 855 }
729 856  
730 857 .control-top .fa {
731   - transform: rotate(45deg) translateY(-7px);
  858 + transform: rotate(45deg) translateY(-7px);
732 859 }
733 860  
734 861 .control-left {
735   - top: 27%;
736   - left: -8%;
737   - transform: rotate(45deg);
738   - border-radius: 5px 0 5px 100%;
  862 + top: 27%;
  863 + left: -8%;
  864 + transform: rotate(45deg);
  865 + border-radius: 5px 0 5px 100%;
739 866 }
740 867  
741 868 .control-left i {
742   - transform: rotate(-45deg);
  869 + transform: rotate(-45deg);
743 870 }
744 871  
745 872 .control-left .control-inner {
746   - right: -1px;
747   - top: -1px;
748   - border-bottom: 1px solid #78aee4;
749   - border-left: 1px solid #78aee4;
750   - border-radius: 0 0 0 100%;
  873 + right: -1px;
  874 + top: -1px;
  875 + border-bottom: 1px solid #78aee4;
  876 + border-left: 1px solid #78aee4;
  877 + border-radius: 0 0 0 100%;
751 878 }
752 879  
753 880 .control-left .fa {
754   - transform: rotate(-45deg) translateX(-7px);
  881 + transform: rotate(-45deg) translateX(-7px);
755 882 }
756 883  
757 884 .control-right {
758   - top: 27%;
759   - right: -8%;
760   - transform: rotate(45deg);
761   - border-radius: 5px 100% 5px 0;
  885 + top: 27%;
  886 + right: -8%;
  887 + transform: rotate(45deg);
  888 + border-radius: 5px 100% 5px 0;
762 889 }
763 890  
764 891 .control-right i {
765   - transform: rotate(-45deg);
  892 + transform: rotate(-45deg);
766 893 }
767 894  
768 895 .control-right .control-inner {
769   - left: -1px;
770   - bottom: -1px;
771   - border-top: 1px solid #78aee4;
772   - border-right: 1px solid #78aee4;
773   - border-radius: 0 100% 0 0;
  896 + left: -1px;
  897 + bottom: -1px;
  898 + border-top: 1px solid #78aee4;
  899 + border-right: 1px solid #78aee4;
  900 + border-radius: 0 100% 0 0;
774 901 }
775 902  
776 903 .control-right .fa {
777   - transform: rotate(-45deg) translateX(7px);
  904 + transform: rotate(-45deg) translateX(7px);
778 905 }
779 906  
780 907 .control-bottom {
781   - left: 27%;
782   - bottom: -8%;
783   - transform: rotate(45deg);
784   - border-radius: 0 5px 100% 5px;
  908 + left: 27%;
  909 + bottom: -8%;
  910 + transform: rotate(45deg);
  911 + border-radius: 0 5px 100% 5px;
785 912 }
786 913  
787 914 .control-bottom i {
788   - transform: rotate(-45deg);
  915 + transform: rotate(-45deg);
789 916 }
790 917  
791 918 .control-bottom .control-inner {
792   - top: -1px;
793   - left: -1px;
794   - border-bottom: 1px solid #78aee4;
795   - border-right: 1px solid #78aee4;
796   - border-radius: 0 0 100% 0;
  919 + top: -1px;
  920 + left: -1px;
  921 + border-bottom: 1px solid #78aee4;
  922 + border-right: 1px solid #78aee4;
  923 + border-radius: 0 0 100% 0;
797 924 }
798 925  
799 926 .control-bottom .fa {
800   - transform: rotate(-45deg) translateY(7px);
  927 + transform: rotate(-45deg) translateY(7px);
801 928 }
  929 +
802 930 .trank {
803   - width: 80%;
804   - height: 180px;
805   - text-align: left;
806   - padding: 0 10%;
807   - overflow: auto;
  931 + width: 80%;
  932 + height: 180px;
  933 + text-align: left;
  934 + padding: 0 10%;
  935 + overflow: auto;
808 936 }
  937 +
809 938 .trankInfo {
810   - width: 80%;
811   - padding: 0 10%;
  939 + width: 80%;
  940 + padding: 0 10%;
812 941 }
813 942 </style>
... ...