You need to enable JavaScript to run this app.
导航
示例代码
最近更新时间:2024.10.17 17:47:09首次发布时间:2024.10.17 17:47:09

本文提供 React Native 点播 SDK 部分功能的详细示例代码。

预渲染示例代码

以下为以 @ant-design/react-nativeCarousel 作为轮播组件容器实现预渲染的示例代码:

// index.tsx
import React, {useCallback, useState, useEffect} from 'react';
import {View, Carousel} from '@ant-design/react-native';
import {StyleSheet, SafeAreaView} from 'react-native';
import {setPreRenderCallback} from '@volcengine/react-native-vod-player';
import type {
  TTVideoEngine,
  VideoSource,
} from '@volcengine/react-native-vod-player';
import PrePlayer from './PrePlayer';

const sourceDataList = [
  {
    url: 'https://demo.com/1.mp4?auth_key=18221***732f',
    vid: 'v0ddfa0***h92v9l0',
    cacheKey: 'v0dd***h92v9l0',
    poster: 'https://demo-cover.com/t***c533b04/57bd2***53fd~tplv-vod-noop.image',
  },
  {
    url: 'https://demo.com/2.mp4?auth_key=182002***598e',
    vid: 'v03dfag10*****4ar8ft0',
    cacheKey: 'v03d***4ar8ft0',
    subUrl: 'https://demo.com/e87***61e?auth_key=176075***71f5&mime_type=text_plain',
    poster: 'https://demo-cover.com/t***c53b04/158***9621c~tplv-vod-noop.image',
  },
  {
    url: 'https://demo.com/3.mp4?auth_key=18***c935',
    vid: 'v03dfag100***9s8kevs0',
    cacheKey: 'v03df***cpig9s8kevs0',
    poster: 'https://demo-cover.com/t***533b04/201***fe9a9f7~tplv-vod-noop.image',
  },
  {
    url: 'https://demo.com/4.mp4?auth_key=1820024284-***a9139',
    vid: 'v02dfag10***39jdg',
    cacheKey: 'v02dfa***imkm239jdg',
    poster: 'https://demo-cover.com/t*****c997******3b04/e12b***4d15c2~tplv-vod-noop.image',
  },
  {
    url: 'https://demo.com/5.mp4?auth_key=1820024298-90***bafc3032',
    vid: 'v03dfag10***ppj65150',
    cacheKey: 'v03df***j65150',
    poster: 'https://demo-cover.com/t***c-8a997967cc533b04/beac53***76d403dea~tplv-vod-noop.image',
  },
  {
    url: 'https://demo.com/6.mp4?auth_key=182219***afec',
    vid: 'v02dfag1***1v0vjl8jmvgg',
    cacheKey: 'v02df***vjl8jmvgg',
    poster: 'https://demo-cover.com/t***33b04/41ac***bbdb6020f~tplv-vod-noop.image',
  },
];

const styles = StyleSheet.create({
  wrapper: {
    flex: 1,
    position: 'relative',
  },
  text: {
    color: '#fff',
    fontSize: 30,
    fontWeight: 'bold',
  },
  info: {
    position: 'absolute',
    top: 40,
    left: 10,
    width: '60%',
    height: 300,
    backgroundColor: 'rgba(255, 255, 255, 0.5)',
  },
});

const PreRender = () => {
  const [currentIndex, setIndex] = useState(0);
  const [isScrollDrag, setScrollDrag] = useState(false);

  const onIndexChanged = useCallback(index => {
    setIndex(index);
  }, []);

  useEffect(() => {
    setPreRenderCallback({
      onPlayerCreated(player: TTVideoEngine, source: VideoSource) {
        const index = sourceDataList.findIndex(e => e.vid === source.vid());
        if (index < 0) {
          return;
        }
        const sourceData = sourceDataList[index];
        const {subUrl} = sourceData;
        if (subUrl) {
          // 设置预渲染播放器的字幕
          player.enableSubThread(true);
          player.enableSubtitle(true);
          player.setSubDesInfoModel({
            list: [
              {
                id: 1,
                language: 'cmn-Hans-CN',
                language_id: 1,
                url: subUrl,
                format: 'webvtt',
                sub_id: 1,
              },
            ],
          });
        }
      },
      onPlayerWillPrepare(player: TTVideoEngine) {
        console.log('player will prepare', player);
      },
      onPlayerExtraSetup(player: TTVideoEngine, source: VideoSource) {
        console.log('player extra setup', player, source);
      },
    });
  }, []);

  const onScrollBeginDrag = useCallback(() => {
    setScrollDrag(true);
  }, []);
  const onScrollEndDrag = useCallback(() => {
    setScrollDrag(false);
  }, []);

  return (
    <SafeAreaView style={styles.wrapper}>
      <Carousel
        style={{width: '100%', height: '100%'}}
        vertical
        afterChange={onIndexChanged}
        onScrollBeginDrag={onScrollBeginDrag}
        onScrollEndDrag={onScrollEndDrag}
        overScrollMode="never">
        {sourceDataList.map((item, index) => {
          return (
            <View key={item.vid}>
              <PrePlayer
                isScrollDrag={isScrollDrag}
                data={item}
                currentIndex={currentIndex}
                index={index}
              />
            </View>
          );
        })}
      </Carousel>
    </SafeAreaView>
  );
};

