You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

657 lines
26 KiB

import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import screenfull from 'screenfull';
import moment from "moment";
import request from 'superagent'
import { VideoServeApi, IotVideoServerRequest, checkAudioVideo } from '$utils'
import { Button, ToastFactory, } from '@douyinfe/semi-ui';
import VideoHeader from './voiceHeader'
import VideoOperation from './videoOperation'
import './videoPlay.less';
import EZUIKit, { log } from 'ezuikit-js'
import flvjs from 'flv.js'
const timeFormat = 'YYYY-MM-DD HH:mm:ss'
const yingshiUrl = 'https://open.ys7.com/ezopen/h5/iframe'
const yingshiCloseSoundKey = 'closeSound'
const yingshiOpenSoundKey = 'openSound'
let videoFront
let videoAfter
// let duration = moment.duration(videoAfter.diff(videoFront))._data.milliseconds;
const VideoPlay = ({ dispatch, actions,
height, width, containerId = 'myPlayer',
// playUrl,
name,
global,
videoStyle,
changeData,
microAppVideo,
videoObj,
sizeWh,
slideDown, //视频下方操作是否滑动
IsSize, //是否按照16:9的比例播放
// videoObj = {
// type: 'yingshi',
// audio: false,
// serialNo: 'G75922040', // 设备序列号 必须
// channelNo: 1, //
// yingshiToken: 'at.6nn6duwz8g8gbd919as9a6ea82bmn31x-709fnp7s3k-13q3v7e-mlov7lysf', // 萤石必须
// playUrlSd: 'ezopen://open.ys7.com/G75922040/1.live', // 必须
// // playUrl: 'ws://221.230.55.27:8081/jessica/34020000001110000077/34020000001310000003',
// playUrlHd: 'ezopen://open.ys7.com/G75922040/1.hd.live',
// replayUrl: 'ezopen://open.ys7.com/G75922040/1.hd.local.rec',
// },
// videoObj = {
// type: 'yingshi',
// audio: false,
// serialNo: 'G56385051', // 设备序列号 必须
// channelNo: 1, //
// yingshiToken: 'at.6nn6duwz8g8gbd919as9a6ea82bmn31x-709fnp7s3k-13q3v7e-mlov7lysf', // 萤石必须
// playUrlSd: 'ezopen://open.ys7.com/G56384814/1.live', // 必须
// // playUrl: 'ws://221.230.55.27:8081/jessica/34020000001110000077/34020000001310000003',
// playUrlHd: 'ezopen://open.ys7.com/G56384814/1.hd.live',
// replayUrl: 'ezopen://open.ys7.com/G56384814/1.hd.local.rec',
// },
// videoObj = {
// type: 'cascade',
// audio: false,
// cloudControl: true,
// serialNo: '34020000001310000001', // 设备序列号 必须
// topSerialNo: '34020000001320000001', // 设备顶级序列号 必须
// playUrlSd: 'wss://221.230.55.27:8082/jessica/34020000001320000001/34020000001310000001', // 必须
// // playUrlHd: 'ezopen://open.ys7.com/G75922040/1.hd.live',
// // replayUrl: 'ezopen://open.ys7.com/G75922040/1.rec',
// },
//
iotVideoServer,
iotVideoPlayServer,
local, //是否本地调用视频
}) => {
if (videoObj.type == 'yingshi') {
videoObj = {
...videoObj,
playUrlSd: `ezopen://open.ys7.com/${videoObj.serialNo}/${videoObj.channelNo || '1'}.live`,
playUrlHd: `ezopen://open.ys7.com/${videoObj.serialNo}/${videoObj.channelNo || '1'}.hd.live`,
replayUrl: `ezopen://open.ys7.com/${videoObj.serialNo}/${videoObj.channelNo || '1'}.hd.local.rec`,
}
} else {
videoObj = {
...videoObj,
playUrlSd: `${iotVideoPlayServer}/jessica/${videoObj.topSerialNo}/${videoObj.serialNo}`,
}
}
const { openness } = actions;
const [jessibuca, setjessibuca] = useState(null)
const [player, setPlayer] = useState(null)
const [isPlaying, setIsPlaying] = useState(false)
const [operationState, setoperationState] = useState()
const [voiceDisY, setVoiceDisY] = useState(0)
const [processDisX, setProcessDisX] = useState(0)
const [isAdjustProcess, setIsAdjustProcess] = useState(false)
const [histroyTime, setHistroyTime] = useState([])
const [histroyBegain, setHistroyBegain] = useState()
const [roll, setRoll] = useState()//滚动备注
const [photo, setPhoto] = useState(1)//滚动备注
const [size, setSize] = useState({ parentWidth: sizeWh?.parentWidth, parentHeight: sizeWh?.parentHeight, width: width || sizeWh?.parentWidth, height: height || sizeWh?.parentHeight }) //视频本身和父级尺寸
const [resolution, setResolution] = useState('sd') // 标清 sd 高清 hd
const [videoMask, setVideoMask] = useState(true) // 视频遮罩
const [disappear, setDisappear] = useState(false) // 视频消失
const [numbers, setNumbers] = useState(false)
const [written, setWritten] = useState('')
const [showTime_, setShowTime] = useState('')
const [showTimeSelect, setShowTimeSelect] = useState(false)
const [hideFullScreenBut, setHideFullScreenBut] = useState(false)
// 标记萤石操作中,等待ifream返回信息后清空
const [yingshiPrepare, setYingshiPrepare] = useState('')
const operationRef = useRef(null)
const Begain = useRef()
const playBackTime = useRef([])
const quanp = useRef()
const processChangeTimeoutRef = useRef(null)
// 标记萤石操作中,等待ifream返回信息后清空
const yingshiPrepareRef = useRef(null)
const playState = useRef(false) //播放状态
const yingshiPlayer = useRef(null)
const jessibucas = useRef(null)
const flvPlayer = useRef()
useEffect(() => {
setRoll(false)
}, [resolution]);
useEffect(() => {
if (disappear || !videoMask) {
let duration = moment.duration(videoAfter?.diff(videoFront))._data.milliseconds;
setTimeout(() => {
if (disappear || duration > 2000) {
setVideoMask(true)
dispatch(openness.getErrorCode(videoObj.videoToken ? { status: numbers, platform: videoObj.type, token: videoObj.videoToken } : { status: numbers, platform: videoObj.type })).then((res) => {
if (res.payload.data) {
if (res.payload.data.paraphraseCustom) {
setWritten(res.payload.data.paraphraseCustom)
} else {
if (res.payload.data.describe) {
setWritten(res.payload.data.describe)
} else {
setWritten('视频异常,问题处理中...')
}
}
} else {
setWritten('视频异常,问题处理中...')
}
});
}
}, duration > 2000 ? 0 : 2000 - duration)
}
}, [disappear, videoMask])
useEffect(() => {
setResolution(changeData?.hdChecked ? 'hd' : 'sd')
if (player) {
videoFront = new moment(); //验证前时间
player.stop().then(() => {
player.play({ url: changeData?.hdChecked ? videoObj.playUrlHd : videoObj.playUrlSd })
})
}
}, [changeData?.hdChecked])
const changeSelectState = (key) => {
// if (videoObj.type == 'yingshi' && yingshiPrepareRef.current) {
// return
// }
const nextOperationState = JSON.parse(JSON.stringify(operationRef.current))
if (key == 'histroy' && nextOperationState.histroy.select) {
// 取消历史播放
setProcessDisX(0)
setHistroyTime([])
playBackTime.current = []
}
for (let k in nextOperationState) {
if (k == key) {
nextOperationState[k].select = !nextOperationState[k].select
} else if (k !== 'fullScreen') {
nextOperationState[k].select = false
}
}
operationRef.current = nextOperationState
if (operationRef.current.histroy.select && histroyTime.length == 0) {
setHistroyTime([moment().subtract(72, 'hours').format(timeFormat), moment().format(timeFormat)])
playBackTime.current = [moment().subtract(72, 'hours').format(timeFormat), moment().format(timeFormat)]
}
setoperationState(nextOperationState)
}
// 实时播放左下方操作栏内容
const operation = [{
key: 'control',
hide: !videoObj.cloudControl,
click: () => {
changeSelectState('control')
}
}, {
key: 'talk',
hide: !videoObj.audio,
click: (e) => {
changeSelectState('talk')
}
}, {
key: 'fullScreen',
hide: hideFullScreenBut,
click: (fullNoChange = false) => {
changeSelectState('fullScreen')
let videoplay = document.getElementById("vcmp_videoplay" + videoObj.id)
if (screenfull.isEnabled) {
if (!fullNoChange) {
screenfull.toggle(videoplay);
}
if (videoObj?.type == 'yingshi' && (player || yingshiPlayer.current)) {
if (operationRef.current?.fullScreen.select) {
(player || yingshiPlayer.current).reSize(screen.width, screen.height);
} else {
(player || yingshiPlayer.current).reSize(size?.width, size?.height);
}
}
}
}
}, {
key: 'histroy',
hide: !Boolean(videoObj.replayUrl),
click: () => {
changeSelectState('histroy')
videoFront = new moment(); //验证前时间
player.stop().then(() => {
if (operationRef.current?.histroy?.select && Begain.current) {
player.play({ url: `${videoObj.replayUrl}?begin=${moment(Begain.current).format("YYYYMMDDHHmmss")}&end=${moment(playBackTime.current[1]).format("YYYYMMDDHHmmss")}` })
}
playState.current = false
})
}
},]
// useEffect(() => {
// createPlay()
// }, [quanp.current])
useEffect(() => {
createPlay()
let nextOperationState = {}
for (let p of operation) {
nextOperationState[p.key] = {
select: false
}
}
setoperationState(nextOperationState)
operationRef.current = nextOperationState
// 全屏状态监听
screenfull.on('change', (e) => {
let curFullElement = screenfull.element
if (curFullElement && curFullElement.id == 'vcmp_videoplay' + videoObj.id) {
if (e?.path[0]?.id?.includes("vcmp_videoplay")) {
if (screenfull.isFullscreen && operationRef.current && !operationRef.current['fullScreen'].select) {
changeSelectState('fullScreen')
}
if (!screenfull.isFullscreen && operationRef.current && operationRef.current['fullScreen'].select) {
changeSelectState('fullScreen')
if (yingshiPlayer.current) {
// yingshiPlayer.current.reSize(size?.width, size?.height);
}
}
setHideFullScreenBut(false)
}
} else if (curFullElement && curFullElement.id == 'rearProjection') {
setHideFullScreenBut(true)
} else {
setHideFullScreenBut(false)
setTimeout(() => {
if (operationRef.current.fullScreen.select) {
operation.find(cf => cf.key == 'fullScreen').click(true)
}
}, 0)
}
});
document.onkeydown = (e) => {
}
// const resize_ = () => {
// const VideoLocal = document.getElementById('vcmp_videoplay')
// setSize({ parentWidth: VideoLocal?.clientWidth, parentHeight: VideoLocal?.clientHeight, width: VideoLocal?.clientWidth, height: VideoLocal?.clientHeight })
// }
// if (local) {
// window.addEventListener('resize', resize_); //只要窗口殴大小发生像素变化就会触发
// }
document.addEventListener("visibilitychange", function () {
const buffered = flvPlayer.current.buffered.end(0) - 0.1
if (buffered - flvPlayer.current.currentTime > 1) {
flvPlayer.current.currentTime = buffered
}
})
return () => {
if (jessibucas.current) {
jessibucas.current.destroy()
}
if (flvPlayer && flvPlayer.current) {
flvPlayer.current.destroy();
}
if (flvPlayer.current || jessibucas.current) {
const bye = request.get(`${iotVideoServer}/api/gb28181/bye?id=${videoObj.topSerialNo}&channel=${videoObj.serialNo}`).then(v => {
})
}
if (yingshiPlayer.current && videoObj.type == 'yingshi') {
yingshiPlayer.current.stop()
}
}
}, [])
useEffect(() => {
if (histroyTime.length) {
setHistroyBegain(histroyTime[0])
Begain.current = histroyTime[0]
document.getElementById('process_point').style.left = 0 + 'px'; // 重置进度条的位置
if (videoObj.type == 'yingshi') {
// yingshiPrepareRef.current = 'play'
// setYingshiPrepare('play')
}
} else {
setHistroyBegain(null)
Begain.current == null
}
}, [histroyTime])
useEffect(() => {
// console.log(processDisX);
if (operationState && operationState.histroy.select) {
if (isAdjustProcess) {
// 调整进度条 更新播放开始时间
if (processChangeTimeoutRef.current) {
clearTimeout(processChangeTimeoutRef.current)
}
processChangeTimeoutRef.current = setTimeout(() => {
setHistroyBegain(
moment(histroyTime[0])
.add(
Math.abs(moment(histroyTime[0]).diff(moment(histroyTime[1]), 'seconds')) * (processDisX / document.getElementById('process_point').parentElement.offsetWidth),
'second'
)
.format(timeFormat)
)
Begain.current = moment(histroyTime[0])
.add(
Math.abs(moment(histroyTime[0]).diff(moment(histroyTime[1]), 'seconds')) * (processDisX / document.getElementById('process_point').parentElement.offsetWidth),
'second'
)
.format(timeFormat)
if (videoObj.type == 'yingshi') {
// yingshiPrepareRef.current = 'play'
// setYingshiPrepare('play')
}
}, 300)
videoFront = new moment(); //验证前时间
player.stop().then(() => {
player.play({ url: `${videoObj.replayUrl}?begin=${moment(Begain.current).format("YYYYMMDDHHmmss")}&end=${moment(playBackTime.current[1]).format("YYYYMMDDHHmmss")}` })
playState.current = false
setIsAdjustProcess(false)
})
} else {
// 随播放时间变化更新进度条
document.getElementById('process_point').style.left = processDisX - 4.5 + 'px'
}
}
}, [processDisX])
// videoObj.type = '!yingshi'
const createPlay = async () => {
if (videoObj.type != 'yingshi') {
try {
// const inviteRes = await IotVideoServerRequest.get(VideoServeApi.invite, {
// id: '36011200002002021114',
// channel: '36011200581314002900'
// }).then(res => {
// console.log(res);
// }, err => {.
// console.log(err);
// })
videoFront = new moment(); //验证前时间
const inviteRes_ = await request.get(`${iotVideoServer}/api/gb28181/invite?id=${videoObj.topSerialNo}&channel=${videoObj.serialNo}`).then((res) => {
videoAfter = new moment(); //验证后时间
})
} catch (error) {
console.log(error.response);
}
let container = document.getElementById(containerId);
// 播放方式 1
// const jessibuca = new window.Jessibuca({
// container: container,
// videoBuffer: 0.2, // 缓存时长
// isResize: false,
// text: "",
// loadingText: "加载中",
// debug: true,
// showBandwidth: false, // 显示网速
// operateBtns: {
// fullscreen: false,
// screenshot: false,
// play: false,
// audio: false,
// },
// forceNoOffscreen: false,
// controlAutoHide: true,
// isNotMute: false,
// });
// setjessibuca(jessibuca)
// jessibucas.current = jessibuca
// play({ jessibuca })
// 播放方式 2
const flv = flvjs.createPlayer({
type: 'flv',
// url: 'ws://10.8.30.42:8081/jessica/34020000001110000001/34020000001310000001.flv',
url: `${iotVideoPlayServer}/jessica/${videoObj.topSerialNo}/${videoObj.serialNo}.flv`,
// url: `${iotVideoPlayServer}/hdl/${videoObj.topSerialNo}/${videoObj.serialNo}.flv`,
isLive: true,
hasAudio: false,
hasVideo: true,
}, {
enableWorker: false,
enalleStashBuffer: true,
stashInitialSize: 128,
lazyLoadMaxDuration: 3 * 60,
seekType: 'range',
autoCleanupSourceBuffer: true,
cors: true,
});
flv.attachMediaElement(container);
flv.load();
flv.play();
flvPlayer.current = flv
} else {
videoFront = new moment(); //验证前时间
const player = new EZUIKit.EZUIKitPlayer({
id: containerId, // 视频容器ID
accessToken: videoObj?.yingshiToken,
url: videoObj.playUrlSd,
width: setupSize('width'),
height: setupSize('height'),
handleSuccess: (e) => { //播放成功
setRoll(true)
setVideoMask(false)
playState.current = true
videoAfter = new moment(); //验证后时间
},
handleError: (e) => { //播放失败
console.log(e, '播放失败');
setNumbers(e.retcode)
videoAfter = new moment(); //验证后时间
setDisappear(true)
},
})
setPlayer(player)
yingshiPlayer.current = player
}
}
const play = (params) => {
if (videoObj.type == 'yingshi') {
} else if ((params.jessibuca || jessibuca) && videoObj.playUrlSd) {
const jes = params.jessibuca || jessibuca
jes.play(videoObj.playUrlSd);
setIsPlaying(true)
}
}
const pause = () => {
if (videoObj.type == 'yingshi' && player) {
} else if (jessibuca) {
jessibuca.pause();
setIsPlaying(false)
}
}
const closeSound = () => {
if (videoObj.type == 'yingshi') {
yingshiPrepareRef.current = yingshiCloseSoundKey
setYingshiPrepare(yingshiCloseSoundKey)
// yingshiOperation(yingshiCloseSoundKey)
}
}
const openSound = () => {
if (videoObj.type == 'yingshi') {
yingshiPrepareRef.current = yingshiOpenSoundKey
setYingshiPrepare(yingshiOpenSoundKey)
// yingshiOperation(yingshiOpenSoundKey)
}
}
const setupSize = (data) => {
if (!operationRef.current?.fullScreen.select) {
let containerWidth = sizeWh?.parentWidth //容器的宽高和视频的宽高
let containerHeight = sizeWh?.parentHeight
let videoWidth = width || sizeWh?.width
let videoHeight = height || sizeWh?.height
if (IsSize == 'true') {
if (containerWidth >= videoWidth && containerHeight >= videoHeight) {
if (videoHeight / videoWidth >= 0.55 && videoHeight / videoWidth <= 0.57) {
} else {
console.warn('宽高不符合尺寸要求,故返回合适的尺寸')
if (videoWidth / 16 > videoHeight / 9) {
videoWidth = (videoHeight / 9) * 16
} else {
videoHeight = (videoWidth / 16) * 9
}
}
} else {
console.warn('传递宽高参数有误,请确认容器大小范围内')
if (containerWidth / 16 > containerHeight / 9) {
videoWidth = (containerHeight / 9) * 16
} else {
videoHeight = (containerWidth / 16) * 9
}
}
if (videoObj.type == 'yingshi' && player) {
player.reSize(videoWidth, videoHeight)
}
} else {
if (videoObj.type == 'yingshi' && player?.pluginStatus) {
player.reSize(videoWidth, videoHeight)
}
}
setSize({ parentWidth: containerWidth, parentHeight: containerHeight, width: videoWidth, height: videoHeight })
return data == 'width' ? videoWidth : data == 'height' ? videoHeight : ''
}
}
useEffect(() => {
setupSize()
}, [sizeWh])
return (
<>
<div className='vcmp_videoplay' style={{ width: size?.parentWidth || '100%', height: size?.parentHeight || '100%', backgroundColor: 'black', overflow: 'hidden', display: 'flex', alignItems: 'center', justifyContent: 'center', }}>
<div id={"vcmp_videoplay" + videoObj.id} className="vcmp_video" style={{ position: 'relative', height: size?.height || '100%', width: size?.width || '100%', minWidth: 240, minHeight: 135, overflow: 'hidden' }}>
{/* <VideoHeader
operationState={operationState} changeSelectState={changeSelectState}
histroyTime={histroyTime}
setoperationState={setoperationState} name={name}
roll={roll}
videoObj={videoObj}
showTime={histroyBegain || moment()}
setProcessDisX={setProcessDisX}
content={videoObj.content}
videoStyle={videoStyle}
player={player}
resolution={resolution}
playState={playState.current}
videoFront={videoFront}
videoAfter={videoFront}
showTime_={showTime_}
setShowTime={setShowTime}
setShowTimeSelect={setShowTimeSelect}
/> */}
{/* 视频内容 */}
{
// <iframe
// frameBorder="0"
// allowFullScreen='true'
// id={containerId}
// src={
// `${yingshiUrl}?audio=${videoObj.audio && operationState && !operationState.histroy.select ? '1' : '0'}&url=${operationState && operationState.histroy.select && histroyBegain ? `${videoObj.replayUrl}?begin=${moment(histroyBegain).format("YYYYMMDDHHmmss")}&end=${moment(histroyTime[1]).format("YYYYMMDDHHmmss")}` : resolution == 'sd' ? videoObj.playUrlSd : videoObj.playUrlHd}&autoplay=${'1'}&accessToken=${videoObj.yingshiToken}`
// }
// // https://open.ys7.com/doc/zh/book/index/live_proto.html
// // 单个播放器的长宽比例限制最小为{width: 400px;height: 300px;}
// width={'100%'}
// height={'100%'}
// wmode="transparent"
// style={{ pointerEvents: 'none' }}
// />
}
{
videoObj.type == 'yingshi' ?
<div id={containerId} style={{ height: '100%', width: '100%', }}></div> :
<video id={containerId}
autoplay muted
style={{ height: '100%', width: '100%', }}>
</video>
}
{/* {videoMask ? <div style={{ height: '100%', width: '100%', position: 'absolute', top: '0', left: '0', backgroundColor: 'black', color: "red", }}><div style={{ position: 'absolute', top: 'calc(60%)', left: 0, zIndex: 99, textAlign: 'center', width: '100%' }}>{written}</div></div> : ''} */}
{/* 下方操作栏 */}
{/* 显示操作功能条件(宽高) */}
{size?.parentWidth > 479 ?
<VideoOperation
operationState={operationState} operation={operation}
voiceDisY={voiceDisY} setVoiceDisY={setVoiceDisY}
processDisX={processDisX} setProcessDisX={setProcessDisX}
isAdjustProcess={isAdjustProcess} setIsAdjustProcess={setIsAdjustProcess}
resolution={resolution} setResolution={setResolution}
histroyTime={histroyTime} setHistroyTime={setHistroyTime}
histroyBegain={histroyBegain}
play={play} pause={pause} closeSound={closeSound} openSound={openSound}
isPlaying={isPlaying}
videoObj={videoObj}
setYingshiPrepare={setYingshiPrepare}
yingshiPrepareRef={yingshiPrepareRef}
slideDown={slideDown}
setPlayer={setPlayer}
player={player}
videoFront={videoFront}
videoAfter={videoFront}
playState={playState.current}
showTime_={showTime_}
playBackTime={playBackTime.current}
showTimeSelect={showTimeSelect}
setShowTimeSelect={setShowTimeSelect}
microAppVideo={microAppVideo}
/> : ""
}
</div>
</div>
</>
)
}
function mapStateToProps (state) {
const { auth, global } = state;
return {
user: auth.user,
iotVideoServer: global.iotVideoServer,
iotVideoPlayServer: global.iotVideoPlayServer,
actions: global.actions,
};
}
export default connect(mapStateToProps)(VideoPlay);