使用UDP协议传输视频流!(分片、缓存)
背景
最近在开发工作中遇到需要两台本地设备之间进行视频流的传输的情况。但是团队一来没有这方面的专业人才,二来视频流的传续数据量很大,针对TCP和UDP的具体选择也不明确。
本文是在上诉背景之下进行的研究和开发工作。
目录
背景
UDP和TCP协议的选择
Socket-UDP协议代码详解
UDP协议发送端
UDP协议接收端
UDP和TCP协议的选择
视频流更加适合UDP协议的传输!
序号 | UDP协议 | TCP协议 |
是否需要握手 | 不需要握手 | 需要握手 |
是否确保数据帧传输准确性 | 不确保 | 确保 |
是否确保数据帧传输顺序一致性 | 不确保 | 确保 |
速度 | 相对高速 | 相对低速 |
对于一些需要准确传输的信息,则选择TCP协议
对于一些需要高速传输和不在意准确性的数据,选择UDP协议。
显然实时视频流就是一个典型的适合UDP协议的数据。
- 实时视频流不在意数据是否完整传输(因为传输错误的帧马上就是过去式了,接着显示新的帧)
- 实时视频流不在意帧是否顺序一致(少数的几帧在短暂的时间戳内顺序不一致无伤大雅)
- 但实时视频流需要帧高速
Socket-UDP协议代码详解
UDP协议发送端
是否常常遇到问题:
OSError: [WinError 10040] 一个在数据报套接字上发送的消息大于内部消息缓冲区或其他一些网络限制,或该用户用于接收数据报的缓冲区比数据报小?
简单,发送端仅需要分片发送即可,当然接收端也需要想要调整。
import cv2
import socket
import time
import struct
import numpy as np
import logging# 配置摄像头和UDP传输参数
FPS_INTERVAL = 0.1 # 每隔0.1秒计算一次帧率
UDP_IP = "127.0.0.1" # 目标接收端IP
UDP_PORT = 12345 # 目标接收端端口
MAX_UDP_SIZE = 1024 # 每个数据包最大传输大小,调整为1024字节# 设置日志
logging.basicConfig(level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger()# 创建UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)# 打开摄像头
cap = cv2.VideoCapture(1)
if not cap.isOpened():logger.error("无法打开摄像头,请检查设备连接")exit(1)cap.set(cv2.CAP_PROP_FRAME_WIDTH, 4000)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 3000)
cap.set(cv2.CAP_PROP_FPS, 60)# 初始化一些变量
frame_count = 0
last_time = time.time()while True:try:ret, frame = cap.read()if not ret:logger.warning("无法读取摄像头帧")continue # 如果读取失败,跳过本次循环# 定义新的大小(宽度,高度)new_dims = (1280, 960) # 新的宽度和高度# 使用cv2.resize()调整图像大小frame = cv2.resize(frame, new_dims, interpolation=cv2.INTER_LINEAR)# 将帧转换为JPEG格式ret, jpeg = cv2.imencode('.jpg', frame)if ret:# 将JPEG图像数据转为字节流data = jpeg.tobytes()data_len = len(data)# 发送帧数据的总长度try:sock.sendto(struct.pack("L", data_len), (UDP_IP, UDP_PORT)) # 发送数据长度except socket.error as e:logger.error(f"发送数据长度失败: {e}")continue # 如果发送失败,跳过本次循环# 分片发送数据for i in range(0, data_len, MAX_UDP_SIZE):packet = data[i:i+MAX_UDP_SIZE]try:sock.sendto(packet, (UDP_IP, UDP_PORT)) # 发送数据片段except socket.error as e:logger.error(f"发送数据片段失败: {e}")continue # 如果发送失败,跳过本次循环# 计算帧率:每帧计算一次current_time = time.time()frame_time = current_time - last_time # 计算当前帧的时间差fps = 1.0 / frame_time if frame_time > 0 else 0 # 帧率 = 1 / 帧间隔# 更新上次帧的时间last_time = current_time# 在左上角显示帧率cv2.putText(frame, f"client-FPS: {fps:.2f}", (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)# 显示视频流cv2.imshow('Camera', frame)except (cv2.error, socket.error) as e:logger.error(f"发生异常: {e}")# 如果发生异常,等待一段时间重试time.sleep(2)continue# 按'q'退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 释放资源
cap.release()
cv2.destroyAllWindows()
UDP协议接收端
在遇到缓存问题的时候,接收端除了增设分片接受以外,还需要进行缓冲区大小的设定,这里推荐为5MB。当然还需要try except之后清空所有的缓冲区!
import cv2
import socket
import struct
import numpy as np
import time
import logging# 配置UDP接收参数
UDP_IP = "127.0.0.1" # 本地IP
UDP_PORT = 12345 # 端口号# 创建UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((UDP_IP, UDP_PORT))# 增加接收缓冲区的大小
sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 1048576 * 5) # 设置接收缓冲区大小为5MB# 用于接收数据的缓冲区
buffer = b''# 设置日志记录
LOG_FORMAT = "%(asctime)s - %(levelname)s - %(message)s"
logging.basicConfig(filename='udp_server.log', level=logging.DEBUG, format=LOG_FORMAT)last_time = time.time()def clear_socket_buffer():"""清空socket的接收缓冲区,丢弃所有未处理的数据。"""while True:# 尝试读取一部分数据sock.settimeout(0.1) # 设置一个短暂的超时避免阻塞try:data = sock.recv(4096) # 尝试读取最大4KB的数据if not data:breakexcept socket.timeout:break # 如果超时,退出循环while True:try:# 接收数据长度(最多接收4字节)data_len, addr = sock.recvfrom(4)if not data_len:continuedata_len = struct.unpack("L", data_len)[0]# 接收图像数据(分片)buffer = b'' # 清空之前的缓冲区while len(buffer) < data_len:packet, addr = sock.recvfrom(1450) # 每次接收一个片段buffer += packet # 将接收到的数据片段拼接到缓冲区# 确保接收到完整数据if len(buffer) == data_len:# 解码图像nparr = np.frombuffer(buffer, np.uint8)frame = cv2.imdecode(nparr, cv2.IMREAD_COLOR)if frame is not None:# 计算并显示帧率fps = 1 / (time.time() - last_time) if (time.time() - last_time) > 0 else 0last_time = time.time()# 在左上角显示帧率cv2.putText(frame, f"Server-FPS: {fps:.2f}", (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)# 显示接收到的图像cv2.imshow('Received Video Stream', frame)else:logging.warning("接收到的图像无法解码!")continue # 如果解码失败,跳过本次循环else:logging.error(f"接收到的数据包大小不匹配: 期望 {data_len} 字节, 实际 {len(buffer)} 字节")continue # 如果数据不完整,跳过本次循环except socket.timeout:logging.warning("接收超时,等待下一帧数据...")continue # 如果超时,继续等待except Exception as e:clear_socket_buffer()logging.error(f"发生异常: {e}")time.sleep(1) # 如果发生异常,休眠2秒后继续尝试# 按 'q' 键退出if cv2.waitKey(1) & 0xFF == ord('q'):break# 释放资源
cv2.destroyAllWindows()
sock.close()
logging.info("服务端退出,释放资源")
其实直接拿去用即可!
相关文章:

