Crow+opencv+websocket实现实时rtsp视频拉取以及显示

2024-06-04 14:44

本文主要是介绍Crow+opencv+websocket实现实时rtsp视频拉取以及显示,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

需求:需要将rtsp视频流放到openharmony界面显示

方案一:使用openharmonyAPP中集成ffmpeg(后续更新)

方案二:使用openharmonyAPP中集成opencv(实际原理和方案一一致,因为opencv中集成了ffmpeg,后续更新)

方案三:将视频在服务端拉取,转base64之后使用websocket发送到前端,在openharmonyAPP中使用一个嵌套的WEB显示html

crow的环境搭建参考Crow 一个c++的后端开发库,类似spring boot、flask等

网上没有找到能白嫖的代码,所以自己写了一个分享出来

//frame_generator.h

#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include <thread.h>// 用于生成视频帧的生成器
class FrameGenerator {
public:bool isOpen = false;std::string _url;FrameGenerator(const std::string& rtsp_url) : cap(rtsp_url), _url(rtsp_url) {if (!cap.isOpened()) {reconnect();}isOpen = true;}void reconnect(){while (!cap.isOpened()){std::cout << "reconnect rtsp ." << std::endl;isOpen = false;cap.open(_url);std::this_thread::sleep_for(std::chrono::seconds(10)); }}const std::string base64_chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz""0123456789+/";std::string base64_encode(const std::string &input) {  std::string encoded;  size_t i = 0, j = 0;  uint8_t byte3[3] = {0};  uint8_t byte4[4] = {0};  // 遍历输入字符串中的每个字符  for (char byte : input) {  byte3[i++] = static_cast<uint8_t>(byte); // 假设输入是ASCII  if (i == 3) {  byte4[0] = (byte3[0] & 0xfc) >> 2;  byte4[1] = ((byte3[0] & 0x03) << 4) | ((byte3[1] & 0xf0) >> 4);  byte4[2] = ((byte3[1] & 0x0f) << 2) | ((byte3[2] & 0xc0) >> 6);  byte4[3] = byte3[2] & 0x3f;  // 添加编码后的字符到结果字符串  for (int k = 0; k < 4; k++) {  encoded += base64_chars[byte4[k]];  }  i = 0;  }  }  // 处理剩余字符(如果有)  if (i != 0) {  for (size_t k = i; k < 3; k++) {  byte3[k] = 0; // 填充剩余字节为0  }  // 执行编码,类似于前面的处理  byte4[0] = (byte3[0] & 0xfc) >> 2;  byte4[1] = ((byte3[0] & 0x03) << 4) | ((byte3[1] & 0xf0) >> 4);  byte4[2] = ((byte3[1] & 0x0f) << 2) | ((byte3[2] & 0xc0) >> 6);  // 添加编码后的字符到结果字符串  for (size_t k = 0; k < i + 1; k++) {  encoded += base64_chars[byte4[k]];  }  // 添加'='以填充到4的倍数  while (i++ < 3) {  encoded += '=';  }  }  std::cout << "base64 size:" << encoded.size() << std::endl;return encoded;  }std::string getFrame() {cv::Mat frame;if (!cap.isOpened()) {cap.open(_url);std::this_thread::sleep_for(std::chrono::seconds(10)); // throw std::runtime_error("Error opening video stream or file");}cap >> frame;if (frame.empty()) {std::cerr << "Error capturing frame" << std::endl;//throw std::runtime_error("Error capturing frame");}std::vector<uchar> buffer;cv::imencode(".jpg", frame, buffer);std::string _f = std::string(buffer.begin(), buffer.end());return base64_encode(_f);}private:cv::VideoCapture cap;
};

//crow websocket

std::mutex mtx2;
std::unordered_set<crow::websocket::connection *> users2;// opecv recv rtsp to wsCROW_WEBSOCKET_ROUTE(app, "/video").onopen([&](crow::websocket::connection &conn){std::cout << "New websocket connection from " << conn.get_remote_ip() << std::endl;std::lock_guard<std::mutex> lock(mtx2);users2.insert(&conn); // 添加新用户到集合中}).onclose([&](crow::websocket::connection &conn, const std::string &reason){std::cout << "Websocket connection closed: " << reason << std::endl;std::lock_guard<std::mutex> lock(mtx2);users2.erase(&conn); // 从集合中移除用户}).onmessage([&](crow::websocket::connection &conn, const std::string &data, bool is_binary){});std::thread videoMessageThread([&](){FrameGenerator generator("rtsp://XXX:8554/main.264");while (true){if (!generator.isOpen){generator.reconnect();continue;}std::lock_guard<std::mutex> lock(mtx2);for (auto user : users2){std::string frame = generator.getFrame();user->send_text(frame);}} });videoMessageThread.detach(); 

