基础使用

融合播放器是基于轻量播放器再次封装的产物,支持可插拔的 Widget 组件,内置封装了一系列播放器控制的方法,开箱即用。

业务匹配

如果您的业务场景需要播放器里内置截图、录制、对讲、云台控制、电量显示等基础功能,无需额外开发,只需专注其它功能的交互, 那么融合播放器是您的最佳选择, 同时融合播放器也支持自定义扩展,可满足您的个性化需求。

安装依赖

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

使用示例

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'
      }}
    />
  )
}

获取播放器的数据

播放器向外暴露了很多数据,比如当前是否处于全屏模式、当前是否处于对讲中等,详情请参照 useCtx api 返回参数中的 RetAtom 类型的数据。 为了避免无关数据引起不必要的更新的问题,播放器暴露出的数据使用 jotai 包装了一层,要获取状态需要使用 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 是展示在播放器上的自定义组件。您可通过 useCtx 返回的实例方法,如 addContent、deleteContent,动态向播放器插入或移除 Widget。

在 Widget 的 props 中,会自动注入 useCtx hook 的上下文对象,便于组件内部访问播放器状态与操作接口。

部件分类

将 Widget 主要分为了五个展示区域:

  1. topLeft: 左上角区域
  2. topRight: 右上角区域
  3. bottomLeft: 左下角区域
  4. bottomRight: 右下角区域
  5. absolute: 绝对定位区域

图中展示的电池电量、温湿度、语音对讲、全屏切换等组件,都是平台已提供的通用 Widget。您可通过 API 便捷插入,示例如下:

使用示例

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(() => {
    // 初始化 Widget
    // 调用此方法后将为播放器添加项目所有使用到的 Widget
    Features.initPlayerWidgets(instance);
  }, [])

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

调用 Features.initPlayerWidgets 会给播放器添加与当前组件上相同的 Widget。

Features.initPlayerWidgets 方法会接收一些入参,详情请参照: initPlayerWidgets

添加自定义 Widget

在实际业务场景中,您可能不会使用全部内置的 Widget,而是更倾向于以下几种方式:

  • ✅ 仅使用部分功能组件(如对讲、截屏)
  • 🔧 基于内置组件扩展自定义功能
  • 🛠️ 完全自定义实现,满足特定业务需求

接下来我们将通过构建一个 Demo,帮助您快速上手

  • ✅ 使用播放器内置的 语音对讲 Widget
  • 🛠️ 自定义实现一个 截屏 Widget
  • 📸 截图成功后,在播放器中展示 Toast 提示
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';
// 手动引入一下css Widget 所需 icon
import '@ray-js/ipc-player-integration/iconfont/iconfont.css';

// 截图按钮 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 = () => {
    // 为了确保只添加了一个 plugin-screenshot-toast 添加之前先删除一遍
    deleteContent('absolute', 'plugin-screenshot-toast');

    // 添加绝对定位的 Widget,
    // 定位信息由 absolutePosition 属性absoluteContentClassName 类型去定义
    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>
        );
      },
    });

    // 实现 5s 后,从播放器中删除 plugin-screenshot-toast
    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(() => {
    // 添加组件提供的 Widget
    instance.addContent('topLeft', {
      id: 'Intercom',
      content: Widgets.VerticalSmallIntercom,
    });
    // 添加自定义实现的 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>
  );
}

效果示意图

关于 Widget 的更复杂一点的功能实现请参照:Animation Widget

事件系统

事件系统采用发布订阅的形式实现,用于实现 Widget 之间的通信,或者从外部获取 Widget 传递的信息且解耦了它们之间的联系。

事件系统的实例,需要从 useCtx 返回的参数中读取,每一个播放器实例都拥有独立的事件系统

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

  const deviceId = '1234567890xxxxxx';

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

类型定义

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

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

使用示例

以下两个 Widget 都被注册到了播放器中

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 = () => {
    // 发布事件
    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(() => {
    // 监听事件
    event.on('event1', listen)
    return () => {
      event.off('event1', listen)
    }
  }, [listen])
  return (
    <View>hello world</View>
  )
}