使用UDP协议传输视频流!(分片、缓存)
背景 最近在开发工作中遇到需要两台本地设备之间进行视频流的传输的情况。但是团队一来没有这方面的专业人才,二来视频流的传续数据量很大,针对TCP和UDP的具体选择也不明确。 本文是在上诉背景之下进行的研究和开发工作。 目录 背景 UDP和TCP协议的…...

Pinia小菠萝(状态管理器)
Pinia 是一个专为 Vue 3 设计的状态管理库,它借鉴了 Vuex 的一些概念,但更加轻量灵活。下面将详细介绍如何使用 Pinia 状态管理库: 安装 Pinia 使用 npm:在项目目录下运行npm install pinia。使用 yarn:在项目目录下运…...

Python知识点:基于Python工具,如何使用Web3.py进行以太坊智能合约开发
开篇,先说一个好消息,截止到2025年1月1日前,翻到文末找到我,赠送定制版的开题报告和任务书,先到先得!过期不候! 基于Python工具Web3.py进行以太坊智能合约开发 简介 智能合约是区块链技术的核…...

【简信CRM-注册安全分析报告】
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...

ssm+vue694基于Java的药店药品信息管理系统的设计与实现
博主介绍:专注于Java(springboot ssm 等开发框架) vue .net php phython node.js uniapp 微信小程序 等诸多技术领域和毕业项目实战、企业信息化系统建设,从业十五余年开发设计教学工作 ☆☆☆ 精彩专栏推荐订阅☆☆☆☆☆不…...

