@zhangxuejing123./sip-phone-sdk 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/README.md +24 -0
  2. package/babel.config.js +5 -0
  3. package/dist/demo.html +21 -0
  4. package/dist/media/outgoing-call2.b8558579.mp3 +0 -0
  5. package/dist/sip-phone-sdk.common.js +42439 -0
  6. package/dist/sip-phone-sdk.css +5 -0
  7. package/dist/sip-phone-sdk.umd.js +42449 -0
  8. package/dist/sip-phone-sdk.umd.min.js +14 -0
  9. package/jsconfig.json +19 -0
  10. package/package-lock.json +15671 -0
  11. package/package.json +99 -0
  12. package/public/SIP_MIX_WEB.js +50 -0
  13. package/public/en.js +24 -0
  14. package/public/favicon.ico +0 -0
  15. package/public/index.html +32 -0
  16. package/public/index.test.html +31 -0
  17. package/public/zh.js +24 -0
  18. package/src/.DS_Store +0 -0
  19. package/src/App.vue +136 -0
  20. package/src/assets/.DS_Store +0 -0
  21. package/src/assets/adudio-open.png +0 -0
  22. package/src/assets/audio-close.png +0 -0
  23. package/src/assets/call-down.png +0 -0
  24. package/src/assets/camera-close.png +0 -0
  25. package/src/assets/camera-open.png +0 -0
  26. package/src/assets/icon_Recording_Fill_Red_Active.svg +15 -0
  27. package/src/assets/icon_Recording_Fill_Red_Inactive.svg +15 -0
  28. package/src/assets/img_Avatar_User.png +0 -0
  29. package/src/assets/normal-logo.png +0 -0
  30. package/src/assets/outgoing-call2.mp3 +0 -0
  31. package/src/components/DialPanelMini.vue +179 -0
  32. package/src/components/MobilePhone copy 2.vue +1173 -0
  33. package/src/components/MobilePhone copy.vue +1046 -0
  34. package/src/components/MobilePhone.vue +1157 -0
  35. package/src/components/index.js +36 -0
  36. package/src/components/vuetify.css +29663 -0
  37. package/src/index.js +100 -0
  38. package/src/lang/en.js +24 -0
  39. package/src/lang/zh.js +24 -0
  40. package/src/libs/SIP_MIX_WEB.js +50 -0
  41. package/src/libs/en.js +24 -0
  42. package/src/libs/tool.js +312 -0
  43. package/src/libs/zh.js +24 -0
  44. package/src/main.js +24 -0
  45. package/src/plugins/vuetify.js +11 -0
  46. package/src/utils/rem.js +22 -0
  47. package/vue.config copy 2.js +39 -0
  48. package/vue.config copy.js +11 -0
  49. package/vue.config.js +39 -0