export default PreRender;

以下为使用 React Native 播放视图组件实现预渲染的示例代码:

// PrePlayer.tsx
import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
import {View, StyleSheet, Image} from 'react-native';
import {
  PlayerViewComponent,
  TTVideoEngine,
  VideoSource,
  createDirectUrlSource,
  getPreRenderEngine,
  setView,
  initPlayer,
  getFirstFrameEngine,
  type DirectUrlSourceInitProps,
} from '@volcengine/react-native-vod-player';

interface SwiperPlayerProps {
  currentIndex: number;
  index: number;
  data: DirectUrlSourceInitProps & {
    poster: string;
  };
  isScrollDrag: boolean;
}

const styles = StyleSheet.create({
  wrap: {
    position: 'relative',
    width: '100%',
    height: '100%',
  },
  player: {
    width: '100%',
    height: '100%',
    backgroundColor: '#000000',
  },
  img: {
    position: 'absolute',
    width: '100%',
    height: '100%',
    resizeMode: 'contain',
    top: 0,
    left: 0,
    backgroundColor: '#000000',
  },
});

const PrePlayer: React.FC<SwiperPlayerProps> = ({
  currentIndex,
  index,
  data,
  isScrollDrag,
}) => {
  const isCurrent = useMemo(
    () => index === currentIndex,
    [index, currentIndex],
  );
  const viewId = useMemo<string>(() => `pre-player-${index}`, [index]);
  const [canInit, setCanInit] = useState(false);
  // 预渲染 engine
  const playerRef = useRef<TTVideoEngine | undefined>();
  const [showPoster, setShowPoster] = useState(true);

  const handleStart = useCallback(() => {
    setCanInit(true);
  }, []);

  const setPlayer = useCallback(async () => {
    if (!playerRef.current) {
      setShowPoster(true);
      const curSource: VideoSource = createDirectUrlSource(data);
      const preRenderEngine = getPreRenderEngine(curSource);
      if (preRenderEngine && index !== 0) {
        // 命中预渲染
        await setView(preRenderEngine, viewId);
        setShowPoster(false);
        playerRef.current = preRenderEngine;
      } else {
        // 未命中预渲染
        const player = await initPlayer({viewId});
        player.setVideoSource(curSource);
        playerRef.current = player;
        playerRef.current?.setListener({
          onReadyToDisplay() {
            setShowPoster(false);
          },
        });
      }
      if (isCurrent) {
        playerRef.current?.play();
      }
    }
  }, [data, index, isCurrent, viewId]);

  useEffect(() => {
    return () => {
      playerRef.current?.close();
    };
  }, []);

  useEffect(() => {
    if (!isCurrent) {
      playerRef.current?.close();
      playerRef.current = undefined;
      setShowPoster(true);
    } else {
      if (canInit) {
        setPlayer();
      }
    }
  }, [canInit, index, isCurrent, setPlayer]);

  const whenDrag = useCallback(async () => {
    if (canInit) {
      const source: VideoSource = createDirectUrlSource(data);
      const preRenderEngine = getFirstFrameEngine(source);
      if (preRenderEngine) {
        console.log('hit prerender, set first frame');
        await setView(preRenderEngine, viewId);
      }
    }
  }, [canInit, data, viewId]);

  useEffect(() => {
    if (index === currentIndex + 1 && isScrollDrag) {
    // 对当前下一个视图使用预渲染的播放器实例设置首帧封面
      whenDrag();
    }
  }, [canInit, currentIndex, index, isCurrent, isScrollDrag, whenDrag]);

  return (
    <View style={styles.wrap}>
      <PlayerViewComponent
        style={styles.player}
        viewId={viewId}
        onLoad={handleStart}
      />
      <Image
        style={[styles.img, showPoster ? {opacity: 1} : {opacity: 0}]}
        source={{uri: data.poster}}
      />
    </View>
  );
};

export default PrePlayer;