使用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("服务端退出,释放资源")

其实直接拿去用即可! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/58493.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【学术论文投稿】React全攻略:构建高性能前端应用的黄金法则

第六届国际科技创新学术交流大会暨管理科学信息化与经济创新发展学术会议&#xff08;MSIEID 2024&#xff09;_艾思科蓝_学术一站式服务平台 更多学术会议论文投稿请看&#xff1a;https://ais.cn/u/nuyAF3 目录 引言 1. React简介 2. React的三大核心概念 2.1 JSX 2.2…

鸿蒙应用App测试-专项测试(DevEco Testing)

注意&#xff1a;大家记得先学通用测试在学专项测试 鸿蒙应用App测试-通用测试-CSDN博客 注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得博主文章写的好的话&#xff0c;可以点下关注&am…

AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。

2024-10-31&#xff0c;由清华大学和北京大学共同创建的AndroidLab数据集&#xff0c;为安卓自主代理的训练和评估提供了一个包含操作环境、行动空间和可复现基准的系统框架&#xff0c;这对于推动安卓代理技术的发展具有重要意义。 数据集地址&#xff1a;Android Instruct|A…

如何在算家云搭建Aatrox-Bert-VITS2(音频生成)

一、模型介绍 ‌ Aatrox - Bert -VITS2 模型是一种基于深度学习的语音合成系统&#xff0c;结合了 BERT 的预训练能力和 VITS2 的微调技术&#xff0c;旨在实现高质量的个性化语音合成。 二、模型搭建流程 1. 创建容器实例 进入算家云的“应用社区”&#xff0c;点击搜索找到…

OpenEuler 使用ffmpeg x11grab捕获屏幕流,rtsp推流,并用vlc播放

环境准备 安装x11grab(用于捕获屏幕流)和libx264(用于编码) # 基础开发环境&x11grab sudo dnf install -y \autoconf \automake \bzip2 \bzip2-devel \cmake \freetype-devel \gcc \gcc-c \git \libtool \make \mercurial \pkgconfig \zlib-devel \libX11-devel \libXext…

QCustomPlot添加自定义的图例,实现隐藏、删除功能(二)

文章目录 QCustomPlot初识和基本效果图实现步骤:详细代码示例:实现原理和解释:使用方法:其他参考要实现一个支持复选框来控制曲线显示和隐藏的自定义 QCPLegend 类,可以通过继承 QCPLegend 并重写绘制和事件处理方法来实现,同时发出信号通知曲线的状态变更。 QCustomPl…

聊一聊Elasticsearch的基本原理与形成机制

1、搜索引擎的基本原理 通常搜索引擎包括&#xff1a;数据采集、文本分析、索引存储、搜索等模块&#xff0c;它们之间的协作流程如下图&#xff1a; 数据采集模块负责采集需要搜索的数据源。 文本分析模块是将结构化数据中的长文本切分成有实际意义的词&#xff0c;这样用户…

PyCharm 导入本地包目录

PyCharm 是最新版 声明一下是野路子 现在我工程目录下有个 gen-py 文件夹&#xff0c;这是我 thrift 编译出来的 Python 依赖包 使用的话&#xff0c;我们可以在代码里加入系统路径 sys.path.append("./gen-py/") 但是这样写&#xff0c;PyCharm 没有提示&#…

80端口被进程 System PID=4 IIS导致的解决方法

是因为80端口被IIS占用。解决办法打开IIS管理器&#xff0c;然后停止&#xff1a;

异步4位计数器(Quartus与Modelsim联合仿真)

异步计数器&#xff08;也称为ripple-through counter&#xff09;的特点是每一位触发器的输出作为下一位触发器的时钟输入&#xff0c;因此计数速度会因为级联触发器的传播延迟而受到限制。这种计数器的最大工作频率通常低于同步计数器。 一、电路符号 输入信号&#xff1a;时…

若依cloud版集成微信扫码登录-绑定篇

