C++ webrtc开发(非原生开发,linux上使用libdatachannel库)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录
  • 前言
  • 一、libdatachannel库的下载和build
  • 二、开始使用
    • 1.
    • 2.引入库
    • 3.开始使用
  • 总结

前言

使用c++开发webrtc在互联网上留下的资料甚少,经过我一段时间的探索,有大概这几种可以用于c++进行webrtc开发的方法。
1.c++ webrtc native 开发,这个开发方法很麻烦,编译这个库十分麻烦,索性网上还留有部分资料可供参考,但是因为我是想在嵌入式上部署webrtc,所以没有考虑这个方法。
2.kvs webrtc c sdk 库二次开发,利用amazon给出的用于aws的sdk,我们编译生成静态库后可以抛弃掉其中信令服务器等内容,利用里面ice部分媒体传输部分完成自己的功能的开发。优点是这个库的对外api的介绍写的挺清楚,缺点是kvs的github社区里面的问题基本都是使用它的整套服务过程中提出的问题,对于单独提取它的一些库来完成自己功能过程中遇到的问题很少,可能你在自己魔改的过程中遇到奇奇怪怪的问题里面一点线索都找不到。其次,这是一个c库,对外的api写的挺清楚,但是遇到bug后你阅读源码的过程中,大量的c代码会让你很难受。并且该库依赖大量的第三方库,编译过程比webrtc native舒适不少,但还是会遇到各种问题,尤其是你想要交叉编译到嵌入式板子上时,可能这个过程更会让你难受。关于使用这个库开发自己的webrtc的中文资料也挺少,推荐这篇博客
嵌入式中实现webrtc的方式
这条路我是跟着这篇文章做过一阵,但是最后还是失败了,如果有人这个方法做出来可以告诉我
3.libdatachannel库,这个库很轻量级,简单make就可以,虽然看名字这个库似乎只能用于datachannel,但实际他是支持媒体传输的,代码质量很高,缺点是要c++17,但是我的嵌入式板子刚好支持c++17,于是就这样使用下来了,体验很不错,基本上使用库遇到的问题你都能在github的issue中找到解决的方法。

一、libdatachannel库的下载和build

没什么好说的,因为这个库真的很轻量级,我甚至没有交叉编译,我直接在嵌入式板子上编译最终都通过了。
make就完事了,中间可能会遇到openssl库的一个问题,google一下就能解决问题。
build教学

二、开始使用

1.

如果直接使用的话,你在make之后再make install一下,大概就会把相关需要的文件放在系统的某个目录下了,你可以把include和lib自己拿出来放到项目文件夹中,或者你再cmake中指明库在系统哪个地方也行,不懂的可以问chatgpt,适当的提示词可以让gpt很快地解决你的问题。除此之外你还需要自己include一个json库,你喜欢的任意一种c++json库都行。

2.引入库

在这里插入图片描述

就像这样引入这些库吧。最重要的是rtc.hpp,别把它忘了就好

3.开始使用

大家可以照看github中examples的代码自己读,自己改出一版自己的webrtc,接下来我来用我自己的代码来做一个简单的教学,内容在代码中的注释展现

