Basic Usage

Fusion Player is a higher-level wrapper built on top of the Lite Player. It supports pluggable Widget components and comes with a set of built-in control methods — ready to use out of the box.

Business Fit

If your business scenario requires built-in features like snapshot, recording, two-way talk, PTZ control, battery status display, etc., with no need to develop them from scratch, Fusion Player is your best choice.
Additionally, Fusion Player supports customization and extension to meet your personalized needs.

Install Dependency

yarn add @ray-js/ipc-player-integration

Basic Usage Example

import { useCtx, IPCPlayerIntegration, Features } from '@ray-js/ipc-player-integration';

function Page() {
  const deviceId = '1234567890xxxxxx';
  
  const instance = useCtx({
    devId: deviceId,
  });
  return (
    <IPCPlayerIntegration
      instance={instance}
      devId={deviceId}
      style={{
        width: '100%',
        height: '300px'
      }}
    />
  )
}

Accessing Player State

The player exposes a variety of data externally, such as whether it is currently in fullscreen mode, or whether two-way talk is active.
For details, please refer to the RetAtom type data in the return value of the useCtx API.

To avoid unnecessary updates caused by unrelated data changes, the exposed data is wrapped with jotai.

To access the actual state values, you need to use useStore.

import { useEffect } from 'react'
import { useCtx, useStore, IPCPlayerIntegration } from '@ray-js/ipc-player-integration';
import '@ray-js/ipc-player-integration/iconfont/iconfont.css';
 
function Page() {
  const deviceId = '1234567890xxxxxx';

  const instance = useCtx({
    devId: deviceId,
  });
  
  const { screenType } = useStore({ screenType: instance.screenType });

  return (
    <IPCPlayerIntegration
      instance={instance}
      devId={deviceId}
    />
  )
}

Widget

Widget is a customizable component rendered on top of the player.
You can dynamically insert or remove widgets using instance methods like addContent and deleteContent returned from the useCtx hook.

The props of each Widget automatically receive the context object from the useCtx hook, making it easy to access player state and control APIs within the component.

Widget Zones

Widgets are grouped into five main display areas:

  1. topLeft: Top-left area
  2. topRight: Top-right area
  3. bottomLeft: Bottom-left area
  4. bottomRight: Bottom-right area
  5. absolute: Absolute positioning area

Widgets shown in the diagram — such as battery level, temperature & humidity, two-way talk, and fullscreen toggle — are common widgets provided by the platform.
You can insert them easily via API, as demonstrated below:

Usage Example

import { useEffect } from 'react'
import { useCtx, IPCPlayerIntegration, Features } from '@ray-js/ipc-player-integration';
import '@ray-js/ipc-player-integration/iconfont/iconfont.css';
 
function Page() {
  const deviceId = '1234567890xxxxxx';
  const instance = useCtx({
    devId: deviceId,
  });

  useEffect(() => {
    // Initialize Widgets  
    // Calling this method will add all the widgets used in the project to the player
    Features.initPlayerWidgets(instance);
  }, [])

  return (
    <IPCPlayerIntegration
      instance={instance}
      devId={deviceId}
    />
  )
}

Calling Features.initPlayerWidgets will add the same widgets to the player as those on the current component.

The Features.initPlayerWidgets method accepts some parameters. For details, please refer to: initPlayerWidgets

Add Custom Widget

In real business scenarios, you may not use all built-in Widgets, but prefer one of the following approaches:

  • ✅ Use only some functional components (e.g., intercom, screenshot)
  • 🔧 Extend custom features based on built-in components
  • 🛠️ Fully customize to meet specific business needs

Next, we will build a Demo to help you get started quickly:

  • ✅ Use the built-in Voice Intercom Widget of the player
  • 🛠️ Custom implement a Screenshot Widget
  • 📸 Show a Toast notification in the player after a successful screenshot
index.tsx
// index.tsx
import React, { useEffect, useRef } from 'react';
import { View } from '@ray-js/components';
import {
  IPCPlayerIntegration,
  useCtx,
  useStore,
  Widgets,
  ComponentConfigProps,
} from '@ray-js/ipc-player-integration';
import Styles from './index.module.less';
// Manually import the icons required by the Widget CSS
import '@ray-js/ipc-player-integration/iconfont/iconfont.css';

