Skip to content

Commit

Permalink
feat: useLocalScreenTrack (#109)
Browse files Browse the repository at this point in the history
  • Loading branch information
guoxianzhe authored Aug 4, 2023
1 parent a040527 commit e0ae84c
Show file tree
Hide file tree
Showing 15 changed files with 550 additions and 4 deletions.
91 changes: 91 additions & 0 deletions examples/basic/src/components/ScreenShare.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
import {
AgoraRTCScreenShareProvider,
LocalAudioTrack,
LocalVideoTrack,
useJoin,
usePublish,
useTrackEvent,
} from "agora-rtc-react";
import type { ILocalAudioTrack, ILocalTrack, ILocalVideoTrack } from "agora-rtc-sdk-ng";
import AgoraRTC from "agora-rtc-sdk-ng";
import { useEffect, useState } from "react";

import { appConfig } from "../utils";

interface ShareScreenProps {
screenShareOn: boolean;
screenTrack: ILocalVideoTrack | [ILocalVideoTrack, ILocalAudioTrack] | null;
onCloseScreenShare?: () => void;
}

export const ScreenShare = ({
screenShareOn,
screenTrack,
onCloseScreenShare,
}: ShareScreenProps) => {
const [client] = useState(() => AgoraRTC.createClient({ mode: "rtc", codec: "vp8" }));

//screen share
const [screenVideoTrack, setScreenVideoTrack] = useState<ILocalVideoTrack | null>(null);
const [screenAudioTrack, setScreenAudioTrack] = useState<ILocalAudioTrack | null>(null);

//join room
useJoin(
{
appid: appConfig.appId,
channel: appConfig.channel,
token: appConfig.token,
uid: appConfig.ShareScreenUID,
},
screenShareOn,
client,
);

//get screen share video track and audio track
useEffect(() => {
if (!screenTrack) {
setScreenAudioTrack(null);
setScreenVideoTrack(null);
} else {
if (Array.isArray(screenTrack)) {
setScreenVideoTrack(
screenTrack.filter(
(track: ILocalTrack) => track.trackMediaType === "video",
)[0] as ILocalVideoTrack,
);
setScreenAudioTrack(
screenTrack.filter(
(track: ILocalTrack) => track.trackMediaType === "audio",
)[0] as ILocalAudioTrack,
);
} else {
setScreenVideoTrack(screenTrack);
}
}
}, [screenTrack]);

//publish screen share
usePublish([screenVideoTrack, screenAudioTrack], screenShareOn, client);

//screen share closed
useTrackEvent(screenVideoTrack, "track-ended", () => {
console.log("screen sharing ended");
onCloseScreenShare && onCloseScreenShare();
});

return (
<AgoraRTCScreenShareProvider client={client}>
{screenShareOn && screenVideoTrack && (
<LocalVideoTrack
disabled={!screenShareOn}
play={screenShareOn}
style={{ width: "300px", height: "300px" }}
track={screenVideoTrack}
/>
)}
{screenAudioTrack && <LocalAudioTrack disabled={!screenShareOn} track={screenAudioTrack} />}
</AgoraRTCScreenShareProvider>
);
};

export default ScreenShare;
1 change: 1 addition & 0 deletions examples/basic/src/components/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export * from "./Room";
export * from "./Client";
export * from "./MediaControl";
export * from "./ToolBox";
export * from "./ScreenShare";
5 changes: 5 additions & 0 deletions examples/basic/src/pages/advanced/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import MultiChannel from "./multi-channel";
import ShareScreen from "./share-screen";
import SwitchLayout from "./switch-layout";

const Advanced = [
Expand All @@ -10,6 +11,10 @@ const Advanced = [
label: "multi-channel",
component: MultiChannel,
},
{
label: "share-screen",
component: ShareScreen,
},
];

export { Advanced };
94 changes: 94 additions & 0 deletions examples/basic/src/pages/advanced/share-screen/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import {
useJoin,
useLocalScreenTrack,
useRemoteAudioTracks,
useRemoteUsers,
useRemoteVideoTracks,
} from "agora-rtc-react";
import { Button } from "antd";
import { useEffect, useState } from "react";

import { Container, MediaControl, Room, ScreenShare } from "../../../components";
import { RenderRemoteUsers } from "../../../components/RemoteUsers";
import { appConfig } from "../../../utils";

export const ShareScreen = () => {
const [calling, setCalling] = useState(false);

useJoin(
{
appid: appConfig.appId,
channel: appConfig.channel,
token: appConfig.token,
},
calling,
);

const [micOn, setMic] = useState(false);
const [cameraOn, setCamera] = useState(false);

//screen share
const [screenShareOn, setScreenShareOn] = useState(false);
//create screen share track
const { screenTrack, error } = useLocalScreenTrack(screenShareOn, {}, "auto");
//if screen got error, close screen share
useEffect(() => {
setScreenShareOn(false);
}, [error]);

const remoteUsers = useRemoteUsers();
const { videoTracks } = useRemoteVideoTracks(
//remember to filter out screen share user to avoid duplicate payment
remoteUsers.filter(user => user.uid !== appConfig.ShareScreenUID),
);
const { audioTracks } = useRemoteAudioTracks(
//remember to filter out screen share user to avoid duplicate payment
remoteUsers.filter(user => user.uid !== appConfig.ShareScreenUID),
);
audioTracks.map(track => track.play());

return (
<Container>
<div className="h-screen p-3">
{calling && (
<>
<Button onClick={() => setScreenShareOn(a => !a)} type="primary">
{screenShareOn ? "stop share screen" : "start share screen"}
</Button>
{screenShareOn && (
<ScreenShare
onCloseScreenShare={() => {
setScreenShareOn(false);
}}
screenShareOn={screenShareOn}
screenTrack={screenTrack}
/>
)}
<Room
cameraOn={cameraOn}
micOn={micOn}
renderRemoteUsers={() => <RenderRemoteUsers videoTracks={videoTracks} />}
/>
</>
)}
</div>
<MediaControl
calling={calling}
cameraOn={cameraOn}
micOn={micOn}
setCalling={() => {
setCalling(a => !a);
setScreenShareOn(false);
}}
setCamera={() => {
setCamera(a => !a);
}}
setMic={() => {
setMic(a => !a);
}}
/>
</Container>
);
};

export default ShareScreen;
5 changes: 5 additions & 0 deletions examples/basic/src/pages/hook/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import UseConnectionState from "./useConnectionState";
import UseCurrentUID from "./useCurrentUID";
import UseIsConnected from "./useIsConnected";
import UseJoin from "./useJoin";
import UseLocalScreenTrack from "./useLocalScreenTrack";
import UseNetworkQuality from "./useNetworkQuality";
import UsePublish from "./usePublish";
import UseRemoteUsers from "./useRemoteUsers";
Expand Down Expand Up @@ -44,6 +45,10 @@ const Hooks = [
label: "useNetworkQuality",
component: UseNetworkQuality,
},
{
label: "useLocalScreenTrack",
component: UseLocalScreenTrack,
},
];