#include "../include/rtc/rtc.hpp"
#include <iostream>
#include "json.hpp"
#include <memory>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <sys/socket.h>
typedef int SOCKET;
static std::string from;
static std::string to;
static std::string sessionid;using std::string;
using std::shared_ptr;
using std::weak_ptr;
using std::cout ;
using std::endl;
const int BUFFER_SIZE = 2048;
template <class T> weak_ptr<T> make_weak_ptr(shared_ptr<T> ptr) { return ptr; }
shared_ptr<rtc::PeerConnection> pc;
rtc::WebSocket ws;
using nlohmann::json;
int main()
{// std::cout << "hehe" << std::endl;//Debug的程度,一般设置为Debug就行,Verbose会展示网络的具体细节rtc::InitLogger(rtc::LogLevel::Verbose);bool flag = false;//ws开启ws.onOpen([&flag]() {std::cout << "WebSocket open" << std::endl;flag = true;});//socket绑定,这一段代码非必须,如果你阅读了github中examples的相关内容,你应该会理解这么做是在干嘛SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);struct sockaddr_in addr = {};addr.sin_family = AF_INET;addr.sin_addr.s_addr = inet_addr("127.0.0.1");addr.sin_port = htons(6000);if (bind(sock, reinterpret_cast<const sockaddr *>(&addr), sizeof(addr)) < 0)throw std::runtime_error("Failed to bind UDP socket on 127.0.0.1:6000");int rcvBufSize = 212992;setsockopt(sock, SOL_SOCKET, SO_RCVBUF, reinterpret_cast<const char *>(&rcvBufSize),sizeof(rcvBufSize));//设置websocket的回调函数,里面相关的json报文解析要按照你自己的信令服务器来进行设置//我来说说里面比较重要的东西//1.setRemoteDescription(需要你传入sdp和type的string),注意当你没有持有offer时,这时里面会//自动调用setLocalDescription然后开始产生你的对offer的answer//对应于js中的写法,就相当于setremotedescription后自动调用了CreateAnswer//而你自己获得answer后却不会产生自己的sdpws.onMessage([](auto data) {// data holds either std::string or rtc::binaryif (!std::holds_alternative<std::string>(data))return;// cout <<"1111" << endl;std::string ndata = std::get<std::string>(data);std::cout << "got data" << ndata << std::endl;json message = json::parse(ndata);std::string type = message["type"].get<std::string>();// cout << "2222" <<endl;if (type == "offer") {std::cout << "get offer " << std::endl;std::string sdp = message["data"]["description"]["sdp"].get<std::string>();std::cout << "get sdp: 
" << sdp << std::endl;pc->setRemoteDescription(rtc::Description(sdp, type));} else if(type == "answer"){std::cout << "get answer " << std::endl;std::string sdp = message["data"]["description"]["sdp"].get<std::string>();std::cout << "get sdp: 
" << sdp << std::endl;pc->setRemoteDescription(rtc::Description(sdp, type));}else if (type == "candidate") {auto candidate = message["data"]["candidate"]["candidate"].get<std::string>();auto mid       = message["data"]["candidate"]["sdpMid"].get<std::string>();// auto mid = message["mid"].get<std::string>();std::cout << "get candidate:
" << candidate << std::endl;pc->addRemoteCandidate(rtc::Candidate(candidate, mid));}});////ws连接,如果你是主动发送offer方,请务必等ws连接完毕后再进行后续,否则可能会出现ws还未连接但是已经收集完description并尝试发送了ws.open("your own websocket url");// int cnt++;while(!flag){;}//stun服务器和turn服务器的设置,如果你要设置turn,其实直接写一个turn地址就够了,这个turn也会用作stun//并且你不用在ip中指明是turn还是stun,当你设置有密码和账号后,就会认为是turn了rtc::Configuration config;// config.iceServers.emplace_back("stun.l.google.com:19302");config.iceServers.emplace_back("url");// const rtc::IceServer turnServer("ip", "port", "name", "password");// config.iceServers.emplace_back(turnServer);//简单的媒体trackrtc::Description::Video media("video", rtc::Description::Direction::SendOnly);media.addH264Codec(96);media.addSSRC( rtc::SSRC(45), "video-send" );pc = std::make_shared<rtc::PeerConnection>(config);auto track = pc->addTrack(media);// pc->createdata//收集本地candidate回调,每收集到一个就会发送一个pc->onLocalCandidate([](rtc::Candidate candidate) {std::cout << "Local Candidate:"<< std::endl;std::cout << std::string(candidate) << std::endl << std::endl;// std::cout << std::string(candidate.mid()) << std::endl;json message;message["type"] = "candidate";message["data"]["from"] = "1433";message["data"]["to"] = "1453";message["data"]["candidate"]["candidate"] = std::string(candidate);message["data"]["candidate"]["sdpMid"] = candidate.mid();message["data"]["session_id"] = "1453-1433";ws.send(message.dump());});//ice状态pc->onStateChange([](rtc::PeerConnection::State state) {std::cout << "[State: " << state << "]" << std::endl;});//本地sdppc->onLocalDescription([](rtc::Description sdp){auto description = pc->localDescription();json message;message["type"] = description->typeString();message["data"]["to"] = "1453";message["data"]["from"] = "1433";message["data"]["description"]["sdp"] = string(description.value());message["data"]["description"]["type"] = description->typeString();message["data"]["session_id"] = "1453-1433";message["data"]["media"] = "video";std::cout << "send answer json :
" << message.dump() << std::endl;ws.send(message.dump());});
//没什么大用,告知candidate收集状态pc->onGatheringStateChange([](rtc::PeerConnection::GatheringState state) {cout << "Gathering State: " << state << endl;if (state == rtc::PeerConnection::GatheringState::Complete) {std::cout  << "gather ok" << std::endl;}});
// pc->setLocalDescription();// pc->onTrack)// const std::string label = "test";// std::cout << "Creating DataChannel with label "" << label << """ << std::endl;// auto dc = pc->createDataChannel(label);// 	dc->onOpen([wdc = make_weak_ptr(dc)]() {// 		// std::cout << "DataChannel from " << id << " open" << std::endl;// 		if (auto dc = wdc.lock())// 			dc->send("Hello from wl");// 	});// 	dc->onClosed([]() { std::cout << "DataChannel from " << " closed" << std::endl; });// 	dc->onMessage([wdc = make_weak_ptr(dc)](auto data) {// 		// data holds either std::string or rtc::binary// 		if (std::holds_alternative<std::string>(data))// 			std::cout << "Message from " << "peer" << " received: " << std::get<std::string>(data)// 			          << std::endl;// 		else// 			std::cout << "Binary message from " << "peer "// 			          << " received, size=" << std::get<rtc::binary>(data).size() << std::endl;// 	});char buffer[2048];int len;while ((len = recv(sock, buffer, BUFFER_SIZE, 0)) >= 0) {if (len < sizeof(rtc::RtpHeader) || !track->isOpen())continue;std::cout << "send buffer: " << len << std::endl;auto rtp = reinterpret_cast<rtc::RtpHeader *>(buffer);rtp->setSsrc(rtc::SSRC(45));track->send(reinterpret_cast<const std::byte *>(buffer), len);}//	while(1);return 0;
}

