diff --git a/code/VideoAccess-VCMP/api/.vscode/launch.json b/code/VideoAccess-VCMP/api/.vscode/launch.json index bb4caed..70913c0 100644 --- a/code/VideoAccess-VCMP/api/.vscode/launch.json +++ b/code/VideoAccess-VCMP/api/.vscode/launch.json @@ -23,7 +23,12 @@ "--iotVideoServerUrl http://221.230.55.27:8081", "--godUrl https://restapi.amap.com/v3", "--godKey 21c2d970e1646bb9a795900dd00093ce", - "--mqttVideoServer tcp://tee2b1be.cn.emqx.cloud:12847" + "--mqttVideoServer tcp://tee2b1be.cn.emqx.cloud:12847", + "--cameraPlayWsHost ws://221.230.55.27:8081", + "--cameraPlayHttpFlvHost http://221.230.55.27:2020", + "--cameraPlayHlsHost http://221.230.55.27:8081", + "--cameraPlayRtmpHost rtmp://221.230.55.27:1935", + "--cameraPlayRtspHost rtsp://221.230.55.27:554" ] }, { diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js index 0341ef6..cb97ebc 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/create.js @@ -361,7 +361,7 @@ async function getCascadeSipList (ctx) { const { models } = ctx.fs.dc const sipListRes = await models.GbCamera.findAll({ - attributes: ['id', 'sipip'], + attributes: ['id', 'streamid', 'sipip'], where: { level: 0, ipctype: '级联', diff --git a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js index 095a00f..47b7ca5 100644 --- a/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js +++ b/code/VideoAccess-VCMP/api/app/lib/controllers/camera/index.js @@ -7,6 +7,7 @@ async function getCameraProject (ctx, next) { const models = ctx.fs.dc.models; const { limit, page, orderBy, orderDirection, keyword, abilityId, type, venderId, nvrId, externalDomain, state } = ctx.query const { userId, token } = ctx.fs.api + const { utils: { getPlayUrl } } = ctx.app.fs let findOption = { attributes: { exclude: ['delete', 'recycleTime',] }, @@ -35,7 +36,7 @@ async function getCameraProject (ctx, next) { } let gbCameraOption = { model: models.GbCamera, - attributes: ['id', 'online'], + attributes: ['id', 'online', 'playUrl'], required: false } if (limit) { @@ -135,6 +136,10 @@ async function getCameraProject (ctx, next) { camera.createUser = { namePresent: corUser ? corUser.namePresent : '' } + + if (camera.type != 'yingshi') { + camera.gbCamera.dataValues.playUrl = getPlayUrl({ topSerialNo: camera.topSerialNo, serialNo: camera.serialNo }) + } } ctx.status = 200; @@ -183,7 +188,7 @@ async function detail (ctx) { const { models } = ctx.fs.dc; const { cameraId } = ctx.params const { userId, token } = ctx.fs.api - const { utils: { rtmp2others } } = ctx.app.fs + const { utils: { getPlayUrl } } = ctx.app.fs const cameraRes = await models.Camera.findOne({ where: { @@ -208,19 +213,21 @@ async function detail (ctx) { } } - const otherUrls = await rtmp2others(cameraRes.rtmp) - const corUser = await ctx.app.fs.authRequest.get(`user/${cameraRes.createUserId}/message`, { query: { token } }) - ctx.status = 200; - ctx.body = { + let rslt = { ...cameraRes.dataValues, station: bindStations, - videoUrl: otherUrls, createUser: { namePresent: corUser[0].namePresent } } + if (cameraRes.type != 'yingshi') { + rslt.gbCamera.playUrl = await getPlayUrl({ topSerialNo: cameraRes.topSerialNo, serialNo: cameraRes.serialNo }) + } + + ctx.status = 200; + ctx.body = rslt } catch (error) { ctx.fs.logger.error(`path: ${ctx.path}, error: ${error}`); ctx.status = 400; diff --git a/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js b/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js index 3e4bd14..0ab221b 100644 --- a/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js +++ b/code/VideoAccess-VCMP/api/app/lib/schedule/freshYingshiMsg.js @@ -9,7 +9,7 @@ module.exports = function (app, opts) { try { const startTime = moment() const { models } = app.fs.dc - const { token4yingshi } = app.fs.utils + const { token4yingshi, getYingshiPlayUrl } = app.fs.utils const secretRes = await models.SecretYingshi.findAll() let deviceList = [] for (let s of secretRes) { @@ -44,6 +44,10 @@ module.exports = function (app, opts) { if (existD) { if (existD.online != storageD.online) { // 状态更新 + if (storageD.online == 'ON' && !existD.playUrl) { + const playUrlRes = await getYingshiPlayUrl({ deviceSerial: d.deviceSerial, token: tokenYingshi }) + storageD.playUrl = playUrlRes + } await models.GbCamera.update(storageD, { where: { id: existD.id @@ -112,15 +116,15 @@ module.exports = function (app, opts) { }); const freshYingshiPlayUrl = schedule.scheduleJob( - // '* * 4 * * *', - '*/1 * * * *', + // '0 0 4 */1 *', + '*/45 * * * *', async () => { const protocolMap = { } try { const { models } = app.fs.dc - const { token4yingshi } = app.fs.utils + const { token4yingshi, getYingshiPlayUrl } = app.fs.utils const secretRes = await models.SecretYingshi.findAll() for (let s of secretRes) { const tokenYingshi = await token4yingshi(s.dataValues) @@ -135,23 +139,22 @@ module.exports = function (app, opts) { } }) if (deviceRes.code == 200) { - let deviceSerials = deviceRes.data.map(d => d.deviceSerial) - - - const devicePlayUrlRes = await app.fs.yingshiRequest.post('lapp/v2/live/address/get', { - query: { - accessToken: tokenYingshi, - deviceSerial: deviceSerials[0], - protocol: 3 - } - }) + for (let d of deviceRes.data) { + const playUrlRes = await getYingshiPlayUrl({ deviceSerial: d.deviceSerial, token: tokenYingshi }) + await models.GbCamera.update({ + playUrl: playUrlRes, + }, { + where: { + streamid: d.deviceSerial + } + }) + } if (deviceRes.data.length == 50) { pageStart++ } else { pageStart = -1 } - } } } diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/camera.js b/code/VideoAccess-VCMP/api/app/lib/utils/camera.js index 3a67428..530d47d 100644 --- a/code/VideoAccess-VCMP/api/app/lib/utils/camera.js +++ b/code/VideoAccess-VCMP/api/app/lib/utils/camera.js @@ -97,11 +97,33 @@ module.exports = function (app, opts) { return gbCameraRes.dataValues } + async function getPlayUrl ({ topSerialNo, serialNo }) { + const { cameraPlayHost } = opts + + if (!topSerialNo || !serialNo) { + return null + } + + return { + liveUrl: { + sd: { + 'WS-RAW': `${cameraPlayHost.ws}/jessica/${topSerialNo}/${serialNo}`, + 'WS-FLV': `${cameraPlayHost.ws}/jessica/${topSerialNo}/${serialNo}.flv`, + 'HTTP-FLV': `${cameraPlayHost.httpFlv}/hdl/${topSerialNo}/${serialNo}.flv`, + 'HLS': `${cameraPlayHost.httpFlv}/hls/${topSerialNo}/${serialNo}.m3u8`, + 'RTMP': `${cameraPlayHost.rtmp}/${topSerialNo}/${serialNo}`, + 'RTSP': `${cameraPlayHost.rtsp}/${topSerialNo}/${serialNo}`, + } + } + } + } + return { getGbCameraLevel1ByStreamId, getGbCameraLevel3ByStreamId, verifyYingshiInfo, verifyIpcInfo, verifyCascadeInfo, + getPlayUrl, } } \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/rtmp2others.js b/code/VideoAccess-VCMP/api/app/lib/utils/rtmp2others.js deleted file mode 100644 index 8e3d364..0000000 --- a/code/VideoAccess-VCMP/api/app/lib/utils/rtmp2others.js +++ /dev/null @@ -1,39 +0,0 @@ -'use strict'; - - -module.exports = function (app, opts) { - async function rtmp2others (rtmp) { - - return { - liveUrl: {// 直播 - hd: {// 高清 - rtmp: 'xx', - hls: 'xx', - flv: 'xx', - ezopen: 'xx', - onvif: 'xx', - }, - sd: {// 标清 - rtmp: 'xx', - hls: 'xx', - flv: 'xx', - ezopen: 'xx', - onvif: 'xx', - } - }, - replayUrl: {// 回放 - cloud: 'xx', - local: 'xx', - } - } - } - - const getYingshiPlayUrl = async (deviceSerial) => { - - } - - return { - rtmp2others, - getYingshiPlayUrl, - } -} \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js b/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js index c12f5dd..839214f 100644 --- a/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js +++ b/code/VideoAccess-VCMP/api/app/lib/utils/token4yingshi.js @@ -68,8 +68,88 @@ module.exports = function (app, opts) { return beloneSecret } + const getYingshiPlayUrl = async ({ deviceSerial, token }) => { + const protocolMap = { + ezopen: 1, + hls: 2, + rtmp: 3, + flv: 4, + } + const qualityMap = { + hd: 1, + sd: 2, + } + const typeMap = { + // live: 1, + cloud: 2, + local: 3 + } + + let playUrl = { + liveUrl: {// 直播 + hd: {// 高清 + rtmp: '', + hls: '', + flv: '', + ezopen: '', + }, + sd: {// 标清 + rtmp: '', + hls: '', + flv: '', + ezopen: '', + } + }, + replayUrl: {// 回放 + cloud: '', + local: '', + } + } + + for (let protocol in protocolMap) { + for (let quality in qualityMap) { + const playUrlRes = await app.fs.yingshiRequest.post('lapp/v2/live/address/get', { + query: { + accessToken: token, + deviceSerial: deviceSerial, + protocol: protocolMap[protocol], + quality: qualityMap[quality], + } + }) + if (playUrlRes.code == 200) { + playUrl.liveUrl[quality][protocol] = playUrlRes.data.url + } else { + return null + } + } + } + + for (let type in typeMap) { + try { + // TODO 这里404 + const playUrlRes = await app.fs.yingshiRequest.post('lapp/v2/replay/address/get', { + query: { + accessToken: token, + deviceSerial: deviceSerial, + type: typeMap[type], + } + }) + if (playUrlRes.code == 200) { + playUrl.replayUrl[type] = playUrlRes.data.url + } else { + // return null + } + } catch (error) { + app.fs.logger.error(`sechedule: freshYingshiPlayUrl, error: ${error}`); + } + } + + return playUrl + } + return { token4yingshi, varifyYingshiBelongSecretBySerialNo, + getYingshiPlayUrl, } } \ No newline at end of file diff --git a/code/VideoAccess-VCMP/api/config.js b/code/VideoAccess-VCMP/api/config.js index bd44859..9c4befd 100644 --- a/code/VideoAccess-VCMP/api/config.js +++ b/code/VideoAccess-VCMP/api/config.js @@ -11,15 +11,24 @@ const dev = process.env.NODE_ENV == 'development'; args.option(['p', 'port'], '启动端口'); args.option(['g', 'pg'], 'postgre服务URL'); args.option(['f', 'fileHost'], '文件中心本地化存储: WebApi 服务器地址(必填), 该服务器提供文件上传Web服务'); + args.option('redisHost', 'redisHost'); args.option('redisPort', 'redisPort'); args.option('redisPswd', 'redisPassword'); + args.option('axyApiUrl', '安心云 api'); args.option('iotAuthApi', 'IOT 鉴权 api'); -args.option('iotVideoServerUrl', '视频后端服务地址'); + args.option('godUrl', '高德地图API请求地址'); args.option('godKey', '高德地图API key'); + +args.option('iotVideoServerUrl', '视频后端服务地址'); args.option('mqttVideoServer', '视频后台 mqtt 服务 URL'); +args.option('cameraPlayWsHost', '视频播放地址 ws://xxx:xxx'); +args.option('cameraPlayHttpFlvHost', '视频播放地址 httpFlv'); +args.option('cameraPlayHlsHost', '视频播放地址 hls'); +args.option('cameraPlayRtmpHost', '视频播放地址 rtmp'); +args.option('cameraPlayRtspHost', '视频播放地址 rtsp'); const flags = args.parse(process.argv); @@ -30,17 +39,26 @@ const IOTA_REDIS_SERVER_HOST = process.env.IOTA_REDIS_SERVER_HOST || flags.redis const IOTA_REDIS_SERVER_PORT = process.env.IOTA_REDIS_SERVER_PORT || flags.redisPort || "6379";//redis 端口 const IOTA_REDIS_SERVER_PWD = process.env.IOTA_REDIS_SERVER_PWD || flags.redisPswd || "";//redis 密码 -const MQTT_VIDEO_SERVER = process.env.MQTT_VIDEO_SERVER || flags.mqttVideoServer; - const IOT_AUTH_API = process.env.IOT_AUTH_API || flags.iotAuthApi; -const IOT_VIDEO_SERVER_URL = process.env.IOT_VIDEO_SERVER_URL || flags.iotVideoServerUrl const AXY_API_URL = process.env.AXY_API_URL || flags.axyApiUrl; + const GOD_URL = process.env.GOD_URL || flags.godUrl || 'https://restapi.amap.com/v3'; const GOD_KEY = process.env.GOD_KEY || flags.godKey; + const YINGSHI_URL = process.env.YINGSHI_URL || flags.yingshiUrl || 'https://open.ys7.com/api'; +const MQTT_VIDEO_SERVER = process.env.MQTT_VIDEO_SERVER || flags.mqttVideoServer; +const IOT_VIDEO_SERVER_URL = process.env.IOT_VIDEO_SERVER_URL || flags.iotVideoServerUrl +const CAMERA_PLAY_WS_HOST = process.env.CAMERA_PLAY_WS_HOST || flags.cameraPlayWsHost; +const CAMERA_PLAY_HTTP_FLV_HOST = process.env.CAMERA_PLAY_HTTP_FLV_HOST || flags.cameraPlayHttpFlvHost; +const CAMERA_PLAY_HLS_HOST = process.env.CAMERA_PLAY_HLS_HOST || flags.cameraPlayHlsHost; +const CAMERA_PLAY_RTMP_HOST = process.env.CAMERA_PLAY_RTMP_HOST || flags.cameraPlayRtmpHost; +const CAMERA_PLAY_RTSP_HOST = process.env.CAMERA_PLAY_RTSP_HOST || flags.cameraPlayRtspHost; + if (!IOT_VIDEO_ACCESS_DB || !IOTA_REDIS_SERVER_HOST || !IOTA_REDIS_SERVER_PORT || !GOD_KEY || !MQTT_VIDEO_SERVER || - !AXY_API_URL || !IOT_VIDEO_SERVER_URL || !IOT_AUTH_API) { + !AXY_API_URL || !IOT_VIDEO_SERVER_URL || !IOT_AUTH_API || + !CAMERA_PLAY_WS_HOST || !CAMERA_PLAY_HTTP_FLV_HOST || !CAMERA_PLAY_HLS_HOST || !CAMERA_PLAY_RTMP_HOST || !CAMERA_PLAY_RTSP_HOST +) { console.log('缺少启动参数,异常退出'); args.showHelp(); process.exit(-1); @@ -72,6 +90,13 @@ const product = { mqtt: { mqttVideoServer: MQTT_VIDEO_SERVER, }, + cameraPlayHost: { + ws: CAMERA_PLAY_WS_HOST, + httpFlv: CAMERA_PLAY_HTTP_FLV_HOST, + hls: CAMERA_PLAY_HLS_HOST, + rtmp: CAMERA_PLAY_RTMP_HOST, + rtsp: CAMERA_PLAY_RTSP_HOST, + }, pssaRequest: [ {// name 会作为一个 request 出现在 ctx.app.fs name: 'authRequest',