export { Hooks };
93 changes: 93 additions & 0 deletions examples/basic/src/pages/hook/useLocalScreenTrack/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import {
LocalVideoTrack,
useJoin,
useLocalScreenTrack,
usePublish,
useTrackEvent,
} from "agora-rtc-react";
import type { ILocalAudioTrack, ILocalTrack, ILocalVideoTrack } from "agora-rtc-sdk-ng";
import { Button, Typography } from "antd";
import { useEffect, useState } from "react";

import { Container } from "../../../components";
import { appConfig } from "../../../utils";
const { Title } = Typography;

export const UseLocalScreenTrack = () => {
//screen share
const [screenShareOn, setScreenShareOn] = useState(false);
const [screenVideoTrack, setScreenVideoTrack] = useState<ILocalVideoTrack | null>(null);
const [screenAudioTrack, setScreenAudioTrack] = useState<ILocalAudioTrack | null>(null);

//join room
useJoin(
{
appid: appConfig.appId,
channel: appConfig.channel,
token: appConfig.token,
uid: appConfig.ShareScreenUID,
},
screenShareOn,
);

//create screen share track
const { screenTrack, error } = useLocalScreenTrack(screenShareOn, {}, "auto");

//get screen share video track and audio track
useEffect(() => {
if (!screenTrack) {
setScreenAudioTrack(null);
setScreenVideoTrack(null);
} else {
if (Array.isArray(screenTrack)) {
setScreenVideoTrack(
screenTrack.filter(
(track: ILocalTrack) => track.trackMediaType === "video",
)[0] as ILocalVideoTrack,
);
setScreenAudioTrack(
screenTrack.filter(
(track: ILocalTrack) => track.trackMediaType === "audio",
)[0] as ILocalAudioTrack,
);
} else {
setScreenVideoTrack(screenTrack);
}
}
}, [screenTrack]);

//publish screen share
usePublish([screenVideoTrack, screenAudioTrack], screenShareOn);

//if screen got error, close screen share and leave room
useEffect(() => {
setScreenShareOn(false);
}, [error]);

//screen share closed
useTrackEvent(screenVideoTrack, "track-ended", () => {
console.log("screen sharing ended");
setScreenShareOn(false);
});

return (
<Container>
<div className="h-screen p-3">
<Title>useLocalScreenTrack</Title>
<Button onClick={() => setScreenShareOn(a => !a)} type="primary">
{screenShareOn ? "stop share screen" : "start share screen"}
</Button>
{screenShareOn && screenVideoTrack && (
<LocalVideoTrack
disabled={!screenShareOn}
play={screenShareOn}
style={{ width: "600px", height: "400px" }}
track={screenVideoTrack}
/>
)}
</div>
</Container>
);
};

