2492 lines
94 KiB
Vue
2492 lines
94 KiB
Vue
<template>
|
||
<view class="page-live">
|
||
<view class="video-container">
|
||
<live-player id="livePlayer" :autoplay="true" @error="error" @statechange="statechange" mode="RTC" :src="isHD ? videoHDSrc :videoSrc" @tap.stop.prevent="obVideoTap" :muted="!openSound" :class="funllScreen ? 'video-item full-screen' : 'video-item'" @fullscreenchange="fullscreenChange" :object-fit="objectFit">
|
||
<!-- loading状态 -->
|
||
<cover-view class="video-loading-container" @tap.stop.prevent="onVideoTap" :hidden="videoLoadingStatus === 100">
|
||
<cover-image class="video-loading-bg" src="../../static/live/images/live/live_loading_bg.png"></cover-image>
|
||
<!-- 停止状态 -->
|
||
<cover-view class="video-loaing video-ready" :hidden="videoLoadingStatus !== 0">
|
||
<cover-image class="loading-gif" src="../../static/live/images/live/landscape_play.png" @tap="handlePlay"></cover-image>
|
||
</cover-view>
|
||
<!-- 加载资源中状态 -->
|
||
<cover-view class="video-loaing" :hidden="videoLoadingStatus === 0 || videoNetWorkError">
|
||
<cover-image class="loading-gif" src="../../static/live/images/live/loading_grey.gif"></cover-image>
|
||
<cover-view class="video-loading-text">视频安全传输中...{{ videoLoadingStatus }}%</cover-view>
|
||
</cover-view>
|
||
</cover-view>
|
||
<!-- 开启隐私遮蔽状态 -->
|
||
<cover-view class="video-loading-container" :hidden="!(panelStatus == 4)">
|
||
<cover-image class="video-loading-bg" src="../../static/live/images/live/live_loading_bg.png"></cover-image>
|
||
<cover-view class="video-loaing">
|
||
<cover-image class="loading-gif" src="../../static/live/images/live/preview_fail_yinsi.png"></cover-image>
|
||
<cover-view class="video-loading-text">已开启隐私遮蔽</cover-view>
|
||
</cover-view>
|
||
</cover-view>
|
||
<!-- 加载资源失败状态 -->
|
||
<cover-view class="video-loading-container" :hidden="!videoNetWorkError">
|
||
<cover-image class="video-loading-bg" src="../../static/live/images/live/live_loading_bg.png"></cover-image>
|
||
<cover-view class="video-loaing">
|
||
<cover-image class="loading-gif" src="../../static/live/images/live/preview_fail.png"></cover-image>
|
||
<cover-view class="video-loading-text">网络不稳定,加载失败</cover-view>
|
||
<cover-view class="video-loading-text reTry" @tap="handlePlay">重试</cover-view>
|
||
</cover-view>
|
||
</cover-view>
|
||
<!-- 设备不在线状态 -->
|
||
<cover-view class="video-loading-container" :hidden="!deviceOffline">
|
||
<cover-image class="video-loading-bg" src="../../static/live/images/live/live_loading_bg.png"></cover-image>
|
||
<cover-view class="video-loaing">
|
||
<cover-image class="loading-gif" src="../../static/live/images/live/preview_fail_offline.png"></cover-image>
|
||
<cover-view class="video-loading-text">设备不在线</cover-view>
|
||
<cover-view class="video-loading-text">离线时间:{{ deviceOfflineTime }}</cover-view>
|
||
</cover-view>
|
||
</cover-view>
|
||
<!-- 清晰度 -->
|
||
<cover-view :class="showHDSelect ? 'hd-select' : 'hd-select hide'">
|
||
<cover-view :class="isHD ? 'hd-option active' : 'hd-option'" @tap.stop.prevent="changeVideoHD">高清</cover-view>
|
||
<cover-view :class="!isHD ? 'hd-option active' : 'hd-option'" @tap.stop.prevent="changeVideoNormal">标清</cover-view>
|
||
</cover-view>
|
||
<!-- 竖屏模式 -->
|
||
<cover-view :class="(fullScreen ? 'hidden' : '') + ' video-controls-container'"></cover-view>
|
||
<cover-view :class="(fullScreen || !showVideoControls ? 'hidden' : '') + ' video-controls-container'">
|
||
<cover-image
|
||
class="controls-img"
|
||
:src="playVideo ? '../../static/live/images/video_icon_stop.png' : '../../static/live/images/video_icon_play.png'"
|
||
@tap.stop.prevent="playVideo ? handleStop() : handlePlay()"
|
||
></cover-image>
|
||
<cover-image
|
||
class="controls-img"
|
||
:src="!openSound ? '../../static/live/images/video_icon_closesound.png' : '../../static/live/images/video_icon_opensound.png'"
|
||
@tap.stop.prevent="handleSound"
|
||
></cover-image>
|
||
<cover-image
|
||
class="controls-img hd"
|
||
:src="isHD ? '../../static/live/images/video_icon_hd.png' : '../../static/live/images/video_icon_bq.png'"
|
||
@tap.stop.prevent="handleHD"
|
||
></cover-image>
|
||
<cover-image class="controls-img" @tap.stop.prevent="fullScreenFun" src="../../static/live/images/video_icon_full.png"></cover-image>
|
||
</cover-view>
|
||
<!-- 横屏模式 -->
|
||
<cover-view :class="(!fullScreen ? 'hidden' : '')+' video-back-container'">
|
||
<cover-image class="back-img" src="../../static/live/images/nav_icon_back_full.png" @tap.stop.prevent="unfullScreen"></cover-image>
|
||
<cover-view class="back-device">{{ deviceName }}</cover-view>
|
||
</cover-view>
|
||
<cover-view :class="((!fullScreen || !showVideoControls) ? 'hidden' : '')+'video-controls-container'">
|
||
<cover-image
|
||
class="controls-img"
|
||
:src="playVideo ? '../../static/live/images/live_icon_stop_full.png' : '../../static/live/images/live_icon_play_full.png'"
|
||
@tap.stop.prevent="playVideo ? 'handleStop' : 'handlePlay'"
|
||
></cover-image>
|
||
<cover-image
|
||
class="controls-img"
|
||
:src="!openSound ? '../../static/live/images/live_icon_unsound_full.png' : '../../static/live/images/live_icon_sound_full.png'"
|
||
@tap.stop.prevent="handleSound"
|
||
></cover-image>
|
||
<cover-image class="controls-img" @tap.stop.prevent="ToggleObjectFit" src="../../static/live/images/live_icon_adapt_full.png"></cover-image>
|
||
</cover-view>
|
||
<cover-view :class="'ptz-limit ' + (ptzLimit ? ptzLimit : 'hidden')"></cover-view>
|
||
</live-player>
|
||
</view>
|
||
<!-- 控制面板 -->
|
||
<view :hidden="panelStatus == 1 || panelStatus == 2 || panelStatus == 5">
|
||
<view class="panel-container">
|
||
<view class="panel-item-container" v-for="(item, index) in list" :key="index">
|
||
<view
|
||
v-if="item.id != 'capture'"
|
||
:class="item.status === 0 ? 'panel-item' : item.status === 1 ? 'panel-item panel-item-active' : 'panel-item panel-item-disable'"
|
||
:data-value="item.id"
|
||
@tap.stop.prevent="tapPanel"
|
||
>
|
||
<view class="panel-image-container">
|
||
<image class="panel-image" :src="item.status === -1 ? item.abnormalImage : item.imageUrl"></image>
|
||
</view>
|
||
<view class="panel-name">{{ item.name }}</view>
|
||
</view>
|
||
|
||
<view
|
||
v-if="item.id == 'capture'"
|
||
:class="playVideo && videoLoadingStatus == 100 ? 'panel-item' : 'panel-item panel-item-disable'"
|
||
:data-value="item.id"
|
||
@tap.stop.prevent="tapPanel"
|
||
>
|
||
<view class="panel-image-container">
|
||
<image class="panel-image" :src="playVideo && videoLoadingStatus == 100 ? item.imageUrl : item.abnormalImage"></image>
|
||
</view>
|
||
<view class="panel-name">{{ item.name }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 云台 -->
|
||
<view :hidden="panelStatus !== 1" class="ptz-container">
|
||
<view class="close">
|
||
<image @tap.stop.prevent="handleBackPanel" class="close-img" src="../../static/live/images/yuntai/close.png"></image>
|
||
</view>
|
||
<view class="ptz-img-container" id="ptz-img-container" @touchstart.stop.prevent="handlePtzTouchStart" @touchend.stop.prevent="handlePtzTouchEnd">
|
||
<image class="ptz-img" :src="currentPtzImg"></image>
|
||
</view>
|
||
</view>
|
||
<!-- 语音播报 -->
|
||
<view class="voice-container" :hidden="panelStatus !==2">
|
||
<view class="close">
|
||
<image @tap.stop.prevent="handleBackPanel" class="close-img" src="../../static/live/images/yuntai/close.png"></image>
|
||
</view>
|
||
<view class="voice-list-title">默认语音</view>
|
||
<scroll-view class="scroll-view" :enable-flex="true" :scroll-y="true" style="height: 100px" @scrolltolower="defaultScrollLower">
|
||
<view class="voice-list-container" v-for="(item, index) in defaultVoiceList" :key="index">
|
||
<view class="voice-list-item" @tap.stop.prevent="playVoice" :data-value="item" data-type="default">
|
||
<view class="name">{{ item.voiceName }}</view>
|
||
<image
|
||
class="gif"
|
||
:src="activeDefaultVoiceName == item.voiceName ? '../../static/live/images/voice.gif' : '../../static/live/images/voice_normal.png'"
|
||
></image>
|
||
</view>
|
||
</view>
|
||
<view class="list-loading" :hidden="!defaultVoiceListLoading">正在载入更多...</view>
|
||
<view class="list-loading" :hidden="(defaultVoiceListLoading || !defaultVoiceNoMore)">已加载全部</view>
|
||
</scroll-view>
|
||
<view class="voice-list-title">录制语音</view>
|
||
<scroll-view class="scroll-view" :enable-flex="true" :scroll-y="true" style="height: 100px" @scrolltolower="customScrollLower">
|
||
<view class="voice-list-container" v-for="(item, index) in customVoiceList" :key="index">
|
||
<view class="voice-list-item" @tap.stop.prevent="playVoice" :data-value="item" data-type="custom">
|
||
<view class="name">{{ item.voiceName }}</view>
|
||
<image
|
||
class="gif"
|
||
:src="activeCustomVoiceName == item.voiceName ? '../../static/live/images/voice.gif' : '../../static/live/images/voice_normal.png'"
|
||
></image>
|
||
</view>
|
||
</view>
|
||
<view class="list-loading" :hidden="!customVoiceListLoading">正在载入更多...</view>
|
||
<view class="list-loading" :hidden="(customVoiceListLoading || !customVoiceNoMore)">已加载全部</view>
|
||
</scroll-view>
|
||
<button class="btn primary" :disabled="sendingOnceVoice" @touchstart.stop.prevent="speakStart" @touchend.stop.prevent="speakEnd">
|
||
{{ sendingOnceVoice ? '请稍后...' : recoderTime === 60 ? '按住说话' : '松开结束' }}
|
||
</button>
|
||
</view>
|
||
|
||
<!-- 对讲 -->
|
||
<view :hidden="panelStatus !== 5" class="talk-container">
|
||
<view class="talk-container-title">
|
||
<image class="closed-icon" src="../../static/live/image/exit.svg" @tap.stop.prevent="exitTalk" />
|
||
<view class="status-contant">
|
||
<view class="loading" v-if="talkStatus == 1"></view>
|
||
<view class="title" v-if="talkStatus == 2">对讲中</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="talk-container-contant">
|
||
<view v-if="talkStatus == 1" class="talk-container-loading"></view>
|
||
<image v-if="talkStatus == 2" class="talk-container-talking" src="../../static/live/image/quanshuanggong.png" />
|
||
<view v-if="talkStatus == 3" class="talk-container-fail">
|
||
<image class="status-image" src="../../static/live/image/quanshuanggong-fail.png" />
|
||
<view class="status-title">启动失败</view>
|
||
<text class="status-subtitle">请检查你的网络并点击重试</text>
|
||
<text class="status-subtitle">再启动对讲</text>
|
||
<button class="btn" @tap.stop.prevent="tryTalkAgain">重试</button>
|
||
</view>
|
||
<view v-if="talkStatus == 4" class="talk-container-fail">
|
||
<image class="status-image" src="../../static/live/image/quanshuanggong-fail2.png" />
|
||
<view class="status-title">启动失败</view>
|
||
<text class="status-subtitle">设备对讲中,请稍后重试</text>
|
||
<button class="btn" @tap.stop.prevent="tryTalkAgain">重试</button>
|
||
</view>
|
||
</view>
|
||
<!-- 推流 -->
|
||
<view class="push-container" v-if="talkStatus == 2">
|
||
<live-pusher
|
||
id="livePusher"
|
||
class="live-pusher-container"
|
||
@error="error"
|
||
:url="pushUrl"
|
||
mode="RTC"
|
||
audio-quality="low"
|
||
@statechange="pusherStateChange"
|
||
:enable-camera="true"
|
||
:enable-mic="openSound"
|
||
@tap.stop.prevent="parseEventDynamicCode($event, pushVideo ? 'stopPush' : 'startPush')"
|
||
></live-pusher>
|
||
</view>
|
||
<!-- 拉流 -->
|
||
<view class="video-container" :data-playId="item.id" @tap="currentPlayContainerFun">
|
||
<live-player id="talk-liveplayer" @error="error" @statechange="statechange" mode="RTC" :src="item.playSrc" class="video-item" autoplay></live-player>
|
||
</view>
|
||
</view>
|
||
<!-- 模态框 -->
|
||
<mp-dialog :title="dialogTitle" :show="dialogShow" @buttontap="tapDialogButton" :buttons="buttons">
|
||
<view>{{ dialogContent }}</view>
|
||
</mp-dialog>
|
||
</view>
|
||
</template>
|
||
<script>
|
||
import mpDialog from '../../component/dialog/dialog';
|
||
import { DateFormat } from '../../common/utils';
|
||
import { OPEN_DOMAIN, PTZ_START, PTZ_STOP } from '../../config/config';
|
||
// 生成uuid
|
||
const wxuuid = function () {
|
||
var s = [];
|
||
var hexDigits = '0123456789abcdef';
|
||
for (var i = 0; i < 36; i++) {
|
||
s[i] = hexDigits.substr(Math.floor(Math.random() * 16), 1);
|
||
}
|
||
s[14] = '4'; // bits 12-15 of the time_hi_and_version field to 0010
|
||
s[19] = hexDigits.substr((s[19] & 3) | 8, 1); // bits 6-7 of the clock_seq_hi_and_reserved to 01
|
||
s[8] = s[13] = s[18] = s[23] = '-';
|
||
var uuid = s.join('');
|
||
return uuid;
|
||
};
|
||
|
||
//index.js
|
||
//获取应用实例
|
||
const app = getApp();
|
||
const recorderManager = uni.getRecorderManager();
|
||
const options = {
|
||
duration: 60000,
|
||
//指定录音的时长,单位 ms,最大为10分钟(600000),默认为1分钟(60000)
|
||
sampleRate: 16000,
|
||
//采样率
|
||
numberOfChannels: 1,
|
||
//录音通道数
|
||
format: 'mp3' //音频格式,有效值 aac/mp3
|
||
};
|
||
|
||
var livePlayerContext;
|
||
export default{
|
||
components: {
|
||
mpDialog
|
||
},
|
||
data(){
|
||
return{
|
||
scene: 1001,
|
||
accessToken: '',
|
||
deviceSerial: '',
|
||
channelNo: '1',
|
||
list: [
|
||
{
|
||
id: 'talk',
|
||
name: '对讲',
|
||
status: -1,
|
||
imageUrl: '../../static/live/image/talk.svg',
|
||
abnormalImage: '../../static/live/image/talk-disabled.svg'
|
||
},
|
||
{
|
||
id: 'ptz',
|
||
name: '云台控制',
|
||
status: -1,
|
||
imageUrl: '../../static/live/image/yuntai.svg',
|
||
abnormalImage: '../../static/live/image/yuntai-disabled.svg'
|
||
},
|
||
{
|
||
id: 'voice',
|
||
name: '语音播报',
|
||
status: -1,
|
||
imageUrl: '../../static/live/image/record.svg',
|
||
abnormalImage: '../../static/live/image/record-disable.svg'
|
||
},
|
||
{
|
||
id: 'playback',
|
||
name: '回看',
|
||
status: 0,
|
||
imageUrl: '../../static/live/image/backward.svg',
|
||
abnormalImage: '../../static/live/image/backward.svg'
|
||
},
|
||
{
|
||
id: 'capture',
|
||
name: '截屏',
|
||
status: -1,
|
||
imageUrl: '../../static/live/image/picture-shoot.svg',
|
||
abnormalImage: '../../static/live/image/pictureshoot-disabled.svg'
|
||
},
|
||
{
|
||
id: 'mirror',
|
||
name: '镜像翻转',
|
||
status: -1,
|
||
imageUrl: '../../static/live/image/jingxiang.svg',
|
||
abnormalImage: '../../static/live/image/jingxiang-disabled.svg'
|
||
},
|
||
{
|
||
id: 'cover',
|
||
name: '镜头遮蔽',
|
||
status: -1,
|
||
imageUrl: '../../static/live/image/yinsi.svg',
|
||
abnormalImage: '../../static/live/image/yinsi-disabled.svg'
|
||
}
|
||
],
|
||
videoSrc:"",
|
||
videoHDSrc: "",
|
||
panelStatus: 0, //0: 展示面板 1:进入云台 2-进入语音播报 3-进入镜像翻转 4-进入镜头遮蔽 5-对讲,
|
||
ptzDisabled: true,
|
||
voiceDiasbled: true,
|
||
mirrorDisabled: true,
|
||
mirrorInterval: false,
|
||
coverDisabled: true,
|
||
coverInterval: false,
|
||
showVideoControls: true,
|
||
autoHideTimer: undefined,
|
||
videoLoadingStatus: 0,
|
||
playVideo: false,
|
||
videoNetWorkError: false,
|
||
objectFit:'contain',
|
||
openSound: true,
|
||
isHD: true,
|
||
showHDSelect: false,
|
||
fullScreen: false,
|
||
ptzStatus: 0, //0-初始化 1-top noraml 2-downnoraml 3-left normal 4-right normal 5-top noraml 6-down limit 7-left limit 8-right limit
|
||
ptzLoading: false,
|
||
ptzLimit: '',
|
||
deviceOffline: false,
|
||
deviceOfflineTime: new Date(),
|
||
deviceName: '',
|
||
currentPtzImg: '../../static/live/images/yuntai/normal.png',
|
||
// 语音播报
|
||
activeDefaultVoiceName: '',
|
||
activeCustomVoiceName: '',
|
||
defaultVoiceList: [], // 默认语音列表
|
||
defaultVoiceTotal: 0, // 默认语音总数
|
||
defaultVoicePage: 0, // 默认语言当前页
|
||
defaultVoiceListLoading: false,
|
||
defaultVoiceNoMore: false,
|
||
customVoiceList: [], // 默认语音列表
|
||
customVoiceTotal: 0, // 默认语音总数
|
||
customVoicePage: 0, // 默认语言当前页
|
||
customVoiceListLoading: false,
|
||
customVoiceListNoMore: false,
|
||
recoderTime:60,
|
||
recoderTimer: undefined,
|
||
sendingOnceVoice: false,
|
||
dialogTitle: '',
|
||
dialogContent: '',
|
||
buttons: [{text: '知道了'}],
|
||
dialogShow: false,
|
||
pathParam: '',
|
||
|
||
// 对讲过程
|
||
talkStatus: 0, //0-未开始/结束,1-加载中,2-进行中, 3-启动失败, 4-设备对讲中
|
||
token1: '', // 用于邀请设备入会
|
||
token2: '', // 用于强制设备退会
|
||
websocketUrl: '',
|
||
customId: 'isZhangqianwei', // 开发者自定义用户id,以此换取clientId
|
||
clientId: 993089712, // 注意:通讯过程中真实的clientId,此处仅供调试,无需赋值
|
||
roomId: 0, // 注意:需要加入的房间号,需外部提供
|
||
joinPassword: '', // 注意:加入房间的密码,需外部提供
|
||
vtmHttpsAddress: '',
|
||
vtmIp: '', // 此处仅供调试,无需赋值
|
||
vtmPort: 8554, // 此处仅供调试,无需赋值
|
||
rtmpAddress: '', // push/play地址
|
||
rtmpPort: '', // 推流拉流端口地址
|
||
lockReconnect: false, // websocket重连
|
||
limit: 0,
|
||
timer: null,
|
||
pushVideo: true, // 推流
|
||
pushVideoContext: null,
|
||
pushUrl: '', // 推流地址
|
||
livePlayerContext: [
|
||
|
||
], // 播放节点
|
||
showVideoControls: false,
|
||
videoNetWorkError: false,
|
||
playSrc: '', // 播放地址
|
||
code:'',
|
||
token:''
|
||
}
|
||
},
|
||
onShow() {
|
||
console.log('show');
|
||
var launchOptions = uni.getEnterOptionsSync();
|
||
const pathParam = launchOptions.query.scene;
|
||
console.log('pathParam:', pathParam);
|
||
this.pathParam=pathParam
|
||
if (pathParam) {
|
||
this.getWxaInfo();
|
||
}
|
||
// Do something when show.
|
||
this.checkNetWork();
|
||
},
|
||
onHide() {
|
||
// Do something when hide.
|
||
console.log('hide');
|
||
this.panelStatus=0,
|
||
this.pathParam=''
|
||
console.log(this.pathParam);
|
||
},
|
||
onLoad(query) {
|
||
var launchOptions = uni.getLaunchOptionsSync();
|
||
const { accessToken, deviceSerial, channelNo, scene } = query;
|
||
console.log(query,'参数')
|
||
this.scene=parseInt(scene, 10) || launchOptions.scene,
|
||
this.accessToken=query.accessToken,
|
||
console.log(this.accessToken,'token')
|
||
this.deviceSerial=query.deviceSerial,
|
||
this.channelNo=1,
|
||
this.panelStatus=0 // todo -0
|
||
this.getPlayUrl();
|
||
this.getDeviceInfo();
|
||
this.getDeviceCoverInfo();
|
||
this.showOneButtonDialog=true
|
||
// 录音模块
|
||
recorderManager.onStart(() => {
|
||
console.log('recorder start');
|
||
});
|
||
recorderManager.onPause(() => {
|
||
console.log('recorder pause');
|
||
this.speakEnd();
|
||
});
|
||
recorderManager.onInterruptionBegin(this.speakEnd);
|
||
recorderManager.onStop((res) => {
|
||
console.log('recorder stop', res);
|
||
const { recoderTime } = this;
|
||
const { tempFilePath } = res;
|
||
if (recoderTime >= 59) {
|
||
this.recoderTime=60
|
||
clearTimeout(this.recoderTimer);
|
||
return false;
|
||
}
|
||
this.sendingOnceVoice=true
|
||
uni.uploadFile({
|
||
url: `${OPEN_DOMAIN}/api/lapp/voice/sendonce`,
|
||
//仅为示例,非真实的接口地址
|
||
filePath: tempFilePath,
|
||
//tempFilePaths[0],
|
||
name: 'voiceFile',
|
||
formData: {
|
||
accessToken: accessToken,
|
||
deviceSerial: deviceSerial,
|
||
channelNo: channelNo
|
||
},
|
||
header: {
|
||
'content-type': 'amultipart/form-data' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
let data = res.data;
|
||
if (!data.code) {
|
||
data = JSON.parse(data);
|
||
}
|
||
console.log(data);
|
||
if (data.code == 200) {
|
||
console.log('发送成功');
|
||
} else if (data.code == '111012') {
|
||
// 设备正忙
|
||
uni.showToast({
|
||
title: '语音下发失败',
|
||
icon: 'none'
|
||
});
|
||
} else if (data.code == '20007') {
|
||
// 设备正忙
|
||
uni.showToast({
|
||
title: '设备不在线',
|
||
icon: 'none'
|
||
});
|
||
} else if (data.code == '20008') {
|
||
// 设备正忙
|
||
uni.showToast({
|
||
title: '设备响应超时',
|
||
icon: 'none'
|
||
});
|
||
} else {
|
||
uni.showToast({
|
||
title: data.msg,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
this.recoderTime=60
|
||
//do something
|
||
},
|
||
|
||
fail: (res) => {
|
||
uni.showToast({
|
||
title: '网络异常',
|
||
icon: 'none'
|
||
});
|
||
},
|
||
complete: () => {
|
||
this.sendingOnceVoice=false
|
||
}
|
||
});
|
||
});
|
||
recorderManager.onFrameRecorded((res) => {
|
||
const { frameBuffer } = res;
|
||
console.log('frameBuffer.byteLength', frameBuffer.byteLength);
|
||
});
|
||
//视频
|
||
livePlayerContext = uni.createLivePlayerContext('livePlayer');
|
||
console.log('livePlayerContext', livePlayerContext);
|
||
},
|
||
/**
|
||
* 用户点击右上角分享
|
||
*/
|
||
onShareAppMessage(res){
|
||
const { accessToken, deviceSerial, channelNo } = this;
|
||
if (res.from === 'button') {
|
||
// 来自页面内转发按钮
|
||
console.log(res.target);
|
||
this.panelStatus=0
|
||
}
|
||
return {
|
||
title: '小程序',
|
||
path: '/packDetail1/pages/device/index1?accessToken=' + accessToken + '&deviceSerial=' + deviceSerial + '&channelNo=' + channelNo + '&scene=1007'
|
||
};
|
||
},
|
||
methods: {
|
||
onLaunch() {
|
||
console.log(onLaunch);
|
||
},
|
||
|
||
onError(msg) {
|
||
console.log(msg);
|
||
},
|
||
|
||
initPannel(moduleId) {},
|
||
|
||
checkNetWork() {
|
||
const _this = this;
|
||
uni.getNetworkType({
|
||
success(res) {
|
||
const networkType = res.networkType;
|
||
if (!networkType || networkType === 'none') {
|
||
uni.showToast({
|
||
title: '当前网络异常',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
getPlayUrl() {
|
||
const { accessToken, deviceSerial, channelNo } = this;
|
||
var _this = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/v2/live/address/get`,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
deviceSerial: deviceSerial,
|
||
// deviceSerial:'J27717024',
|
||
channelNo: channelNo,
|
||
supportH265:1,
|
||
expireTime: 86400,
|
||
quality: 2,
|
||
protocol: 3
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log(res.data);
|
||
const { list } = this;
|
||
if (res.data.code == 200 && res.data.data && res.data.data.url) {
|
||
var result = res.data;
|
||
if (result.code == 200) {
|
||
this.videoSrc=result.data.url
|
||
this.videoHDSrc=result.rtmpHd
|
||
} else {
|
||
list[0].status = -1;
|
||
list[1].status = -1;
|
||
list[2].status = -1;
|
||
list[4].status = -1;
|
||
list[5].status = -1;
|
||
list[6].status = -1;
|
||
_this.list=list,
|
||
_this.dialogContent=result.msg,
|
||
_this.showVideoControls=false
|
||
}
|
||
} else if (res.data.code == '20001' || res.data.code == '20002' || res.data.code == '20018' || res.data.code == '10001') {
|
||
// 设备不存在 / 不属于用户
|
||
this.dialogTitle='获取播放地址失败',
|
||
this.dialogContent='该用户不拥有该设备',
|
||
this.dialogShow=true
|
||
} else {
|
||
console.log('获取播放地址失败');
|
||
// _this.openPlayUrl();
|
||
}
|
||
}
|
||
});
|
||
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/v2/live/address/get`,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
deviceSerial: deviceSerial,
|
||
channelNo: channelNo,
|
||
expireTime: 86400,
|
||
quality: 1,
|
||
supportH265:1,
|
||
protocol: 3
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log(res.data);
|
||
const { list } = this;
|
||
if (res.data.code == 200 && res.data.data && res.data.data.url) {
|
||
var result = res.data;
|
||
if (result.code == 200) {
|
||
//videoSrc: result.rtmp,
|
||
this.videoHDSrc=result.data.url
|
||
} else {
|
||
llist[0].status = -1;
|
||
list[1].status = -1;
|
||
list[2].status = -1;
|
||
list[4].status = -1;
|
||
list[5].status = -1;
|
||
list[6].status = -1;
|
||
_this.list=list,
|
||
_this.dialogContent=result.msg,
|
||
_this.showVideoControls=false
|
||
}
|
||
} else {
|
||
console.log('获取高清播放地址失败');
|
||
// _this.openPlayUrl();
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
/*
|
||
* 获取设备基本信息
|
||
*/
|
||
getDeviceCoverInfo() {
|
||
const { accessToken, deviceSerial, channelNo } = this;
|
||
console.log(accessToken, deviceSerial, channelNo);
|
||
var _this = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/device/scene/switch/status`,
|
||
method: 'POST',
|
||
data: {
|
||
accessToken,
|
||
deviceSerial,
|
||
channelNo
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log(res.data);
|
||
if (res.data.code == 200 && res.data.data) {
|
||
const result = res.data.data;
|
||
let list = this.list;
|
||
if (result.enable == 1) {
|
||
// 当前镜头遮蔽中
|
||
|
||
list[0].status = -1;
|
||
list[1].status = -1;
|
||
list[2].status = -1;
|
||
list[4].status = -1;
|
||
list[5].status = -1;
|
||
list[6].status = 1;
|
||
this.videoNetWorkError=false,
|
||
this.showVideoControls=false,
|
||
this.panelStatus=4,
|
||
this.videoLoadingStatus=100,
|
||
this.list=list
|
||
console.log('panelStatus', this.panelStatus);
|
||
}
|
||
}
|
||
},
|
||
error: (err) => {
|
||
console.log(err);
|
||
}
|
||
});
|
||
},
|
||
|
||
/*
|
||
* 获取设备基本信息
|
||
*/
|
||
getDeviceInfo() {
|
||
const { accessToken, deviceSerial, channelNo, showVideoControls, videoNetWorkError, videoLoadingStatus } = this;
|
||
var _this = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/device/info`,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
accessToken,
|
||
deviceSerial,
|
||
channelNo
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log(res.data);
|
||
if (res.data.code == 200 && res.data.data) {
|
||
let list = this.list;
|
||
var result = res.data.data;
|
||
_this.deviceName=result.deviceName,
|
||
_this.deviceOffline=result.status !== 1,
|
||
_this.videoNetWorkError=result.status !== 1 ? false : videoNetWorkError,
|
||
_this.showVideoControls=result.status !== 1 ? false : showVideoControls,
|
||
_this.deviceOfflineTime=DateFormat(new Date(result.updateTime), 'yyyy-MM-dd hh:mm:ss'),
|
||
_this.deviceIsEncrypt=result.isEncrypt,
|
||
_this.videoLoadingStatus=result.isEncrypt === 1 || result.status !== 1 ? 100 : videoLoadingStatus
|
||
// 配置标题
|
||
if (result.deviceName) {
|
||
uni.setNavigationBarTitle({
|
||
title: result.deviceName
|
||
});
|
||
}
|
||
if (result.status != 1) {
|
||
// 设备不在线
|
||
list[0].status = -1;
|
||
list[1].status = -1;
|
||
list[2].status = -1;
|
||
list[4].status = -1;
|
||
list[5].status = -1;
|
||
list[6].status = -1;
|
||
_this.list=list,
|
||
_this.showVideoControls=false
|
||
} else if (result.isEncrypt == 1) {
|
||
// 设备被加密
|
||
list[0].status = -1;
|
||
list[1].status = -1;
|
||
list[2].status = -1;
|
||
list[4].status = -1;
|
||
list[5].status = -1;
|
||
list[6].status = -1;
|
||
_this.list=list,
|
||
_this.dialogTitle='设备被加密',
|
||
_this.dialogContent='设备已被加密,无法继续查看,请前往萤石云app解密。',
|
||
_this.dialogShow=true,
|
||
_this.showVideoControls=false
|
||
} else {
|
||
// 获取设备能力
|
||
// _this.getDeviceCapacity();
|
||
_this.getDeviceCoverInfo();
|
||
}
|
||
} else if (res.data.code == '20001' || res.data.code == '20002' || res.data.code == '20018') {
|
||
// 设备不存在 / 不属于用户
|
||
this.dialogTitle='获取播放地址失败',
|
||
this.dialogContent='该用户不拥有该设备',
|
||
this.dialogShow=true
|
||
} else if (res.data.code == 10029) {
|
||
uni.showToast({
|
||
title: '个人版接口调用超限,请升级企业版',
|
||
icon: 'none'
|
||
});
|
||
} else if (res.data.code == 10002) {
|
||
uni.showToast({
|
||
title: res.data.msg,
|
||
icon: 'none'
|
||
});
|
||
setTimeout(() => {
|
||
this.pageBack();
|
||
}, 2000);
|
||
} else {
|
||
uni.showToast({
|
||
title: res.data.msg,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
/*
|
||
* 获取设备能力集
|
||
*/
|
||
getDeviceCapacity() {
|
||
const { accessToken, deviceSerial, channelNo, playVideo } = this;
|
||
var _this = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/device/capacity`,
|
||
method: 'POST',
|
||
data: {
|
||
accessToken,
|
||
deviceSerial
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log(res.data);
|
||
if (res.data.code == 200 && res.data.data) {
|
||
var result = res.data.data;
|
||
let list = this.list;
|
||
list[0].status = result.support_talk != (1 || 4) ? -1 : 0;
|
||
list[1].status = result.support_ptz == 0 ? -1 : 0;
|
||
list[2].status = result.support_talk != 1 ? -1 : 0;
|
||
list[5].status = result.ptz_top_bottom_mirror == 0 ? -1 : 0;
|
||
list[6].status = result.support_privacy == 0 ? -1 : 0;
|
||
if (!playVideo) {
|
||
// 非视频播放成功状态下
|
||
list[0].status = -1;
|
||
list[1].status = -1;
|
||
list[4].status = -1;
|
||
list[5].status = -1;
|
||
}
|
||
_this.list=list
|
||
} else if (res.data.code == '20002' || res.data.code == '20018') {
|
||
// 设备不存在 / 不属于用户
|
||
this.dialogTitle='设备被删除',
|
||
this.dialogContent='设备已从账号下删除,无法继续查看',
|
||
this.dialogShow=true
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
fullScreenFun() {
|
||
var _this = this;
|
||
livePlayerContext.requestFullScreen({
|
||
direction: 90,
|
||
success: function () {
|
||
_this.fullScreen=true
|
||
}
|
||
});
|
||
console.log('开启全屏');
|
||
},
|
||
|
||
unfullScreen() {
|
||
var _this = this;
|
||
livePlayerContext.exitFullScreen({
|
||
success: function () {
|
||
_this.fullScreen=false
|
||
}
|
||
});
|
||
console.log('开启全屏');
|
||
},
|
||
|
||
ToggleObjectFit() {
|
||
var objectFit = this.objectFit;
|
||
if(objectFit === 'contain'){
|
||
this.objectFit = 'fillCrop'
|
||
}else{
|
||
this.objectFit = 'contain'
|
||
}
|
||
},
|
||
|
||
fullscreenChange(event) {
|
||
console.log('监听到全屏变化', event);
|
||
},
|
||
|
||
getDefaultVoice: function () {
|
||
const { accessToken } = this;
|
||
var that = this;
|
||
console.info('默认语音: 第' + that.defaultVoicePage + '页');
|
||
var that = this;
|
||
that.defaultVoiceListLoading=true
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/voice/query`,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
default: 'true',
|
||
pageStart: that.defaultVoicePage,
|
||
pageSize: 5
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success(res) {
|
||
console.log(res.data);
|
||
var defaultVoiceList = that.defaultVoiceList;
|
||
defaultVoiceList = defaultVoiceList.concat(res.data.data);
|
||
if (res.data.data && res.data.data.length > 0) {
|
||
that.defaultVoiceList=defaultVoiceList,
|
||
that.defaultVoicePage=that.defaultVoicePage + 1
|
||
} else {
|
||
that.defaultVoiceNoMore=true
|
||
}
|
||
that.defaultVoiceListLoading=false
|
||
},
|
||
error(error) {
|
||
console.log(error);
|
||
that.defaultVoiceListLoading=false
|
||
}
|
||
});
|
||
},
|
||
|
||
getCustomVoice: function () {
|
||
const { accessToken } = this;
|
||
var that = this;
|
||
console.info('用户自定义语音: 第' + that.defaultVoicePage + '页');
|
||
var that = this;
|
||
that.customVoiceListLoading=true
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/voice/query`,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
default: false,
|
||
pageStart: that.defaultVoicePage,
|
||
pageSize: 5
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success(res) {
|
||
console.log(res.data);
|
||
var customVoiceList = that.customVoiceList;
|
||
customVoiceList = customVoiceList.concat(res.data.data);
|
||
if (res.data.data && res.data.data.length > 0) {
|
||
that.customVoiceList=customVoiceList,
|
||
that.customVoicePage=that.customVoicePage + 1
|
||
} else {
|
||
that.customVoiceNoMore=true
|
||
}
|
||
that.customVoiceListLoading=false
|
||
},
|
||
error(error) {
|
||
console.log(error);
|
||
that.customVoiceListLoading=false
|
||
}
|
||
});
|
||
},
|
||
|
||
// 滚动至低端事件
|
||
defaultScrollLower: function () {
|
||
var that = this;
|
||
console.info('defaultScrollLower 第' + that.defaultVoicePage + '页');
|
||
this.getDefaultVoice();
|
||
},
|
||
|
||
// 滚动至低端事件
|
||
customScrollLower: function () {
|
||
var that = this;
|
||
console.info('customScrollLower 第' + that.defaultVoicePage + '页');
|
||
this.getCustomVoice();
|
||
},
|
||
|
||
handlePlay(callback) {
|
||
console.log('handelPlay', this.playVideo, this.isHD);
|
||
console.log('播放地址:', this.isHD, this.videoHDSrc, this.videoSrc);
|
||
let { list } = this;
|
||
this.checkNetWork();
|
||
livePlayerContext.play({
|
||
success: () => {
|
||
// playVideo: true,
|
||
this.showVideoControls=true,
|
||
// videoLoadingStatus: 100,
|
||
this.videoNetWorkError=false
|
||
if (callback && typeof callback === 'function') {
|
||
callback();
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
this.checkNetWork();
|
||
uni.showToast({
|
||
title: '网络异常',
|
||
icon: 'none'
|
||
});
|
||
console.log('开始播放失败');
|
||
list[4].status = 0;
|
||
this.videoNetWorkError=true,
|
||
this.showVideoControls=false,
|
||
this.videoLoadingStatus=100,
|
||
this.list=[...list]
|
||
console.log(this.list,'lisr')
|
||
}
|
||
});
|
||
},
|
||
|
||
handleStop(callback) {
|
||
console.log('stop');
|
||
let { list } = this;
|
||
livePlayerContext.stop({
|
||
success: () => {
|
||
list[1].status = -1;
|
||
this.playVideo=false
|
||
this.videoLoadingStatus=0
|
||
this.list=list
|
||
this.panelStatus=0
|
||
if (callback && typeof callback === 'function') {
|
||
callback();
|
||
}
|
||
},
|
||
fail: (error) => {
|
||
console.log('停止播放失败');
|
||
}
|
||
});
|
||
},
|
||
|
||
autoHideControl() {
|
||
console.log('showHdSelect', this.showHDSelect);
|
||
const _this = this;
|
||
clearTimeout(this.autoHideTimer);
|
||
this.autoHideTimer = setTimeout(() => {
|
||
const { showHDSelect } = _this;
|
||
if (!showHDSelect) {
|
||
this.showVideoControls=false
|
||
}
|
||
}, 5000);
|
||
},
|
||
|
||
handleSound(e) {
|
||
var openSound = this.openSound;
|
||
this.openSound=!openSound
|
||
},
|
||
|
||
handleHD(e) {
|
||
var showHDSelect = this.showHDSelect;
|
||
console.log('handleHD', showHDSelect);
|
||
this.showHDSelect=!showHDSelect
|
||
},
|
||
|
||
changeVideoHD(e) {
|
||
var _this = this;
|
||
this.showHDSelect=false,
|
||
this.isHD=true
|
||
this.handleStop(_this.handlePlay);
|
||
},
|
||
|
||
changeVideoNormal(e) {
|
||
this.showHDSelect=false,
|
||
this.isHD=false
|
||
this.handleStop(this.handlePlay);
|
||
},
|
||
|
||
statechange(e) {
|
||
console.log('live-player code:', e.detail.code, e.detail);
|
||
const { code } = e.detail;
|
||
let { videoLoadingStatus, panelStatus } = this;
|
||
const { list } = this;
|
||
switch (code) {
|
||
case 2007:
|
||
//启动loading
|
||
videoLoadingStatus = 0;
|
||
this.playVideo=true,
|
||
this.videoLoadingStatus=0
|
||
break;
|
||
case 2001:
|
||
//连接服务器
|
||
videoLoadingStatus = 20 + Math.floor(Math.random() * 10 + 1);
|
||
console.log(videoLoadingStatus,'进度')
|
||
break;
|
||
case 2002:
|
||
//已经连接 RTMP 服务器,开始拉流
|
||
videoLoadingStatus = 40 + Math.floor(Math.random() * 10 + 1);
|
||
console.log(videoLoadingStatus,'进度')
|
||
break;
|
||
case 2008:
|
||
// 解码器启动
|
||
console.log(videoLoadingStatus,'进度')
|
||
break;
|
||
case 2009:
|
||
//视频分辨率改动
|
||
console.log(videoLoadingStatus,'进度')
|
||
break;
|
||
case 2004:
|
||
// 视频播放开始
|
||
videoLoadingStatus = 80 + Math.floor(Math.random() * 10 + 1);
|
||
console.log(videoLoadingStatus,'进度')
|
||
break;
|
||
case 2003:
|
||
//网络接收到首个视频数据包(IDR)
|
||
videoLoadingStatus = 100;
|
||
// let {list} = this.data;
|
||
list[4].status = 0;
|
||
this.playVideo=true,
|
||
this.list=[...list]
|
||
this.autoHideControl();
|
||
this.getDeviceCapacity();
|
||
break;
|
||
case 2103:
|
||
//网络断连, 已启动自动重连(本小程序不自动重连)
|
||
// videoLoadingStatus = 100;
|
||
// this.handleStop();
|
||
// 获取设备状态
|
||
// this.getDeviceInfo();
|
||
break;
|
||
case 3001:
|
||
case 3002:
|
||
case 3003:
|
||
case 3005:
|
||
// 播放失败
|
||
videoLoadingStatus = 100;
|
||
// this.getDeviceInfo();
|
||
this.checkNetWork();
|
||
this.handleStop(this.playError);
|
||
list[5].status = -1;
|
||
list[4].status = -1;
|
||
// playVideo: false,
|
||
this.showVideoControls=false,
|
||
this.videoNetWorkError=true,
|
||
this.videoLoadingStatus=100,
|
||
this.list=list
|
||
break;
|
||
case -2301:
|
||
// 经多次重连抢救无效,更多重试请自行重启播放
|
||
videoLoadingStatus = 100;
|
||
list[5].status = -1;
|
||
list[4].status = -1;
|
||
// playVideo: false,
|
||
this.showVideoControls=false,
|
||
this.videoNetWorkError=true,
|
||
this.videoLoadingStatus=100,
|
||
this.list=[...list]
|
||
break;
|
||
}
|
||
this.videoLoadingStatus=videoLoadingStatus
|
||
},
|
||
|
||
playError() {
|
||
this.showVideoControls=false,
|
||
this.videoNetWorkError=true,
|
||
this.videoLoadingStatus=100
|
||
// this.getPlayUrl();
|
||
this.getDeviceInfo();
|
||
this.getDeviceCoverInfo();
|
||
},
|
||
|
||
error(e) {
|
||
console.log('live-player', e);
|
||
console.error('live-player error:', e.detail.errMsg);
|
||
if (e.detail.errCode == 10001) {
|
||
uni.showToast({
|
||
title: '视频直播对讲需要你手机授权微信录音或麦克风权限',
|
||
icon: 'none',
|
||
duration: 3000
|
||
});
|
||
}
|
||
},
|
||
|
||
onVideoTap(e) {
|
||
console.log('点击视频');
|
||
const { deviceOffline, showVideoControls, panelStatus, videoNetWorkError } = this;
|
||
if (deviceOffline || panelStatus === 4 || videoNetWorkError) {
|
||
return false;
|
||
}
|
||
if (showVideoControls) {
|
||
this.showVideoControls=false
|
||
clearTimeout(this.autoHideTimer);
|
||
} else {
|
||
this.showVideoControls=true
|
||
this.autoHideControl();
|
||
}
|
||
},
|
||
|
||
tapPanel(event) {
|
||
const { accessToken, deviceSerial, channelNo } = this;
|
||
var tValue = event.currentTarget.dataset.value;
|
||
var list = this.list;
|
||
var panelStatus = this.panelStatus;
|
||
switch (tValue) {
|
||
case 'talk':
|
||
if (list[0].status === -1) {
|
||
return false;
|
||
}
|
||
panelStatus = 5;
|
||
this.initTalk();
|
||
break;
|
||
case 'ptz':
|
||
if (list[1].status === -1) {
|
||
return false;
|
||
}
|
||
panelStatus = 1;
|
||
break;
|
||
case 'voice':
|
||
if (list[2].status === -1) {
|
||
return false;
|
||
}
|
||
panelStatus = 2;
|
||
this.getDefaultVoice();
|
||
this.getCustomVoice();
|
||
break;
|
||
case 'playback':
|
||
this.goToLive();
|
||
break;
|
||
case 'capture':
|
||
if (list[4].status === -1) {
|
||
return false;
|
||
}
|
||
this.screenShoot();
|
||
break;
|
||
case 'mirror':
|
||
if (list[5].status === -1) {
|
||
return false;
|
||
}
|
||
if (panelStatus === 3) {
|
||
panelStatus = 0;
|
||
list[4].status = 0;
|
||
list[5].status = 0;
|
||
this.sceneMirror(2);
|
||
} else {
|
||
panelStatus = 3;
|
||
this.sceneMirror(2);
|
||
}
|
||
break;
|
||
case 'cover':
|
||
if (list[6].status === -1) {
|
||
return false;
|
||
}
|
||
if (panelStatus === 4) {
|
||
// 镜头遮蔽中
|
||
this.sceneCover(0);
|
||
} else {
|
||
this.sceneCover(1);
|
||
}
|
||
break;
|
||
default:
|
||
panelStatus = 0;
|
||
}
|
||
this.panelStatus=panelStatus,
|
||
this.list=list
|
||
},
|
||
|
||
startRecord(e) {
|
||
recorderManager.start(options);
|
||
},
|
||
|
||
stopRecord(e) {
|
||
recorderManager.stop();
|
||
},
|
||
|
||
handleBackPanel(event) {
|
||
var tValue = event.currentTarget.dataset.value;
|
||
this.panelStatus=0
|
||
},
|
||
|
||
handlePtzTouchStart(event) {
|
||
// var { offsetLeft, offsetTop } = event.currentTarget;
|
||
// var {clientX,clientY} = event.touches[0];
|
||
var { ptzStatus, ptzLoading } = this;
|
||
// var centerLeft = 104 + offsetLeft;
|
||
// var centerTop = 104 + offsetTop;
|
||
// var left = clientX - centerLeft;
|
||
// var top = clientY - centerTop;
|
||
uni.createSelectorQuery()
|
||
.in(uni)
|
||
.select('#ptz-img-container')
|
||
.boundingClientRect((rect) => {
|
||
let { clientX, clientY } = event.touches[0];
|
||
let rectLeft = rect.left;
|
||
let rectTop = rect.top;
|
||
var centerLeft = 104 + rectLeft;
|
||
var centerTop = 104 + rectTop;
|
||
var left = clientX - centerLeft;
|
||
var top = clientY - centerTop;
|
||
console.log('点击了页面方位:pageY', clientY);
|
||
console.log('云盘位置:top', rect.top);
|
||
if (ptzLoading) {
|
||
uni.showToast({
|
||
title: '操作过于频繁,建议长按转动',
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
if (Math.abs(left) > Math.abs(top)) {
|
||
if (left > 0) {
|
||
this.handlePtzControl(3);
|
||
ptzStatus = 4;
|
||
} else {
|
||
ptzStatus = 3;
|
||
this.handlePtzControl(2);
|
||
}
|
||
} else {
|
||
if (top > 0) {
|
||
ptzStatus = 2;
|
||
this.handlePtzControl(1);
|
||
} else {
|
||
ptzStatus = 1;
|
||
this.handlePtzControl(0);
|
||
}
|
||
}
|
||
this.ptzStatus=ptzStatus
|
||
})
|
||
.exec();
|
||
},
|
||
|
||
handlePtzTouchEnd(event) {
|
||
let { clientX, clientY } = event.changedTouches[0];
|
||
const _this = this;
|
||
uni.createSelectorQuery()
|
||
.in(uni)
|
||
.select('#ptz-img-container')
|
||
.boundingClientRect((rect) => {
|
||
let rectLeft = rect.left;
|
||
let rectTop = rect.top;
|
||
var centerLeft = 104 + rectLeft;
|
||
var centerTop = 104 + rectTop;
|
||
var left = clientX - centerLeft;
|
||
var top = clientY - centerTop;
|
||
if (Math.abs(left) > Math.abs(top)) {
|
||
if (left > 0) {
|
||
_this.handlePtzControl(3, 'stop');
|
||
} else {
|
||
_this.handlePtzControl(2, 'stop');
|
||
}
|
||
} else {
|
||
if (top > 0) {
|
||
_this.handlePtzControl(1, 'stop');
|
||
} else {
|
||
_this.handlePtzControl(0, 'stop');
|
||
}
|
||
}
|
||
})
|
||
.exec();
|
||
this.ptzStatus=0
|
||
},
|
||
|
||
handlePtzControl(position, type) {
|
||
const { accessToken, deviceSerial, channelNo, ptzLoading } = this;
|
||
let ptzLimit = '';
|
||
const ptzTopImgSuccess = '../../static/live/images/yuntai/top.png';
|
||
const ptzTopImgFailed = '../../static/live/images/yuntai/top_limit.png';
|
||
const ptzDownImgSuccess = '../../static/live/images/yuntai/down.png';
|
||
const ptzDownImgFailed = '../../static/live/images/yuntai/down_limit.png';
|
||
const ptzLeftImgSuccess = '../../static/live/images/yuntai/left.png';
|
||
const ptzLeftImgFailed = '../../static/live/images/yuntai/left_limit.png';
|
||
const ptzRightImgSuccess = '../../static/live/images/yuntai/right.png';
|
||
const ptzRightImgFailed = '../../static/live/images/yuntai/right_limit.png';
|
||
const ptzNormalImg = '../../static/live/images/yuntai/normal.png';
|
||
let ptzStatus = this.ptzStatus;
|
||
let currentPtzImg = this.currentPtzImg;
|
||
var url = `${OPEN_DOMAIN}${PTZ_START}`;
|
||
if (type == 'stop') {
|
||
url = `${OPEN_DOMAIN}${PTZ_STOP}`;
|
||
}
|
||
if (ptzLoading && type === 'start') {
|
||
uni.showToast({
|
||
title: '操作过于频繁,建议长按转动',
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
this.ptzLoading=true
|
||
uni.request({
|
||
url: url,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
deviceSerial: deviceSerial,
|
||
channelNo: channelNo,
|
||
direction: position,
|
||
speed: 1
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
const code = res.data.code;
|
||
console.log('云台控制', res.data);
|
||
if (code == 10029) {
|
||
uni.showToast({
|
||
title: '个人版接口调用超限,请升级企业版',
|
||
icon: 'none'
|
||
});
|
||
} else if (code != 200) {
|
||
uni.showToast({
|
||
title: res.data.msg,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
if (type == 'stop') {
|
||
ptzStatus = 0;
|
||
currentPtzImg = ptzNormalImg;
|
||
} else {
|
||
switch (position) {
|
||
case 0:
|
||
ptzStatus = 1;
|
||
currentPtzImg = code == 200 ? ptzTopImgSuccess : ptzTopImgFailed;
|
||
ptzLimit = code == 200 ? '' : 'top';
|
||
break;
|
||
case 1:
|
||
ptzStatus = 2;
|
||
currentPtzImg = code == 200 ? ptzDownImgSuccess : ptzDownImgFailed;
|
||
ptzLimit = code == 200 ? '' : 'down';
|
||
break;
|
||
case 2:
|
||
ptzStatus = 3;
|
||
currentPtzImg = code == 200 ? ptzLeftImgSuccess : ptzLeftImgFailed;
|
||
ptzLimit = code == 200 ? '' : 'left';
|
||
break;
|
||
case 3:
|
||
ptzStatus = 4;
|
||
currentPtzImg = code == 200 ? ptzRightImgSuccess : ptzRightImgFailed;
|
||
ptzLimit = code == 200 ? '' : 'right';
|
||
break;
|
||
default:
|
||
ptzStatus = 0;
|
||
currentPtzImg = ptzTopImgSuccess;
|
||
ptzLimit = '';
|
||
}
|
||
}
|
||
this.ptzStatu=ptzStatus,
|
||
this.currentPtzImg=currentPtzImg,
|
||
this.ptzLoading=false,
|
||
this.ptzLimit=ptzLimit
|
||
},
|
||
error: (err) => {
|
||
this.ptzLoading=false
|
||
}
|
||
});
|
||
},
|
||
|
||
screenShoot(e) {
|
||
const { playVideo, videoLoadingStatus } = this;
|
||
if (!playVideo || videoLoadingStatus != 100) {
|
||
console.log('非播放状态下点击截图');
|
||
return false;
|
||
}
|
||
console.log('开始截图');
|
||
// livePlayerContext.snapshot('raw');
|
||
let that = this;
|
||
uni.getSetting({
|
||
success(res) {
|
||
if (res.authSetting['scope.writePhotosAlbum']) {
|
||
that.saveImg();
|
||
} else if (res.authSetting['scope.writePhotosAlbum'] === undefined) {
|
||
uni.authorize({
|
||
scope: 'scope.writePhotosAlbum',
|
||
success() {
|
||
that.saveImg();
|
||
},
|
||
fail() {
|
||
that.authConfirm();
|
||
}
|
||
});
|
||
} else {
|
||
that.authConfirm();
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
saveImg() {
|
||
livePlayerContext
|
||
.snapshot('raw')
|
||
.then((data) => {
|
||
console.log('data', data);
|
||
if (data) {
|
||
console.log(data);
|
||
uni.saveImageToPhotosAlbum({
|
||
filePath: data.tempImagePath,
|
||
success(res) {
|
||
console.log('保存成功', res);
|
||
uni.showToast({
|
||
title: '截图已保存至手机相册',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
}
|
||
})
|
||
.catch((err) => {
|
||
console.log('err', err);
|
||
});
|
||
},
|
||
|
||
// 授权拒绝后,再次授权提示弹窗
|
||
authConfirm() {
|
||
let that = this;
|
||
uni.showModal({
|
||
content: '您没打开保存图片权限,是否去设置打开?',
|
||
confirmText: '确认',
|
||
cancelText: '取消',
|
||
success: function (res) {
|
||
if (res.confirm) {
|
||
uni.openSetting({
|
||
success: (res) => {
|
||
if (res.authSetting['scope.writePhotosAlbum']) {
|
||
that.saveImg();
|
||
} else {
|
||
uni.showToast({
|
||
title: '您没有授权,无法保存到相册',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
} else {
|
||
uni.showToast({
|
||
title: '您没有授权,无法保存到相册',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
|
||
// 镜头遮蔽
|
||
sceneCover(enable) {
|
||
var _this = this;
|
||
let { deviceSerial, channelNo, accessToken, coverInterval, list, panelStatus, videoNetWorkError } = this;
|
||
if (coverInterval) {
|
||
uni.showToast({
|
||
title: '操作过于频繁',
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
this.coverInterval=true
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/device/scene/switch/set`,
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
deviceSerial: deviceSerial,
|
||
channelNo: channelNo,
|
||
enable: enable
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log(res.data);
|
||
if (res.data.code == 200) {
|
||
if (enable == 0) {
|
||
list[6].status = 0;
|
||
panelStatus = 0;
|
||
videoNetWorkError = false;
|
||
this.getDeviceCapacity();
|
||
this.handlePlay();
|
||
} else {
|
||
list[0].status = -1;
|
||
list[1].status = -1;
|
||
list[2].status = -1;
|
||
list[4].status = -1;
|
||
list[5].status = -1;
|
||
panelStatus = 4;
|
||
list[6].status = 1;
|
||
// this.handleStop();
|
||
this.playVideo=false
|
||
}
|
||
setTimeout(() => {
|
||
this.coverInterval=false
|
||
}, 5000);
|
||
this.videoNetWorkError=videoNetWorkError,
|
||
this.list=list,
|
||
this.panelStatus=panelStatus
|
||
} else {
|
||
uni.showToast({
|
||
title: res.data.msg,
|
||
icon: 'none'
|
||
});
|
||
this.coverInterval=false,
|
||
this.panelStatus=panelStatus
|
||
}
|
||
},
|
||
fail: (res) => {
|
||
uni.showToast({
|
||
title: '网络异常',
|
||
icon: 'none'
|
||
});
|
||
this.coverInterval=false
|
||
}
|
||
});
|
||
},
|
||
|
||
// 镜象翻转
|
||
sceneMirror(enable) {
|
||
const { deviceSerial, channelNo, accessToken, mirrorInterval } = this;
|
||
var _this = this;
|
||
if (mirrorInterval) {
|
||
uni.showToast({
|
||
title: '操作过于频繁',
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
this.mirrorInterval=true
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/device/ptz/mirror`,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
deviceSerial: deviceSerial,
|
||
channelNo: channelNo,
|
||
command: enable
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log(res.data);
|
||
if (res.data.code == 200) {
|
||
setTimeout(() => {
|
||
this.mirrorInterval=false
|
||
}, 5000);
|
||
} else {
|
||
uni.showToast({
|
||
title: res.data.msg,
|
||
icon: 'none'
|
||
});
|
||
this.mirrorInterval=false
|
||
}
|
||
},
|
||
fail: (res) => {
|
||
uni.showToast({
|
||
title: '网络异常',
|
||
icon: 'none'
|
||
});
|
||
this.mirrorInterval=false
|
||
}
|
||
});
|
||
},
|
||
|
||
playVoice(event) {
|
||
const { accessToken, deviceSerial, channelNo } = this;
|
||
var { duration, voiceName, fileUrl } = event.currentTarget.dataset.value;
|
||
var type = event.currentTarget.dataset.type;
|
||
console.log('type', type);
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/voice/send`,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
deviceSerial: deviceSerial,
|
||
channelNo: channelNo,
|
||
fileUrl: fileUrl
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log(res.data);
|
||
const { list } = this;
|
||
if (res.data.code == 200) {
|
||
list[2].status = 1;
|
||
if(type === 'custom'){
|
||
this.activeCustomVoiceName = voiceName
|
||
}else{
|
||
this.activeCustomVoiceName = ''
|
||
}
|
||
if(type === 'default'){
|
||
this.activeDefaultVoiceName = voiceName
|
||
}else{
|
||
this.activeDefaultVoiceName = ''
|
||
}
|
||
this.list=list
|
||
setTimeout(() => {
|
||
list[2].status = 0;
|
||
this.activeCustomVoiceName='',
|
||
this.activeDefaultVoiceName='',
|
||
this.list=list
|
||
}, duration * 1000);
|
||
} else if (res.data.code == '111012') {
|
||
// 设备正忙
|
||
uni.showToast({
|
||
title: '语音下发失败',
|
||
icon: 'none'
|
||
});
|
||
} else if (res.data.code == '20007') {
|
||
// 设备正忙
|
||
uni.showToast({
|
||
title: '设备不在线',
|
||
icon: 'none'
|
||
});
|
||
} else if (res.data.code == '20008') {
|
||
// 设备正忙
|
||
uni.showToast({
|
||
title: '设备响应超时',
|
||
icon: 'none'
|
||
});
|
||
} else {
|
||
uni.showToast({
|
||
title: res.data.msg,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
fail: (res) => {
|
||
uni.showToast({
|
||
title: '网络异常',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
speakStart(event) {
|
||
let recoderTime = this.recoderTime;
|
||
this.recoderTime=59
|
||
recorderManager.start(options);
|
||
uni.showToast({
|
||
icon: 'none',
|
||
duration: 60000,
|
||
image: '../../static/live/images/voice_talk4.png',
|
||
title: '剩余' + recoderTime
|
||
});
|
||
this.recoderTimer = setInterval(() => {
|
||
if (recoderTime > 0) {
|
||
--recoderTime;
|
||
uni.showToast({
|
||
icon: 'none',
|
||
duration: 60000,
|
||
image: '../../static/live/images/voice_talk4.png',
|
||
title: '剩余' + recoderTime
|
||
});
|
||
this.recoderTime=recoderTime
|
||
} else {
|
||
clearInterval(this.recoderTimer);
|
||
this.speakEnd();
|
||
}
|
||
// console.log("recoderTime", recoderTime)
|
||
}, 1000);
|
||
},
|
||
|
||
speakEnd(event) {
|
||
uni.hideToast();
|
||
let recoderTime = this.recoderTime;
|
||
if (recoderTime >= 59) {
|
||
uni.showToast({
|
||
title: '时间太短了',
|
||
icon: 'none'
|
||
});
|
||
recorderManager.stop();
|
||
clearInterval(this.recoderTimer);
|
||
return false;
|
||
} else {
|
||
uni.hideToast();
|
||
recorderManager.stop();
|
||
clearInterval(this.recoderTimer);
|
||
}
|
||
},
|
||
|
||
tapDialogButton(e) {
|
||
this.dialogShow=false
|
||
this.pageBack();
|
||
},
|
||
|
||
pageBack() {
|
||
const { scene } = this;
|
||
console.log('scene', scene);
|
||
// wx.showToast({
|
||
// title: `code ${scene}`,
|
||
// })
|
||
if ([1007, 1008, 1036, 1037, 1038, 1044].indexOf(scene) === -1) {
|
||
uni.navigateBack({
|
||
delta: 1
|
||
});
|
||
} else {
|
||
uni.reLaunch({
|
||
url: '/pages/home/home'
|
||
});
|
||
}
|
||
},
|
||
|
||
// 通过二位码进入直播,通过uuid获取accessToken、deviceSerial、channelNo
|
||
getWxaInfo: function () {
|
||
const { pathParam } = this;
|
||
console.log('getWxaInfo:', pathParam);
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/device/getWxaInfo`,
|
||
//仅为示例,并非真实的接口地址
|
||
method: 'POST',
|
||
data: {
|
||
uuid: pathParam
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log('通过uuid获取', res.data);
|
||
const res1 = res.data.data;
|
||
if (res1) {
|
||
this.accessToken=res1.accessToken,
|
||
this.deviceSerial=res1.deviceSerial,
|
||
this.channelNo=res1.channelNo
|
||
this.getPlayUrl();
|
||
this.getDeviceInfo();
|
||
this.getDeviceCoverInfo();
|
||
}
|
||
},
|
||
fail: (res) => {
|
||
console.log('获取accesstoken失败');
|
||
// wx.showToast({
|
||
// title: '网络异常',
|
||
// icon: 'none'
|
||
// })
|
||
}
|
||
});
|
||
},
|
||
|
||
// 返回回放
|
||
goToLive() {
|
||
const { accessToken, deviceSerial, channelNo } = this;
|
||
let url = '/packDetail1/pages/device/playback?accessToken=' + accessToken + '&deviceSerial=' + deviceSerial + '&channelNo=' + channelNo;
|
||
uni.navigateTo({
|
||
url: url
|
||
});
|
||
},
|
||
|
||
// 对讲-------------------
|
||
// 退出对讲
|
||
exitTalk() {
|
||
this.panelStatus=0,
|
||
this.talkStatus=0,
|
||
this.pushUrl=''
|
||
this.websocketClose();
|
||
},
|
||
|
||
// 进入对讲pannel
|
||
initTalk() {
|
||
// 初始化当前用户id
|
||
const uuid = wxuuid();
|
||
this.customId=uuid,
|
||
this.talkStatus=1
|
||
this.startTalk();
|
||
},
|
||
|
||
// 重试
|
||
tryTalkAgain() {
|
||
// 初始化当前用户id
|
||
const uuid = wxuuid();
|
||
this.customId=uuid,
|
||
this.talkStatus=1
|
||
this.websocketClose();
|
||
this.startTalk();
|
||
},
|
||
|
||
// 开始对讲-①获取设备能力集
|
||
startTalk() {
|
||
const { accessToken, deviceSerial } = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/lapp/device/capacity`,
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
deviceSerial: deviceSerial
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log('设备能力集:', res.data);
|
||
if (res.data.code == 200) {
|
||
const supportTalk = res.data.data.support_talk;
|
||
console.log('supportTalk', supportTalk);
|
||
if (supportTalk == 1 || 4) {
|
||
// 设备能力集中为1/4表示支持全双工
|
||
this.getToken();
|
||
}
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.log(err);
|
||
}
|
||
});
|
||
},
|
||
|
||
// 对讲-②获取两个取流token
|
||
getToken() {
|
||
const { accessToken } = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/user/token`,
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
count: 2,
|
||
clientType: '0',
|
||
featureCode: '123',
|
||
osVersion: '2.3.6',
|
||
sdkVersion: 'v.1.0.20140720',
|
||
netType: 'UNKNOWN'
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log('取流token:', res.data);
|
||
if (res.data.resultCode == 200) {
|
||
const tokens = res.data.tokenArray;
|
||
if (tokens && tokens.length == 2) {
|
||
this.token1=tokens[0],
|
||
this.token2=tokens[1]
|
||
// 创建房间,并查询房间信息
|
||
this.orderRoom();
|
||
}
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.log(err);
|
||
}
|
||
});
|
||
},
|
||
|
||
// 对讲-③创建房间并查询房间信息
|
||
orderRoom() {
|
||
// 检查网络状态
|
||
this.checkNetWork();
|
||
const { customId, accessToken } = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/v3/conference/gen`,
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
customId: customId
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded'
|
||
},
|
||
success: (res) => {
|
||
console.log('预定创建并查询房间信息:', res.data);
|
||
const code = res.data.code || res.data.meta.code;
|
||
if (code == 200) {
|
||
const clientId = res.data.clientId;
|
||
const conferenceInfos = res.data.conferenceInfos;
|
||
const roomId = conferenceInfos.roomId;
|
||
const vtmHttpsAddress = conferenceInfos.vtmHttpsAddress;
|
||
const vtmAddress = conferenceInfos.vtmAddress;
|
||
let vtmIp = '';
|
||
let vtmPort = '';
|
||
if (vtmAddress && vtmAddress.length > 0) {
|
||
vtmIp = vtmAddress.split(':')[0];
|
||
vtmPort = vtmAddress.split(':')[1];
|
||
}
|
||
this.clientId=clientId,
|
||
this.vtmHttpsAddress=vtmHttpsAddress,
|
||
this.vtmIp=vtmIp,
|
||
this.vtmPort=vtmPort,
|
||
this.roomId=roomId
|
||
this.advanceDevice();
|
||
} else if (code == '10002') {
|
||
uni.showToast({
|
||
title: 'accessToken过期或异常',
|
||
icon: 'none'
|
||
});
|
||
} else if (code == '404') {
|
||
uni.showToast({
|
||
title: '未找到房间信息',
|
||
icon: 'none'
|
||
});
|
||
} else {
|
||
uni.showToast({
|
||
title: '创建房间-' + res.data.meta.message,
|
||
icon: 'none'
|
||
});
|
||
this.talkStatus=3 // 启动失败
|
||
}
|
||
},
|
||
|
||
fail: (err) => {
|
||
console.log(err);
|
||
}
|
||
});
|
||
},
|
||
|
||
// 对讲-④邀请设备入会
|
||
advanceDevice() {
|
||
const { accessToken, roomId, deviceSerial, channelNo, token1 } = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/v3/conference/device/invite`,
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
roomId: roomId,
|
||
deviceSerial: deviceSerial,
|
||
channelNo: channelNo,
|
||
mode: 2,
|
||
authType: 5,
|
||
token: token1
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log('邀请设备入会:', res.data);
|
||
if (res.data.meta.code == 200) {
|
||
console.log('邀请设备入会成功,加入模式为对讲模式');
|
||
this.getWebsocket();
|
||
} else {
|
||
if (res.data.meta.code == 80502) {
|
||
this.talkStatus=4 // 设备对讲中
|
||
} else {
|
||
uni.showToast({
|
||
title: '邀请设备入会-' + res.data.meta.message,
|
||
icon: 'none'
|
||
});
|
||
this.talkStatus=3 // 启动失败
|
||
}
|
||
}
|
||
},
|
||
|
||
fail: (err) => {
|
||
console.log(err);
|
||
this.talkStatus=3 // 启动失败
|
||
}
|
||
});
|
||
},
|
||
|
||
// 对讲-⑤强制设备退会
|
||
exitDevice() {
|
||
const { accessToken, roomId, deviceSerial, channelNo, token2 } = this;
|
||
uni.request({
|
||
url: `${OPEN_DOMAIN}/api/v3/conference/device/kickout`,
|
||
method: 'POST',
|
||
data: {
|
||
accessToken: accessToken,
|
||
roomId: roomId,
|
||
deviceSerial: deviceSerial,
|
||
channelNo: channelNo,
|
||
authType: 5,
|
||
token: token2
|
||
},
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log('强制设备退出房间:', res.data);
|
||
if (res.data.meta.code == 200) {
|
||
console.log('强制设备退出房间成功');
|
||
// this.getWebsocket()
|
||
} else {
|
||
uni.showToast({
|
||
title: '强制设备退出房间-' + res.data.meta.message,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.log(err);
|
||
}
|
||
});
|
||
},
|
||
|
||
// 获取websocket地址
|
||
getWebsocket() {
|
||
const { vtmHttpsAddress, roomId } = this;
|
||
const url = 'https://' + vtmHttpsAddress + '/vtm/rtmpavc/' + roomId; // 注意:此处需配置小程序域名
|
||
// const url = 'https://vtmrcgnginx.ys7.com:8555/vtm/rtmpavc/' + roomId;
|
||
uni.request({
|
||
url: url,
|
||
method: 'GET',
|
||
header: {
|
||
'content-type': 'application/x-www-form-urlencoded' // 默认值
|
||
},
|
||
|
||
success: (res) => {
|
||
console.log('vtm连接返回:', res.data);
|
||
if (res.data.retcode == 0 || res.data.retcode == 200) {
|
||
const wsurl = 'wss://' + res.data.serverAddr + ':' + res.data.serverPort + '/rtmpavc'; // 注意:此处需配置小程序域名
|
||
this.websocketUrl=wsurl,
|
||
this.rtmpAddress=res.data.serverAddr
|
||
this.socketConfig();
|
||
} else if (res.data.retcode == 503) {
|
||
uni.showToast({
|
||
title: '无可用服务',
|
||
icon: 'none'
|
||
});
|
||
this.talkStatus=3 // 启动失败
|
||
|
||
this.exitDevice();
|
||
} else {
|
||
console.log('获取websocket地址失败', res.data.retcode);
|
||
uni.showToast({
|
||
title: '获取websocket地址失败',
|
||
icon: 'none'
|
||
});
|
||
this.talkStatus=3 // 启动失败
|
||
|
||
this.exitDevice();
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
console.log('获取websocket地址失败', err);
|
||
uni.showToast({
|
||
title: '获取websocket地址失败',
|
||
icon: 'none'
|
||
});
|
||
this.talkStatus=3 // 启动失败
|
||
}
|
||
});
|
||
},
|
||
|
||
// 配置socket
|
||
socketConfig() {
|
||
let that = this;
|
||
const { websocketUrl, clientId } = this;
|
||
if (Object.keys(app.globalData.socketTask).length <= 0 || app.globalData.socketTask.readyState === 3) {
|
||
console.log('开始连接socket');
|
||
// socketTask的属性值readyState,取下面4个状态值:CONNECTING: 0 连接中 、OPEN: 1 已连接 、CLOSING: 2 关闭中 、CLOSED: 3 已关闭
|
||
// 直接判断SocketTask对象的属性值readyState,如果是1的话就表示连接可用。
|
||
console.log('websocketURL:', websocketUrl);
|
||
let socketTask = null;
|
||
const websocketConnect = new Promise((resolve, reject) => {
|
||
socketTask = uni.connectSocket({
|
||
url: websocketUrl,
|
||
// url: 'wss://test12jsdecoder.ys7.com/?version=0.1&cipherSuites=0&sessionID=',
|
||
success: (res) => {
|
||
console.log('connectSocket连接中', res);
|
||
uni.showToast({
|
||
title: 'websocket连接中,请稍候',
|
||
icon: 'none'
|
||
});
|
||
resolve(res);
|
||
},
|
||
fail: (err) => {
|
||
console.log('connectSocket失败', err);
|
||
uni.showToast({
|
||
title: 'webSocket连接失败',
|
||
icon: 'none'
|
||
});
|
||
this.talkStatus=3 // 启动失败
|
||
|
||
this.exitDevice();
|
||
}
|
||
});
|
||
});
|
||
websocketConnect.then((data) => {
|
||
if (data.errMsg == 'connectSocket:ok') {
|
||
app.globalData.socketTask = socketTask;
|
||
that.initEventHandle();
|
||
}
|
||
});
|
||
}
|
||
},
|
||
|
||
// 初始化websocket事件
|
||
initEventHandle() {
|
||
const socketTask = app.globalData.socketTask;
|
||
const that = this;
|
||
const { clientId, roomId, joinPassword, vtmIp, vtmPort, rtmpAddress } = this;
|
||
socketTask.onOpen((res) => {
|
||
console.log('监听WebSocket开启', res);
|
||
// 打开bav会话
|
||
let param = {
|
||
cmdType: 1000,
|
||
vtmIp: vtmIp,
|
||
vtmPort: parseInt(vtmPort),
|
||
clientId: clientId,
|
||
roomId: roomId,
|
||
authentication: `roomId=${roomId}&password=${joinPassword}`,
|
||
mode: 2
|
||
};
|
||
if (socketTask.readyState === 1) {
|
||
uni.sendSocketMessage({
|
||
data: JSON.stringify(param)
|
||
});
|
||
console.log('websocket发送给服务端的消息:', param);
|
||
}
|
||
});
|
||
socketTask.onMessage((res) => {
|
||
console.log('接收到WebSocket消息', res);
|
||
if (res.data) {
|
||
const result = JSON.parse(res.data);
|
||
if (result.cmdType == '1003') {
|
||
// 微信小程序推流
|
||
const rtmpPort = result.rtmpPort;
|
||
if (result.rtmpIp) {
|
||
const rtmpIp = result.rtmpIp;
|
||
} else {
|
||
const rtmpIp = rtmpAddress;
|
||
}
|
||
const pushUrl = `rtmp://${rtmpIp}:${rtmpPort}/livestream/wechat?roomId=${roomId}&pushClientId=${clientId}`;
|
||
console.log('推流地址:', pushUrl);
|
||
that.pushUrl=pushUrl,
|
||
that.talkStatus=2 // 对讲
|
||
var pushcontext = uni.createLivePusherContext('livePusher', this);
|
||
that.pushVideoContext=pushcontext
|
||
// 开始推流
|
||
that.startPush();
|
||
} else if (result.cmdType == '1002') {
|
||
// 微信小程序拉流
|
||
|
||
const playClientId = result.playClientId;
|
||
const rtmpPort = result.rtmpPort;
|
||
if (result.rtmpIp) {
|
||
const rtmpIp = result.rtmpIp;
|
||
} else {
|
||
const rtmpIp = rtmpAddress;
|
||
}
|
||
const playSrc = `rtmp://${rtmpIp}:${rtmpPort}/livestream/wechat?roomId=${roomId}&pushClientId=${clientId}&playClientId=${playClientId}`;
|
||
console.log('拉流地址:', playSrc);
|
||
this.talkStatus=2,
|
||
// 对讲
|
||
this.playSrc=playSrc
|
||
const playcontext = uni.createLivePlayerContext('talk-liveplayer', that);
|
||
console.log('当前拉流窗口~~~~~', playcontext);
|
||
// 播放
|
||
that.autoPlay(playcontext);
|
||
} else if (result.cmdType == '1005') {
|
||
// 某端退出房间
|
||
const curPlayClientId = 'paly' + result.playClientId;
|
||
uni.showToast({
|
||
title: result.playClientId + '将关闭',
|
||
icon: 'none'
|
||
});
|
||
this.talkStatus=3 // 对讲失败
|
||
// 对应播放端停止播放
|
||
console.log('当前退出窗口', curPlayClientId);
|
||
} else if (result.cmdType == '1001') {
|
||
const code = result.code;
|
||
const msg = result.msg;
|
||
console.log('websocket返回的msg', code + '-->' + msg);
|
||
let content = '';
|
||
switch (code) {
|
||
case 0:
|
||
break;
|
||
case 10001:
|
||
content = 'websocket请求参数错误';
|
||
break;
|
||
case 10002:
|
||
content = 'flv转封装失败';
|
||
break;
|
||
case 10003:
|
||
content = 'rtp转封装失败';
|
||
break;
|
||
case 10004:
|
||
content = 'rtmp读写错误 ';
|
||
break;
|
||
case 10005:
|
||
content = 'rtmp接收缓存溢出';
|
||
break;
|
||
case 6:
|
||
content = '连接sts服务失败';
|
||
break;
|
||
case 11:
|
||
content = '无效的房间号';
|
||
break;
|
||
case 14:
|
||
content = 'sts连接vtm失败';
|
||
break;
|
||
case 17:
|
||
content = '房间已满';
|
||
break;
|
||
case 18:
|
||
content = 'auth认证失败';
|
||
break;
|
||
case 35:
|
||
content = '房间号不存在';
|
||
break;
|
||
}
|
||
if (content) {
|
||
uni.showToast({
|
||
title: content,
|
||
icon: 'none',
|
||
duration: 300
|
||
});
|
||
// 关闭websocket
|
||
that.websocketClose();
|
||
this.talkStatus=3 // 对讲失败
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
socketTask.onClose((res) => {
|
||
console.log('WebSocket已关闭', res);
|
||
uni.showToast({
|
||
title: 'websocket连接已关闭',
|
||
icon: 'none'
|
||
});
|
||
// 监听到websocket关闭,关闭推流
|
||
this.stopPush();
|
||
// 强制
|
||
this.exitDevice();
|
||
this.talkStatus=3 // 对讲失败
|
||
});
|
||
|
||
socketTask.onError((err) => {
|
||
console.log('WebSocket连接失败', err);
|
||
uni.showToast({
|
||
title: 'WebSocket连接失败',
|
||
icon: 'none'
|
||
});
|
||
// 强制
|
||
this.exitDevice();
|
||
this.talkStatus=3 // 对讲失败
|
||
});
|
||
},
|
||
|
||
// 关闭websocket
|
||
websocketClose() {
|
||
// 关闭websocket连接
|
||
const socketTask = app.globalData.socketTask;
|
||
const that = this;
|
||
this.exitDevice();
|
||
const { clientId, roomId } = this;
|
||
if (Object.keys(socketTask).length === 0) {
|
||
return;
|
||
}
|
||
|
||
// 关闭bav会话
|
||
let param = {
|
||
cmdType: 1004,
|
||
// 通知服务端断开websocket
|
||
clientId: clientId,
|
||
roomId: roomId
|
||
};
|
||
uni.sendSocketMessage({
|
||
data: JSON.stringify(param)
|
||
});
|
||
console.log('websocket发送给服务端的消息:', param);
|
||
socketTask.close({
|
||
code: 1000,
|
||
success: (res) => {
|
||
console.log('websocket已断开连接', res);
|
||
}
|
||
});
|
||
},
|
||
|
||
// push
|
||
pusherStateChange: function (e) {
|
||
console.log('>>> live-pusher onPushStateChange:', e.detail.code);
|
||
const code = e.detail.code;
|
||
let content = '';
|
||
switch (code) {
|
||
case 1001:
|
||
console.log('code', code, '已经连接推流服务器');
|
||
break;
|
||
case 1002:
|
||
console.log('code', code, '已经与服务器握手完毕,开始推流');
|
||
break;
|
||
case 1003:
|
||
console.log('code', code, '打开摄像头成功');
|
||
break;
|
||
case 1004:
|
||
console.log('code', code, '录屏启动成功');
|
||
break;
|
||
case 1005:
|
||
console.log('code', code, '推流动态调整分辨率');
|
||
break;
|
||
case 1006:
|
||
console.log('code', code, '推流动态调整码率');
|
||
break;
|
||
case 1007:
|
||
console.log('code', code, '首帧画面采集完成, 推流成功');
|
||
break;
|
||
case 1008:
|
||
console.log('code', code, '编码器启动');
|
||
break;
|
||
case -1301:
|
||
console.log('code', code, '打开摄像头失败');
|
||
break;
|
||
case -1302:
|
||
console.log('code', code, '打开麦克风失败');
|
||
break;
|
||
case -1303:
|
||
console.log('code', code, '视频编码失败');
|
||
break;
|
||
case -1304:
|
||
console.log('code', code, '音频编码失败');
|
||
break;
|
||
case -1306:
|
||
console.log('code', code, '不支持的音频采样率');
|
||
break;
|
||
case -1307:
|
||
console.log('code', code, '网络断连,且经多次重连抢救无效,更多重试请自行重启推流');
|
||
content = '网络出错,推流失败';
|
||
break;
|
||
case -1308:
|
||
console.log('code', code, '开始录屏失败,可能是被用户拒绝');
|
||
break;
|
||
case -1309:
|
||
console.log('code', code, '录屏失败,不支持的Android系统版本,需要5.0以上的系统');
|
||
break;
|
||
case -1310:
|
||
console.log('code', code, '录屏被其他应用打断了');
|
||
break;
|
||
case -1311:
|
||
console.log('code', code, 'Android Mic打开成功,但是录不到音频数据');
|
||
break;
|
||
case -1312:
|
||
console.log('code', code, '录屏动态切横竖屏失败');
|
||
break;
|
||
case 1101:
|
||
console.log('code', code, '网络状况不佳:上行带宽太小,上传数据受阻');
|
||
break;
|
||
case 1102:
|
||
console.log('code', code, '网络断连, 已启动自动重连');
|
||
break;
|
||
case 1103:
|
||
console.log('code', code, '硬编码启动失败,采用软编码');
|
||
break;
|
||
case 1104:
|
||
console.log('code', code, '视频编码失败');
|
||
break;
|
||
case 1105 || 1106:
|
||
console.log('code', code, '新美颜软编码启动失败,采用老的软编码');
|
||
break;
|
||
case 3001:
|
||
console.log('code', code, 'RTMP -DNS解析失败');
|
||
break;
|
||
case 3002:
|
||
console.log('code', code, 'RTMP服务器连接失败');
|
||
break;
|
||
case 3003:
|
||
console.log('code', code, 'RTMP服务器握手失败');
|
||
break;
|
||
case 3004:
|
||
console.log('code', code, 'RTMP服务器主动断开,请检查推流地址的合法性或防盗链有效期');
|
||
break;
|
||
case 3005:
|
||
console.log('code', code, 'RTMP 读/写失败');
|
||
break;
|
||
default:
|
||
console.log('未知错误');
|
||
break;
|
||
}
|
||
uni.showToast({
|
||
title: content,
|
||
icon: 'none'
|
||
});
|
||
},
|
||
|
||
stopPush() {
|
||
console.log('停止推流');
|
||
this.pushVideoContext.stop();
|
||
this.pushVideo=false
|
||
},
|
||
|
||
startPush() {
|
||
console.log('开始推流,推流地址为:', this.pushUrl);
|
||
uni.showToast({
|
||
title: '开始推流,推流地址为:'
|
||
});
|
||
uni.showToast({
|
||
title: this.pushUrl
|
||
});
|
||
this.pushVideoContext.start();
|
||
this.pushVideo=true
|
||
},
|
||
|
||
error(e) {
|
||
console.log('live-player', e);
|
||
console.error('live-player error:', e.detail.errMsg);
|
||
if (e.detail.errCode == 10001) {
|
||
uni.showToast({
|
||
title: '视频播放或录制需要你手机授权微信录音或麦克风权限',
|
||
icon: 'none',
|
||
duration: 3000
|
||
});
|
||
}
|
||
},
|
||
|
||
autoPlay(curPlayerContext) {
|
||
this.checkNetWork();
|
||
curPlayerContext.play({
|
||
success: () => {
|
||
console.log('播放对讲音频成功');
|
||
},
|
||
fail: (error) => {
|
||
console.log('播放对讲音频失败:', error);
|
||
}
|
||
});
|
||
},
|
||
|
||
currentPlayContainerFun() {
|
||
console.log('占位:函数 _currentPlayContainer 未声明');
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style lang="scss">
|
||
@import './live.css';
|
||
</style> |