Skip to content

Commit 6799330

Browse files
committed
feat: video的一些设置,代码优化
1 parent f68bf82 commit 6799330

File tree

6 files changed

+215
-121
lines changed

6 files changed

+215
-121
lines changed

client/packages/rtc-web/.eslintrc.cjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module.exports = {
1818
},
1919
plugins: ['vue', '@typescript-eslint', 'prettier'],
2020
rules: {
21-
indent: ['error', 2],
21+
indent: ['error', 2, { offsetTernaryExpressions: true }],
2222
semi: ['error', 'always'],
2323
'vue/attribute-hyphenation': 'off',
2424
'@typescript-eslint/no-explicit-any': 'off',

client/packages/rtc-web/src/components/menu-action.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ type PropTypes = {
1212
color?: string;
1313
tipDir?: string;
1414
btn?: boolean;
15+
disabled?: boolean;
1516
}[];
1617
gap?: number;
1718
};
@@ -50,12 +51,16 @@ const handleClick = (name: string) => emits('clickIcon', name);
5051
@click="handleClick(item.name)"
5152
/>
5253
</emoji-picker>
53-
<button v-else-if="item.btn" class="btn-circle btn">
54+
<button
55+
v-else-if="item.btn"
56+
class="btn-circle btn"
57+
:disabled="item.disabled"
58+
@click="handleClick(item.name)"
59+
>
5460
<svg-icon
5561
:color="item.color"
5662
:name="item.name"
5763
class="h-6 w-6 cursor-pointer"
58-
@click="handleClick(item.name)"
5964
/>
6065
</button>
6166
<svg-icon

client/packages/rtc-web/src/config/constant.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export type MenuActionType = {
44
color?: string;
55
tipDir?: string;
66
btn?: boolean;
7+
disabled?: boolean;
78
};
89

910
export const ChatAction: MenuActionType[] = [
@@ -31,19 +32,22 @@ export const VideoControlMenuAction: MenuActionType[] = [
3132
color: '#707070',
3233
tipDir: 'tooltip-top',
3334
btn: true,
35+
disabled: false,
3436
},
3537
{
3638
name: 'mirror-image',
3739
tip: '开启镜像',
3840
color: '#707070',
3941
tipDir: 'tooltip-top',
4042
btn: true,
43+
disabled: false,
4144
},
4245
{
4346
name: 'hang-up',
4447
tip: '结束通话',
4548
color: undefined,
4649
tipDir: 'tooltip-top',
4750
btn: true,
51+
disabled: false,
4852
},
4953
];
Lines changed: 129 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,102 +1,174 @@
11
import { useUserMedia, useDevicesList } from '@vueuse/core';
2-
import { Ref, computed, nextTick, onMounted, ref, watchEffect } from 'vue';
2+
import { Ref, computed, ref, shallowRef, watch, watchEffect } from 'vue';
33

44
export type VideoShareOption = {
5-
audio: Ref<MediaDeviceInfo | undefined>;
5+
audio?: Ref<MediaDeviceInfo | undefined>;
6+
speaker?: Ref<string>;
67
};
78

