西瓜播放器是一个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