您可以在渲染之前从 websocket 中对图像进行下采样吗
Posted
技术标签:
【中文标题】您可以在渲染之前从 websocket 中对图像进行下采样吗【英文标题】:Can you downsample an image from a websocket prior to rendering 【发布时间】:2021-05-22 20:34:10 【问题描述】:我有一个应用程序通过 websocket 大约每秒从服务器提取图像几次。应用程序负载很大,图像有时会跳过渲染。
图像本身的分辨率远高于所需的分辨率,因此在渲染之前对其进行下采样会有很大帮助。这可能吗?
这里是 websocket 代码:
const ws = new WebSocket(config.videoWebSocketURL);
<img className=styles.img src=`data:image/jpeg;base64,$data` />
欢迎任何其他加速渲染的理想。
【问题讨论】:
您是否尝试从静止图像(来自网络摄像头或视频源)进行实时视频流传输?我已经多次沿着这条路线走 - 它没有扩展。是什么产生了这些帧? 图片。它不是为了扩展或用于商业用途。这是一个概念验证 我所说的“无法缩放”的意思是,几乎不可能实时传输大型 JPEG 图像。 img 标签本身会为您缩小图像。但我怀疑真正的问题出在服务器端——你将帧推入套接字的速度比你的网络应用程序消耗的速度要快。你能在服务器端下采样吗? 另外,所有这些都是推测,因为您提供的架构细节很少。 如果您的应用程序已经很难处理大图像,那么添加一个下采样过程不会进一步削弱它吗?如果可能,这应该在服务器端完成。 【参考方案1】:每秒大约几次通过 websocket 拉入图像
你正在让自己走上痛苦的道路。
使用旨在显示动画内容的正确协议和接口:HTTP + <video>
标签。
如果我有一堆 jpeg 文件作为源文件,我会将它们在服务器端(发送之前)转换为视频。然后我会将流直接发送到 html5 video
标签。这解决了许多问题,例如背压和缓冲问题、跳帧等。
对于转换,可能的解决方案是使用 ffmpeg,例如:
ffmpeg -framerate 10 -i frame-%d.jpg output.mp4
如果您真的想在浏览器中手动进行此渲染, 那么可能的探索方向是:
使用canvas 和requestAnimationFrame() API - 画布在immediate graphics mode 中工作,它可以渲染高达每秒60 帧; 使用createImageBitmap() 进行图像处理(注意:下采样和调整大小尚不兼容跨浏览器); 使用网络工作者和OffscreenCanvas 对象,可以potentially speed up operations(注意:这是实验性功能)。演示
在运行它之前,将一些名为 frame-0.jpeg
到 frame-6.jpeg
的图像放在此应用文件旁边。
package.json
"name": "images-on-canvas",
"version": "1.0.0",
"description": "",
"main": "app.js",
"engines":
"node": "14.15.5"
,
"scripts":
"start": "nodemon app.js",
"test": "echo \"Error: no test specified\" && exit 1"
,
"keywords": [],
"author": "",
"license": "ISC",
"dependencies":
"express": "^4.17.1",
"express-ws": "^4.0.0"
,
"devDependencies":
"nodemon": "^2.0.7"
app.js
const port = process.env.PORT || 3000
const path = require('path')
const fs = require('fs')
const app = require('express')()
require('express-ws')(app)
app.get('/', (req, res) => res.sendFile(path.resolve(__dirname, 'index.html')))
app.ws('/', async function (socket)
// send frame files 0 to 6
for (let i = 0, count = 6; i <= count; i += 1)
try
let data = await fs.promises.readFile(path.resolve(__dirname, `frame-$i.jpeg`))
socket.send(data)
catch (e)
console.error(e)
)
app.listen(port, () => console.log(`Listening on port $port`))
index.html
<!DOCTYPE html>
<html>
<head>
<title>Rendering Test</title>
<meta charset="utf-8">
</head>
<body>
<canvas id="mycanvas" ></canvas>
<script type="text/javascript">
var cv = document.getElementById('mycanvas')
var ctx = cv.getContext('2d')
var ws = new WebSocket(location.origin.replace(/^http/, 'ws'))
var sprites = []
ws.onmessage = function onMessage(event)
// NOTE: check compatibility for options object, works in Chrome 52+
// https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap#browser_compatibility
var opts =
resizeWidth: 200,
resizeHeight: 200,
resizeQuality: 'pixelated'
createImageBitmap(event.data, 0, 0, 1024, 1024, opts).then(function (sprite)
sprites.push(sprite)
)
// NOTE: classic way to draw on canvas
// var img = new Image()
// img.addEventListener('load', function onLoad(event)
// ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
// ctx.drawImage(img, 0, 0, ctx.canvas.width, ctx.canvas.height)
// )
// img.src = URL.createObjectURL(event.data)
function draw(high_res_timestamp)
if (sprites.length)
console.log(high_res_timestamp)
var sprite = sprites.shift()
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height)
ctx.drawImage(sprite, 0, 0, ctx.canvas.width, ctx.canvas.height)
requestAnimationFrame(draw)
draw()
</script>
</body>
</html>
写完所有内容后,我真的认为更好的方法是在服务器端执行此操作,正如我在上面所写的那样。
为什么?您有可预测的输出,并且提供视频是一个已解决的问题。这样一来,您就可以避免使用实验性 API 或没有得到很好支持的 API 带来的所有麻烦,并且可能需要编写、测试和维护的代码更少。
【讨论】:
有趣。这正是我的想法。我不知道为什么它最初是这样完成的。服务器上的代码需要特定图像是有原因的,但我们没有理由在浏览器中以这种方式显示它。 该项目是在 React 中完成的,我正在放置一个类组件以将渲染限制为每秒一次或两次。我希望这可以将开销降至最低。 我已经更新了探索客户端渲染的答案,也许有帮助!以上是关于您可以在渲染之前从 websocket 中对图像进行下采样吗的主要内容,如果未能解决你的问题,请参考以下文章
如何在反应中从对象映射动态设置 sessionStorage