//前端接收

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebSocket Video Stream</title><style>html, body {margin: 0;padding: 0;height: 100%;overflow: hidden; /* 隐藏超出视口的内容 */}canvas {display: block;background-color: #f0f0f0; /* 可选:添加一个背景色以便在没有内容时看到canvas */width: 100vw; /* 使用视口宽度 */height: 100vh; /* 使用视口高度 */object-fit: cover; /* 如果需要的话,可以保持图像的宽高比 */}</style>
</head>
<body>
<canvas id="videoCanvas" width="1280" height="720"></canvas><script>// 获取canvas元素和它的2D渲染上下文let canvas = document.getElementById('videoCanvas');let ctx = canvas.getContext('2d');// 创建WebSocket连接let ws = new WebSocket('ws://XXX:8080/video');// WebSocket连接打开时的处理ws.onopen = function (event) {console.log('WebSocket is open now.');// 如果需要,向服务器发送开始传输的消息ws.send('START_STREAMING');};// 接收服务器发送的消息ws.onmessage = function (event) {console.log('imageData.', event.data);// 假设服务器发送的是base64编码的JPEG图片let imageData = 'data:image/jpeg;base64,' + event.data;// 创建一个新的Image对象来加载图片let img = new Image();// 图片加载完成后绘制到canvas上img.onload = function () {ctx.clearRect(0, 0, canvas.width, canvas.height);ctx.drawImage(img, 0, 0, img.width, img.height, 0, 0, canvas.width, canvas.height);};// 设置图片的src为base64编码的数据img.src = imageData;};// WebSocket连接错误时的处理ws.onerror = function (error) {console.error('WebSocket Error: ', error);};// WebSocket连接关闭时的处理ws.onclose = function (event) {if (event.wasClean) {console.log('WebSocket connection closed cleanly, code=' + event.code + ' reason=' + event.reason);} else {console.error('WebSocket connection died');}};
</script>
</body>
</html>

这篇关于Crow+opencv+websocket实现实时rtsp视频拉取以及显示的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1030347

相关文章

c++ 类成员变量默认初始值的实现

《c++类成员变量默认初始值的实现》本文主要介绍了c++类成员变量默认初始值,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录C++类成员变量初始化c++类的变量的初始化在C++中,如果使用类成员变量时未给定其初始值,那么它将被

Qt使用QSqlDatabase连接MySQL实现增删改查功能

《Qt使用QSqlDatabase连接MySQL实现增删改查功能》这篇文章主要为大家详细介绍了Qt如何使用QSqlDatabase连接MySQL实现增删改查功能,文中的示例代码讲解详细,感兴趣的小伙伴... 目录一、创建数据表二、连接mysql数据库三、封装成一个完整的轻量级 ORM 风格类3.1 表结构

基于Python实现一个图片拆分工具

《基于Python实现一个图片拆分工具》这篇文章主要为大家详细介绍了如何基于Python实现一个图片拆分工具,可以根据需要的行数和列数进行拆分,感兴趣的小伙伴可以跟随小编一起学习一下... 简单介绍先自己选择输入的图片,默认是输出到项目文件夹中,可以自己选择其他的文件夹,选择需要拆分的行数和列数,可以通过

Python中将嵌套列表扁平化的多种实现方法

《Python中将嵌套列表扁平化的多种实现方法》在Python编程中,我们常常会遇到需要将嵌套列表(即列表中包含列表)转换为一个一维的扁平列表的需求,本文将给大家介绍了多种实现这一目标的方法,需要的朋... 目录python中将嵌套列表扁平化的方法技术背景实现步骤1. 使用嵌套列表推导式2. 使用itert

Python使用pip工具实现包自动更新的多种方法

《Python使用pip工具实现包自动更新的多种方法》本文深入探讨了使用Python的pip工具实现包自动更新的各种方法和技术,我们将从基础概念开始,逐步介绍手动更新方法、自动化脚本编写、结合CI/C... 目录1. 背景介绍1.1 目的和范围1.2 预期读者1.3 文档结构概述1.4 术语表1.4.1 核

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

Spring Boot中WebSocket常用使用方法详解

《SpringBoot中WebSocket常用使用方法详解》本文从WebSocket的基础概念出发,详细介绍了SpringBoot集成WebSocket的步骤,并重点讲解了常用的使用方法,包括简单消... 目录一、WebSocket基础概念1.1 什么是WebSocket1.2 WebSocket与HTTP

Python使用python-can实现合并BLF文件

《Python使用python-can实现合并BLF文件》python-can库是Python生态中专注于CAN总线通信与数据处理的强大工具,本文将使用python-can为BLF文件合并提供高效灵活... 目录一、python-can 库:CAN 数据处理的利器二、BLF 文件合并核心代码解析1. 基础合

Python使用OpenCV实现获取视频时长的小工具

《Python使用OpenCV实现获取视频时长的小工具》在处理视频数据时,获取视频的时长是一项常见且基础的需求,本文将详细介绍如何使用Python和OpenCV获取视频时长,并对每一行代码进行深入解析... 目录一、代码实现二、代码解析1. 导入 OpenCV 库2. 定义获取视频时长的函数3. 打开视频文

golang版本升级如何实现

《golang版本升级如何实现》:本文主要介绍golang版本升级如何实现问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录golanwww.chinasem.cng版本升级linux上golang版本升级删除golang旧版本安装golang最新版本总结gola