quic-go实现屏幕广播程序

news2024/10/9 18:58:28

最近在折腾quic-go, 突然想起屏广适合用udp实现,而http3基于quic-go,后者又基于udp, 所以玩一下。

先贴出本机运行效果图:
在这里插入图片描述

功能(实现)说明:

1.服务器先启动作为共享屏幕方,等待客户端连接上来
2.客户端连接
3.客户端和服务器建立连接后,服务器主动打开stream

在一个for 循环中:每秒操作30次下面操作:

	4.服务器开始抓取本机屏幕内容,转换成Image
	5.数据传输协议:Image字节长度 + Image内容

6.客户端按上述协议接收数据,解析成Image对象,放界面上展示


服务端代码:

package main

import (
	"bytes"
	"context"
	"crypto/rand"
	"crypto/rsa"
	"crypto/x509"
	"encoding/binary"
	"encoding/pem"
	"fmt"
	"github.com/quic-go/quic-go"
	"image"
	"image/png"
	"log"
	"math/big"
	"os"
	"time"

	"crypto/tls"
	"github.com/kbinani/screenshot"
)

const addr = "localhost:4000"

var currentDir, _ = os.Getwd()

var quicConf = &quic.Config{
	Allow0RTT:                      true,
	MaxIdleTimeout:                 40 * time.Second,
	InitialStreamReceiveWindow:     1 << 20,  // 1 MB
	MaxStreamReceiveWindow:         6 << 20,  // 6 MB
	InitialConnectionReceiveWindow: 2 << 20,  // 2 MB
	MaxConnectionReceiveWindow:     12 << 20, // 12 MB
}

func main() {
	//listener, err := quic.ListenAddr(addr, generateTLSConfig(), quicConf)
	listener, err := quic.ListenAddr(addr, generateTLSConfig2(), quicConf)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("Server listening on", addr)

	for {
		// 接受客户端连接
		sess, err := listener.Accept(context.Background())
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println("New client connected")
		go handleConnection(sess)
	}
}

func handleConnection(sess quic.Connection) {
	stream, err := sess.OpenStream()
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println("New stream opened:", stream.StreamID())
	defer stream.Close()
	var b []byte
	for {
		// 捕获桌面屏幕
		img, err := captureScreen()
		if err != nil {
			log.Fatal(err)
		}

		// 将图像编码为 PNG 格式
		var buf bytes.Buffer
		err = png.Encode(&buf, img)
		if err != nil {
			log.Fatal(err)
		}

		// magic校验
		//n, err := stream.Write([]byte{0x05, 0x19})
		//if err != nil {
		//	log.Fatal(err)
		//}
		b = buf.Bytes()
		//var headLenBuf = make([]byte, 4)
		//binary.BigEndian.PutUint32(headLenBuf, uint32(len(b)))
		//_, err = stream.Write(headLenBuf)
		err = binary.Write(stream, binary.BigEndian, uint32(len(b)))
		if err != nil {
			log.Fatal(err)
		}
		// 将图像数据发送到客户端
		_, err = stream.Write(b)
		if err != nil {
			log.Fatal(err)
		}
		// 每秒捕获并传输一帧
		time.Sleep(1 * time.Second / 30)
	}
}

func captureScreen() (image.Image, error) {
	bounds := screenshot.GetDisplayBounds(0) // 捕获主屏幕
	img, err := screenshot.CaptureRect(bounds)
	if err != nil {
		return nil, err
	}
	return img, nil
}

/*
*
openssl req -x509 -newkey rsa:4096 -keyout privkey.pem -out cert.pem -days 365 -nodes
*/
func generateTLSConfig() *tls.Config {
	// 使用自签名证书
	// goland运行使用它
	cert, err := tls.LoadX509KeyPair(currentDir+"/screenbroadcast/cert.pem", currentDir+"/screenbroadcast/privkey.pem")
	// 命令行运行使用它
	//cert, err := tls.LoadX509KeyPair("cert.pem", "privkey.pem")
	if err != nil {
		log.Fatal(err)
	}
	return &tls.Config{
		Certificates: []tls.Certificate{cert},
		NextProtos:   []string{"h3-29"},
	}
}