8-
export const useVideoShare = (option: VideoShareOption) => {
9-
const { audio: audioRef } = option;
9+
export const useVideoShare = (option: VideoShareOption = {}) => {
1010
const currentCamera = ref<string>();
11+
const currentAudioInput = ref<string>();
12+
const currentAudioOutput = ref<string>();
1113

12-
const { videoInputs: cameras } = useDevicesList({
14+
const audioRef = ref(option.audio);
15+
const speakerRef = ref(option.speaker);
16+
17+
const mediaLoaded = ref(false);
18+
19+
// 切换 麦克风
20+
watch(
21+
audioRef,
22+
(value) => {
23+
currentAudioInput.value = value?.deviceId;
24+
},
25+
{
26+
immediate: true,
27+
}
28+
);
29+
30+
// 切换 扬声器
31+
watch(
32+
speakerRef,
33+
(value) => {
34+
currentAudioOutput.value = value;
35+
},
36+
{
37+
immediate: true,
38+
}
39+
);
40+
41+
watch(currentAudioOutput, (v) => {
42+
if (v) {
43+
setSinkId(v);
44+
}
45+
});
46+
47+
// 收集的错误信息
48+
// const errorMsg = ref({
49+
// audio: '',
50+
// });
51+
52+
const {
53+
videoInputs: cameras,
54+
audioInputs,
55+
audioOutputs,
56+
} = useDevicesList({
1357
requestPermissions: true,
1458
onUpdated() {
59+
// 初始化默认 摄像头、扬声器、麦克风
60+
1561
if (!cameras.value.find((i) => i.deviceId === currentCamera.value))
1662
currentCamera.value = cameras.value[0]?.deviceId;
63+
64+
if (
65+
!audioInputs.value.find((i) => i.deviceId === currentAudioInput.value)
66+
)
67+
currentAudioInput.value = audioInputs.value[0]?.deviceId;
68+
69+
if (
70+
!audioOutputs.value.find((i) => i.deviceId === currentAudioOutput.value)
71+
) {
72+
currentAudioOutput.value = audioOutputs.value[0]?.deviceId;
73+
}
1774
},
1875
});
1976

20-
const video = ref<HTMLVideoElement>();
77+
const video = shallowRef<HTMLVideoElement>();
78+
79+
const constraints = computed(() => {
80+
const audioDevice = currentAudioInput.value
81+
? { deviceId: currentAudioInput.value }
82+
: false;
83+
84+
const videoDevice = currentCamera.value
85+
? {
86+
deviceId: currentCamera.value,
87+
facingMode: 'user',
88+
frameRate: {
89+
ideal: 30,
90+
max: 60,
91+
},
92+
}
93+
: false;
94+
return {
95+
audio: audioDevice,
96+
video: videoDevice,
97+
};
98+
});
99+
100+
const { stream, enabled, stop, restart } = useUserMedia({
101+
constraints,
102+
});
103+
104+
const audioTracks = ref<MediaStreamTrack[]>([]);
105+
const videoTracks = ref<MediaStreamTrack[]>([]);
106+
107+
const audioEnabled = ref(false);
108+
const videoEnabled = ref(false);
21109

110+
// 切换 video、audio 渲染
22111
const switchTrackEnable = (type: 'video' | 'audio', flag: boolean) => {
23112
if (stream.value) {
24113
if (type === 'audio') {
25-
const audioStream = stream.value.getAudioTracks();
26-
if (audioStream?.length) {
27-
audioStream[0].enabled = flag;
114+
if (audioTracks.value?.length) {
115+
audioEnabled.value = flag;
116+
audioTracks.value.forEach((item) => (item.enabled = flag));
28117
}
29118
}
30119
if (type === 'video') {
31-
const videoStream = stream.value.getVideoTracks();
32-
if (videoStream?.length) {
33-
videoStream[0].enabled = flag;
120+
if (videoTracks.value?.length) {
121+
videoEnabled.value = flag;
122+
videoTracks.value.forEach((item) => {
123+
item.enabled = flag;
124+
});
34125
}
35126
}
36127
}
37128
};
38129

39-
const audioEnabled = computed(() => {
40-
return stream.value?.getAudioTracks()[0].enabled;
41-
});
42-
43-
const videoEnabled = computed(() => {
44-
return stream.value?.getVideoTracks()[0].enabled;
45-
});
46-
47-
const audioDevice = computed(() => {
48-
return audioRef.value ? { deviceId: audioRef.value.deviceId } : true;
49-
});
50-
51-
const stop = () => {
52-
enabled.value = false;
53-
};
54-
55-
const { stream, enabled } = useUserMedia({
56-
constraints: computed(() => {
57-
return {
58-
audio: audioDevice.value,
59-
video: {
60-
deviceId: currentCamera.value, // 前后置
61-
facingMode: 'user',
62-
// 码率
63-
frameRate: {
64-
ideal: 30,
65-
max: 60,
66-
},
67-
},
68-
};
69-
}),
70-
});
71-
72130
const setSinkId = (sinkId: string) => {
73131
(video.value! as any).setSinkId(sinkId);
74132
};
75133

76-
watchEffect(() => {
77-
if (video.value) {
78-
video.value.srcObject = stream.value!;
79-
switchTrackEnable('audio', false);
80-
switchTrackEnable('video', false);
134+
// 先静音和关闭视频渲染
135+
watch(stream, (v) => {
136+
if (v) {
137+
if (video.value) {
138+
video.value.srcObject = stream.value!;
139+
if (stream.value) {
140+
audioTracks.value = stream.value.getAudioTracks();
141+
videoTracks.value = stream.value.getVideoTracks();
142+
}
143+
switchTrackEnable('audio', false);
144+
switchTrackEnable('video', false);
145+
mediaLoaded.value = true;
146+
}
81147
}
82148
});
83149

84-
watchEffect(() => {
85-
if (cameras.value.length) {
86-
currentCamera.value = cameras.value[0].deviceId;
150+
// 进入页面先连接
151+
const watchEnableStop = watchEffect(() => {
152+
if (
153+
currentAudioInput.value &&
154+
currentCamera.value &&
155+
currentAudioOutput.value
156+
) {
157+
enabled.value = true;
158+
watchEnableStop();
87159
}
88160
});
89161

90-
onMounted(() => {
91-
enabled.value = true;
92-
});
93-
94162
return {
95163
video,
96164
setSinkId,
97165
switchTrackEnable,
166+
restart,
98167
stop,
99168
audioEnabled,
100169
videoEnabled,
170+
mediaLoaded,
171+
currentAudioInput,
172+
currentAudioOutput,
101173
};
102174
};

0 commit comments

Comments
 (0)