feat: 声网插件自由控制媒体流推送

并优化了自我画面的展示
pull/64/head
moonrailgun 2 years ago
parent 0cfa409caf
commit 356e7edd58

@ -1,45 +1,46 @@
import { IconBtn } from '@capital/component';
import React, { useState } from 'react';
import React from 'react';
import { useClient, useMicrophoneAndCameraTracks } from './client';
import { useMeetingStore } from './store';
import { getClientLocalTrack } from './utils';
export const Controls: React.FC<{
onClose: () => void;
}> = React.memo((props) => {
const client = useClient();
const [trackState, setTrackState] = useState({ video: false, audio: false });
const { ready, tracks } = useMicrophoneAndCameraTracks();
const mediaPerm = useMeetingStore((state) => state.mediaPerm);
const mute = async (type: 'audio' | 'video') => {
if (type === 'audio') {
if (trackState.audio === true) {
// await tracks[0].setEnabled(false);
await client.unpublish(tracks[0]);
if (mediaPerm.audio === true) {
const track = getClientLocalTrack(client, 'audio');
if (track) {
await client.unpublish(track);
}
} else {
// await tracks[0].setEnabled(true);
await client.publish(tracks[0]);
}
setTrackState((ps) => {
return { ...ps, audio: !ps.audio };
});
useMeetingStore.getState().setMediaPerm({ audio: !mediaPerm.audio });
} else if (type === 'video') {
if (trackState.video === true) {
// await tracks[1].setEnabled(false);
await client.unpublish(tracks[1]);
if (mediaPerm.video === true) {
const track = getClientLocalTrack(client, 'video');
if (track) {
await client.unpublish(track);
}
} else {
// await tracks[1].setEnabled(true);
await client.publish(tracks[1]);
}
setTrackState((ps) => {
return { ...ps, video: !ps.video };
});
useMeetingStore.getState().setMediaPerm({ video: !mediaPerm.video });
}
};
const leaveChannel = async () => {
await client.leave();
client.removeAllListeners();
useMeetingStore.getState().clearUser();
useMeetingStore.getState().reset();
// we close the tracks to perform cleanup
tracks[0].close();
tracks[1].close();
@ -49,16 +50,16 @@ export const Controls: React.FC<{
return (
<div className="controller">
<IconBtn
icon={trackState.video ? 'mdi:video' : 'mdi:video-off'}
title={trackState.video ? '关闭摄像头' : '开启摄像头'}
icon={mediaPerm.video ? 'mdi:video' : 'mdi:video-off'}
title={mediaPerm.video ? '关闭摄像头' : '开启摄像头'}
disabled={!ready}
size="large"
onClick={() => mute('video')}
/>
<IconBtn
icon={trackState.audio ? 'mdi:microphone' : 'mdi:microphone-off'}
title={trackState.audio ? '关闭麦克风' : '开启麦克风'}
icon={mediaPerm.audio ? 'mdi:microphone' : 'mdi:microphone-off'}
title={mediaPerm.audio ? '关闭麦克风' : '开启麦克风'}
disabled={!ready}
size="large"
onClick={() => mute('audio')}

@ -2,6 +2,8 @@ import { UserName } from '@capital/component';
import { AgoraVideoPlayer, IAgoraRTCRemoteUser } from 'agora-rtc-react';
import React from 'react';
import styled from 'styled-components';
import { useClient, useMicrophoneAndCameraTracks } from './client';
import { useMeetingStore } from './store';
const Root = styled.div`
width: 95%;
@ -12,6 +14,7 @@ const Root = styled.div`
aspect-ratio: 16/9;
justify-self: center;
align-self: center;
overflow: hidden;
.player {
width: 100%;
@ -42,3 +45,20 @@ export const VideoView: React.FC<{
);
};
VideoView.displayName = 'VideoView';
export const OwnVideoView: React.FC<{}> = React.memo(() => {
const { ready, tracks } = useMicrophoneAndCameraTracks();
const client = useClient();
const mediaPerm = useMeetingStore((state) => state.mediaPerm);
return (
<Root>
{ready && mediaPerm.video && (
<AgoraVideoPlayer className="player" videoTrack={tracks[1]} />
)}
<UserName className="name" userId={String(client.uid)} />
</Root>
);
});
OwnVideoView.displayName = 'OwnVideoView';

@ -1,26 +1,20 @@
import { AgoraVideoPlayer } from 'agora-rtc-react';
import React from 'react';
import styled from 'styled-components';
import { useMicrophoneAndCameraTracks } from './client';
import { useMeetingStore } from './store';
import { VideoView } from './VideoView';
import { OwnVideoView, VideoView } from './VideoView';
const Root = styled.div`
height: 70vh;
/* align-self: flex-start; */
display: grid;
grid-template-columns: repeat(auto-fit, minmax(440px, 1fr));
`;
export const Videos: React.FC = React.memo(() => {
const users = useMeetingStore((state) => state.users);
const { ready, tracks } = useMicrophoneAndCameraTracks();
return (
<Root>
{/* AgoraVideoPlayer component takes in the video track to render the stream,
you can pass in other props that get passed to the rendered div */}
{ready && <AgoraVideoPlayer className="vid" videoTrack={tracks[1]} />}
<OwnVideoView />
{users.length > 0 &&
users.map((user) => {

@ -1,22 +1,34 @@
import type { IAgoraRTCRemoteUser } from 'agora-rtc-react';
import create from 'zustand';
interface MediaPerm {
video: boolean;
audio: boolean;
}
interface MeetingState {
/**
*
*/
users: IAgoraRTCRemoteUser[];
/**
*
*/
mediaPerm: MediaPerm;
appendUser: (user: IAgoraRTCRemoteUser) => void;
removeUser: (user: IAgoraRTCRemoteUser) => void;
clearUser: () => void;
/**
*
*/
updateUserInfo: (user: IAgoraRTCRemoteUser) => void;
setMediaPerm: (perm: Partial<MediaPerm>) => void;
reset: () => void;
}
export const useMeetingStore = create<MeetingState>((set) => ({
users: [],
mediaPerm: { video: false, audio: false },
appendUser: (user: IAgoraRTCRemoteUser) => {
set((state) => ({
users: [...state.users, user],
@ -29,9 +41,6 @@ export const useMeetingStore = create<MeetingState>((set) => ({
};
});
},
clearUser: () => {
set({ users: [] });
},
updateUserInfo: (user: IAgoraRTCRemoteUser) => {
set((state) => {
const users = [...state.users];
@ -47,4 +56,19 @@ export const useMeetingStore = create<MeetingState>((set) => ({
};
});
},
setMediaPerm: (perm: Partial<MediaPerm>) => {
set((state) => ({
mediaPerm: {
...state.mediaPerm,
...perm,
},
}));
},
reset: () => {
set({
users: [],
mediaPerm: { video: false, audio: false },
});
},
}));

@ -0,0 +1,15 @@
import type { IAgoraRTCClient, ILocalTrack } from 'agora-rtc-react';
/**
*
*/
export function getClientLocalTrack(
client: IAgoraRTCClient,
trackMediaType: 'audio' | 'video'
): ILocalTrack | null {
return (
client.localTracks.find(
(track) => track.trackMediaType === trackMediaType
) ?? null
);
}
Loading…
Cancel
Save