前言 集成微信扫码登录的话&#xff0c;需要把项目的账号和微信账号进行绑定&#xff0c;然后才可以进行扫码登录 本篇内容是项目绑定微信 还需要申请一个微信开放平台账号 微信公众平台申请&#xff08;测试平台&#xff09;-CSDN博客 平台的项目回调接口可以先不写&…

鸿蒙应用App测试-通用测试

注意&#xff1a;大家记得学完通用测试记得再学鸿蒙专项测试 鸿蒙应用App测试-专项测试&#xff08;DevEco Testing&#xff09;-CSDN博客 注意&#xff1a;博主有个鸿蒙专栏&#xff0c;里面从上到下有关于鸿蒙next的教学文档&#xff0c;大家感兴趣可以学习下 如果大家觉得…

Linux 配置JDK

文章目录 一、下载Oracle-JDK1.1、如何正确的下载JDK二、配置JDK环境变量2.1 环境变量配置2.1.1、修改vim /etc/profile 添加jdk的路径一、下载Oracle-JDK 1.1、如何正确的下载JDK 首先我要安装的是oracle-jdk,这个时候什么地方都不要去,就去oracle的官网,然后找到,jdk的下…

springBoot 自动配置与starter

目录 一、自动配置 Springboot实现自动配置的核心机制 Conditional的作用是什么&#xff1f; 如何自定义自动配置&#xff1f; 步骤 例子分析 自动配置的优先级 如何禁用特定的自动配置&#xff1f; 二、starter 如何理解Spring Boot中的starter&#xff1f; 如何自…

Json 类型与多值索引 — OceanBase 4.3.2 AP 功能体验

本文来自 2024年OceanBase技术征文大赛——“让技术被看见 | OceanBase 布道师计划”的用户征文。也欢迎更多的技术爱好者参与征文&#xff0c;赢取万元大奖。和我们一起&#xff0c;用文字让代码跳动起来&#xff01; 参与2024年OceanBase技术征文大赛>> MySQL在5.7.8…

Spark on YARN:Spark集群模式之Yarn模式的原理、搭建与实践

Spark 的介绍与搭建&#xff1a;从理论到实践-CSDN博客 Spark 的Standalone集群环境安装与测试-CSDN博客 PySpark 本地开发环境搭建与实践-CSDN博客 Spark 程序开发与提交&#xff1a;本地与集群模式全解析-CSDN博客 目录 一、Spark on YARN 的优势 &#xff08;一&#…

java后端工程师转行AI大模型岗,工作、自我提升两不误!

随着技术的不断进步&#xff0c;人工智能&#xff08;AI&#xff09;已经成为当今科技领域最热门的话题之一。许多开发者开始考虑从传统的软件开发领域&#xff0c;如Java&#xff0c;转向人工智能领域&#xff0c;今天小编和大家一起来探讨Java开发者是否可以转型到人工智能&a…

Rust-宏编程

巴山楚水凄凉地&#xff0c;二十三年弃置身。 怀旧空吟闻笛赋&#xff0c;到乡翻似烂柯人。 沉舟侧畔千帆过&#xff0c;病树前头万木春。 今日听君歌一曲&#xff0c;暂凭杯酒长精神。 ——《酬乐天扬州初逢席上见赠》唐刘禹锡 【哲理】翻覆的船只旁仍有千千万万的帆船经过&a…

leetcode912.排序数组的题解

题目描述&#xff1a; 题目要求在不使用任何内置函数的情况下解决问题&#xff0c;时间复杂度为 O(nlog(n))。 笔者使用了快速排序&#xff0c;但是直接使用最原始的快速排序&#xff0c;有些特殊的测试用例会超时。 1&#xff09;如果数组本身基本有序&#xff0c;则使用原始…

TikTok品牌出海:从“流量为王”到“价值为王”

随着市场竞争的加剧&#xff0c;品牌逐渐意识到&#xff0c;仅仅依靠流量已不足以在海外市场立足&#xff0c;必须实现从“流量为王”到“价值为王”的转变。本文Nox聚星将和大家探讨品牌如何与TikTok达人合作&#xff0c;在海外市场中建立长期稳定的品牌形象。 一、品牌出海的…