Sentinel微服务保护
目录 雪崩问题 解决雪崩问题的方法: 我们使用sentinel组件实现微服务的保护 一:下载sentinel 二.启动sentinel 三.访问:localhost:8080 默认的账号和密码都是sentinel 微服务整合sentinel 一.导入sentinel依赖 二.在application.yml配置…...

喜讯!实在Agent智能体入选《2024年度最佳企业服务AI产品榜》
10 月 30 日,硅星人主办的首届 AI 创造者大会(ACC)于北京中关村盛大举行。此次大会旨在为 AI 产业生态的共建者构筑一个多元化的交流平台。大会邀请了硅星人创始人兼 CEO 骆轶航、国家地方共建具身智能机器人创新中心 CTO 唐剑、百度智能云应…...

Aop+自定义注解实现数据字典映射
数据字典 Web项目开发中,字典表的一般都会存在,主要用来给整个系统提供基础服务。 比如男女性别的类型可以使用0和1来进行表示,在存储数据和查询数据的时候,就可以使用字典表中的数据进行翻译处理。 再比如之前做的一个项目中宠物…...

大语言模型(LLM)入门级选手初学教程 III
指令微调 一、指令数据的构建 包括任务描述(也称为指令)、任务输入-任务输出以及可选的示例。 Self-Instruct 指令数据生成:从任务池中随机选取少量指令数据作为示例,并针对Chat-GPT 设计精细指令来提示模型生成新的微调数据…...

STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入
STM32G0xx使用LL库将Flash页分块方式存储数据实现一次擦除可多次写入 参考例程例程说明一、存储到Flash中的数据二、Flash最底层操作(解锁,加锁,擦除,读写)三、从Flash块中读取数据五、测试验证 参考例程 STM32G0xx HAL和LL库Flash读写擦除操…...

SAP B1 认证考试习题 - 解析版(三)
前一篇:《SAP B1 认证考试习题 - 解析版(二)》 题目纯享版合集:《SAP B1 认证考试习题 - 纯享版》 五、运费(附加费用) 57. 以下哪个选项能够影响库存商品的价格 A. 仅为总量级别的附加费用 B. 只为行级…...

数据库开发规范
一、概述 本规范规定了,软件项目团队开发数据库的全流程规范。规范覆盖了数据库设计、管理及配套文件等。 二、项目阶段 项目阶段包括需求评审(需求分析阶段)、技术评审(方案阶段)、数据库开发…...

使用python向钉钉群聊发送消息
使用python向钉钉群聊发送消息 一、在钉钉群中新建机器人二、使用代码发送消息 一、在钉钉群中新建机器人 在群设置中添加机器人 选择自定义 勾选对应的安全设置 完成后会展示webhook,将地址复制出来,并记录,后面会用到 二、使用代码发送消…...

YOLOv11改进:SE注意力机制【注意力系列篇】(附详细的修改步骤,以及代码,与其他一些注意力机制相比,不仅准确度更高,而且模型更加轻量化。)
如果实验环境尚未搭建成功,可以参考这篇文章 ->【YOLOv11超详细环境搭建以及模型训练(GPU版本)】 文章链接为:YOLOv11超详细环境搭建以及模型训练(GPU版本)-CSDN博客 -------------------------------…...

STM32 基于HAL库和STM32cubeIDE的应用教程 (二)--GPIO的使用
如果有什么不懂的地方欢迎私聊博主,欢迎小白,博主必一一解答。 在 STM32 中,GPIO(通用输入输出)是与外部硬件接口进行交互的主要方式之一。STM32 HAL 库提供了简洁的接口来配置和控制 GPIO 引脚。下面是使用 STM32 HA…...

【毫米波雷达(七)】自动驾驶汽车中的精准定位——RTK定位技术
一、什么是RTK? RTK,英文全名叫做Real-time kinematic,也就是实时动态。这是一个简称,全称其实应该是RTK(Real-time kinematic,实时动态)载波相位差分技术。 二、RTK的组装 如上图所示&#x…...

Transformer和BERT的区别
Transformer和BERT的区别比较表: 两者的位置编码: 为什么要对位置进行编码? Attention提取特征的时候,可以获取全局每个词对之间的关系,但是并没有显式保留时序信息,或者说位置信息。就算打乱序列中token…...

