udp 视频传输_webrtc视频流传输

udp 视频传输_webrtc视频流传输在UDP实时图像传输一文中,我们介绍了如何使用UDP来实现视频的实时传输,并使用C#进行了发送端和接收端的搭建。但是这篇文章中的方法是对整张图片进行JPEG压缩,并通过UDP一次性地发送到接收端,由于一个UDP数据包只能发送64k字节的数据,所以这篇文章中的方法的图片传输大小是有限制的,实测只能发送480P视频中的图像。所以在本文中,我们将继续采取逐帧发送的形式,以1080P的视频为例,实现更高清晰度(1080×1920×31080\times1920\times31080×1920×3)的图像实时传

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

UDP实时图像传输一文中,介绍了如何使用UDP来实现图像的实时传输,并使用C#进行了发送端和接收端的搭建。但是文中的方法是对整张图片进行JPEG压缩,并通过UDP一次性地发送到接收端,由于一个UDP数据包只能发送64k字节的数据,所以该方法的图片传输大小是有限制的,实测只能发送480P视频中的图像。

所以本文将继续采取逐帧发送的形式,以1080P的视频为例,实现更高清晰度( 1080 × 1920 × 3 1080\times 1920\times 3 1080×1920×3)的图像实时传输。

基本流程

本文中的高清晰度图像传输就是在前文方法的基础上,在发送端添加了切片压缩传输以及并行加速的步骤,而接收端则相应地使用多线程进行数据接收,分别接收压缩后的切片数据,再拼接起来进行显示。流程如下


系统框图

实验环境

  • VS2019 / .NET4.7.1 / C#(开发环境)

  • EmguCV 4.1(用于读取、压缩图像,使用方法见上一篇文章

  • PC(测试环境)

发送端

在发送端我们需要达到的效果如下,左边用来显示原始图像,右上角用来显示各个切片,右下角用来处理接收端的连接请求。
在这里插入图片描述
首先设置一些参数

// 实例化一个VideoCapture,选择从本地文件读取视频
private VideoCapture capture = new VideoCapture("../../video/04.mp4");
// 设置读取的图片宽度
const int WIDTH = 1920;  
// 设置读取的图片高度
const int HEIGHT = 1080; 
// 切片数量
const int NUM_SLICE = 24; 

然后进行图像的显示以及切片。初始化一组显示控件,用来显示切片后的结果:

private void Form1_Load(object sender, EventArgs e)
{ 
   
    // 设置图像大小
    capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameWidth, WIDTH);
    capture.SetCaptureProperty(Emgu.CV.CvEnum.CapProp.FrameHeight, HEIGHT);

    // 获取面板控件的大小
    int w = panel_imgs.Width;
    int h = panel_imgs.Height;
    // 在面板panel_imgs上添加显示控件,用于显示每个切片
    for (int i = 0; i < NUM_SLICE; i++)
    { 
   
        ImageBox imgb = new ImageBox();
        imgb.Left = 0;
        imgb.Top = i * h / NUM_SLICE;
        imgb.Width = w;
        imgb.Height = h / NUM_SLICE - 1;
        imgb.SizeMode = PictureBoxSizeMode.StretchImage;
        imgb.Visible = true;

        imgbox[i] = imgb;
        panel_imgs.Controls.Add(imgbox[i]);
    }
    // 在下拉文本框cbb_localIP中显示该计算机中的IPv4地址
    cbb_localIP.Text = GetLocalIPv4Address().ToString();
}

最后就是图像的读取、切片、压缩、发送等处理函数,这处理过程中,使用了Parallel.For并行加速功能,相对于串行的for循环,并行速度提高了一倍左右(不知道为啥我四核八线程的处理器只能降低一半的运行时间)