// Screenshot Button Widget
function ShotScreen(props: ComponentConfigProps) {
  const {
    IPCPlayerInstance,
    saveToAlbum,
    deleteContent,
    screenType: screenTypeAtom,
    addContent,
  } = props;
  const { screenType } = useStore({
    screenType: screenTypeAtom,
  });
  const timer = useRef(null);
  const timeToCloseToast = () => {
    clearInterval(timer.current);
    // @ts-ignore
    timer.current = setTimeout(() => {
      deleteContent('absolute', 'plugin-screenshot-toast');
    }, 5000);
  };

  const showShotToast = () => {
    // To ensure only one plugin-screenshot-toast is added, delete it first before adding
    deleteContent('absolute', 'plugin-screenshot-toast');

    // Add an absolutely positioned Widget,
    // Positioning is defined by the absolutePosition property and absoluteContentClassName type   
    addContent('absolute', {
      id: 'plugin-screenshot-toast',
      absoluteContentClassName: 'css name',
      absolutePosition: {
        position: 'absolute',
        top: '12px',
        left: '16px',
        right: '16px',
      },
      content: () => {
        return (
          <View
            style={{ color: '#fff', backgroundColor: '#000', width: '100%', textAlign: 'center' }}
          >
            ScreenShot Success
          </View>
        );
      },
    });

    // Remove plugin-screenshot-toast from the player after 5 seconds
    timeToCloseToast();
  };

  const handClick = () => {
    ty.authorize({
      scope: 'scope.writePhotosAlbum',
      success: success => {
        IPCPlayerInstance.snapshot({
          saveToAlbum,
          success: res => {
            console.log(res, 'res');
            showShotToast();
          },
        });
      },
    });
  };

  return (
    <View onClick={handClick} style={{ color: '#fff' }}>
      ScreenShot
    </View>
  );
}

export default function Page(props) {
  const devId = props.location.query.deviceId;
  const instance = useCtx({
    devId,
  });

  useEffect(() => {
    // Add the Widget provided by the component
    instance.addContent('topLeft', {
      id: 'Intercom',
      content: Widgets.VerticalSmallIntercom,
    });
    // Add the custom implemented Widget
    instance.addContent('bottomLeft', {
      id: 'ScreenShot',
      content: ShotScreen,
    });
  }, []);

  return (
    <View className={Styles.container}>
      <View className={Styles.playerWarp} style={{ marginTop: 300 }}>
        <IPCPlayerIntegration
          style={{ width: '100%', height: '300px' }}
          playerFit="cover"
          instance={instance}
          devId={instance.devId}
        />
      </View>
    </View>
  );
}

Demo Preview

For more advanced widget functionalities, please refer to: Animation Widget

Event System

The event system is implemented using a publish-subscribe model.
It allows communication between Widgets, or external components to receive information from Widgets, while keeping them decoupled.

The event system instance can be accessed from the return value of useCtx.
Each player instance has its own independent event system.

import { useCtx, IPCPlayerIntegration, Features } from '@ray-js/ipc-player-integration';
 
function Page() {

  const deviceId = '1234567890xxxxxx';

  const instance = useCtx({
    devId: deviceId,
  });
  const { event } = instance;
  
}

Type Definition

type Task = (...args) => void;

export type EventInstance = {
  on: (type: string, cb: Task) => void;
  off: (type: string, cb: Task) => void;
  emit: (type: string, ...args) => void;
};

Usage Example

The following two Widgets are both registered to the player.

Widget1

import React, { useEffect } from 'react';
import { View } from '@ray-js/components';
import {
  ComponentConfigProps,
} from '@ray-js/ipc-player-integration';
function Widget1(props: ComponentConfigProps) {
  const { event } = props;
  const handClick = () => {
    // Emit an event
    event.emit('event1', 'hello')
  }
  return (
    <View onClick={handClick}></View>
  )
}

Widget2

import React, { useEffect } from 'react';
import { View } from '@ray-js/components';
import {
  ComponentConfigProps,
} from '@ray-js/ipc-player-integration';
function Widget2(props: ComponentConfigProps) {
  const { event } = props;
  const listen = (e) => {
    console.log(e) // hello
  }
  useEffect(() => {
    // Listen to an event
    event.on('event1', listen)
    return () => {
      event.off('event1', listen)
    }
  }, [listen])
  return (
    <View>hello world</View>
  )
}