Browse Source

视频播放组件 40%

release_0.0.2
yuan_yi 3 years ago
parent
commit
b75fee46ea
  1. 42
      code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js
  2. 4
      code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js
  3. 3
      code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js
  4. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-control-select.png
  5. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-control-unselect.png
  6. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-fullScreen-select.png
  7. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-fullScreen-unselect.png
  8. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-histroy-select.png
  9. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-histroy-unselect.png
  10. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-talk-select.png
  11. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-talk-unselect.png
  12. BIN
      code/VideoAccess-VCMP/web/client/assets/images/background/videoPlayBg.png
  13. 1
      code/VideoAccess-VCMP/web/client/assets/js/jessibuca/decoder.js
  14. BIN
      code/VideoAccess-VCMP/web/client/assets/js/jessibuca/decoder.wasm
  15. 1
      code/VideoAccess-VCMP/web/client/assets/js/jessibuca/jessibuca.js
  16. BIN
      code/VideoAccess-VCMP/web/client/assets/video/cross_loading.mp4
  17. BIN
      code/VideoAccess-VCMP/web/client/assets/video/login_bg.mp4
  18. 9
      code/VideoAccess-VCMP/web/client/index.ejs
  19. 1
      code/VideoAccess-VCMP/web/client/index.html
  20. 6
      code/VideoAccess-VCMP/web/client/src/components/index.js
  21. 48
      code/VideoAccess-VCMP/web/client/src/components/textScroll.jsx
  22. 19
      code/VideoAccess-VCMP/web/client/src/components/textScroll.less
  23. 143
      code/VideoAccess-VCMP/web/client/src/components/videoPlay.jsx
  24. 0
      code/VideoAccess-VCMP/web/client/src/components/videoPlay.less
  25. 32
      code/VideoAccess-VCMP/web/client/src/components/videoPlayModal.jsx
  26. 5
      code/VideoAccess-VCMP/web/client/src/components/videoPlayModal.less
  27. 11
      code/VideoAccess-VCMP/web/client/src/layout/containers/layout/index.jsx
  28. 1
      code/VideoAccess-VCMP/web/client/src/layout/index.jsx
  29. 18
      code/VideoAccess-VCMP/web/client/src/sections/auth/containers/login.jsx
  30. 1314
      code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/camera.jsx

42
code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js