private void ProcessFram() // 图像读取、切片、发送
{ 
   
    DateTime startDT = System.DateTime.Now;
    while (true)
    { 
   
        // 计算两次循环间的间隔,并显示在左上角
        DateTime stopDT = System.DateTime.Now;
        TimeSpan ts = stopDT.Subtract(startDT);
        this.Text = "图片处理耗时:" + ts.TotalMilliseconds + "ms";
        startDT = System.DateTime.Now;

        // 读取一张图片
        Mat currentImage = capture.QueryFrame();
        // 显示摄像头/视频流的图像
        imageBox0.Image = currentImage;

        int N = HEIGHT / NUM_SLICE;

        // 对图像进行切片,并将切片压缩后发送到接收端 
        Parallel.For(0, NUM_SLICE, i => // Parallel并行加速
                     { 
   
                         // 从原图中切割,输入参数:原始图片 行范围row 列范围col
                         img[i] = new Mat(currentImage, new Range(i * N, (i + 1) * N - 1), new Range(0, WIDTH));
                         // 显示
                         imgbox[i].Image = img[i];

                         // 转换格式
                         Image<Rgb, Byte> img_trans = img[i].ToImage<Rgb, Byte>();
                         // JPEG压缩
                         byte[] bytes = img_trans.ToJpegData(95);

                         // UDP配置
                         UdpClient udpClient = new UdpClient();
                         //IPAddress ipaddress = IPAddress.Parse("192.168.0.105");
                         IPAddress ipaddress = remoteIP;
                         IPEndPoint endpoint = new IPEndPoint(ipaddress, 8000 + 10 * i);
                         // UDP发送
                         udpClient.Send(bytes, bytes.Length, endpoint);
                         udpClient.Close();
                     }
                    );
    }
}

在初始化函数中添加以下程序就可以执行包含切片、压缩、发送等操作的线程

Thread transFrames = new Thread(ProcessFram);
transFrames.Start();

接收端

接收端比较简单,实现效果如下,因为在接收端没有对图片进行更进一步的处理,所以本文只在接收端添加了若干个显示控件,用来显示每个切片,但是从观感上每个切片依次连接,形成了一张完整的图片。
在这里插入图片描述
首先进行参数设置

 // 切片数量,与发送端保持一致
 const int NUM_SLICE = 24; 
 // 为每一个切片创建一个显示控件
 PictureBox[] imgbox = new PictureBox[NUM_SLICE];
 // 为每一个切片创建一个UDP套接字
 Socket[] udpServer = new Socket[NUM_SLICE];

在初始化过程中添加显示控件,与发送端类似

int w = panel_imgs.Width;
int h = panel_imgs.Height;
// 在面板panel_imgs上添加显示接收到的图片的控件
for (int i = 0; i < NUM_SLICE; i++)
{ 
   
    // 设置PictureBox的位置、大小等参数
    PictureBox imgb = new PictureBox();
    imgb.Left = 0;
    imgb.Top = i * h / NUM_SLICE;
    imgb.Width = w;
    imgb.Height = h / NUM_SLICE + 1;
    imgb.SizeMode = PictureBoxSizeMode.StretchImage;
    imgb.Visible = true;

    // 添加到面板panel_imgs上
    imgbox[i] = imgb;                
    panel_imgs.Controls.Add(imgbox[i]);
}

接下来需要为每个切片创建一个接收线程

for (int i = 0; i < NUM_SLICE; i++)
{ 
   
	new Thread(new ParameterizedThreadStart(ImgReceive))
    { 
   
        IsBackground = true
    }.Start(8000 + i * 10); // 输入参数为端口号,依次增加
}

最后就是接收线程的入口函数ImgReceive的内容

private void ImgReceive(object arg)
{ 
   
    // 网络端口号
    int port = (int)arg;
    int index = port % 8000 / 10;

    // 创建套接字
    udpServer[index] = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
    // 绑定IP和端口
    udpServer[index].Bind(new IPEndPoint(IPAddress.Parse(cbb_remoteIP.Text), port));
    // 开启数据接收线程
    while (true)
    { 
   
        EndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, 0);
        // 设置一个64k的字节数组作为缓存
        byte[] data = new byte[65536];
        int length = udpServer[index].ReceiveFrom(data, ref remoteEndPoint);//此方法把数据来源ip、port放到第二个参数中

        MemoryStream ms = new MemoryStream(data, 0, length);
        // 将图像显示到对应的PictureBox控件上
        Image img = Image.FromStream(ms);
        imgbox[index].Image = img;
    }
}

测试结果

测试时发送端和接收端都在同一台PC上运行,运行流程与结果如这张GIF所示,左边是发送端,右边是接收端