linux 加载uPD720201固件
硬件 jetson orin nano jetpack 35.5.0 uPD720201是瑞萨推出的怕pcie扩展usb3.0芯片,支持flash主动加载与在系统被动加载 本文介绍如何做到没接flash情况下由系统加载固件 在uPD720201没接spi flash时候nano启动会报XhciDxe错误而自动重启,首先需要在ue…...

C语言中的信号量semaphore详解
在C语言中,**信号量(Semaphore)**是一种常用的同步机制,用于控制多个线程或进程对共享资源的访问。信号量可以实现类似于锁的效果,但更为灵活,适用于并发编程场景。 1. 什么是信号量 信号量可以看作是一个…...

0087__DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区
DirectX11 With Windows SDK--02 顶点/像素着色器的创建、顶点缓冲区-CSDN博客...

Windows换机华为擎云(银河麒麟V10+麒麟9000C CPU)后,使用selenium的程序怎么办(20241030)
原本的 seleniumChrome 已无法正常工作。chromedriver 报错:不支持 Linux/aarch64。 1、尝试Firefox、edge驱动。Firefox有一个geckodriver版本与Firefox版本的对照表,我看了一下,感觉他们是始终跟进新技术的。银河麒麟的很多库都是几年前的…...

linux 下 signal() 函数的用法,信号类型在哪里定义的?
--------------------------------------------------- author: hjjdebug date: 2024年 11月 07日 星期四 14:47:33 CST description: linux 下 signal() 函数的用法 --------------------------------------------------- signal 是linux 下最基础的进程通讯机制…...

享元模式及其运用场景:结合工厂模式和单例模式优化内存使用
介绍 享元模式(Flyweight Pattern)是一种结构型设计模式,它通过共享对象来减少内存使用,尤其是对于大量相似对象的场景。享元模式通常与工厂模式和单例模式结合使用,从而有效地控制和复用对象的创建。在享元模式中&am…...

【物联网技术】ESP8266 WIFI模块在STA模式下实现UDP与电脑/手机网络助手通信——UDP数据透传
前言:完成ESP8266 WIFI模块在STA模式下实现UDP与电脑/手机网络助手通信——实现UDP数据透传 STA模式,通俗来说就是模块/单片机去连接路由器/热点来通信。 UDP协议,是传输层协议,UDP没有服务器和客户端的说法。 本实验需要注意,wifi模块/单片机与电脑/手机需要连接在同一个…...

【SQL Server】华中农业大学空间数据库实验报告 实验一 数据库
实验目的 熟悉了解掌握SQL Server软件的基本操作与使用方法,认识界面,了解其两个基本操作系统文件,并能熟练区分与应用交互式与T-SQL式两种方法在SQL Server中如何进行操作;学习有关数据库的基本操作,包括:…...

操作系统页面置换算法Java实现(LFU,OPT,LRU,LFU,CLOCK)
FIFO先进先出算法 java import java.util.LinkedList; import java.util.Queue; public class Main { //先进先出的思想 是 用一个队列去模拟数据 如果当前不存在就是发生缺页中断了 就需要添加 如果已经满了 将队头的元素出队 即可 //先进先出 就是一个数组 frameCount publi…...

Request和Response
前言 这一节主要讲的是Request和Response还有一些实例 1. 介绍 就是这两个参数 WebServlet("/demo7") public class ServletDemo7 extends HttpServlet {Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletExcepti…...

【青牛科技】GC8549替代LV8549/ONSEMI在摇头机、舞台灯、打印机和白色家电等产品上的应用分析
引言 在现代电子产品中,控制芯片的性能直接影响到设备的功能和用户体验。摇头机、舞台灯、打印机和白色家电等领域对控制精度、功耗和成本等方面的要求日益提高。LV8549/ONSEMI等国际品牌的芯片曾是这些产品的主要选择,但随着国内半导体技术的进步&…...

(十二)JavaWeb后端开发——MySQL数据库
目录 1.数据库概述 2.MyQSL 3.数据库设计 DDL 4.MySQL常见数据类型 5.DML 1.数据库概述 数据库:DataBase(DB),是存储和管理数据的仓库 数据库管理系统:DataBase ManagementSystem(DBMS),操纵和管理数据库的大型软件 SQL&a…...

pnpm管理多工作区依赖
pnpm是一个支持多包仓库的一个包管理工具,那么怎么可以在项目根目录下执行pnpm install的时候,也能同步让所有的工作区都能够通安装依赖呢? 方式一,在执行pnpm install指令的时候,添加recursive参数: pnpm install --recursive 方式二,在项目的根目录下通过pnpm的配置文件p…...