func generateTLSConfig2() *tls.Config {
	key, err := rsa.GenerateKey(rand.Reader, 1024)
	if err != nil {
		panic(err)
	}
	template := x509.Certificate{SerialNumber: big.NewInt(1)}
	certDER, err := x509.CreateCertificate(rand.Reader, &template, &template, &key.PublicKey, key)
	if err != nil {
		panic(err)
	}
	keyPEM := pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(key)})
	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: certDER})

	tlsCert, err := tls.X509KeyPair(certPEM, keyPEM)
	if err != nil {
		panic(err)
	}
	return &tls.Config{
		Certificates: []tls.Certificate{tlsCert},
		NextProtos:   []string{"h3-29"},
	}
}

客户端代码:

package main

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/binary"
	"fmt"
	"github.com/quic-go/quic-go"
	"image"
	"image/png"
	"io"
	"log"
	"time"

	"github.com/faiface/pixel"
	"github.com/faiface/pixel/pixelgl"
)

const addr = "localhost:4000"

var headLenBuf = make([]byte, 4)

func main() {
	pixelgl.Run(run)
}

func run() {
	tlsConf := &tls.Config{
		InsecureSkipVerify: true,
		NextProtos:         []string{"h3-29"},
	}

	quicConfig := &quic.Config{
		MaxIdleTimeout:  40 * time.Second,
		KeepAlivePeriod: 30 * time.Second, // 使用quic的心跳机制
	}
	// 创建 QUIC 连接到服务器
	sess, err := quic.DialAddr(context.Background(), addr, tlsConf, quicConfig)
	if err != nil {
		log.Fatal(err)
	}

	// 接收一个 QUIC stream:没错,是server主动推送数据过来,先发起的open stream
	stream, err := sess.AcceptStream(context.Background())
	if err != nil {
		log.Fatal(err)
	}

	// 创建窗口显示接收的屏幕图像
	cfg := pixelgl.WindowConfig{
		Title:     "Screen Broadcast",
		Bounds:    pixel.R(0, 0, 1024, 680),
		VSync:     true,
		Resizable: true,
	}
	win, err := pixelgl.NewWindow(cfg)
	if err != nil {
		log.Fatal(err)
	}
	for !win.Closed() {
		// 接收图像数据
		img, err := receiveImage(stream)
		if err != nil {
			if err == io.EOF {
				break
			}
			log.Fatal(err)
		}

		// 将图像转换为 pixel.Picture
		pic := pixel.PictureDataFromImage(img)

		// 绘制图像
		sprite := pixel.NewSprite(pic, pic.Bounds())
		win.Clear(pixel.RGB(0, 0, 0))
		sprite.Draw(win, pixel.IM.Moved(win.Bounds().Center()))
		win.Update()
	}
}

func receiveImage(stream quic.Stream) (image.Image, error) {
	//_, err := io.ReadFull(stream, headLenBuf[:2])
	//if err != nil {
	//	return nil, err
	//}
	//if headLenBuf[0] != 0x05 && headLenBuf[1] != 0x19 {
	//	return nil, errors.New("invalid magic")
	//}
	_, err := io.ReadFull(stream, headLenBuf)
	if err != nil {
		fmt.Println("video Error reading:", err.Error())
		return nil, err
	}
	headLen := binary.BigEndian.Uint32(headLenBuf)
	var buf bytes.Buffer
	// 从 QUIC stream 读取图像数据
	_, err = io.CopyN(&buf, stream, int64(headLen))
	if err != nil {
		return nil, err
	}

	// 解码 PNG 图像
	img, err := png.Decode(&buf)
	if err != nil {
		return nil, err
	}

	return img, nil
}

下面开始说其中涉及到的坑:

当我本机(mac m1) OS版本为 12.1 时,运行服务器程序失败:

../../../../go/pkg/mod/github.com/kbinani/screenshot@v0.0.0-20240820160931-a8a2c5d0e191/darwin.go:9:10: fatal error:
'ScreenCaptureKit/ScreenCaptureKit.h' file not found
#include <ScreenCaptureKit/ScreenCaptureKit.h>

网上说升级系统到12.3+,因为ScreenCaptureKit 是 macOS 12.3 及更高版本中引入的 API,用于捕获屏幕内容。但是我升级到12.7.6后仍然报错…
在这里插入图片描述

然后看github.com/kbinani/screenshot源码:我当前下载的screenshot版本需要14.4+ ?

#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ > MAC_OS_VERSION_14_4

FYI:我不敢升级到15版本,,,不敢。。。只是小版本升级

最后解决办法:使用低版本的screenshot:
去官网:https://pkg.go.dev/github.com/kbinani/screenshot@v0.0.0-20240820160931-a8a2c5d0e191/example?tab=versions
在这里插入图片描述
使用低版本的2023试试:

jelex@jelexxudeMacBook-Pro screenbroadcast % go get github.com/kbinani/screenshot@v0.0.0-20230831090513-3e604f0f372a

最后果然没问题了!

坑二:client程序无法交叉编译打包

在这里插入图片描述
我没有在windows电脑上验证,如果有使用windows版本的golang使用者看到本篇后,是否可以帮忙打包验证?

坑三:打包服务端程序成exe,在另一台电脑上运行,本机mac 作为客户端连接后没反应,直到超时报错退出:
2024/10/09 15:29:43 timeout: no recent network activity

是否有道友愿意联调?FYI: 我周边没有golang开发者,他们电脑上没安装golang环境…

或者有大佬知道这个问题能直接赐教吗?

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

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

相关文章

Linux SSH服务

Linux SSH&#xff08;Secure Shell&#xff09;服务是一种安全的远程登录协议&#xff0c;用于在Linux操作系统上远程登录和执行命令。它提供了加密的通信通道&#xff0c;可以在不安全的网络环境中安全地进行远程访问。 SSH服务在Linux系统中通常使用OpenSSH软件包来实现。它…

计算机中的BIOS是什么?BIOS设置界面怎么进入?

计算机术语中我们常说的BIOS是基本输入输出系统&#xff08;Basic Input & Output System&#xff09;的简称。它是一组固化在计算机主板上的ROM芯片中的程序&#xff0c;计算机启动时最早运行的软件之一。它保存着计算机最重要的基本输入输出的程序、开机自检程序和系统自…

20241008深度学习动手篇

文章目录 1.如何写一个神经网络进行训练?1.1创建一个子类,搭建你需要的神经网络结构1.2 加载数据集1.3 自定义一些指标评估函数1.4训练1.5 结果展示 2.参考文献 1.如何写一个神经网络进行训练? 1.1创建一个子类,搭建你需要的神经网络结构 # File: 241008LeNet.py # Author:…

RTSP RTP RTCP SDP基础知识

理论 流&#xff08;Streaming &#xff09; 是近年在 Internet 上出现的新概念&#xff0c;其定义非常广泛&#xff0c;主要是指通过网络传输多媒体数据的技术总称。 流式传输分为两种 顺序流式传输 (Progressive Streaming) 实时流式传输 (Real time Streaming) ​​​​​…

李强总理签署国务院令 公布《网络数据安全管理条例》

中华人民共和国国务院令 第790号 《网络数据安全管理条例》已经2024年8月30日国务院第40次常务会议通过&#xff0c;现予公布&#xff0c;自2025年1月1日起施行。 总理 李强 2024年9月24日 网络数据安全管理条例 第一章 总则 第一条 为了规范网络数据处理活动&#xff0c;保…

SpringBoot日常:redission的接入使用和源码解析

文章目录 一、简介二、集成redissionpom文件redission 配置文件application.yml文件启动类 三、JAVA 操作案例字符串操作哈希操作列表操作集合操作有序集合操作布隆过滤器操作分布式锁操作 四、源码解析 一、简介 Redisson 是一个在 Redis 的基础上实现的 Java 驻内存数据网格…

基于java+springboot的旅游信息网站、旅游景区门票管理系统设计与实现

该系统是基于javaspringboot开发的旅游景区门票管理系统。是给师弟开发的大四实习作品。学习过程中&#xff0c;遇到问题可以咨询github作者。 演示地址 前台地址&#xff1a; http://travel.gitapp.cn 后台地址&#xff1a; http://travel.gitapp.cn/admin 后台管理帐号&am…

开发一个ftp上传客户端

文章目录 需求分析Tkinter基本用法多窗口切换FTP上传 程序打包源码 需求 项目中有个小功能模块 &#xff0c;需要win下实现ftp上传功能&#xff0c;编写一个DEMO测试 要求 界面简单选择本地文件 上传ftp服务器显示进度条显示状态上传完成后显示URL分享地址 分析 Tkinter Tkint…

【读书笔记·VLSI电路设计方法解密】问题6:超大规模集成电路(VLSI)设计实现的主要方法是什么

现代芯片设计实践的主要方法包括: 定制设计现场可编程门阵列 (FPGA)基于标准单元的设计 (ASIC)平台/结构化ASIC在定制设计方法中,每个晶体管都是手动设计和布局的。这种方法的主要优势在于电路可以高度优化以提高速度、减少面积或降低功耗。然而,由于涉及大量手工工作,这种…

