海信医疗-远程超声管理平台-信创国产化
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

378 lines
12 KiB

1 month ago
<template>
<el-dialog
title="设置"
:visible.sync="visible"
width="600px"
:close-on-click-modal="false"
@close="handleClose"
>
<el-tabs v-model="activeTab" :tab-position="'left'" class="settings-tabs">
1 month ago
<!-- 视频设置 -->
<el-tab-pane name="video">
<span slot="label"><i class="el-icon-video-camera"></i> 视频</span>
<el-form :model="videoConfig" label-width="140px" class="settings-form">
<!-- 摄像头选择使用外部传入的摄像头列表 -->
1 month ago
<el-form-item label="摄像头/工作站">
<el-select
v-model="videoConfig.camera"
placeholder="请选择"
style="width: 240px"
@change="getCameraResolutions"
1 month ago
>
<el-option
v-for="cam in effectiveCameraList"
:key="cam.id"
:label="cam.name"
:value="cam.id"
1 month ago
/>
</el-select>
</el-form-item>
<el-form-item label="采集卡">
<el-select
v-model="videoConfig.captureCard"
placeholder="请选择"
style="width: 240px"
/>
</el-form-item>
<el-form-item label="网络调控策略">
<el-select
v-model="videoConfig.networkStrategy"
placeholder="请选择"
style="width: 240px"
>
<el-option label="弱网下流畅度优先" value="smooth" />
<el-option label="清晰度优先" value="quality" />
<el-option label="平衡模式" value="balance" />
1 month ago
</el-select>
</el-form-item>
<!-- 摄像头分辨率 -->
1 month ago
<el-form-item label="摄像头分辨率">
<el-select
v-model="videoConfig.cameraResolution"
placeholder="请选择"
style="width: 240px"
>
<el-option
v-for="res in supportedResolutions"
:key="res.label"
:label="res.label"
:value="res.label"
/>
1 month ago
</el-select>
</el-form-item>
<el-form-item label="采集卡分辨率">
<el-select
v-model="videoConfig.captureResolution"
placeholder="请选择"
style="width: 240px"
/>
</el-form-item>
<el-form-item label="采集卡帧率">
<el-select
v-model="videoConfig.captureFps"
placeholder="请选择"
style="width: 240px"
/>
</el-form-item>
<el-form-item label="采集卡码率(2-8M)">
<el-input
v-model="videoConfig.captureBitrate"
style="width: 240px"
/>
<i class="el-icon-edit edit-icon" style="margin-left: 10px"></i>
</el-form-item>
<el-form-item label="视频镜像">
<el-radio-group v-model="videoConfig.mirror">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</el-tab-pane>
<!-- 音频设置 -->
<el-tab-pane name="audio">
<span slot="label"><i class="el-icon-microphone"></i> 音频</span>
<el-form :model="audioConfig" label-width="140px" class="settings-form">
1 month ago
<el-form-item label="麦克风">
<el-select
v-model="audioConfig.mic"
placeholder="请选择麦克风"
1 month ago
style="width: 240px"
@change="startMicTest"
1 month ago
>
<el-option
v-for="mic in effectiveMicList"
:key="mic.id"
:label="mic.name"
:value="mic.id"
1 month ago
/>
</el-select>
</el-form-item>
<!-- 麦克风音量实时测试条 -->
1 month ago
<el-form-item label="麦克风测试">
<el-slider
v-model="micTestPercent"
1 month ago
class="progress-slider"
disabled
1 month ago
/>
</el-form-item>
<!-- 扬声器选择 -->
1 month ago
<el-form-item label="扬声器">
<el-select
v-model="audioConfig.speaker"
placeholder="请选择扬声器"
1 month ago
style="width: 240px"
@change="playSpeakerTest"
1 month ago
>
<el-option
v-for="spk in effectiveSpeakerList"
:key="spk.id"
:label="spk.name"
:value="spk.id"
1 month ago
/>
</el-select>
</el-form-item>
<!-- 扬声器音量实时测试条 -->
1 month ago
<el-form-item label="扬声器测试">
<el-slider
v-model="speakerTestPercent"
1 month ago
class="progress-slider"
disabled
1 month ago
/>
</el-form-item>
</el-form>
</el-tab-pane>
</el-tabs>
</el-dialog>
</template>
<script>
export default {
name: "SettingsDialog",
props: {
visible: {
type: Boolean,
default: false,
},
videoConfig: {
type: Object,
required: true,
1 month ago
},
audioConfig: {
type: Object,
required: true,
},
cameraList: {
type: Array,
default: () => [],
},
micList: {
type: Array,
default: () => [],
},
speakerList: {
type: Array,
default: () => [],
1 month ago
},
},
data() {
return {
activeTab: "video",
micTestPercent: 0,
speakerTestPercent: 0,
videoDevices: [],
audioInputDevices: [],
audioOutputDevices: [],
supportedResolutions: [],
micAudioContext: null,
micAnalyser: null,
micAnimation: null,
speakerAudio: null,
1 month ago
};
},
computed: {
effectiveCameraList() {
if (this.cameraList && this.cameraList.length) {
return this.cameraList;
}
return this.videoDevices.map(d => ({ id: d.deviceId, name: d.label || '未知摄像头' }));
},
effectiveMicList() {
if (this.micList && this.micList.length) {
return this.micList;
}
return this.audioInputDevices.map(d => ({ id: d.deviceId, name: d.label || '未知麦克风' }));
},
effectiveSpeakerList() {
if (this.speakerList && this.speakerList.length) {
return this.speakerList;
}
return this.audioOutputDevices.map(d => ({ id: d.deviceId, name: d.label || '未知扬声器' }));
},
},
1 month ago
watch: {
visible(val) {
if (val) {
this.getDevices();
this.micTestPercent = 10;
this.speakerTestPercent = 10;
} else {
this.stopAllTest();
1 month ago
}
},
},
methods: {
async getDevices() {
if (this.cameraList && this.cameraList.length) {
this.getCameraResolutions();
return;
}
try {
await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
const devices = await navigator.mediaDevices.enumerateDevices();
this.videoDevices = devices.filter((d) => d.kind === "videoinput");
this.audioInputDevices = devices.filter((d) => d.kind === "audioinput");
this.audioOutputDevices = devices.filter((d) => d.kind === "audiooutput");
if (this.videoDevices.length && !this.videoConfig.camera) {
this.videoConfig.camera = this.videoDevices[0].deviceId;
this.getCameraResolutions();
}
if (this.audioInputDevices.length && !this.audioConfig.mic) {
this.audioConfig.mic = this.audioInputDevices[0].deviceId;
this.startMicTest();
}
if (this.audioOutputDevices.length && !this.audioConfig.speaker) {
this.audioConfig.speaker = this.audioOutputDevices[0].deviceId;
}
} catch (err) {
console.error("获取设备失败", err);
}
},
async getCameraResolutions() {
if (!this.videoConfig.camera) return;
try {
const stream = await navigator.mediaDevices.getUserMedia({
video: { deviceId: this.videoConfig.camera },
});
const track = stream.getVideoTracks()[0];
const caps = track.getCapabilities ? track.getCapabilities() : { width: { max: 1920 } };
const list = [
{ w: 3840, h: 2160, label: "4K" },
{ w: 1920, h: 1080, label: "1080P" },
{ w: 1280, h: 720, label: "720P" },
{ w: 640, h: 480, label: "480P" },
].filter((item) => (caps.width && caps.width.max >= item.w) || true);
this.supportedResolutions = list;
if (list.length && !this.videoConfig.cameraResolution) {
this.videoConfig.cameraResolution = list[0].label;
}
stream.getTracks().forEach((t) => t.stop());
} catch (e) {
this.supportedResolutions = [
{ w: 1920, h: 1080, label: "1080P" },
{ w: 1280, h: 720, label: "720P" },
{ w: 640, h: 480, label: "480P" },
];
}
},
async startMicTest() {
this.stopMicTest();
try {
const stream = await navigator.mediaDevices.getUserMedia({
audio: { deviceId: this.audioConfig.mic },
});
this.micAudioContext = new AudioContext();
this.micAnalyser = this.micAudioContext.createAnalyser();
this.micAnalyser.fftSize = 256;
const source = this.micAudioContext.createMediaStreamSource(stream);
source.connect(this.micAnalyser);
const dataArray = new Uint8Array(this.micAnalyser.frequencyBinCount);
const update = () => {
if (!this.micAnalyser) return;
this.micAnalyser.getByteFrequencyData(dataArray);
const sum = dataArray.reduce((a, b) => a + b, 0);
const vol = Math.min(100, sum / 50);
this.micTestPercent = vol;
this.micAnimation = requestAnimationFrame(update);
};
update();
} catch (err) {
console.log("麦克风测试失败");
}
},
stopMicTest() {
if (this.micAnimation) {
cancelAnimationFrame(this.micAnimation);
this.micAnimation = null;
}
if (this.micAudioContext) {
this.micAudioContext.close();
this.micAudioContext = null;
this.micAnalyser = null;
}
this.micTestPercent = 10;
},
async playSpeakerTest() {
try {
if (this.speakerAudio) {
this.speakerAudio.pause();
this.speakerAudio = null;
}
this.speakerTestPercent = 10;
const audio = new Audio(
"data:audio/wav;base64,UklGRnoGAABXQVZFZm10IBAAAAABAAEAQB8AAEAfAAABAAgAZGF0YQoGAACBhYqFbF1fdJmWl5hQVjMpOkhUXMDc1d3d1dPQ1NDR0dLR0dDQ0M/Pz8vLy8rKysnJyeHh4d3d3dHR0dDQ0M/Pz8vLy8rKysnJyeHh4d3d3dHR0dHR0dHR0dHR0dHR0dHQ="
);
audio.muted = false;
if (this.audioConfig.speaker && audio.setSinkId) {
await audio.setSinkId(this.audioConfig.speaker);
}
audio.onplaying = () => {
this.speakerTestPercent = 80;
setTimeout(() => (this.speakerTestPercent = 40), 300);
setTimeout(() => (this.speakerTestPercent = 70), 600);
setTimeout(() => (this.speakerTestPercent = 30), 900);
setTimeout(() => (this.speakerTestPercent = 10), 1200);
};
audio.play();
this.speakerAudio = audio;
} catch (e) {
console.log("扬声器测试失败");
}
},
stopAllTest() {
this.stopMicTest();
if (this.speakerAudio) {
this.speakerAudio.pause();
this.speakerAudio = null;
}
this.speakerTestPercent = 10;
},
1 month ago
handleClose() {
this.$emit("update:visible", false);
},
},
};
</script>
<style lang="scss" scoped>
.settings-form {
padding: 10px;
}
.edit-icon {
margin-left: 5px;
cursor: pointer;
}
.progress-slider {
width: 240px;
}
</style>