总结

libdatachannel是一个很好用的webrtc库,经过测试,它的协议栈能和firefox和flutter-webrtc兼容

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

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

相关文章

深入理解 Apache Shiro:安全框架全解析

亲爱的小伙伴们&#x1f618;&#xff0c;在求知的漫漫旅途中&#xff0c;若你对深度学习的奥秘、JAVA 、PYTHON与SAP 的奇妙世界&#xff0c;亦或是读研论文的撰写攻略有所探寻&#x1f9d0;&#xff0c;那不妨给我一个小小的关注吧&#x1f970;。我会精心筹备&#xff0c;在…

Coturn 实战指南:WebRTC 中的 NAT 穿透利器

1. 什么是 Coturn&#xff1f; Coturn 是一种开源的 TURN(Traversal Using Relays around NAT)服务器&#xff0c;用于解决 NAT 穿透问题。它帮助客户端在受限网络环境(例如防火墙或 NAT 后面)中实现双向通信&#xff0c;常用于 WebRTC 应用、VoIP、在线游戏等场景。 2. Cotur…

React的局限性是什么?

性能&#xff1a; 虚拟 DOM 虽然提高了渲染性能&#xff0c;但在某些情况下可能会造成性能瓶颈&#xff0c;尤其是在处理大量数据或复杂更新时。对于非UI任务&#xff08;如计算密集型操作&#xff09;&#xff0c;React 本身并不擅长。 学习曲线&#xff1a; 对于初学者来说&a…

生信技能65 - SRA数据库公共数据自动化下载及SRA批量自动化拆分

根据NCBI Metadata数据表,实现SRA数据库公共数据自动化下载及SRA批量自动化拆分。 1. 程序逻辑 根据SraRunTable.csv自动从公共数据库下载SRA文件 ;模式0(默认)为下载模式,模式1为拆分模式,拆分支持进度显示;提取Metadata关键信息数据,重新写入新的文本文件。2. 运行示…

美化和定制你的Django Admin:使用SimpleUI

SimpleUI是一个简洁、美观的Django后台管理界面,它可以让你的Django Admin更加直观和易用。本文将指导你如何安装和配置SimpleUI,并进行自定义配置。 目录 安装Django创建Django项目创建Django app安装SimpleUI测试安装是否成功数据库迁移注册超级管理员登录验证自定义配置 …

python学习笔记—7—变量拼接

1. 字符串的拼接 print(var_1 var_2) print("supercarry" "doinb") name "doinb" sex "man" score "100" print("sex:" sex " name:" name " score:" score) 注意&#xff1a; …

datahub-postgres 连接

1、postgres 远程TCP/IP连接 1、修改postgres配置 /opt/homebrew/var/postgresql14/postgresql.conf #listen_addresses localhost, 127.0.0.1 # what IP address(es) to listen on;listen_addresses * # 增加这一条 2、修改/opt/homebrew/var/postgresql14/pg_hba.…

ElasticSearch 搜索、排序、分页功能

一、DSL 查询文档 ElasticSearch 的查询依然是基于 json 风格的 DSL 来实现的。 官方文档&#xff1a;https://www.elastic.co/guide/en/elasticsearch/reference/8.15/query-dsl.html 1.1 DSL 查询分类 常见的查询类型包括&#xff1a; 查询所有&#xff1a;查询出所有数…

Unreal Engine 5 (UE5) 中“变换“(Transform)类型变量

在 Unreal Engine 5 (UE5) 中&#xff0c;"变换"&#xff08;Transform&#xff09;类型变量是一个非常常见和重要的数据类型&#xff0c;它用于表示 对象的位置&#xff08;Location&#xff09;、旋转&#xff08;Rotation&#xff09; 和 缩放&#xff08;Scale&a…