@ -96,6 +96,7 @@ async function createYingshi (ctx) {
async function getNvrSteam (ctx) { async function getNvrSteam (ctx) {
let errMsg = '获取 NVR 视频流失败' let errMsg = '获取 NVR 视频流失败'
try { try {
const { models } = ctx.fs.dc
const { streamId } = ctx.query const { streamId } = ctx.query
const { utils: { getGbCameraLevel3ByStreamId } } = ctx.app.fs const { utils: { getGbCameraLevel3ByStreamId } } = ctx.app.fs
@ -109,8 +110,24 @@ async function getNvrSteam (ctx) {
throw errMsg throw errMsg
} }
const addedRes = await models.Camera.findAll({
attributes: ['id', 'name', 'serialNo'],
where: {
nvrId: nvrRes.id
}
})
const cameraRes = await getGbCameraLevel3ByStreamId({ streamId }) const cameraRes = await getGbCameraLevel3ByStreamId({ streamId })
for (let c of cameraRes) {
let preAdd = addedRes.find(ad => ad.dataValues.serialNo == c.streamid)
if (preAdd) {
c.dataValues.camera = preAdd.dataValues
} else {
c.dataValues.camera = null
}
}
ctx.status = 200; ctx.status = 200;
ctx.body = cameraRes ctx.body = cameraRes
} catch (error) { } catch (error) {
@ -198,6 +215,7 @@ async function createNvrCamera (ctx) {
name: c.name, name: c.name,
sip: corCamera.sipip, sip: corCamera.sipip,
cloudControl: c.cloudControl, cloudControl: c.cloudControl,
nvrId: nvrRes.id,
createTime: moment().format(), createTime: moment().format(),
createUserId: userId, createUserId: userId,
forbidden: false, forbidden: false,
@ -216,6 +234,9 @@ async function createNvrCamera (ctx) {
if (updateData.length) { if (updateData.length) {
for (let u of updateData) { for (let u of updateData) {
await models.Camera.update(u, { await models.Camera.update(u, {
where: {
id: u.id
},
transaction transaction
}) })
} }
@ -324,6 +345,26 @@ async function createIpcCamera (ctx) {
} }
} }
async function getCascadeSipList (ctx) {
try {
const { models } = ctx.fs.dc
const sipListRes = await models.GbCamera.findAll({
attributes: ['id', 'streamid'],
where: {
level: 0,
ipctype: '级联',
}
})
ctx.status = 200;
ctx.body = sipListRes
} catch (error) {
ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`);
ctx.status = 400;
ctx.body = {}
}
}
async function verifyCascadeCamera (ctx) { async function verifyCascadeCamera (ctx) {
let errMsg = '校验级联摄像头信息失败' let errMsg = '校验级联摄像头信息失败'
try { try {
@ -401,6 +442,7 @@ module.exports = {
createNvrCamera, createNvrCamera,
verifyIpcCamera, verifyIpcCamera,
createIpcCamera, createIpcCamera,
getCascadeSipList,
verifyCascadeCamera, verifyCascadeCamera,
createCascadeCamera, createCascadeCamera,
}; };

4
code/VideoAccess-VCMP/api/app/lib/controllers/nvr/index.js

@ -16,7 +16,7 @@ async function edit (ctx, next) {
where: { where: {
streamid: serialNo, streamid: serialNo,
level: 0, level: 0,
ipctype: 'nvr' // ipctype: 'nvr'
} }
}) })
@ -77,7 +77,7 @@ async function verify (ctx, next) {
where: { where: {
streamid: serialNo, streamid: serialNo,
level: 0, level: 0,
ipctype: 'nvr' // ipctype: 'nvr'
} }
}) })

3
code/VideoAccess-VCMP/api/app/lib/routes/camera/index.js

@ -27,6 +27,9 @@ module.exports = function (app, router, opts) {
app.fs.api.logAttr['POST/camera/create/ipc'] = { content: '创建IPC摄像头', visible: false }; app.fs.api.logAttr['POST/camera/create/ipc'] = { content: '创建IPC摄像头', visible: false };
router.post('/camera/create/ipc', cameraCreate.createIpcCamera); router.post('/camera/create/ipc', cameraCreate.createIpcCamera);
app.fs.api.logAttr['GET/camera/sip_list/cascade'] = { content: '获取级联摄像头sip列表', visible: false };
router.get('/camera/sip_list/cascade', cameraCreate.getCascadeSipList);
app.fs.api.logAttr['POST/camera/verify/cascade'] = { content: '验证级联摄像头信息', visible: false }; app.fs.api.logAttr['POST/camera/verify/cascade'] = { content: '验证级联摄像头信息', visible: false };
router.post('/camera/verify/cascade', cameraCreate.verifyCascadeCamera); router.post('/camera/verify/cascade', cameraCreate.verifyCascadeCamera);

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-control-select.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-control-unselect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 906 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-fullScreen-select.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-fullScreen-unselect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-histroy-select.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-histroy-unselect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-talk-select.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/video-icon-talk-unselect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
code/VideoAccess-VCMP/web/client/assets/images/background/videoPlayBg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1012 B

1
code/VideoAccess-VCMP/web/client/assets/js/jessibuca/decoder.js

File diff suppressed because one or more lines are too long

BIN
code/VideoAccess-VCMP/web/client/assets/js/jessibuca/decoder.wasm

Binary file not shown.

1
code/VideoAccess-VCMP/web/client/assets/js/jessibuca/jessibuca.js

File diff suppressed because one or more lines are too long

BIN
code/VideoAccess-VCMP/web/client/assets/video/cross_loading.mp4

Binary file not shown.

BIN
code/VideoAccess-VCMP/web/client/assets/video/login_bg.mp4

Binary file not shown.

9
code/VideoAccess-VCMP/web/client/index.ejs

@ -2,13 +2,14 @@
<html> <html>
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" /> <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
<link rel="shortcut icon" href="/assets/images/favicon.ico"> <link rel="shortcut icon" href="/assets/images/favicon.ico">
<script src="/assets/js/jessibuca/jessibuca.js"></script>
</head> </head>
<body> <body>
<div id='App'></div> <div id='App'></div>
</body> </body>
</html> </html>

1
code/VideoAccess-VCMP/web/client/index.html

@ -5,6 +5,7 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" /> <meta name="viewport" content="minimum-scale=1, initial-scale=1, width=device-width" />
<link rel="shortcut icon" href="/assets/images/favicon.ico"> <link rel="shortcut icon" href="/assets/images/favicon.ico">
<script src="/assets/js/jessibuca/jessibuca.js"></script>
</head> </head>
<body> <body>

6
code/VideoAccess-VCMP/web/client/src/components/index.js

@ -1,10 +1,12 @@
'use strict'; 'use strict';
import SimpleFileDownButton from './simpleFileDownButton' import SimpleFileDownButton from './simpleFileDownButton'
import Coming from './coming' import Coming from './coming'
import ReminderBox from './reminderBox' import ReminderBox from './reminderBox'
import VideoPlayModal from './videoPlayModal'
export { export {
SimpleFileDownButton, SimpleFileDownButton,
Coming, Coming,
ReminderBox ReminderBox,
VideoPlayModal,
}; };

48
code/VideoAccess-VCMP/web/client/src/components/textScroll.jsx

@ -0,0 +1,48 @@
import React, { useRef, useEffect, useState } from 'react'
import moment from 'moment'
import './textScroll.less'
function TextScroll (props) {
const { content, duration } = props
const [showContent, setShowContent] = useState('1231231')
useEffect(() => {
let repeatTime = moment()
let refreshTime = moment()
const scroll = () => {
let contentParent = document.getElementById('marquee_box')
document.getElementById('contentPMakeUp').style.width = contentParent.clientWidth + 'px'
//
if (moment().diff(refreshTime) > 1000 / 60) {
const contentP = document.getElementById('contentP')
//
if (moment().diff(repeatTime) > 1000 * 1.5) {
contentP.style.visibility = 'visible'
}
if (moment().diff(repeatTime) > 1000 * 3) {
contentParent.scrollLeft = contentParent.scrollLeft + 1
}
if (contentParent.scrollLeft >= contentP.clientWidth + 24) {
contentParent.scrollLeft = 0
repeatTime = moment()
setShowContent('asdasd' + Math.random())
contentP.style.visibility = 'hidden'
}
refreshTime = moment()
}
window.requestAnimationFrame(scroll)
}
window.requestAnimationFrame(scroll)
}, [])
return (
<div className="marquee_box" id='marquee_box' style={{ overflow: 'hidden' }} >
<p style={{ position: 'relative', left: 24 }}>
<p id='contentP' style={{ display: 'inline-block', visibility: 'hidden' }}>{showContent}</p>
<p id='contentPMakeUp' style={{ width: 0, display: 'inline-block' }}></p>
</p>
</div>
)
}
export default React.memo(TextScroll)

19
code/VideoAccess-VCMP/web/client/src/components/textScroll.less

@ -0,0 +1,19 @@
.marquee_box {
width: 100%;
height: 100%;
word-break: keep-all;
white-space: nowrap;
// display: flex;
// align-items: center;
}
.marquee_box p {
// display: inline-block;
padding: 0;
margin: 0;
}
.marquee_box:hover p {
animation-play-state: paused;
cursor: default;
}

143
code/VideoAccess-VCMP/web/client/src/components/videoPlay.jsx

@ -0,0 +1,143 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import moment from 'moment'
import { Button, Modal, Col, Row, Space } from "@douyinfe/semi-ui";
import TextScroll from './textScroll'
import './videoPlay.less';
const VideoPlay = ({ height, width }) => {
const [jessibuca, setjessibuca] = useState(null)
const [playUrl, setPlayUrl] = useState('http://flv.bdplay.nodemedia.cn/live/bbb.flv')
const [isPlaying, setIsPlaying] = useState(false)
const [operationState, setoperationState] = useState()
const operation = [{
key: 'control',
click: () => { console.log(121212); }
}, {
key: 'talk',
click: () => { console.log(121212); }
}, {
key: 'fullScreen',
click: () => { console.log(121212); }
}, {
key: 'histroy',
click: () => { console.log(121212); }
},]
useEffect(() => {
create()
let nextOperationState = {}
for (let p of operation) {
nextOperationState[p.key] = {
select: false
}
}
setoperationState(nextOperationState)
}, [])
const create = () => {
let $container = document.getElementById('container');
const jessibuca = new window.Jessibuca({
container: $container,
videoBuffer: 0.2, //
isResize: false,
text: "",
loadingText: "加载中",
debug: true,
showBandwidth: true, //
operateBtns: {
fullscreen: true,
screenshot: true,
play: true,
audio: true,
},
forceNoOffscreen: false,
isNotMute: false,
});
setjessibuca(jessibuca)
}
useEffect(() => {
play()
}, [jessibuca])
const play = () => {
if (jessibuca && playUrl) {
jessibuca.play(playUrl);
setIsPlaying(true)
}
}
const pause = () => {
if (jessibuca) {
jessibuca.pause();
setIsPlaying(false)
}
}
const butStyle = {
border: '1px solid #fff', display: 'inline-block', color: '#fff', padding: '0 10px',
display: 'flex', alignItems: 'center', height: '64%', marginLeft: 12
}
return (
<>
<div style={{ height: height || '100%', width: width || '100%' }}>
<div style={{ position: 'relative', }}>
<div style={{
height: 42, lineHeight: '42px', background: '#00000026',
position: 'absolute', width: '100%', zIndex: 99,
color: '#fff'
}}>
<Row >
<Col span={9} style={{
backgroundImage: 'url(/assets/images/background/videoPlayBg.png)',
backgroundSize: '100% 100%',
backgroundRepeat: 'no-repeat',
textAlign: 'center'
}}>123</Col>
<Col span={15} style={{}}>
<div style={{ paddingRight: 12 }}>
<TextScroll content={['asdadasdasdasdasdasd','123123']} duration={6} />
</div>
</Col>
</Row>
</div>
<div id="container" style={{ height: height || '100%', width: width || '100%' }}></div>
<div style={{
height: 42, lineHeight: '42px', background: 'linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.8) 100%)', padding: '0 12px',
display: 'flex', justifyContent: 'space-between',
position: 'absolute', bottom: 0, width: '100%', zIndex: 99
}}>
<div style={{ display: 'flex', alignItems: 'center' }}>
{
operationState ?
operation.map(p => {
return <img
src={`/assets/images/background/video-icon-${p.key}-${operationState[p.key].select ? 'select' : 'unselect'}.png`}
height={26}
style={{ marginRight: 24 }}
/>
}) : ''
}
</div>
<div style={{ display: 'flex', alignItems: 'center' }}>
<div style={butStyle}>标清</div>
<div style={butStyle}>高清</div>
</div>
</div>
</div>
</div>
</>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoPlay);

0
code/VideoAccess-VCMP/web/client/src/components/videoPlay.less

32
code/VideoAccess-VCMP/web/client/src/components/videoPlayModal.jsx

@ -0,0 +1,32 @@
import React, { useState, useEffect, useRef } from "react";
import { connect } from "react-redux";
import moment from 'moment'
import { Button, Modal, } from "@douyinfe/semi-ui";
import VideoPlay from './videoPlay'
import './videoPlayModal.less';
const VideoPlayModal = ({ visible }) => {
return (
<Modal
visible={visible}
header={null}
footer={null}
size={'large'}
style={{}}
bodyStyle={{}}
className="videoModal"
>
<VideoPlay height={460} />
</Modal>
)
}
function mapStateToProps (state) {
const { auth } = state;
return {
user: auth.user,
};
}
export default connect(mapStateToProps)(VideoPlayModal);

5
code/VideoAccess-VCMP/web/client/src/components/videoPlayModal.less

@ -0,0 +1,5 @@
.videoModal {
.semi-modal-content {
padding: 0;
}
}

11
code/VideoAccess-VCMP/web/client/src/layout/containers/layout/index.jsx

@ -71,6 +71,7 @@ const LayoutContainer = props => {
if (dom) { if (dom) {
if (!scrollbar) { if (!scrollbar) {
scrollbar = new PerfectScrollbar('#page-content', { suppressScrollX: true }); scrollbar = new PerfectScrollbar('#page-content', { suppressScrollX: true });
scrollbar.update();
} else { } else {
scrollbar.update(); scrollbar.update();
dom.scrollTop = 0; dom.scrollTop = 0;
@ -85,8 +86,16 @@ const LayoutContainer = props => {
<div style={{ <div style={{
position: 'absolute', height: '100%', width: '100%', position: 'absolute', height: '100%', width: '100%',
display: 'flex', alignItems: 'center', placeContent: 'center', display: 'flex', alignItems: 'center', placeContent: 'center',
backgroundColor: "#000"
}}> }}>
载入中... <video
autoPlay loop muted
style={{
// width: "100%", objectFit: "cover", objectPosition: 'left top', height: 'calc(100vh - 4px)'
}}
src="/assets/video/cross_loading.mp4"
type="video/mp4"
/>
</div> </div>
: :
<> <>

1
code/VideoAccess-VCMP/web/client/src/layout/index.jsx

@ -150,6 +150,7 @@ const Root = props => {
/> />
))) )))
// IOT system cross
window.addEventListener('message', async function (e) { // message window.addEventListener('message', async function (e) { // message
const { data } = e const { data } = e
if (data && data.action) { if (data && data.action) {

18
code/VideoAccess-VCMP/web/client/src/sections/auth/containers/login.jsx

@ -30,12 +30,20 @@ const Login = props => {
return ( return (
<div style={{ <div style={{
height: '100vh', // height: '100vh',
backgroundImage: "url('/assets/images/background/loginBackground.png')", // backgroundImage: "url('/assets/images/background/loginBackground.png')",
backgroundSize: 'cover', // backgroundSize: 'cover',
backgroundRepeat: 'no-repeat', // backgroundRepeat: 'no-repeat',
position: 'relative', // position: 'relative',
}}> }}>
<video
autoPlay loop muted
style={{
width: "100%", objectFit: "cover", objectPosition: 'left top', height: 'calc(100vh - 4px)'
}}
src="/assets/video/login_bg.mp4"
type="video/mp4"
/>
<div style={{ <div style={{
width: 446, width: 446,
height: 348, height: 348,

1314
code/VideoAccess-VCMP/web/client/src/sections/equipmentWarehouse/containers/camera.jsx

File diff suppressed because it is too large
Loading…
Cancel
Save