mirror of
https://gitee.com/pan648540858/wvp-GB28181-pro.git
synced 2026-06-22 02:57:49 +08:00
支持拉框放大和拉框缩小
This commit is contained in:
parent
3417244705
commit
70bc01bd90
@ -170,7 +170,7 @@ public class DeviceControl {
|
||||
@Parameter(name = "lengthx", description = "拉框长度像素值", required = true)
|
||||
@Parameter(name = "lengthy", description = "拉框宽度像素值", required = true)
|
||||
@GetMapping("drag_zoom/zoom_in")
|
||||
public DeferredResult<WVPResult<String>> dragZoomIn(@RequestParam String deviceId, String channelId,
|
||||
public void dragZoomIn(@RequestParam String deviceId, String channelId,
|
||||
@RequestParam int length,
|
||||
@RequestParam int width,
|
||||
@RequestParam int midpointx,
|
||||
@ -182,28 +182,20 @@ public class DeviceControl {
|
||||
}
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
Assert.notNull(device, "设备不存在");
|
||||
DeferredResult<WVPResult<String>> result = new DeferredResult<>();
|
||||
deviceService.dragZoomIn(device, channelId, length, width, midpointx, midpointy, lengthx,lengthy, (code, msg, data) -> {
|
||||
result.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
result.onTimeout(() -> {
|
||||
log.warn("[设备拉框放大] 操作超时, 设备未返回应答指令, {}", deviceId);
|
||||
result.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答"));
|
||||
});
|
||||
return result;
|
||||
deviceService.dragZoomIn(device, channelId, length, width, midpointx, midpointy, lengthx,lengthy);
|
||||
}
|
||||
|
||||
@Operation(summary = "拉框缩小", security = @SecurityRequirement(name = JwtUtils.HEADER))
|
||||
@Parameter(name = "deviceId", description = "设备国标编号", required = true)
|
||||
@Parameter(name = "channelId", description = "通道国标编号")
|
||||
@Parameter(name = "length", description = "播放窗口长度像素值", required = true)
|
||||
@Parameter(name = "width", description = "拉框中心的横轴坐标像素值", required = true)
|
||||
@Parameter(name = "width", description = "播放窗口宽像素值", required = true)
|
||||
@Parameter(name = "midpointx", description = "拉框中心的横轴坐标像素值", required = true)
|
||||
@Parameter(name = "midpointy", description = "拉框中心的纵轴坐标像素值", required = true)
|
||||
@Parameter(name = "lengthx", description = "拉框长度像素值", required = true)
|
||||
@Parameter(name = "lengthy", description = "拉框宽度像素值", required = true)
|
||||
@GetMapping("/drag_zoom/zoom_out")
|
||||
public DeferredResult<WVPResult<String>> dragZoomOut(@RequestParam String deviceId,
|
||||
public void dragZoomOut(@RequestParam String deviceId,
|
||||
@RequestParam(required = false) String channelId,
|
||||
@RequestParam int length,
|
||||
@RequestParam int width,
|
||||
@ -217,14 +209,6 @@ public class DeviceControl {
|
||||
}
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
Assert.notNull(device, "设备不存在");
|
||||
DeferredResult<WVPResult<String>> result = new DeferredResult<>();
|
||||
deviceService.dragZoomOut(device, channelId, length, width, midpointx, midpointy, lengthx,lengthy, (code, msg, data) -> {
|
||||
result.setResult(new WVPResult<>(code, msg, data));
|
||||
});
|
||||
result.onTimeout(() -> {
|
||||
log.warn("[设备拉框放大] 操作超时, 设备未返回应答指令, {}", deviceId);
|
||||
result.setResult(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答"));
|
||||
});
|
||||
return result;
|
||||
deviceService.dragZoomOut(device, channelId, length, width, midpointx, midpointy, lengthx,lengthy);
|
||||
}
|
||||
}
|
||||
|
||||
@ -185,9 +185,9 @@ public interface IDeviceService {
|
||||
|
||||
void homePosition(Device device, String channelId, Boolean enabled, Integer resetTime, Integer presetIndex, ErrorCallback<String> callback);
|
||||
|
||||
void dragZoomIn(Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy, ErrorCallback<String> callback);
|
||||
void dragZoomIn(Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy);
|
||||
|
||||
void dragZoomOut(Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy, ErrorCallback<String> callback);
|
||||
void dragZoomOut(Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy);
|
||||
|
||||
void deviceStatus(Device device, ErrorCallback<String> callback);
|
||||
|
||||
|
||||
@ -1276,7 +1276,7 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragZoomIn(Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy, ErrorCallback<String> callback) {
|
||||
public void dragZoomIn(Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy) {
|
||||
if (!userSetting.getServerId().equals(device.getServerId())) {
|
||||
redisRpcService.dragZoomIn(device.getServerId(), device, channelId, length, width, midpointx, midpointy, lengthx, lengthy);
|
||||
return;
|
||||
@ -1292,16 +1292,15 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
cmdXml.append("<LengthY>" + lengthy+ "</LengthY>\r\n");
|
||||
cmdXml.append("</DragZoomIn>\r\n");
|
||||
try {
|
||||
sipCommander.dragZoomCmd(device, channelId, cmdXml.toString(), callback);
|
||||
sipCommander.dragZoomCmd(device, channelId, cmdXml.toString());
|
||||
} catch (InvalidArgumentException | SipException | ParseException e) {
|
||||
log.error("[命令发送失败] 拉框放大: {}", e.getMessage());
|
||||
callback.run(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage(), null);
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragZoomOut(Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy, ErrorCallback<String> callback) {
|
||||
public void dragZoomOut(Device device, String channelId, int length, int width, int midpointx, int midpointy, int lengthx, int lengthy) {
|
||||
if (!userSetting.getServerId().equals(device.getServerId())) {
|
||||
redisRpcService.dragZoomOut(device.getServerId(), device, channelId, length, width, midpointx, midpointy, lengthx, lengthy);
|
||||
return;
|
||||
@ -1317,10 +1316,9 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
cmdXml.append("<LengthY>" + lengthy+ "</LengthY>\r\n");
|
||||
cmdXml.append("</DragZoomOut>\r\n");
|
||||
try {
|
||||
sipCommander.dragZoomCmd(device, channelId, cmdXml.toString(), callback);
|
||||
sipCommander.dragZoomCmd(device, channelId, cmdXml.toString());
|
||||
} catch (InvalidArgumentException | SipException | ParseException e) {
|
||||
log.error("[命令发送失败] 拉框放大: {}", e.getMessage());
|
||||
callback.run(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage(), null);
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@ -293,7 +293,7 @@ public interface ISIPCommander {
|
||||
* @param channelId 通道id
|
||||
* @param cmdString 前端控制指令串
|
||||
*/
|
||||
void dragZoomCmd(Device device, String channelId, String cmdString, ErrorCallback<String> callback) throws InvalidArgumentException, SipException, ParseException;
|
||||
void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException;
|
||||
|
||||
|
||||
void playbackControlCmd(Device device, DeviceChannel channel, String stream, String content, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent) throws SipException, InvalidArgumentException, ParseException;
|
||||
|
||||
@ -1292,7 +1292,7 @@ public class SIPCommander implements ISIPCommander {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dragZoomCmd(Device device, String channelId, String cmdString, ErrorCallback<String> callback) throws InvalidArgumentException, SipException, ParseException {
|
||||
public void dragZoomCmd(Device device, String channelId, String cmdString) throws InvalidArgumentException, SipException, ParseException {
|
||||
|
||||
String cmdType = "DeviceControl";
|
||||
int sn = (int) ((Math.random() * 9 + 1) * 100000);
|
||||
@ -1311,9 +1311,6 @@ public class SIPCommander implements ISIPCommander {
|
||||
dragXml.append(cmdString);
|
||||
dragXml.append("</Control>\r\n");
|
||||
|
||||
MessageEvent<String> messageEvent = MessageEvent.getInstance(cmdType, sn + "", channelId, 1000L, callback);
|
||||
messageSubscribe.addSubscribe(messageEvent);
|
||||
|
||||
Request request = headerProvider.createMessageRequest(device, dragXml.toString(), SipUtils.getNewViaTag(), SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
|
||||
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()),request);
|
||||
}
|
||||
|
||||
@ -390,9 +390,7 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
|
||||
cmdXml.append("<LengthX>" + dragZoom.getLengthX() + "</LengthX>\r\n");
|
||||
cmdXml.append("<LengthY>" + dragZoom.getLengthY() + "</LengthY>\r\n");
|
||||
cmdXml.append("</" + type.getVal() + ">\r\n");
|
||||
cmder.dragZoomCmd(device, deviceChannel.getDeviceId(), cmdXml.toString(), (code, msg, data) -> {
|
||||
|
||||
});
|
||||
cmder.dragZoomCmd(device, deviceChannel.getDeviceId(), cmdXml.toString());
|
||||
responseAck(request, Response.OK);
|
||||
} catch (Exception e) {
|
||||
log.error("[命令发送失败] 拉框控制: {}", e.getMessage());
|
||||
|
||||
@ -332,18 +332,15 @@ public class RedisRpcDeviceController extends RpcController {
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
deviceService.dragZoomIn(device, channelId, length, width, midpointx, midpointy, lengthx, lengthy, (code, msg, data) -> {
|
||||
response.setStatusCode(ErrorCode.SUCCESS.getCode());
|
||||
response.setBody(new WVPResult<>(code, msg, data));
|
||||
// 手动发送结果
|
||||
sendResponse(response);
|
||||
});
|
||||
deviceService.dragZoomIn(device, channelId, length, width, midpointx, midpointy, lengthx, lengthy);
|
||||
}catch (ControllerException e) {
|
||||
response.setStatusCode(e.getCode());
|
||||
response.setBody(WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMsg()));
|
||||
sendResponse(response);
|
||||
return response;
|
||||
}
|
||||
return null;
|
||||
response.setStatusCode(ErrorCode.SUCCESS.getCode());
|
||||
response.setBody(WVPResult.success());
|
||||
return response;
|
||||
}
|
||||
|
||||
@RedisRpcMapping("dragZoomOut")
|
||||
@ -367,18 +364,15 @@ public class RedisRpcDeviceController extends RpcController {
|
||||
return response;
|
||||
}
|
||||
try {
|
||||
deviceService.dragZoomOut(device, channelId, length, width, midpointx, midpointy, lengthx, lengthy, (code, msg, data) -> {
|
||||
response.setStatusCode(ErrorCode.SUCCESS.getCode());
|
||||
response.setBody(new WVPResult<>(code, msg, data));
|
||||
// 手动发送结果
|
||||
sendResponse(response);
|
||||
});
|
||||
deviceService.dragZoomOut(device, channelId, length, width, midpointx, midpointy, lengthx, lengthy);
|
||||
}catch (ControllerException e) {
|
||||
response.setStatusCode(e.getCode());
|
||||
response.setBody(WVPResult.fail(ErrorCode.ERROR100.getCode(), e.getMsg()));
|
||||
sendResponse(response);
|
||||
return response;
|
||||
}
|
||||
return null;
|
||||
response.setStatusCode(ErrorCode.SUCCESS.getCode());
|
||||
response.setBody(WVPResult.success());
|
||||
return response;
|
||||
}
|
||||
|
||||
@RedisRpcMapping("alarm")
|
||||
|
||||
@ -283,3 +283,19 @@ export function getRegisterTimeStatistics({ deviceId, count }) {
|
||||
})
|
||||
}
|
||||
|
||||
export function dragZoomIn(params) {
|
||||
return request({
|
||||
method: 'get',
|
||||
url: '/api/device/control/drag_zoom/zoom_in',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
export function dragZoomOut(params) {
|
||||
return request({
|
||||
method: 'get',
|
||||
url: '/api/device/control/drag_zoom/zoom_out',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
204
web/src/mixins/dragZoom.js
Normal file
204
web/src/mixins/dragZoom.js
Normal file
@ -0,0 +1,204 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
dragGridEnabled: true,
|
||||
overlayCanvas: null,
|
||||
overlayCtx: null,
|
||||
dragActive: false,
|
||||
dragStart: null,
|
||||
dragCurrent: null,
|
||||
dragVideoRect: null,
|
||||
dragCallback: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dragRect() {
|
||||
if (!this.dragStart || !this.dragCurrent) return null
|
||||
return {
|
||||
left: Math.min(this.dragStart.x, this.dragCurrent.x),
|
||||
top: Math.min(this.dragStart.y, this.dragCurrent.y),
|
||||
width: Math.abs(this.dragCurrent.x - this.dragStart.x),
|
||||
height: Math.abs(this.dragCurrent.y - this.dragStart.y)
|
||||
}
|
||||
},
|
||||
dragInfo() {
|
||||
if (!this.dragRect) return null
|
||||
return {
|
||||
midX: Math.round(this.dragRect.left + this.dragRect.width / 2),
|
||||
midY: Math.round(this.dragRect.top + this.dragRect.height / 2),
|
||||
width: Math.round(this.dragRect.width),
|
||||
height: Math.round(this.dragRect.height)
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
this._removeCanvas()
|
||||
},
|
||||
methods: {
|
||||
getVideoElement() {
|
||||
return null
|
||||
},
|
||||
_ensureCanvas() {
|
||||
this._removeCanvas()
|
||||
const videoRect = this.getVideoRect()
|
||||
if (!videoRect) return null
|
||||
const parentRect = this.$el.getBoundingClientRect()
|
||||
const w = Math.round(videoRect.width)
|
||||
const h = Math.round(videoRect.height)
|
||||
const canvas = document.createElement('canvas')
|
||||
canvas.style.position = 'absolute'
|
||||
canvas.style.left = (videoRect.left - parentRect.left) + 'px'
|
||||
canvas.style.top = (videoRect.top - parentRect.top) + 'px'
|
||||
canvas.style.width = w + 'px'
|
||||
canvas.style.height = h + 'px'
|
||||
canvas.width = w
|
||||
canvas.height = h
|
||||
canvas.style.zIndex = '999'
|
||||
canvas.style.pointerEvents = 'none'
|
||||
console.log('this.dragGridEnabled: ' + this.dragGridEnabled)
|
||||
if (this.dragGridEnabled) {
|
||||
console.log('加载网格背景')
|
||||
canvas.style.backgroundImage =
|
||||
'linear-gradient(rgba(64, 158, 255, 0.3) 1px, transparent 2px),' +
|
||||
'linear-gradient(90deg, rgba(64, 158, 255, 0.3) 1px, transparent 2px)'
|
||||
canvas.style.backgroundSize = '25px 25px'
|
||||
canvas.style.border = '2px solid #409EFF'
|
||||
}
|
||||
|
||||
this.$el.appendChild(canvas)
|
||||
console.log(this.$el)
|
||||
const ctx = canvas.getContext('2d')
|
||||
this.overlayCanvas = canvas
|
||||
this.overlayCtx = ctx
|
||||
return { canvas, ctx }
|
||||
},
|
||||
_removeCanvas() {
|
||||
this._unbindDragEvents()
|
||||
if (this.overlayCanvas && this.overlayCanvas.parentNode) {
|
||||
this.overlayCanvas.parentNode.removeChild(this.overlayCanvas)
|
||||
}
|
||||
this.overlayCanvas = null
|
||||
this.overlayCtx = null
|
||||
},
|
||||
_bindDragEvents() {
|
||||
const c = this.overlayCanvas
|
||||
if (!c) return
|
||||
c.style.pointerEvents = 'auto'
|
||||
c.style.cursor = 'crosshair'
|
||||
c.addEventListener('mousedown', this._onDragMouseDown)
|
||||
c.addEventListener('mousemove', this._onDragMove)
|
||||
c.addEventListener('mouseup', this._onDragEnd)
|
||||
c.addEventListener('mouseleave', this._onDragEnd)
|
||||
},
|
||||
_unbindDragEvents() {
|
||||
const c = this.overlayCanvas
|
||||
if (!c) return
|
||||
c.style.pointerEvents = 'none'
|
||||
c.style.cursor = 'default'
|
||||
c.removeEventListener('mousedown', this._onDragMouseDown)
|
||||
c.removeEventListener('mousemove', this._onDragMove)
|
||||
c.removeEventListener('mouseup', this._onDragEnd)
|
||||
c.removeEventListener('mouseleave', this._onDragEnd)
|
||||
},
|
||||
_drawOverlay() {
|
||||
const ctx = this.overlayCtx
|
||||
const canvas = this.overlayCanvas
|
||||
if (!ctx || !canvas) return
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height)
|
||||
if (this.dragRect) {
|
||||
this._drawDragRect(ctx)
|
||||
}
|
||||
},
|
||||
_drawDragRect(ctx) {
|
||||
const r = this.dragRect
|
||||
if (!r) return
|
||||
ctx.strokeStyle = '#409EFF'
|
||||
ctx.lineWidth = 2
|
||||
ctx.setLineDash([6, 3])
|
||||
ctx.fillStyle = 'rgba(64, 158, 255, 0.15)'
|
||||
ctx.beginPath()
|
||||
ctx.rect(r.left, r.top, r.width, r.height)
|
||||
ctx.fill()
|
||||
ctx.stroke()
|
||||
ctx.setLineDash([])
|
||||
const info = this.dragInfo
|
||||
if (!info) return
|
||||
const text = '\u4E2D\u5FC3: (' + info.midX + ', ' + info.midY + ') \u5927\u5C0F: ' + info.width + ' \u00D7 ' + info.height
|
||||
ctx.font = '12px sans-serif'
|
||||
const textW = ctx.measureText(text).width
|
||||
const labelW = textW + 16
|
||||
const labelH = 22
|
||||
const labelX = r.left
|
||||
const labelY = r.top + r.height + 6
|
||||
ctx.fillStyle = 'rgba(0, 0, 0, 0.7)'
|
||||
ctx.fillRect(labelX, labelY, labelW, labelH)
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.fillText(text, labelX + 8, labelY + 15)
|
||||
},
|
||||
startDragZoom(callback) {
|
||||
this._ensureCanvas()
|
||||
this._bindDragEvents()
|
||||
this.dragCallback = callback || null
|
||||
this.dragActive = true
|
||||
this.dragStart = null
|
||||
this.dragCurrent = null
|
||||
this.dragVideoRect = null
|
||||
},
|
||||
_onDragMouseDown(e) {
|
||||
if (!this.dragActive) return
|
||||
e.preventDefault()
|
||||
const videoRect = this.getVideoRect()
|
||||
if (!videoRect) return
|
||||
this.dragVideoRect = videoRect
|
||||
this.dragStart = {
|
||||
x: e.clientX - videoRect.left,
|
||||
y: e.clientY - videoRect.top
|
||||
}
|
||||
this.dragCurrent = { ...this.dragStart }
|
||||
this._drawOverlay()
|
||||
},
|
||||
_onDragMove(e) {
|
||||
if (!this.dragActive || !this.dragStart || !this.dragVideoRect) return
|
||||
e.preventDefault()
|
||||
this.dragCurrent = {
|
||||
x: e.clientX - this.dragVideoRect.left,
|
||||
y: e.clientY - this.dragVideoRect.top
|
||||
}
|
||||
this._drawOverlay()
|
||||
},
|
||||
_onDragEnd() {
|
||||
if (!this.dragActive) return
|
||||
if (!this.dragStart || !this.dragCurrent) return
|
||||
const sx = Math.min(this.dragStart.x, this.dragCurrent.x)
|
||||
const sy = Math.min(this.dragStart.y, this.dragCurrent.y)
|
||||
const ex = Math.max(this.dragStart.x, this.dragCurrent.x)
|
||||
const ey = Math.max(this.dragStart.y, this.dragCurrent.y)
|
||||
const rectW = ex - sx
|
||||
const rectH = ey - sy
|
||||
if (rectW < 10 || rectH < 10) {
|
||||
this._resetDrag()
|
||||
return
|
||||
}
|
||||
if (this.dragCallback) {
|
||||
this.dragCallback({
|
||||
length: Math.round(this.dragVideoRect.width),
|
||||
width: Math.round(this.dragVideoRect.height),
|
||||
midpointx: Math.round(sx + rectW / 2),
|
||||
midpointy: Math.round(sy + rectH / 2),
|
||||
lengthx: Math.round(rectW),
|
||||
lengthy: Math.round(rectH)
|
||||
})
|
||||
}
|
||||
this._resetDrag()
|
||||
},
|
||||
_resetDrag() {
|
||||
this._unbindDragEvents()
|
||||
this.dragActive = false
|
||||
this.dragStart = null
|
||||
this.dragCurrent = null
|
||||
this.dragVideoRect = null
|
||||
this.dragCallback = null
|
||||
this._removeCanvas()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -7,6 +7,7 @@ import {
|
||||
startScan, stopCruise,
|
||||
stopScan, wiper
|
||||
} from '@/api/frontEnd'
|
||||
import { dragZoomIn, dragZoomOut } from '@/api/device'
|
||||
|
||||
const actions = {
|
||||
setSpeedForScan({ commit }, params) {
|
||||
@ -208,6 +209,26 @@ const actions = {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
dragZoomIn({ commit }, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dragZoomIn(params).then(response => {
|
||||
const { data } = response
|
||||
resolve(data)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
},
|
||||
dragZoomOut({ commit }, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
dragZoomOut(params).then(response => {
|
||||
const { data } = response
|
||||
resolve(data)
|
||||
}).catch(error => {
|
||||
reject(error)
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -38,8 +38,10 @@ const h265webPlayer = {}
|
||||
* @see https://github.com/numberwolf/h265web.js/blob/master/example_normal/index.js
|
||||
*/
|
||||
const token = 'base64:QXV0aG9yOmNoYW5neWFubG9uZ3xudW1iZXJ3b2xmLEdpdGh1YjpodHRwczovL2dpdGh1Yi5jb20vbnVtYmVyd29sZixFbWFpbDpwb3JzY2hlZ3QyM0Bmb3htYWlsLmNvbSxRUTo1MzEzNjU4NzIsSG9tZVBhZ2U6aHR0cDovL3h2aWRlby52aWRlbyxEaXNjb3JkOm51bWJlcndvbGYjODY5NCx3ZWNoYXI6bnVtYmVyd29sZjExLEJlaWppbmcsV29ya0luOkJhaWR1'
|
||||
import dragZoom from '../../mixins/dragZoom'
|
||||
export default {
|
||||
name: 'H265web',
|
||||
mixins: [dragZoom],
|
||||
props: ['videoUrl', 'error', 'hasAudio', 'height', 'showButton'],
|
||||
data() {
|
||||
return {
|
||||
@ -247,6 +249,12 @@ export default {
|
||||
},
|
||||
setPlaybackRate: function(speed) {
|
||||
h265webPlayer[this._uid].setPlaybackRate(speed)
|
||||
},
|
||||
getVideoElement() {
|
||||
return this.$refs.playerBox
|
||||
},
|
||||
getVideoRect() {
|
||||
return this.getVideoElement().getBoundingClientRect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,39 +1,41 @@
|
||||
<template>
|
||||
<div
|
||||
ref="container"
|
||||
style="width:100%; height: 100%; background-color: #000000;margin:0 auto;position: relative;"
|
||||
@dblclick="fullscreenSwich"
|
||||
@mouseenter="showBar = true" @mouseleave="showBar = false"
|
||||
>
|
||||
<div id="buttonsBox" class="buttons-box" v-if="showButton === undefined || showButton" :style="{ opacity: showBar ? 1 : 0, pointerEvents: showBar ? 'auto' : 'none' }">
|
||||
<div class="buttons-box-left">
|
||||
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick" />
|
||||
<i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause" />
|
||||
<i class="iconfont icon-stop jessibuca-btn" @click="stop" />
|
||||
<i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="mute()" />
|
||||
<i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="cancelMute()" />
|
||||
</div>
|
||||
<div class="buttons-box-right">
|
||||
<span class="jessibuca-btn">{{ kBps }} kb/s</span>
|
||||
<!-- <i class="iconfont icon-file-record1 jessibuca-btn"></i>-->
|
||||
<!-- <i class="iconfont icon-xiangqing2 jessibuca-btn" ></i>-->
|
||||
<i
|
||||
class="iconfont icon-camera1196054easyiconnet jessibuca-btn"
|
||||
style="font-size: 1rem !important"
|
||||
@click="screenshot"
|
||||
/>
|
||||
<i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick" />
|
||||
<i v-if="!fullscreen" class="iconfont icon-weibiaoti10 jessibuca-btn" @click="fullscreenSwich" />
|
||||
<i v-if="fullscreen" class="iconfont icon-weibiaoti11 jessibuca-btn" @click="fullscreenSwich" />
|
||||
<div
|
||||
ref="container"
|
||||
style="width:100%; height: 100%; background-color: #000000;margin:0 auto;position: relative;"
|
||||
@dblclick="fullscreenSwich"
|
||||
@mouseenter="showBar = true" @mouseleave="showBar = false"
|
||||
>
|
||||
<div id="buttonsBox" class="buttons-box" v-if="showButton === undefined || showButton" :style="{ opacity: showBar ? 1 : 0, pointerEvents: showBar ? 'auto' : 'none' }">
|
||||
<div class="buttons-box-left">
|
||||
<i v-if="!playing" class="iconfont icon-play jessibuca-btn" @click="playBtnClick" />
|
||||
<i v-if="playing" class="iconfont icon-pause jessibuca-btn" @click="pause" />
|
||||
<i class="iconfont icon-stop jessibuca-btn" @click="stop" />
|
||||
<i v-if="isNotMute" class="iconfont icon-audio-high jessibuca-btn" @click="mute()" />
|
||||
<i v-if="!isNotMute" class="iconfont icon-audio-mute jessibuca-btn" @click="cancelMute()" />
|
||||
</div>
|
||||
<div class="buttons-box-right">
|
||||
<span class="jessibuca-btn">{{ kBps }} kb/s</span>
|
||||
<!-- <i class="iconfont icon-file-record1 jessibuca-btn"></i>-->
|
||||
<!-- <i class="iconfont icon-xiangqing2 jessibuca-btn" ></i>-->
|
||||
<i
|
||||
class="iconfont icon-camera1196054easyiconnet jessibuca-btn"
|
||||
style="font-size: 1rem !important"
|
||||
@click="screenshot"
|
||||
/>
|
||||
<i class="iconfont icon-shuaxin11 jessibuca-btn" @click="playBtnClick" />
|
||||
<i v-if="!fullscreen" class="iconfont icon-weibiaoti10 jessibuca-btn" @click="fullscreenSwich" />
|
||||
<i v-if="fullscreen" class="iconfont icon-weibiaoti11 jessibuca-btn" @click="fullscreenSwich" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const jessibucaPlayer = {}
|
||||
import dragZoom from '../../mixins/dragZoom'
|
||||
export default {
|
||||
name: 'Jessibuca',
|
||||
mixins: [dragZoom],
|
||||
props: ['videoUrl', 'error', 'hasAudio', 'height', 'showButton'],
|
||||
data() {
|
||||
return {
|
||||
@ -280,6 +282,14 @@ export default {
|
||||
if (jessibucaPlayer[this._uid]) {
|
||||
jessibucaPlayer[this._uid].resize()
|
||||
}
|
||||
},
|
||||
getVideoElement() {
|
||||
return this.$refs.container.querySelector('canvas')
|
||||
},
|
||||
getVideoRect() {
|
||||
const container = this.$refs.container
|
||||
const canvas = this.getVideoElement()
|
||||
return canvas ? canvas.getBoundingClientRect() : container.getBoundingClientRect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,6 +102,16 @@ export default {
|
||||
if (this.$refs[this.activePlayer]) {
|
||||
this.$refs[this.activePlayer].pause()
|
||||
}
|
||||
},
|
||||
getVideoRect() {
|
||||
const player = this.$refs[this.activePlayer]
|
||||
return player && player.getVideoRect ? player.getVideoRect() : null
|
||||
},
|
||||
startDragZoom(callback) {
|
||||
const player = this.$refs[this.activePlayer]
|
||||
if (player && player.startDragZoom) {
|
||||
player.startDragZoom(callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -47,9 +47,12 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="ptz-func-row">
|
||||
<div class="ptz-func-btn" title="拉框放大" @mousedown.prevent="$emit('iris-move', { command: 'in', speed: controSpeed })" @mouseup.prevent="$emit('iris-stop')">
|
||||
<div class="ptz-func-btn" title="拉框放大" @click="$emit('toggle-drag-zoom')">
|
||||
<i class="iconfont icon-guangquan" /><span>拉框放大</span>
|
||||
</div>
|
||||
<div class="ptz-func-btn" title="拉框缩小" @click="$emit('toggle-drag-zoom-out')">
|
||||
<i class="iconfont icon-guangquan-" /><span>拉框缩小</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -8,8 +8,10 @@
|
||||
|
||||
<script>
|
||||
let webrtcPlayer = null
|
||||
import dragZoom from '../../mixins/dragZoom'
|
||||
export default {
|
||||
name: 'RtcPlayer',
|
||||
mixins: [dragZoom],
|
||||
props: {
|
||||
videoUrl: { type: String, default: '' },
|
||||
error: { default: '' },
|
||||
@ -21,7 +23,6 @@ export default {
|
||||
timer: null
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {},
|
||||
destroyed() {
|
||||
clearTimeout(this.timer)
|
||||
@ -82,6 +83,35 @@ export default {
|
||||
console.log('player 事件回调')
|
||||
console.log(type)
|
||||
console.log(message)
|
||||
},
|
||||
getVideoElement() {
|
||||
return document.getElementById('webRtcPlayerBox')
|
||||
},
|
||||
getVideoRect() {
|
||||
const video = this.getVideoElement()
|
||||
const rect = video.getBoundingClientRect()
|
||||
if (video.videoWidth && video.videoHeight) {
|
||||
const natRatio = video.videoWidth / video.videoHeight
|
||||
const disRatio = rect.width / rect.height
|
||||
let w, h, x, y
|
||||
if (natRatio > disRatio) {
|
||||
w = rect.width
|
||||
h = w / natRatio
|
||||
x = 0
|
||||
y = (rect.height - h) / 2
|
||||
} else {
|
||||
h = rect.height
|
||||
w = h * natRatio
|
||||
x = (rect.width - w) / 2
|
||||
y = 0
|
||||
}
|
||||
return {
|
||||
left: rect.left + x, top: rect.top + y,
|
||||
right: rect.left + x + w, bottom: rect.top + y + h,
|
||||
width: w, height: h
|
||||
}
|
||||
}
|
||||
return rect
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,6 +124,7 @@ export default {
|
||||
#rtcPlayer{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
#webRtcPlayerBox{
|
||||
width: 100%;
|
||||
|
||||
@ -191,6 +191,8 @@
|
||||
设备录像控制-停止</el-dropdown-item>
|
||||
<el-dropdown-item command="ptzConfig" :disabled="device == null || device.online === 0">
|
||||
云台配置</el-dropdown-item>
|
||||
<el-dropdown-item command="audioTalk" :disabled="device == null || device.online === 0">
|
||||
语音对讲</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
|
||||
</el-dropdown>
|
||||
@ -212,12 +214,14 @@
|
||||
<devicePlayer ref="devicePlayer" />
|
||||
<channel-edit v-if="editId" :id="editId" :close-edit="closeEdit" />
|
||||
<ptzConfig v-if="ptzConfigChannelDeviceId" :device-id="ptzConfigDeviceId" :channel-device-id="ptzConfigChannelDeviceId" @close="closePtzConfig" />
|
||||
<audioTalk ref="audioTalk" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import devicePlayer from '../../dialog/devicePlayer.vue'
|
||||
import audioTalk from '../../dialog/audioTalk.vue'
|
||||
import Edit from './edit.vue'
|
||||
import ptzConfig from '@/views/device/common/ptzConfig.vue'
|
||||
|
||||
@ -225,6 +229,7 @@ export default {
|
||||
name: 'ChannelList',
|
||||
components: {
|
||||
devicePlayer,
|
||||
audioTalk,
|
||||
ChannelEdit: Edit,
|
||||
ptzConfig
|
||||
},
|
||||
@ -395,6 +400,8 @@ export default {
|
||||
console.log(itemData.channelId)
|
||||
this.ptzConfigDeviceId = this.deviceId
|
||||
this.ptzConfigChannelDeviceId = itemData.deviceId
|
||||
} else if (command === 'audioTalk') {
|
||||
this.$refs.audioTalk.openDialog(this.deviceId, itemData.deviceId)
|
||||
}
|
||||
},
|
||||
queryRecords: function(itemData) {
|
||||
|
||||
248
web/src/views/dialog/audioTalk.vue
Normal file
248
web/src/views/dialog/audioTalk.vue
Normal file
@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog
|
||||
title="语音对讲"
|
||||
top="10vh"
|
||||
width="65vw"
|
||||
:close-on-click-modal="false"
|
||||
:visible.sync="showDialog"
|
||||
@close="close()"
|
||||
>
|
||||
<div style="display: flex; gap: 16px;">
|
||||
<div style="flex: 1; min-width: 0;">
|
||||
<div v-if="!showPlayer" class="player-placeholder">
|
||||
<el-button
|
||||
type="primary"
|
||||
icon="el-icon-video-play"
|
||||
:loading="previewLoading"
|
||||
@click="startPreview"
|
||||
>开启预览</el-button>
|
||||
</div>
|
||||
<playerTabs
|
||||
v-if="showPlayer"
|
||||
ref="playerTabs"
|
||||
style="min-height: 60vh;"
|
||||
:has-audio="hasAudio"
|
||||
:show-button="true"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="broadcast-panel">
|
||||
<div style="text-align: center;">
|
||||
<video id="audioTalkVideo" controls autoplay style="width: 0; height: 0">
|
||||
Your browser is too old which doesn't support HTML5 video.
|
||||
</video>
|
||||
<el-radio-group v-model="talkMode" size="big" @change="onModeChange">
|
||||
<el-radio-button :label="false">喊话</el-radio-button>
|
||||
<el-radio-button :label="true">对讲</el-radio-button>
|
||||
</el-radio-group>
|
||||
<p style="color: #909399; font-size: 14px; margin-top: 4px;">
|
||||
{{ talkMode ? '双向语音交互,可听到设备声音' : '单向喊话,仅向设备发送语音' }}
|
||||
</p>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<el-button
|
||||
:type="getTalkButtonType()"
|
||||
:disabled="talkStatus === -2"
|
||||
circle
|
||||
icon="el-icon-microphone"
|
||||
style="font-size: 32px; padding: 24px;"
|
||||
@click="talkButtonClick()"
|
||||
/>
|
||||
<p style="margin-top: 16px; color: #606266;">
|
||||
<span v-if="talkStatus === -2">正在释放资源</span>
|
||||
<span v-if="talkStatus === -1">点击开始{{ talkMode ? '对讲' : '喊话' }}</span>
|
||||
<span v-if="talkStatus === 0">等待接通中...</span>
|
||||
<span v-if="talkStatus === 1">请说话</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import playerTabs from '../common/playerTabs.vue'
|
||||
|
||||
export default {
|
||||
name: 'AudioTalk',
|
||||
components: { playerTabs },
|
||||
data() {
|
||||
return {
|
||||
showDialog: false,
|
||||
showPlayer: false,
|
||||
previewLoading: false,
|
||||
deviceId: null,
|
||||
channelId: null,
|
||||
hasAudio: false,
|
||||
streamInfo: null,
|
||||
talkMode: false,
|
||||
talkStatus: -1,
|
||||
broadcastRtc: null,
|
||||
talkAudioRtc: null,
|
||||
talkAudioRetryTimer: null,
|
||||
talkAudioFailed: false,
|
||||
talkAudioPlayStream: null,
|
||||
playConnected: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.talkStatus = -1
|
||||
},
|
||||
methods: {
|
||||
openDialog(deviceId, channelId) {
|
||||
if (this.showDialog) return
|
||||
this.deviceId = deviceId
|
||||
this.channelId = channelId
|
||||
this.talkMode = false
|
||||
this.showPlayer = false
|
||||
this.streamInfo = null
|
||||
this.showDialog = true
|
||||
},
|
||||
onModeChange() {
|
||||
if (this.talkStatus > -1) {
|
||||
this.stopTalk()
|
||||
}
|
||||
},
|
||||
startPreview() {
|
||||
this.previewLoading = true
|
||||
this.$store.dispatch('play/play', [this.deviceId, this.channelId])
|
||||
.then(data => {
|
||||
this.streamInfo = data
|
||||
this.hasAudio = data.hasAudio
|
||||
this.showPlayer = true
|
||||
this.$nextTick(() => {
|
||||
if (this.$refs.playerTabs) {
|
||||
this.$refs.playerTabs.setStreamInfo(data)
|
||||
}
|
||||
})
|
||||
})
|
||||
.catch(e => {
|
||||
this.$message({ showClose: true, message: e, type: 'error' })
|
||||
})
|
||||
.finally(() => {
|
||||
this.previewLoading = false
|
||||
})
|
||||
},
|
||||
getTalkButtonType() {
|
||||
if (this.talkStatus === -2) return 'primary'
|
||||
if (this.talkStatus === -1) return 'primary'
|
||||
if (this.talkStatus === 0) return 'warning'
|
||||
if (this.talkStatus === 1) return 'danger'
|
||||
},
|
||||
async talkButtonClick() {
|
||||
if (this.talkStatus === -1) {
|
||||
await this.startTalk()
|
||||
} else if (this.talkStatus === 1) {
|
||||
this.stopTalk()
|
||||
}
|
||||
},
|
||||
async startTalk() {
|
||||
this.talkStatus = 0
|
||||
try {
|
||||
const data = await this.$store.dispatch('play/broadcastStart', [this.deviceId, this.channelId, this.talkMode])
|
||||
const si = data.streamInfo
|
||||
const url = document.location.protocol.includes('https') ? si.rtcs : si.rtc
|
||||
this.startWebrtcPush(url)
|
||||
} catch (e) {
|
||||
this.$message({ showClose: true, message: e, type: 'error' })
|
||||
this.talkStatus = -1
|
||||
}
|
||||
},
|
||||
startWebrtcPush(url) {
|
||||
this.$store.dispatch('user/getUserInfo')
|
||||
.then((data) => {
|
||||
if (data === null) { this.talkStatus = -1; return }
|
||||
const pushKey = data.pushKey
|
||||
url += '&sign=' + pushKey
|
||||
|
||||
if (this.broadcastRtc) {
|
||||
this.broadcastRtc.close()
|
||||
}
|
||||
this.broadcastRtc = new ZLMRTCClient.Endpoint({
|
||||
debug: true,
|
||||
zlmsdpUrl: url,
|
||||
simulecast: false,
|
||||
useCamera: false,
|
||||
audioEnable: true,
|
||||
videoEnable: false,
|
||||
recvOnly: false
|
||||
})
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_NOT_SUPPORT, () => { this.talkStatus = -1 })
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, () => { this.talkStatus = -1 })
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, () => { this.talkStatus = -1 })
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, (e) => {
|
||||
if (e === 'connecting') this.talkStatus = 0
|
||||
else if (e === 'connected') this.talkStatus = 1
|
||||
else if (e === 'disconnected') this.talkStatus = -1
|
||||
})
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, () => { this.talkStatus = -1 })
|
||||
})
|
||||
.catch(() => { this.talkStatus = -1 })
|
||||
},
|
||||
async stopTalk() {
|
||||
this.talkStatus = -2
|
||||
if (this.broadcastRtc) {
|
||||
this.broadcastRtc.close()
|
||||
this.broadcastRtc = null
|
||||
}
|
||||
if (this.talkAudioRtc) {
|
||||
this.talkAudioRtc.close()
|
||||
this.talkAudioRtc = null
|
||||
}
|
||||
if (this.talkAudioRetryTimer) {
|
||||
clearTimeout(this.talkAudioRetryTimer)
|
||||
this.talkAudioRetryTimer = null
|
||||
}
|
||||
this.talkAudioFailed = false
|
||||
this.talkAudioPlayStream = null
|
||||
this.playConnected = false
|
||||
try {
|
||||
await this.$store.dispatch('play/broadcastStop', [this.deviceId, this.channelId])
|
||||
} catch (e) {
|
||||
console.warn('停止对讲失败', e)
|
||||
}
|
||||
this.talkStatus = -1
|
||||
},
|
||||
close() {
|
||||
if (this.showPlayer && this.$refs.playerTabs) {
|
||||
this.$refs.playerTabs.stop()
|
||||
}
|
||||
this.stopTalk()
|
||||
this.streamInfo = null
|
||||
this.showPlayer = false
|
||||
this.showDialog = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.player-placeholder {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
aspect-ratio: 16 / 9;
|
||||
background: #1a1a1a;
|
||||
}
|
||||
.broadcast-panel {
|
||||
width: 220px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 16px 10px;
|
||||
border-left: 1px solid #ebeef5;
|
||||
}
|
||||
.broadcast-panel > div:first-child {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.broadcast-panel > div:last-child {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
@ -5,7 +5,7 @@
|
||||
v-if="showVideoDialog"
|
||||
v-el-drag-dialog
|
||||
title="视频播放"
|
||||
top="2vh"
|
||||
top="10vh"
|
||||
width="65vw"
|
||||
:close-on-click-modal="false"
|
||||
:visible.sync="showVideoDialog"
|
||||
@ -15,7 +15,8 @@
|
||||
|
||||
<div class="player-side">
|
||||
<div class="player-container" :style="{ height: playerHeight }">
|
||||
<playerTabs ref="playerTabs" :has-audio="hasAudio" :show-button="true" @playerChanged="playerChanged"/>
|
||||
<playerTabs ref="playerTabs" :has-audio="hasAudio" :show-button="true"
|
||||
@playerChanged="playerChanged" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -29,6 +30,8 @@
|
||||
@focus-stop="onFocusStop"
|
||||
@iris-move="onIrisMove"
|
||||
@iris-stop="onIrisStop"
|
||||
@toggle-drag-zoom="toggleDragZoom('in')"
|
||||
@toggle-drag-zoom-out="toggleDragZoom('out')"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -97,23 +100,6 @@
|
||||
<el-tab-pane label="编码信息" name="codec">
|
||||
<mediaInfo v-if="tabActiveName === 'codec'" ref="mediaInfo" :app="app" :stream="streamId" :media-server-id="mediaServerId" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane v-if="showBroadcast" label="语音对讲" name="broadcast">
|
||||
<div style="padding: 0 10px">
|
||||
<el-radio-group v-model="broadcastMode" :disabled="broadcastStatus !== -1">
|
||||
<el-radio :label="true">喊话(Broadcast)</el-radio>
|
||||
<el-radio :label="false">对讲(Talk)</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div class="trank" style="text-align: center;">
|
||||
<el-button :type="getBroadcastStatus()" :disabled="broadcastStatus === -2" circle icon="el-icon-microphone" style="font-size: 32px; padding: 24px;margin-top: 24px;" @click="broadcastStatusClick()" />
|
||||
<p>
|
||||
<span v-if="broadcastStatus === -2">正在释放资源</span>
|
||||
<span v-if="broadcastStatus === -1">点击开始对讲</span>
|
||||
<span v-if="broadcastStatus === 0">等待接通中...</span>
|
||||
<span v-if="broadcastStatus === 1">请说话</span>
|
||||
</p>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
|
||||
@ -124,7 +110,6 @@
|
||||
|
||||
<script>
|
||||
import elDragDialog from '@/directive/el-drag-dialog'
|
||||
import crypto from 'crypto'
|
||||
import playerTabs from '../common/playerTabs.vue'
|
||||
import ptzControls from '../common/ptzControls.vue'
|
||||
import PtzPreset from '../common/ptzPreset.vue'
|
||||
@ -147,16 +132,13 @@ export default {
|
||||
hasAudio: false,
|
||||
isLoging: false,
|
||||
showVideoDialog: false,
|
||||
showBroadcast: true,
|
||||
streamInfo: null,
|
||||
broadcastMode: true,
|
||||
broadcastRtc: null,
|
||||
broadcastStatus: -1,
|
||||
playerHeight: '48vh',
|
||||
playerUrlInfo: {
|
||||
playerUrl: null,
|
||||
playUrl: null,
|
||||
}
|
||||
},
|
||||
dragZoomDirection: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@ -165,7 +147,6 @@ export default {
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.broadcastStatus = -1
|
||||
},
|
||||
methods: {
|
||||
ptzSpeed(speed) {
|
||||
@ -249,68 +230,34 @@ export default {
|
||||
}
|
||||
this.videoUrl = ''
|
||||
this.showVideoDialog = false
|
||||
this.stopBroadcast()
|
||||
},
|
||||
copyUrl: function(dropdownItem) {
|
||||
this.$copyText(dropdownItem).then(() => {
|
||||
this.$message.success({ showClose: true, message: '成功拷贝到粘贴板' })
|
||||
}, () => {})
|
||||
},
|
||||
getBroadcastStatus() {
|
||||
if (this.broadcastStatus == -2) return 'primary'
|
||||
if (this.broadcastStatus == -1) return 'primary'
|
||||
if (this.broadcastStatus == 0) return 'warning'
|
||||
if (this.broadcastStatus === 1) return 'danger'
|
||||
|
||||
toggleDragZoom(direction) {
|
||||
this.dragZoomDirection = direction
|
||||
this.$refs.playerTabs.startDragZoom((params) => {
|
||||
params.deviceId = this.deviceId
|
||||
params.channelId = this.channelId
|
||||
const action = this.dragZoomDirection === 'in' ? 'frontEnd/dragZoomIn' : 'frontEnd/dragZoomOut'
|
||||
const successMsg = this.dragZoomDirection === 'in' ? '拉框放大成功' : '拉框缩小成功'
|
||||
const failMsg = this.dragZoomDirection === 'in' ? '拉框放大失败' : '拉框缩小失败'
|
||||
this.$store.dispatch(action, params).then(() => {
|
||||
this.$message({ showClose: true, message: successMsg, type: 'success' })
|
||||
}).catch(() => {
|
||||
this.$message({ showClose: true, message: failMsg, type: 'error' })
|
||||
})
|
||||
this.dragZoomDirection = ''
|
||||
})
|
||||
},
|
||||
broadcastStatusClick() {
|
||||
if (this.broadcastStatus === -1) {
|
||||
this.broadcastStatus = 0
|
||||
this.$store.dispatch('play/broadcastStart', [this.deviceId, this.channelId, this.broadcastMode])
|
||||
.then(data => {
|
||||
const si = data.streamInfo
|
||||
if (document.location.protocol.includes('https')) {
|
||||
this.startBroadcast(si.rtcs)
|
||||
} else {
|
||||
this.startBroadcast(si.rtc)
|
||||
}
|
||||
})
|
||||
} else if (this.broadcastStatus === 1) {
|
||||
this.broadcastStatus = -1
|
||||
this.broadcastRtc.close()
|
||||
}
|
||||
},
|
||||
startBroadcast(url) {
|
||||
this.$store.dispatch('user/getUserInfo')
|
||||
.then((data) => {
|
||||
if (data === null) { this.broadcastStatus = -1; return }
|
||||
const pushKey = data.pushKey
|
||||
url += '&sign=' + crypto.createHash('md5').update(pushKey, 'utf8').digest('hex')
|
||||
this.broadcastRtc = new ZLMRTCClient.Endpoint({
|
||||
debug: true, zlmsdpUrl: url, simulecast: false, useCamera: false,
|
||||
audioEnable: true, videoEnable: false, recvOnly: false
|
||||
})
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_NOT_SUPPORT, () => { this.broadcastStatus = -1 })
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR, () => { this.broadcastStatus = -1 })
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED, () => { this.broadcastStatus = -1 })
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.WEBRTC_ON_CONNECTION_STATE_CHANGE, (e) => {
|
||||
if (e === 'connecting') this.broadcastStatus = 0
|
||||
else if (e === 'connected') this.broadcastStatus = 1
|
||||
else if (e === 'disconnected') this.broadcastStatus = -1
|
||||
})
|
||||
this.broadcastRtc.on(ZLMRTCClient.Events.CAPTURE_STREAM_FAILED, () => { this.broadcastStatus = -1 })
|
||||
}).catch(() => { this.broadcastStatus = -1 })
|
||||
},
|
||||
stopBroadcast() {
|
||||
this.broadcastRtc && this.broadcastRtc.close()
|
||||
this.broadcastStatus = -1
|
||||
this.$store.dispatch('play/broadcastStop', [this.deviceId, this.channelId])
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
#devicePlayer .el-dialog { margin-top: 2vh !important; }
|
||||
#devicePlayer .el-dialog__body { padding: 10px 20px; }
|
||||
.dhsdk-player-body { display: flex; gap: 16px; }
|
||||
.player-side { flex: 3; min-width: 0; }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user