mediasfu-reactnative-expo
Version:
mediasfu-reactnative-expo – Expo-managed React Native WebRTC SDK for video conferencing, webinars, live streaming, broadcast, screen sharing, whiteboard, chat, recording, live subtitles, translation, and AI agent rooms on iOS, Android, and web. Prebuilt r
248 lines • 8.01 kB
JavaScript
// FlexibleVideo.tsx
import React, { useEffect, useState } from 'react';
import { View, StyleSheet } from 'react-native';
/**
* FlexibleVideo - Video grid layout with screen sharing and annotation support
*
* FlexibleVideo is a specialized React Native component for displaying video streams
* in a flexible grid layout. It extends FlexibleGrid with additional support for
* screen sharing overlays and real-time annotation capabilities.
*
* **Key Features:**
* - Dynamic video grid with custom rows/columns
* - Aspect ratio preservation for video streams
* - Screen sharing overlay (Screenboard) support
* - Real-time screen annotation capabilities
* - Custom cell dimensions and background colors
* - Efficient video stream rendering
*
* **UI Customization:**
* This component can be replaced via `uiOverrides.flexibleVideoComponent` to
* provide a completely custom video grid implementation.
*
* @component
* @param {FlexibleVideoOptions} props - Configuration options for the FlexibleVideo component
*
* @returns {JSX.Element} Rendered video grid with optional screen sharing overlay
*
* @example
* // Basic usage - 2x2 video grid
* import React from 'react';
* import { FlexibleVideo, VideoCard } from 'mediasfu-reactnative-expo';
*
* function VideoGrid() {
* const participants = [p1, p2, p3, p4];
*
* const videoComponents = participants.map((p, idx) => (
* <VideoCard
* key={idx}
* name={p.name}
* participant={p}
* videoStream={p.stream}
* parameters={sessionParams}
* />
* ));
*
* return (
* <FlexibleVideo
* customWidth={400}
* customHeight={300}
* rows={2}
* columns={2}
* componentsToRender={videoComponents}
* showAspect={true}
* backgroundColor="#000"
* />
* );
* }
*
* @example
* // With screen sharing annotation
* <FlexibleVideo
* customWidth={800}
* customHeight={600}
* rows={1}
* columns={1}
* componentsToRender={[screenShareVideo]}
* showAspect={true}
* backgroundColor="#1a1a2e"
* Screenboard={<ScreenboardComponent />}
* annotateScreenStream={true}
* localStreamScreen={localScreenStream}
* />
*
* @example
* // Using uiOverrides for complete video grid replacement
* import { MyCustomVideoGrid } from './MyCustomVideoGrid';
*
* const sessionConfig = {
* credentials: { apiKey: 'your-api-key' },
* uiOverrides: {
* flexibleVideoComponent: {
* component: MyCustomVideoGrid,
* injectedProps: {
* layout: 'spotlight',
* transition: 'smooth',
* },
* },
* },
* };
*
* // MyCustomVideoGrid.tsx
* export const MyCustomVideoGrid = (props: FlexibleVideoOptions & { layout: string; transition: string }) => {
* return (
* <View style={{ flex: 1 }}>
* {props.layout === 'spotlight' ? (
* <View style={{ flex: 1 }}>{props.componentsToRender[0]}</View>
* ) : (
* props.componentsToRender.map((component, idx) => (
* <View key={idx} style={{ width: props.customWidth, height: props.customHeight }}>
* {component}
* </View>
* ))
* )}
* {props.Screenboard}
* </View>
* );
* };
*/
const FlexibleVideo = ({ customWidth, customHeight, rows, columns, componentsToRender, showAspect = false, backgroundColor = 'transparent', Screenboard, annotateScreenStream = false, localStreamScreen, style, renderContent, renderContainer, }) => {
const [key, setKey] = useState(0);
const [cardWidth, setCardWidth] = useState(customWidth);
const [cardHeight, setCardHeight] = useState(customHeight);
const [, setCardTop] = useState(0);
const [cardLeft, setCardLeft] = useState(0);
const [canvasLeft, setCanvasLeft] = useState(0);
useEffect(() => {
setKey((prevKey) => prevKey + 1);
}, [columns]);
useEffect(() => {
if (annotateScreenStream && localStreamScreen) {
const videoTrack = localStreamScreen.getVideoTracks()[0];
const videoSettings = videoTrack.getSettings();
const videoHeight = videoSettings.height || customHeight;
const videoWidth = videoSettings.width || customWidth;
setCardWidth(videoWidth);
setCardHeight(videoHeight);
setCardTop(Math.floor((customHeight - videoHeight) / 2));
setCardLeft(Math.floor((customWidth - videoWidth) / 2));
setCanvasLeft(cardLeft < 0 ? cardLeft : 0);
}
else {
setCardWidth(customWidth);
setCardHeight(customHeight);
setCardTop(0);
setCardLeft(0);
setCanvasLeft(0);
}
}, [
customWidth,
customHeight,
localStreamScreen,
annotateScreenStream,
cardLeft,
]);
/**
* Renders the grid layout based on the number of rows and columns.
*
* @returns {React.ReactNode[]} Array of React elements representing the grid.
*/
const renderGrid = () => {
const grid = [];
for (let row = 0; row < rows; row++) {
const rowComponents = [];
for (let col = 0; col < columns; col++) {
const index = row * columns + col;
const component = componentsToRender[index];
rowComponents.push(<View key={col} style={[
styles.gridItem,
{
width: cardWidth,
height: cardHeight,
backgroundColor,
margin: 1,
padding: 0,
borderRadius: 0,
left: cardLeft,
},
]}>
{component}
</View>);
}
grid.push(<View key={row} style={styles.rowContainer}>
{rowComponents}
</View>);
}
return grid;
};
const dimensions = {
width: cardWidth * columns,
height: cardHeight * rows,
};
const defaultContent = (<>
{renderGrid()}
{Screenboard && (<View style={[
styles.screenboardOverlay,
{
top: 0,
left: canvasLeft,
width: cardWidth,
height: cardHeight,
backgroundColor: 'rgba(0, 0, 0, 0.005)',
zIndex: 2,
},
]}>
{Screenboard}
</View>)}
</>);
const content = renderContent
? renderContent({ defaultContent, dimensions })
: defaultContent;
const defaultContainer = (<View key={key} style={[
styles.gridContainer,
{
padding: 0,
flex: 1,
margin: 0,
position: 'relative',
display: showAspect ? 'flex' : 'none',
maxWidth: customWidth,
overflow: 'hidden',
left: cardLeft > 0 ? cardLeft : 0,
},
style,
]}>
{content}
</View>);
return renderContainer
? renderContainer({ defaultContainer, dimensions })
: defaultContainer;
};
export default FlexibleVideo;
/**
* Stylesheet for the FlexibleVideo component.
*/
const styles = StyleSheet.create({
gridContainer: {
// Additional container styles can be added here if needed
},
rowContainer: {
flexDirection: 'row',
},
gridItem: {
flex: 1,
margin: 1,
padding: 0,
borderRadius: 0,
},
screenboardOverlay: {
position: 'absolute',
top: 0,
left: 0,
// width and height are set dynamically via inline styles
backgroundColor: 'rgba(0, 0, 0, 0.005)',
zIndex: 2,
// Additional overlay styles can be added here
},
});
//# sourceMappingURL=FlexibleVideo.js.map