export default UseLocalScreenTrack;
4 changes: 4 additions & 0 deletions examples/basic/src/utils/const.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import type { UID } from "agora-rtc-sdk-ng";
import CryptoJS from "crypto-js";

let appId = import.meta.env.AGORA_APPID;
const ShareScreenUID: UID = 10;

if (import.meta.env.AGORA_AES_SALT) {
// only for github-pages demo
const bytes = CryptoJS.AES.decrypt(import.meta.env.AGORA_APPID, import.meta.env.AGORA_AES_SALT);
Expand All @@ -11,6 +14,7 @@ const appConfig = {
appId: appId,
channel: import.meta.env.AGORA_CHANNEL || "test",
token: import.meta.env.AGORA_TOKEN ? import.meta.env.AGORA_TOKEN : null,
ShareScreenUID: ShareScreenUID,
};

function getAgoraTokens() {
Expand Down
2 changes: 1 addition & 1 deletion examples/mobx/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>

<script src="https://download.agora.io/sdk/release/AgoraRTC_N-4.18.0.js"></script>
<script src="https://unpkg.com/agora-rtc-react@2.0.0-alpha.0/dist/agora-rtc-react.iife.0.0.1.js"></script>
<script src="https://unpkg.com/agora-rtc-react@2.0.0-alpha.0/dist/agora-rtc-react.iife.2.0.0-alpha.0.js"></script>

<script type="module" src="/src/main.tsx"></script>
</body>
Expand Down
2 changes: 1 addition & 1 deletion examples/mobx/src/stores/share-screen.store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { listen } from "agora-rtc-react";
import { listen } from "agora-rtc-react/src/misc/listen";
import type {
IAgoraRTCClient,
ILocalAudioTrack,
Expand Down
2 changes: 1 addition & 1 deletion examples/mobx/src/stores/users.store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { listen } from "agora-rtc-react";
import { listen } from "agora-rtc-react/src/misc/listen";
import type { IAgoraRTCClient, IAgoraRTCRemoteUser, UID } from "agora-rtc-sdk-ng";
import { makeAutoObservable, observable } from "mobx";
import { SideEffectManager } from "side-effect-manager";
Expand Down
1 change: 1 addition & 0 deletions packages/agora-rtc-react/src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ export * from "./useRemoteAudioTracks";
export * from "./useRemoteVideoTracks";
export * from "./useRemoteUserTrack";
export * from "./useRemoteUsers";
export * from "./useLocalScreenTrack";
5 changes: 5 additions & 0 deletions packages/agora-rtc-react/src/hooks/useLocalMicrophoneTrack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ export function useLocalMicrophoneTrack(
const isUnmountRef = useIsUnmounted();

useAsyncEffect(async () => {
if (!isUnmountRef.current) {
setIsLoading(false);
setError(null);
}

if (isConnected && ready && !track) {
try {
if (!isUnmountRef.current) {
Expand Down
Loading

0 comments on commit e0ae84c

Please sign in to comment.