html 浏览器播放摄像头视频流(rtsp),录制,多摄像头等

Html5 

ffmpeg 安装

ffmpeg 安装和常见问题

功能清单

  • api 添加摄像头视频流
  • 根据新增视频流自动发起流转换,并转发到http 服务端口
  • 根据视频流,获取当前快照
  • 服务端录制保存
  • web前段查看视频

服务端代码

  • websocket服务
  • http转换流接收服务
  • web api接口服务
//websocket-relay.js

var fs = require('fs'),
  http = require('http'),
  WebSocket = require('ws');

if (process.argv.length < 3) {
  console.log(
    'Usage: \n' +
    'node websocket-relay.js  [ ]'
  );
  process.exit();
}

var STREAM_SECRET = process.argv[2],
  STREAM_PORT = process.argv[3] || 8081,
  WEBSOCKET_PORT = process.argv[4] || 8082,
  HTTP_PORT = process.argv[5] || 8083,
  RECORD_STREAM = false;

// Websocket Server
var socketServer = new WebSocket.Server({port: WEBSOCKET_PORT, perMessageDeflate: false});
socketServer.connectionCount = 0;
socketServer.on('connection', function (socket, upgradeReq) {

  var params = upgradeReq.url.substr(1).split('/');
  if (!params || params.length != 1) {
    console.log("deviceId param was loss");
    response.end();
  }
  socket.deviceId = params[0];
  socketServer.connectionCount++;
  console.log(
    'New WebSocket Connection: ',
    (upgradeReq || socket.upgradeReq).socket.remoteAddress,
    (upgradeReq || socket.upgradeReq).headers['user-agent'],
    '(' + socketServer.connectionCount + ' total)'
  );
  socket.on('close', function (code, message) {
    socketServer.connectionCount--;
    console.log(
      'Disconnected WebSocket (' + socketServer.connectionCount + ' total)'
    );
  });
  socketServer.broadcast("fda")
});
socketServer.broadcast = function (deviceId, data) {
  socketServer.clients.forEach(function each(client) {
    if (client.deviceId && client.deviceId == deviceId) {
      if (client.readyState === WebSocket.OPEN) {
        client.send(data);
      }
    }
  });
};

// HTTP Server to accept incomming MPEG-TS Stream from ffmpeg
var streamServer = http.createServer(function (request, response) {
  var params = request.url.substr(1).split('/');

  if (params[1] !== STREAM_SECRET) {
    console.log(
      'Failed Stream Connection: ' + request.socket.remoteAddress + ':' +
      request.socket.remotePort + ' - wrong secret.'
    );
    response.end();
  }

  response.connection.setTimeout(0);
  console.log(
    'Stream Connected: ' +
    request.socket.remoteAddress + ':' +
    request.socket.remotePort
  );
  request.on('data', function (data) {
    socketServer.broadcast(params[0], data);
    if (request.socket.recording) {
      request.socket.recording.write(data);
    }
  });
  request.on('end', function () {
    if (request.socket.recording) {
      request.socket.recording.close();
    }
  });

  // Record the stream to a local file?
  if (RECORD_STREAM) {
    var path = 'recordings/' + Date.now() + '.ts';
    request.socket.recording = fs.createWriteStream(path);
  }
})
// Keep the socket open for streaming
streamServer.headersTimeout = 0;
streamServer.listen(STREAM_PORT);

// HTTP Server to start convert shell
const express = require('express');
const bodyParser = require('body-parser');
const child_process = require('child_process');

const httpServer = express();
httpServer.use(bodyParser());
httpServer.use(bodyParser.json());
httpServer.use(bodyParser.urlencoded({extended: true}));

httpServer.killByProcessName = function (name) {
  var shell = `ps aux | grep "${name}" |grep -v grep| cut -c 9-15 | xargs kill -9`;
  try {
    console.log("exec", shell);
    var result = child_process.execSync(shell, {stdio: 'ignore'});
    console.log("进程已存在", result);
  } catch (e) {
    console.log("进程不存在(假定只有不存在的时候才会报错)")
    return false;
  }
  return true;
}

httpServer.get('/', function (req, res) {
  res.send('摄像头服务');
});
const error_image = "data:image/jpg;base64,默认图片"
/**
抓帧
**/
httpServer.get('/snapshot', function (req, res) {
  if (!req.query.deviceId || !req.query.streamUrl) {
    res.json({succee: false, msg: "缺少参数"});
    return;
  }
  try {
    var frameShell = `ffmpeg -y -i "${req.query.streamUrl}" -ss 1 -frames:v 1 ./${req.query.deviceId}.jpeg`;
    console.log("snapshot", frameShell);
    child_process.execSync(frameShell, {stdio: 'ignore'});
    let bitmap = fs.readFileSync(`./${req.query.deviceId}.jpeg`);
    let base64str = Buffer.from(bitmap, 'binary').toString('base64');
    res.send("data:image/jpg;base64," + base64str);
    return;
  } catch (e) {
    console.log("在抓帧时获得异常,返回默认", e)
  }
  res.send(error_image);
});
httpServer.post('/camera', function (req, res) {
  console.log(req.body)
  if (!req.body.deviceId || !req.body.streamUrl) {
    res.json({succee: false, msg: "缺少参数"});
    return;
  }
  var exist = httpServer.killByProcessName(`http://127.0.0.1:8082/${req.body.deviceId}`);
  var cmd = `ffmpeg -rtsp_transport tcp -i "${req.body.streamUrl}" -f mpegts -b:v 800k -codec:v mpeg1video -codec:a mp2 -s 640x480 -r 32 "http://127.0.0.1:8082/${req.body.deviceId}/123456/640/480/"`;
  try {
    child_process.exec(cmd, function (error, stdout, stderr) {
      if (error) {
        console.log(error)
      }
      console.log("error:" + error);
      console.log("stdout:" + stdout);
      console.log("stderr:" + stderr);
    });
    console.log(`添加[${req.body.deviceId}:${req.body.streamUrl}]成功!`)
  } catch (e) {
    console.log(`添加[${req.body.deviceId}:${req.body.streamUrl}]失败!`, e)
    return res.json({succee: false, msg: "添加失败!"})
  }
  exist ? res.json({succee: true, msg: "已启动脚本!"}) : res.json({succee: true, msg: "已重启脚本!"});
});
httpServer.listen(HTTP_PORT);

console.log('Listening for incomming MPEG-TS Stream on http://127.0.0.1:' + STREAM_PORT + '/');
console.log('Awaiting WebSocket connections on ws://127.0.0.1:' + WEBSOCKET_PORT + '/');
console.log('Web Api service on http://127.0.0.1:' + HTTP_PORT + '/');

package.json

 "dependencies": {
    "body-parser": "^1.19.0",
    "child_process": "^1.0.2"
  }

启动服务

node websocket-relay.js 123456 8082 8084 8087

html源码


<html>
<head>
  <title>JSMpeg Stream Clienttitle>
  <style type="text/css">
    html, body {
      background-color: #111;
      text-align: center;
    }
  style>
head>
<body>
<canvas id="video-canvas">canvas>
<script type="text/javascript" src="jsmpeg.min.js">script>
<script type="text/javascript">
  var canvas = document.getElementById('video-canvas');
  var url = 'ws://192.168.100.13:8084/deviceId1';
  var player = new JSMpeg.Player(url, {canvas: canvas});
script>
body>
html>

常见问题

  1. 视频延迟:取决于比特率和分别率,越低自然延迟就低。
  2. html 部署,可以直接使用http-server模块部署。

你可能感兴趣的:(Java,shell,流媒体处理)