在这里插入图片描述

运行流程中的TCP创建与连接的程序本文没有给出,可以参考上一篇文章。
如果有时间的话,就继续添加利用时间戳计算延迟的程序。另外,发送端的并行处理效率有待提高,后续可以使用C++进行openMP加速看看效果。
完整项目在这里https://download.csdn.net/download/qq_42688495/12416877(资源里没有视频文件,使用时将视频放到\ImgTransmitPlus\ImgTransmitPlus\video中,再更改发送端代码中的视频文件名即可)

———– 2020.8.28更新 ———–
TCP对传输的数据大小没有限制,且能保证传输的可靠性,详见TCP实时图像传输

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/186247.html原文链接:https://javaforall.net

(0)
上一篇 2022年10月3日 下午9:16
下一篇 2022年10月3日 下午9:36


相关推荐

  • 2021年程序员平均工资_中国程序员数量

    2021年程序员平均工资_中国程序员数量前言两会期间,包括腾讯董事会主席兼首席执行官马化腾,百度董事长兼CEO李彦宏,小米创始人兼CEO雷军等一众互联网科技大佬提出了一系列的提案,围绕数字经济,自动驾驶,网络安全,智能制造等细分领域,仿佛手握未来一年的数字化密码,为我们开启新世界的大门。他们是新世界的指向灯,在他们背后则是无数的新世界探索者,通过指尖代码为未来铺路的程序员们,一直以来,作为备受人们关注的群体,互联网的飞速发展时期离不开他们。为了更好地顺应时代发展形式,运用技术改善生活,程序员客栈对中国程序员薪资和生活现状做了一些

    2022年10月10日
    4
  • mysql的驱动jar包_各版本MySQL数据库驱动程序jar包大全(java连接mysql驱动jar包)

    mysql的驱动jar包_各版本MySQL数据库驱动程序jar包大全(java连接mysql驱动jar包)java连接mysql数据库,数据库程序开发步骤首先就要加载JDBC驱动程序,不同版本的mysql数据库需要的MySQL数据库驱动程序jar包版本也不同。不少同学问怎么下载java连接mysql的驱动jar包?今天给大家分享:各版本MySQL数据库驱动程序jar包大全(java连接mysql驱动jar包)项目中如何加载MySQL数据库驱动程序jar包如:mysql-connector-java-8…

    2022年5月22日
    190
  • 混元1.8B翻译模型API封装:Flask快速教程,1小时上线

    混元1.8B翻译模型API封装:Flask快速教程,1小时上线

    2026年3月13日
    2
  • update Google play services_apfs for windows

    update Google play services_apfs for windows原文首发:http://mobileside.cn/post/2010/02/10/GoogleBuzzForWindowsPhone.aspx  昨天晚上,google推出了他们的Buzz产品。什么是Buzz,简单点说,他就是把微博服务和他的地图以及邮件结合到了一起,其实这是一个很典型的LBS。下载地址为:http://www.google.com/gmm/Google

    2022年8月31日
    5
  • ds1302实时时钟lcd1602显示_电子时钟单片机

    ds1302实时时钟lcd1602显示_电子时钟单片机DS1302时钟模块LCD1602显示DS1302时钟模块相关介绍基本介绍时序图DS1302时钟模块相关介绍基本介绍DS1302可慢速充电实时时钟芯片包含实时时钟/日历和31字节的非易失性静态RAM。它经过一个简单的串行接口与微处理器通信。实时时钟/日历可对秒,分,时,日,周,月,和年进行计数,对于小于31天的月,月末的日期自动进行调整,还具有闰年校正的功能。时钟可以采用24小时格式或带AM(上午)/PM(下午)的12小时格式。31字节的==RAM==可以用来临时保存一

    2025年7月16日
    6
  • php开源在线客服系统_开源在线客服系统源码

    php开源在线客服系统_开源在线客服系统源码介绍:PHP来客在线客服系统源码带安装教程一键安装淘宝买的版本,状态比流通版本还是要好很多。不支持前端商户注册。网盘下载地址:http://www.bytepan.com/jUjduu3BKfx图片:

    2025年11月22日
    6

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号