西瓜播放器是一个Web视频播放器类库,它本着一切都是组件化的原则设计了独立可拆卸的 UI 组件。更重要的是它不只是在 UI 层有灵活的表现,在功能上也做了大胆的尝试:摆脱视频加载、缓冲、格式支持对 video 的依赖。尤其是在 mp4 点播上做了较大的努力,让本不支持流式播放的 mp4 能做到分段加载,这就意味着可以做到清晰度无缝切换、加载控制、节省视频流量。同时,它也集成了对 flv、hls、dash 的点播和直播支持。
安装:
- npm install xgplayer
-
- npm install xgplayer-flv
-
- npm install xgplayer-hls
按需引入即可,代码如下:
- import MonitorLiveBroadComp from "./pages/Monitor/index";
- import { useEffect, useState } from 'react'
- import logo from './logo.svg'
- import './App.css';
- import mp4Player from 'xgplayer';
- import flvPlayer from 'xgplayer-flv';
- import hlsPlayer from 'xgplayer-hls';
- import ismobilejs from 'ismobilejs';
- import picNotStart from './assets/img/img_video_default_notstart@2x.png';
- import picTranscode from './assets/img/img_video_default_transcoding@2x.png';
- import querystring from 'query-string';
- import { apiHost } from "./hosts";
-
- const {cid, token} = querystring.parse(window.location.search);
- if (!cid || !token) console.warn('需要token和cid信息');
-
- const isMobile = ismobilejs();
-
- enum clsSt {
- wait = 0,
- earlyExtra = 3,
- during = 1,
- lateExtra = 4,
- end = 2,
- }
-
- function App() {
- const [mode, setMode] = useState<'hls'|'flv'|'mp4'|'stop'|'wait'|'transcode'>('wait');
- const [url, setUrl] = useState('');
- useEffect(() => {
- fetch(apiHost+`/api/client/monitor/stream?classroomId=${cid}`, {
- method: 'GET',
- headers: { token: ''+token }
- }).then(data => data.json()).then(json => {
- if (json.code !== 200) throw new Error(`获取推流信息失败(${json.code}, ${json.msg}).`);
- const {status, streamUrl, videoUrl} = json.data;
- const inClass = [clsSt.earlyExtra, clsSt.lateExtra, clsSt.during].indexOf(status) > -1;
- if (inClass && streamUrl) {
- const streamType = isMobile.apple.device ? 'hls' : 'flv';
- setMode(streamType);
- setUrl(streamUrl.replace(/\.(flv|m3u8)$/, streamType === 'flv' ? '.flv' : '.m3u8'));
- } else if (inClass) {
- setMode('stop');
- setUrl('');
- } else if (status === clsSt.end && videoUrl) {
- setMode('mp4');
- setUrl(videoUrl);
- } else if (status === clsSt.end && !videoUrl) {
- setMode('transcode');
- setUrl('');
- }
- }).catch(err => {console.error(err)});
- return () => {}
- }, []);
- useEffect(() => {
- const Player = ({
- 'flv': flvPlayer,
- 'hls': hlsPlayer,
- 'mp4': mp4Player,
- } as any)[mode];
- let playerIns: any;
- url && setTimeout(() => {
- playerIns = new Player(Object.assign({
- id: 'monitorVideo',
- url: url,
- width: '100%',
- height: '100%',
- playsinline: true,
- closeVideoTouch: true,
- ignores: ['fullscreen'],
- }, ({
- 'flv': {
- isLive: true,
- preloadTime: 30,
- minCachedTime: 5,
- // cors: false,
- },
- 'hls': {
- isLive: true,
- config: {duration: NaN}
- },
- 'mp4' : {
- playbackRate: [0.5, 0.75, 1, 1.5, 2],
- },
- }as any)[mode]));
- mode === 'hls' && Object.defineProperty(playerIns, 'duration', {value: NaN});
- }, 0)
- return () => {
- playerIns?.pause();
- playerIns?.destroy();
- };
- }, [url, mode]);
-
- return (
- <div className={isMobile.apple.tablet ? 'App ipad' : 'App'}>
- <div className="video-wrap">
- <div id="monitorVideo" className="video-container">div>
- {mode === 'transcode' && <div className="notify"><img src={picNotStart} alt="" /><p>视频转码中...p>div> }
- {mode === 'wait' && <div className="notify"><img src={picTranscode} alt="" /><p>课程尚未开始p>div> }
- {mode === 'stop' && <div className="notify"><p>主讲可能退出教室,请尝试刷新p>div> }
- div>
- <MonitorLiveBroadComp />
- div>
- )
- }
-
- export default App