什么是物联网nb水表?

物联网NB水表是一种利用NB-IoT(窄带物联网)技术实现远程数据传输的智能水表。这种水表不仅能够精确计量用户的用水量&#xff0c;还能通过无线通信技术实现数据的远程传输和管理。下面我们来详细介绍物联网NB水表的主要特点和功能。 一、基本概念 -定义&#xff1a;物联网NB水…

nVisual集成项目交付模式升级方案

集 成 项 目 的 普 遍 现 状 1 集成项目的普遍现状 设计、工程和运维各部门使用不同的软件工具&#xff0c;缺乏有效的协同&#xff0c;工程数据无法有效积累并转化为运维数据&#xff1b; 传统项目验收交付模式已经无法满足用户的需求&#xff0c;需要项目交付后协助用…

分治算法(6)_归并排序_交易逆序对的总数

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 分治算法(6)_归并排序_交易逆序对的总数 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48…

怎么去掉图片上的文字不留痕迹?学会这5种P图方法轻松解决

图片编辑已成为我们日常生活和工作中不可或缺的一部分。但有时候&#xff0c;图片上的一些文字却成了我们分享或使用的障碍。如何无痕去除图片上的文字呢&#xff1f;今天&#xff0c;我将为大家介绍5种高效工具&#xff0c;让你轻松P图&#xff0c;一起来学习下吧。 工具一&am…

ESP32利用WebServer进行设备配置

目标需求 利用esp32的WebServer功能&#xff0c;展示一个网页&#xff0c;对里面的参数进行配置&#xff0c;并以json文本格式保存到flash里面。 1、定义HTML const char index_html[] PROGMEM R"rawliteral( <!DOCTYPE html> <html lang"en"> …

前沿论文 M5Product 组会 PPT

对比学习&#xff08;Contrast learning&#xff09;&#xff1a;对比学习是一种自监督学习方法&#xff0c;用于在没有标签的情况下&#xff0c;通过让模型学习哪些数据点相似或不同来学习数据集的一般特征。假设一个试图理解世界的新生婴儿。在家里&#xff0c;假设有两只猫和…

PPT在线画SWOT分析图!这2个在线软件堪称办公必备!

swot分析ppt怎么做&#xff1f; swot分析是一个非常常用的战略分析框架&#xff0c;经常会在ppt中使用。想在ppt中绘制swot分析图&#xff0c;使用自带的形状工具可以制作出来&#xff0c;但绘制效率不够高&#xff0c;在需要大批量制作的场景下&#xff0c;会让人非常心累………

【WebGis开发 - Cesium】三维可视化项目教程---初始化场景

系列文章目录 【WebGis开发 - Cesium】三维可视化项目教程—视点管理 目录 系列文章目录引言一、Cesium引入项目1.1 下载资源1.2 项目引入Cesium 二、初始化地球2.1 创建基础文件2.1.1 创建Cesium工具方法文件2.1.2 创建主页面 2.2 看下效果 三、总结 引言 本教程主要是围绕Ce…

现场直击!2023望繁信科技产品发布会精彩回顾

2023望繁信科技产品发布会圆满结束。 感谢200余名企业代表、合作伙伴、媒体到场参会&#xff0c;感谢3万多位关注望繁信科技和流程挖掘的朋友在线观看直播。 在会上&#xff0c;我们正式分享了望繁信科技多年深耕流程挖掘领域的思考、积累和部署&#xff0c;发布了过去一年在…

Pyppeteer:如何在 Python 中使用 Puppeteer 和 Browserless?

Python 中的 Pyppeteer 是什么&#xff1f; Pyppeteer 是流行的 Node.js 库 Puppeteer 的 Python 移植版本&#xff0c;用于以编程方式控制无头 Chrome 或 Chromium 浏览器。 本质上&#xff0c;Pyppeteer 允许 Python 开发人员在 Web 浏览器中自动执行任务&#xff0c;例如抓…

webm格式怎么转换成mp4?值得给你推荐的几种简单方法

webm格式怎么转换成mp4&#xff1f;MP4支持多种音频和视频编解码器&#xff0c;如H.264和AAC&#xff0c;用户可以根据需要调整视频和音频质量&#xff0c;以满足不同需求。同时&#xff0c;许多视频编辑软件广泛支持MP4格式&#xff0c;使得剪辑、合成和特效处理变得更加便捷。…