PyTorch 切片运算 (Slice Operator)

PyTorch 切片运算 {Slice Operator} 1. [:, -1, :]2. [:, [-1], :]References 1. [:, -1, :] https://github.com/karpathy/llama2.c/blob/master/model.py import torchlogits torch.arange(1, 16) print("logits.shape:", logits.shape) print("logits:\n&…

2025系统架构师(一考就过):选择题基础知识二

考点14&#xff1a;知识产权和标准化 真题1&#xff1a;甲软件公司受乙企业委托安排公司软件设计师开发了信息系统管理软件&#xff0c;由于在委托开发合同中未对软件著作权归属作出明确的约定&#xff0c;所以该信息系统管理软件的著作权由(甲) 享有。 真题2&#xff1a;根据…

【ubuntu18.04】ubuntu18.04安装EasyCwmp操作说明

参考链接 Tutorial – EasyCwmphttps://easycwmp.org/tutorial/ EasyCwmp 介绍 EasyCwmp 设计包括 2 个部分&#xff1a; EasyCwmp 核心&#xff1a;它包括 TR069 CWMP 引擎&#xff0c;负责与 ACS 服务器的通信。它是用 C 语言开发的。EasyCwmp DataModel&#xff1a;它包…

Jenkins流水线初体验(六)

DevOps之安装和配置 Jenkins (一) DevOps 之 CI/CD入门操作 (二) Sonar Qube介绍和安装(三) Harbor镜像仓库介绍&安装 (四) Jenkins容器使用宿主机Docker(五) Jenkins流水线初体验(六) 一、Jenkins流水线任务介绍 之前采用Jenkins的自由风格构建的项目,每个步骤…

CentOS 7.9 更换 YUM:解决宝塔安装困境的探索之旅

在进行网站搭建工作时&#xff0c;我满怀期待地准备安装宝塔面板&#xff0c;然而却遭遇了安装失败的挫折。经过一番排查与思考&#xff0c;我将目光聚焦到了系统的 YUM 上&#xff0c;怀疑它可能是导致问题的“罪魁祸首”。于是&#xff0c;我毅然决定对 CentOS 7.9 的 YUM 进…

如何解决垂直越权与水平越权问题

大家好&#xff0c;我是G探险者。 关于权限管理我们可能都了解过基于角色的访问控制RBAC技术。 但是关于水平越权和垂直越权的问题可能了解不多。 今天我们来聊聊垂直越权和水平越权的问题。 现在的Web应用和系统里&#xff0c;权限管理是确保数据安全和防止滥用的关键。垂直…

(SAST检测规则-8)连接字符串中的硬编码密码

严重等级&#xff1a;高危 缺陷详解&#xff1a; 在构建数据驱动的应用程序时&#xff0c;开发者通常需要通过数据库连接字符串与数据库进行交互。将敏感信息&#xff08;如密码、服务器IP地址或加密密钥&#xff09;硬编码在源代码中会带来以下风险&#xff1a; 信息暴露&a…

Spring Boot整合 RabbitMQ

文章目录 一. 引入依赖二. 添加配置三. Work Queue(工作队列模式)声明队列生产者消费者 四. Publish/Subscribe(发布订阅模式)声明队列和交换机生产者消费者 五. Routing(路由模式)声明队列和交换机生产者消费者 六. Topics(通配符模式)声明队列和交换机生产者消费者 一. 引入依…

Qwen 论文阅读记录

本文仅作自己初步熟悉大模型&#xff0c;梳理之用&#xff0c;慢慢会更改/增加/删除&#xff0c;部分细节尚未解释&#xff0c;希望不断学习之后&#xff0c;能够完善补充。若有同道之人&#xff0c;欢迎指正探讨。 关于后面的code-qwen and math-qwen&#xff0c;我个人认为依…

Python 元类(Meta Class):解密 Python 面向对象编程的幕后推手

在 Python 编程中&#xff0c;我们每天都在和类打交道&#xff0c;但是你是否也和我一样想过&#xff1a;类本身是什么&#xff1f;是谁创建了类&#xff1f;元类&#xff08;Meta Class&#xff09;就是用来创建类的"类"。今天让我们一起深入理解这个强大而神秘的特…

JCR一区牛顿-拉夫逊优化算法+分解对比!VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测

JCR一区牛顿-拉夫逊优化算法分解对比&#xff01;VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测 目录 JCR一区牛顿-拉夫逊优化算法分解对比&#xff01;VMD-NRBO-Transformer-BiLSTM多变量时序光伏功率预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.中科院…