@@ -0,0 +1,1046 @@
1
+ <!-- 主模板:视频通话界面 -->
2
+ <template>
3
+ <!-- 主容器:使用flex垂直布局,内容居中对齐 -->
4
+ <div class="d-flex flex-column align-center justify-space-between sip-call-main">
5
+ <!-- 顶部提示信息 -->
6
+ <div style="position: absolute; top: 0; left: 0; background: transparent; color: #FFFFFF; width: 100%; z-index: 9999; height: 0; padding-top: 10px; padding-left: 10px;">
7
+ {{ prompt }}
8
+ </div>
9
+ <!-- 位置信息显示区域 -->
10
+ <div class="location-wrapper" v-if="showLocation">
11
+ <!-- 显示经纬度和海拔信息 -->
12
+ <p>LAT: {{latitude}}</p>
13
+ <p>LNG: {{longitude}}</p>
14
+ <p>ALT: {{altitude}}</p>
15
+ </div>
16
+ <!-- 通话信息显示区域 -->
17
+ <div class="d-flex flex-column align-center" v-if="!miniMode && !showDialPad" style="margin-top: 60px;">
18
+ <!-- 应用logo -->
19
+ <img class="elevation-4" alt="logo" :src="require('@/assets/normal-logo.png')" width="72" height="72" style="border-radius: 50%;"/>
20
+ <!-- 通话状态提示 -->
21
+ <p class="mt-6 mb-0" style="font-size: 16px; color: #FFFFFF;">{{callPrompt}}</p>
22
+ </div>
23
+
24
+ <!-- 功能按钮区域 -->
25
+ <div class="pr-10 pl-10 btn-group" v-if="!miniMode && !showDialPad">
26
+ <v-row>
27
+ <v-col cols="12" class="text-center">
28
+ <!-- 通话计时器 -->
29
+ <div
30
+ v-show="isCalling && showHangup" style="font-size: 16px; color: #FFFFFF;">
31
+ 视频时长{{duration}}
32
+ </div>
33
+ </v-col>
34
+ </v-row>
35
+ <!-- 第一行按钮组 -->
36
+ <v-row class="d-flex justify-space-around mb-10">
37
+ <!-- 拨号按钮 -->
38
+ <v-btn
39
+ large
40
+ fab
41
+ color="#FFFFFF"
42
+ v-show="!isCalling && !needDial"
43
+ @click="onMakeCallBtnClick"
44
+ >
45
+ <v-icon>mdi-phone</v-icon>
46
+ <span class="btn-label">拨号</span>
47
+ </v-btn>
48
+
49
+ <!-- 麦克风静音/取消静音按钮 -->
50
+ <!-- 当正在通话且显示挂断按钮时显示 -->
51
+ <!-- 点击时触发onMuteBtnClick方法,传入'audio'参数 -->
52
+ <v-btn
53
+ style="opacity: .6"
54
+ color="blue-grey"
55
+ fab
56
+ large
57
+ dark
58
+ v-show="isCalling && showHangup"
59
+ @click="onMuteBtnClick('audio')"
60
+ >
61
+ <!-- 静音状态显示开启的麦克风图标 -->
62
+ <img v-if="!isAudioMute" :src="require('@/assets/adudio-open.png')" alt="mic-on" width="24" height="24">
63
+ <img v-else :src="require('@/assets/audio-close.png')" alt="mic-off" width="24" height="24">
64
+ <!-- 显示当前状态的文字提示 -->
65
+ <!-- <span class="btn-label">{{ isAudioMute ? '取消静音' : '静音' }}</span> -->
66
+ </v-btn>
67
+
68
+ <!-- 摄像头开关按钮 -->
69
+ <v-btn
70
+ style="opacity: .6"
71
+ color="blue-grey"
72
+ fab
73
+ large
74
+ dark
75
+ v-show="isCalling && showHangup"
76
+ @click="onMuteBtnClick('video')"
77
+ >
78
+ <img v-if="!isVideoMute" :src="require('@/assets/camera-open.png')" alt="mic-on" width="24" height="24">
79
+ <img v-else :src="require('@/assets/camera-close.png')" alt="mic-off" width="24" height="24">
80
+ </v-btn>
81
+
82
+ <!-- 显示/隐藏本地视频按钮 -->
83
+ <!-- <v-btn
84
+ style="opacity: .6"
85
+ color="blue-grey"
86
+ fab
87
+ large
88
+ dark
89
+ v-show="isCalling && showHangup"
90
+ @click="onShowLocalVideoBtnClick"
91
+ >
92
+ <v-icon v-if="localVideo.show">mdi-monitor</v-icon>
93
+ <v-icon v-else>mdi-monitor-multiple</v-icon>
94
+ <span class="btn-label">{{ localVideo.show ? '隐藏本地' : '显示本地' }}</span>
95
+ </v-btn> -->
96
+ </v-row>
97
+
98
+ <!-- 第二行按钮组 -->
99
+ <v-row class="d-flex justify-space-around mt-10">
100
+ <!-- 拨号盘按钮 -->
101
+ <v-btn
102
+ style="opacity: .6"
103
+ large
104
+ fab
105
+ v-show="needDial && !showDialPad"
106
+ @click="onDialPadBtnClick"
107
+ >
108
+ <v-icon>mdi-dialpad</v-icon>
109
+ <span class="btn-label">拨号盘</span>
110
+ </v-btn>
111
+
112
+ <!-- 挂断按钮 -->
113
+ <v-btn
114
+ style="opacity: 1; background: transparent; box-shadow: none;"
115
+ fab
116
+ large
117
+ v-show="isCalling && showHangup"
118
+ @click="onHangupBtnClick"
119
+ >
120
+ <img :src="require('@/assets/call-down.png')" alt="hangup" width="56" height="56">
121
+ </v-btn>
122
+
123
+ <!-- 切换摄像头按钮 -->
124
+ <v-btn
125
+ style="opacity: .6"
126
+ color="blue-grey"
127
+ fab
128
+ large
129
+ dark
130
+ v-show="isCalling && showHangup"
131
+ @click="onSwitchCameraBtnClick"
132
+ >
133
+ <v-icon>mdi-camera-flip</v-icon>
134
+ </v-btn>
135
+ </v-row>
136
+ </div>
137
+
138
+ <!-- 视频容器区域 -->
139
+ <v-expand-transition>
140
+ <div class="sip-call-wrapper" v-show="showVideoCt" @click="onSipCallClick">
141
+ <!-- 远程视频窗口 -->
142
+ <vue-draggable-resizable
143
+ v-show="remoteVideo.show"
144
+ :w="remoteVideo.w"
145
+ :h="remoteVideo.h"
146
+ :x="remoteVideo.x"
147
+ :y="remoteVideo.y"
148
+ :z="remoteVideo.z"
149
+ :draggable="remoteVideo.draggable"
150
+ :handles="[]"
151
+ :parent="true"
152
+ :active="false"
153
+ class-name="elevation-6 video-panel"
154
+ ref="remoteVideoWrapper"
155
+ @mousedown.native="onLocalVideoDragCallback($event, 'remoteVideo')"
156
+ >
157
+ <v-btn v-if="miniMode" class="video-normal-mode" dark text x-small width="24" height="24" min-width="24" @click="onChangeVideoNormalModeBtnClick">
158
+ <v-icon>mdi-arrow-bottom-right-bold-box</v-icon>
159
+ </v-btn>
160
+ <video
161
+ v-show="remoteVideo.show"
162
+ autoplay="autoplay"
163
+ webkit-playsinline="true"
164
+ playsinline="true"
165
+ x-webkit-airplay="true"
166
+ x5-video-player-type="h5"
167
+ x5-video-orientation="h5"
168
+ ref="remoteVideo"
169
+ class="sip-call-video"
170
+ style="object-fit: contain;"
171
+ controls
172
+ ></video>
173
+ </vue-draggable-resizable>
174
+ <!-- 本地视频窗口 -->
175
+ <vue-draggable-resizable
176
+ v-show="localVideo.show"
177
+ :w="localVideo.w"
178
+ :h="localVideo.h"
179
+ :x="localVideo.x"
180
+ :y="localVideo.y"
181
+ :z="localVideo.z"
182
+ :draggable="localVideo.draggable"
183
+ :handles="[]"
184
+ :parent="true"
185
+ :active="false"
186
+ class-name="elevation-6 video-panel"
187
+ ref="localVideoWrapper"
188
+ @mousedown.native="onLocalVideoDragCallback($event, 'localVideo')"
189
+ >
190
+ <video
191
+ v-show="localVideo.show"
192
+ autoplay="autoplay"
193
+ muted
194
+ webkit-playsinline="true"
195
+ playsinline="true"
196
+ x-webkit-airplay="true"
197
+ x5-video-player-type="h5"
198
+ x5-video-orientation="h5"
199
+ ref="localVideo"
200
+ class="sip-call-video"
201
+ controls
202
+ ></video>
203
+ </vue-draggable-resizable>
204
+
205
+ <!-- 视频控制区域 -->
206
+ <!-- <template v-if="!miniMode">
207
+ 通话计时器
208
+ <span class="video-timer">
209
+ <img :src="require('@/assets/icon_Recording_Fill_Red_Active.svg')" alt="icon" width="24" height="24" class="mr-2">
210
+ {{duration}}
211
+ </span>
212
+
213
+ 全屏切换按钮
214
+ <v-btn class="camera-switch-btn" dark text x-small width="24" height="24" min-width="24" @click="onChangeVideoFullScreenBtnClick">
215
+ <v-icon>mdi-box-shadow</v-icon>
216
+ <span class="btn-label">全屏</span>
217
+ </v-btn>
218
+ </template> -->
219
+
220
+ </div>
221
+ </v-expand-transition>
222
+
223
+ <!-- 拨号盘组件 -->
224
+ <v-expand-transition>
225
+ <dial-panel-mini
226
+ :isCalling="isCalling"
227
+ v-show="showDialPad"
228
+ @makeCall="onDialPadMakeCallBtnClick"
229
+ @close="showDialPad=false"
230
+ @dtmf="onDialPadDtmfBtnClick"
231
+ @hangup="onHangupBtnClick"
232
+ ref="dialPanelMini"
233
+ />
234
+ </v-expand-transition>
235
+
236
+ <!-- 音频播放器(用于播放提示音) -->
237
+ <v-bottom-sheet v-model="showAudioPlayer" hide-overlay>
238
+ <audio :src="require('@/assets/outgoing-call2.mp3')" loop ref="audioPlayer"></audio>
239
+ </v-bottom-sheet>
240
+
241
+ <!-- 提示信息弹窗 -->
242
+ <v-snackbar v-model="showSnackbar" :timeout="2000">
243
+ {{snackbarText}}
244
+ </v-snackbar>
245
+ </div>
246
+ </template>
247
+
248
+ <script>
249
+ import 'webrtc-adapter'
250
+ import SipMix from '@/libs/SIP_MIX_WEB'
251
+ import DialPanelMini from '@/components/DialPanelMini'
252
+ import VueDraggableResizable from 'vue-draggable-resizable'
253
+ import { closeStream } from '@/libs/tool'
254
+
255
+
256
+ export default {
257
+ name: 'MobilePhone',
258
+ api: null,
259
+ locateHandler: -1,
260
+ sendPositionHandler: -1,
261
+ clientHeight: 0,
262
+ clientWidth: 0,
263
+
264
+ components: {
265
+ VueDraggableResizable,
266
+ DialPanelMini
267
+ },
268
+
269
+ props: {
270
+ calledNum: { // 被叫
271
+ type: String,
272
+ default: ''
273
+ },
274
+
275
+ callingNum: { // 主叫
276
+ type: String,
277
+ default: 'H5User',
278
+ validator: (v) => !!v
279
+ },
280
+
281
+ callType: {
282
+ type: String,
283
+ default: 'videoOnly',
284
+ validator: v => ['audio', 'video', 'videoOnly'].indexOf(v) > -1
285
+ },
286
+
287
+ debug: {
288
+ type: Boolean,
289
+ default: false
290
+ },
291
+
292
+ needDial: {
293
+ type: Boolean,
294
+ default: false
295
+ },
296
+
297
+ autoHideLocalVideo: {
298
+ type: Boolean,
299
+ default: false
300
+ },
301
+
302
+ showLocation: {
303
+ type: Boolean,
304
+ default: true
305
+ },
306
+
307
+ callData: {
308
+ type: String,
309
+ default: ''
310
+ }
311
+ },
312
+
313
+ data: function () {
314
+ return {
315
+ prompt: `${this.$t('serverConnecting')}...`,
316
+ callPrompt: this.$t('pleaseBePatient'),
317
+
318
+ isCalling: false,
319
+ showHangup: false,
320
+ showAudioPlayer: true,
321
+ showVideoCt: false,
322
+ showDialPad: false,
323
+ showSnackbar: false,
324
+
325
+ miniMode: false,
326
+ dialNum: '',
327
+
328
+ controls: '',
329
+
330
+ isAudioMute: false,
331
+ isVideoMute: false,
332
+ cameraType: 'front',
333
+ videoInputList: [],
334
+ videoInputIndex: 0,
335
+
336
+ localVideo: {
337
+ w: 1,
338
+ h: 1,
339
+ x: 0,
340
+ y: 0,
341
+ z: 1000,
342
+ show: false,
343
+ draggable: true
344
+ },
345
+
346
+ remoteVideo: {
347
+ w: 1,
348
+ h: 1,
349
+ x: 0,
350
+ y: 0,
351
+ z: 1000,
352
+ show: false,
353
+ draggable: true
354
+ },
355
+
356
+ connected: false,
357
+ callDuration: 0,
358
+
359
+ durationHandler: -1,
360
+ snackbarText: '',
361
+
362
+ callId: '',
363
+ lastCallId: '',
364
+ queueCount: 0,
365
+
366
+ latitude: 0,
367
+ longitude: 0,
368
+ altitude: 0
369
+ }
370
+ },
371
+
372
+ watch: {
373
+ isCalling (is) {
374
+ if (is) {
375
+ this.startDurationCount()
376
+ } else {
377
+ this.stopDurationCount()
378
+ }
379
+ },
380
+ callId (id) {
381
+ this.lastCallId = id || this.lastCallId
382
+ },
383
+ prompt (v) {
384
+ console.info('Prompt', v)
385
+ }
386
+ },
387
+
388
+ computed: {
389
+ duration () {
390
+ let s = this.callDuration % 60
391
+ s = s < 10 ? `0${s}` : `${s}`
392
+ let m = parseInt(this.callDuration / 60)
393
+ m = m < 10 ? `0${m}` : `${m}`
394
+ return `${m}:${s}`
395
+ }
396
+ },
397
+
398
+ methods: {
399
+ _resetCmpWHLRTB ({rl =0, rt = 0, ll = 0, lt = 0} = {}) {
400
+ this.$refs.remoteVideoWrapper.left = rl
401
+ this.$refs.remoteVideoWrapper.top = rt
402
+
403
+ this.$refs.localVideoWrapper.left = ll
404
+ this.$refs.localVideoWrapper.top = lt
405
+
406
+ this.$refs.localVideoWrapper.parentWidth = this.clientWidth
407
+ this.$refs.localVideoWrapper.parentHeight = this.clientHeight
408
+ this.$refs.localVideoWrapper.right = this.clientWidth - this.localVideo.w
409
+ this.$refs.localVideoWrapper.bottom = this.clientHeight - this.localVideo.h
410
+
411
+ this.$refs.remoteVideoWrapper.parentWidth = this.clientWidth
412
+ this.$refs.remoteVideoWrapper.parentHeight = this.clientHeight
413
+ this.$refs.remoteVideoWrapper.right = this.clientWidth - this.remoteVideo.w
414
+ this.$refs.remoteVideoWrapper.bottom = this.clientHeight - this.remoteVideo.h
415
+ },
416
+
417
+ toMiniMode () {
418
+ this._resetCmpWHLRTB()
419
+ Object.assign(this.localVideo, {
420
+ show: false,
421
+ draggable: false
422
+ })
423
+ this.miniMode = true
424
+
425
+ setTimeout(() => {
426
+ Object.assign(this.remoteVideo, {
427
+ show: true,
428
+ w: parseInt(this.clientWidth / 3),
429
+ h: parseInt(this.clientHeight / 3),
430
+ x: 0,
431
+ y: 0,
432
+ z: 1100,
433
+ draggable: true
434
+ })
435
+ }, 500)
436
+ },
437
+
438
+ toNormalMode () {
439
+ this._resetCmpWHLRTB()
440
+ Object.assign(this.localVideo, {
441
+ show: true,
442
+ w: parseInt(this.clientWidth / 3),
443
+ h: parseInt(this.clientHeight / 3),
444
+ x: 0,
445
+ y: 0,
446
+ z: 1100,
447
+ draggable: false
448
+ })
449
+ this.miniMode = false
450
+
451
+ setTimeout(() => {
452
+ // this._resetCmpWHLRTB()
453
+ Object.assign(this.remoteVideo, {
454
+ show: true,
455
+ w: this.clientWidth,
456
+ h: this.clientHeight,
457
+ x: 0,
458
+ y: 0,
459
+ z: 1000,
460
+ draggable: true
461
+ })
462
+ }, 500)
463
+ },
464
+
465
+ onDialPadBtnClick () {
466
+ this.showDialPad = true
467
+ },
468
+
469
+ onDialPadMakeCallBtnClick ({dialNum}) {
470
+ this.dialNum = dialNum
471
+ this.onMakeCallBtnClick()
472
+ },
473
+
474
+ onDialPadDtmfBtnClick (v) {
475
+ this.sipMix.sendDTMF({tones: v})
476
+ },
477
+
478
+ onChangeVideoMiniModeBtnClick () {
479
+ this.toMiniMode()
480
+ },
481
+
482
+ onChangeVideoNormalModeBtnClick () {
483
+ this.toNormalMode()
484
+ },
485
+
486
+ onChangeVideoFullScreenBtnClick () {
487
+ if (this.remoteVideo.show) {
488
+ this.$refs.remoteVideoWrapper.left = 0
489
+ this.$refs.remoteVideoWrapper.top = 0
490
+
491
+ this.$refs.localVideoWrapper.left = 0
492
+ this.$refs.localVideoWrapper.top = 0
493
+
494
+ this.$nextTick(() => {
495
+ if (this.localVideo.w === this.clientWidth) {
496
+ Object.assign(this.localVideo, {
497
+ w: parseInt(this.clientWidth / 3),
498
+ h: parseInt(this.clientHeight / 3),
499
+ z: 1100,
500
+ draggable: true
501
+ })
502
+ Object.assign(this.remoteVideo, {
503
+ w: this.clientWidth,
504
+ h: this.clientHeight,
505
+ z: 1000,
506
+ draggable: false
507
+ })
508
+ } else {
509
+ Object.assign(this.localVideo, {
510
+ w: this.clientWidth,
511
+ h: this.clientHeight,
512
+ z: 1000,
513
+ draggable: false
514
+ })
515
+ Object.assign(this.remoteVideo, {
516
+ w: parseInt(this.clientWidth / 3),
517
+ h: parseInt(this.clientHeight / 3),
518
+ z: 1100,
519
+ draggable: true
520
+ })
521
+ }
522
+ })
523
+ }
524
+ },
525
+
526
+ onLocalVideoDragCallback (e, target) {
527
+ if (target === 'remoteVideo' && this.remoteVideo.w !== this.clientWidth) {
528
+ e.stopPropagation()
529
+ } else if (target === 'localVideo' && this.localVideo.w !== this.clientWidth) {
530
+ e.stopPropagation()
531
+ }
532
+ },
533
+
534
+ onSipCallClick (){
535
+ this.autoShowHangupBtn()
536
+ },
537
+
538
+ async onMakeCallBtnClick() {
539
+ if (!this.needDial) {
540
+ this.dialNum = this.calledNum
541
+ }
542
+
543
+ const params = { target: this.dialNum }
544
+ if (this.callType === 'videoOnly') {
545
+ params.video = Object.assign({
546
+ facingMode: 'user'
547
+ }, window.config.videoParams)
548
+ params.audio = false
549
+ } else if (this.callType === 'video') {
550
+ params.video = Object.assign({
551
+ facingMode: 'user'
552
+ }, window.config.videoParams)
553
+ params.audio = true
554
+ } else {
555
+ params.video = false
556
+ params.audio = true
557
+ }
558
+
559
+ if (this.callData) {
560
+ params.callData = this.callData
561
+ }
562
+
563
+ // console.info('Call Params', params)
564
+ try {
565
+ await this.sipMix.call(params)
566
+ } catch (e) {
567
+ this.snackbarText = this.$t('gotCameraStreamFailed')
568
+ this.showSnackbar = true
569
+ }
570
+
571
+ this.isCalling = true
572
+ this.showHangup = true
573
+ if (this.needDial) {
574
+ this.callPrompt = `${this.$t('calling')} ${this.dialNum}`
575
+ this.showDialPad = false
576
+ } else {
577
+ // this.callPrompt = `${this.$t('calling')}...${this.$t('pleaseBePatient')}`
578
+ this.callPrompt = `${this.$t('pleaseBePatient')}`
579
+ }
580
+
581
+ },
582
+
583
+ onHangupBtnClick () {
584
+ this.sipMix.hangup()
585
+ },
586
+
587
+ onMuteBtnClick (type) {
588
+ if (type === 'audio') {
589
+ if (this.isAudioMute) {
590
+ this.sipMix.unmute({audio: true})
591
+ } else {
592
+ this.sipMix.mute({audio: true})
593
+ }
594
+ this.isAudioMute = !this.isAudioMute
595
+ } else {
596
+ if (this.isVideoMute) {
597
+ this.sipMix.unmute({video: true})
598
+ } else {
599
+ this.sipMix.mute({video: true})
600
+ }
601
+ this.isVideoMute = !this.isVideoMute
602
+ }
603
+ },
604
+
605
+
606
+ onShowLocalVideoBtnClick () {
607
+ // this.localVideo.show = !this.localVideo.show
608
+ this.localVideo.z = (this.localVideo.z === 1 ? 1100 : 1)
609
+ },
610
+
611
+ dealCallEndOrFail () {
612
+ // this.$refs.audioPlayer.currentTime = 0
613
+ this.$refs.audioPlayer.pause()
614
+ this.showVideoCt = false
615
+ this.remoteVideo.show = false
616
+ this.localVideo.show = false
617
+ this.localVideo.w = this.clientWidth
618
+ this.localVideo.h = this.clientHeight
619
+ this.isCalling = false
620
+ this.callId = ''
621
+ this.prompt = this.$t('serverConnected')
622
+
623
+ if (this.needDial) {
624
+ this.showDialPad = true
625
+ }
626
+
627
+ this.stopDurationCount()
628
+ this.toNormalMode()
629
+ this.stopSendPosition()
630
+ },
631
+
632
+ autoShowHangupBtn () {
633
+ this.showHangup = true
634
+ /* setTimeout(() => {
635
+ this.showHangup = false
636
+ }, 3000) */
637
+ },
638
+
639
+ stopDurationCount () {
640
+ this.callDuration = 0
641
+ clearInterval(this.durationHandler)
642
+ },
643
+
644
+ startDurationCount () {
645
+ this.stopDurationCount()
646
+ this.durationHandler = setInterval(() => {
647
+ this.callDuration++
648
+ }, 1000)
649
+ },
650
+
651
+ async onSwitchCameraBtnClick() {
652
+ const list = await navigator.mediaDevices.enumerateDevices()
653
+ console.warn(list)
654
+ let videoOption = {
655
+ facingMode: 'user',
656
+ }
657
+ if (this.cameraType === 'front') {
658
+ videoOption = {
659
+ facingMode: { exact: 'environment' }
660
+ }
661
+ }
662
+ Object.assign(videoOption, window.config.videoParams)
663
+
664
+ const stream = await navigator.mediaDevices.getUserMedia({
665
+ video: videoOption,
666
+ audio: false
667
+ })
668
+ if (stream) {
669
+ this.cameraType = this.cameraType === 'front' ? 'back' : 'front'
670
+ this.$refs.localVideo.srcObject = stream
671
+ this.sipMix.changeLocalVideoTrack({stream})
672
+ }
673
+ },
674
+
675
+ setDialNum (dialNum) {
676
+ this.$refs.dialPanelMini.setDialNum(dialNum)
677
+ },
678
+
679
+ async checkBrowser() {
680
+ const stream = await navigator.mediaDevices.getUserMedia({video: true, audio: true})
681
+ if (stream) {
682
+ closeStream(stream)
683
+ } else {
684
+ return Promise.reject(this.$t('cameraOrMicUnsupported'))
685
+ }
686
+
687
+
688
+ if (!window.RTCPeerConnection) {
689
+ return Promise.reject(this.$t('WebRTCUnsupported'))
690
+ }
691
+
692
+ const list = await navigator.mediaDevices.enumerateDevices()
693
+ const vList = list.filter(e => e.kind === 'videoinput')
694
+ vList.forEach(device => {
695
+ console.warn({
696
+ label: device.label,
697
+ deviceId: device.deviceId,
698
+ groupId: device.groupId
699
+ })
700
+ })
701
+
702
+ this.videoInputList = vList
703
+
704
+ return Promise.resolve(true)
705
+ },
706
+
707
+ startSip ({debug, password, register, autoDial = false}) {
708
+ const {domain, port, path} = window.config
709
+
710
+ this.sipMix = SipMix.getInstance({
711
+ domain, port,
712
+ path
713
+ })
714
+ this.sipMix.on('socketError', () => this.prompt = this.$t('connectionFailedPleaseCheckYourNetwork'))
715
+
716
+ this.sipMix.on('connecting', () => this.prompt = this.$t('serverConnected') + '...')
717
+ this.sipMix.on('connected', () => {
718
+ this.prompt = this.$t('serverConnected')
719
+ if (!register && autoDial) {
720
+ this.onMakeCallBtnClick()
721
+ }
722
+ })
723
+ this.sipMix.on('disconnected', () => this.prompt = this.$t('connectionClosed'))
724
+
725
+ // 非注册模式不会触发以下3个事件
726
+ this.sipMix.on('registered', () => {
727
+ this.prompt = this.$t('registered')
728
+ if (register && autoDial) {
729
+ this.onMakeCallBtnClick()
730
+ }
731
+ })
732
+ this.sipMix.on('unregistered', () => this.prompt = this.$t('unregistered'))
733
+ this.sipMix.on('registrationFailed', () => this.prompt = this.$t('registerFailed'))
734
+
735
+
736
+ this.sipMix.on('newSession', ({sessionId}) => {
737
+ console.info('New session, session id: ', sessionId)
738
+ })
739
+
740
+ this.sipMix.on('failed', (e) => {
741
+ console.warn('failed', e)
742
+ this.dealCallEndOrFail()
743
+ this.callPrompt = this.$t('cancelled')
744
+ setTimeout(() => {
745
+ this.callPrompt = this.$t('pleaseBePatient')
746
+ }, 2000)
747
+ })
748
+
749
+ this.sipMix.on('ended', () => {
750
+ console.warn('ended')
751
+ this.dealCallEndOrFail()
752
+ this.callPrompt = this.$t('hangedUp')
753
+ setTimeout(() => {
754
+ this.callPrompt = this.$t('pleaseBePatient')
755
+ }, 2000)
756
+ })
757
+
758
+ this.sipMix.on('localStream', async (stream, info) => {
759
+ console.info('localStream', stream, info)
760
+ this.localVideo.w = this.clientWidth
761
+ this.localVideo.h = this.clientHeight
762
+ this.localVideo.draggable = false
763
+ this.showVideoCt = true
764
+ this.localVideo.show = true
765
+
766
+ await this.$nextTick()
767
+ this.$refs.localVideo.srcObject = stream
768
+ })
769
+
770
+ this.sipMix.on('remoteStream', (stream, info) => {
771
+ console.info('remoteStream', stream, info)
772
+ this._resetCmpWHLRTB()
773
+
774
+ Object.assign(this.localVideo, {
775
+ w: parseInt(this.clientWidth / 3),
776
+ h: parseInt(this.clientHeight / 3),
777
+ x: 0,
778
+ y: 80,
779
+ z: 1100,
780
+ draggable: true
781
+ })
782
+ this.$nextTick(() => {
783
+ this.$refs.localVideoWrapper.moveVertically(80)
784
+ })
785
+
786
+
787
+ setTimeout(async () => {
788
+ Object.assign(this.remoteVideo, {
789
+ show: true,
790
+ w: this.clientWidth,
791
+ h: this.clientHeight,
792
+ x: 0,
793
+ y: 0,
794
+ z: 1000,
795
+ draggable: false
796
+ })
797
+
798
+ await this.$nextTick()
799
+ if (this.autoHideLocalVideo) {
800
+ this.localVideo.show = false
801
+ }
802
+ this.$refs.remoteVideo.srcObject = stream
803
+ }, 500)
804
+
805
+ })
806
+
807
+ this.sipMix.on('remoteRing', () => {
808
+ this.callPrompt = this.$t('remoteRinging')
809
+ // this.$refs.audioPlayer.currentTime = 0
810
+ const promise = this.$refs.audioPlayer.play()
811
+ if (promise !== undefined) {
812
+ promise.catch(error => {
813
+ console.info(error)
814
+ if (error.name === 'NotAllowedError') {
815
+ this.showAudioPlayer = true
816
+ }
817
+ })
818
+ }
819
+ })
820
+
821
+ this.sipMix.on('accepted', (e) => {
822
+ this.callPrompt = this.$t('remoteAccept')
823
+ const {callId} = e
824
+ this.callId = callId
825
+ this.$refs.audioPlayer.pause()
826
+ this.autoShowHangupBtn()
827
+ })
828
+
829
+ this.sipMix.on('confirmed', () => {
830
+ this.startSendPosition()
831
+ })
832
+
833
+ this.sipMix.on('newInfo', ({content, contentType}) => {
834
+ console.info('New Info', contentType, content)
835
+ })
836
+
837
+ this.sipMix.start({
838
+ deviceId: this.callingNum,
839
+ password,
840
+ register,
841
+ debug
842
+ })
843
+ },
844
+
845
+ startLocate () {
846
+ if (navigator.geolocation) {
847
+ navigator.geolocation.getCurrentPosition(position => {
848
+ console.info('Position', position)
849
+ const {latitude, longitude, altitude } = position?.coords || {}
850
+ console.info(`获取地里位置信息: ${latitude}, ${longitude}, ${altitude}`)
851
+ /*this.sendCustomEvent({
852
+ act: 'position',
853
+ params: {latitude, longitude, accuracy}
854
+ })*/
855
+ this.latitude = latitude
856
+ this.longitude = longitude
857
+ this.altitude = altitude
858
+ }, () => {
859
+ console.info('获取地里位置信息失败')
860
+ })
861
+
862
+ this.locateHandler = setTimeout (() => {
863
+ this.startLocate()
864
+ }, 15000)
865
+ }
866
+ },
867
+
868
+ stopLocate () {
869
+ clearTimeout(this.locateHandler)
870
+ },
871
+
872
+ startSendPosition () {
873
+ if (this.showLocation && this.locateHandler) {
874
+ this.sipMix.sendInfo({
875
+ encode: false,
876
+ content: {
877
+ type: 'Position',
878
+ content: {
879
+ latitude: this.latitude,
880
+ longitude: this.longitude,
881
+ altitude: this.altitude
882
+ }
883
+ }
884
+ })
885
+ /*this.sipMix.sendMessage({
886
+ target: this.calledNum,
887
+ content: {
888
+ type: 'Position',
889
+ content: {
890
+ latitude: this.latitude,
891
+ longitude: this.longitude,
892
+ altitude: this.altitude
893
+ }
894
+ }
895
+ })*/
896
+ }
897
+ this.sendPositionHandler = setTimeout(() => {
898
+ this.startSendPosition()
899
+ }, 15000)
900
+ },
901
+
902
+ stopSendPosition () {
903
+ clearTimeout(this.sendPositionHandler)
904
+ }
905
+ },
906
+
907
+ async mounted() {
908
+ console.info('MobilePhone mounted')
909
+ await this.checkBrowser().catch(e => {
910
+ this.showSnackbar = true
911
+ this.snackbarText = e
912
+ })
913
+
914
+ setTimeout(() => {
915
+ const {clientHeight, clientWidth} = window.document.body
916
+ this.clientHeight = clientHeight
917
+ this.clientWidth = clientWidth
918
+ this.localVideo.w = clientWidth
919
+ this.localVideo.h = clientHeight
920
+
921
+ if (this.showLocation) {
922
+ this.startLocate()
923
+ }
924
+ }, 200)
925
+
926
+ this.$nextTick(() => {
927
+ this.showAudioPlayer = false
928
+ })
929
+ }
930
+ }
931
+ </script>
932
+
933
+ <style lang="scss">
934
+ .sip-call-main {
935
+ background-color: #737778;
936
+ padding: 25% 10px 20% !important;
937
+ width: 100vw;
938
+ height: 100vh;
939
+ overflow: hidden;
940
+ }
941
+
942
+ .sip-call-wrapper {
943
+ position: absolute;
944
+ left: 0;
945
+ top: 0;
946
+ width: 100vw;
947
+ height: 100vh;
948
+ z-index: 100;
949
+ }
950
+
951
+ .btn-group {
952
+ width: 100%;
953
+ z-index: 4000;
954
+ }
955
+
956
+ @media screen and (min-width: 1024px) {
957
+ .btn-group {
958
+ position: absolute;
959
+ bottom: 80px;
960
+ width: 640px;
961
+ z-index: 4000;
962
+ }
963
+ }
964
+
965
+ .sip-call-video {
966
+ width: 100%;
967
+ height: 100%;
968
+ object-fit: cover;
969
+ background-color: #000000;
970
+ }
971
+
972
+ .video-panel {
973
+ // touch-action: none;
974
+ position: absolute;
975
+ display: flex;
976
+ flex-direction: column;
977
+ justify-content: space-around;
978
+ align-items: center;
979
+ }
980
+
981
+ .video-timer {
982
+ background-color: rgba(255,255,255,0.80);
983
+ position: absolute;
984
+ top: 40px;
985
+ left: 16px;
986
+ font-size: 14px;
987
+ font-weight: bold;
988
+ padding: 2px 8px 2px 6px;
989
+ border-radius: 12px;
990
+ z-index: 2000;
991
+ display: flex;
992
+ justify-content: space-around;
993
+ align-items: center;
994
+ }
995
+
996
+ .video-switch-btn {
997
+ position: absolute !important;
998
+ top: 40px;
999
+ right: 80px;
1000
+ z-index: 2000;
1001
+ img {
1002
+ background-color: #000000;
1003
+ border-radius: 6px;
1004
+ border: 1px solid #000000;
1005
+ }
1006
+ }
1007
+
1008
+ .video-mini-mode {
1009
+ position: absolute !important;
1010
+ top: 40px;
1011
+ right: 48px;
1012
+ z-index: 2000;
1013
+ }
1014
+
1015
+ .camera-switch-btn {
1016
+ position: absolute !important;
1017
+ top: 40px;
1018
+ right: 16px;
1019
+ z-index: 2000;
1020
+ }
1021
+
1022
+ .video-normal-mode {
1023
+ position: absolute !important;
1024
+ top: 16px;
1025
+ right: 16px;
1026
+ z-index: 2000;
1027
+ }
1028
+
1029
+ .location-wrapper {
1030
+ position: absolute;
1031
+ top: 0;
1032
+ right: 0;
1033
+ width: 150px;
1034
+ background-color: #00000055;
1035
+ color: #FFFFFF;
1036
+ padding: 6px;
1037
+ p {
1038
+ font-size: 12px;
1039
+ margin: 0;
1040
+ padding: 0;
1041
+ width: 100%;
1042
+ white-space: nowrap;
1043
+ text-overflow: ellipsis;
1044
+ }
1045
+ }
1046
+ </style>