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兼容
相关文章:

C++ webrtc开发(非原生开发,linux上使用libdatachannel库)
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、libdatachannel库的下载和build二、开始使用 1.2.引入库3.开始使用 总结 前言 使用c开发webrtc在互联网上留下的资料甚少,经过我一段时间的探…...

C语言刷题
1. 题目描述 根据给出的三角形3条边a:b.c(a.b,c<100.000),计算三角形的周长和面积。 输入描述: 一行,三角形3条边(能构成三角形),中间用一个空格隔开. 输出描述: 一行,三角形周长和面积保留两位小数,中问用一个空…...
LabVIEW实现RFID通信
目录 1、RFID通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联网系…...

Linux 网络流量控制 - 实现概述
摘要 Linux 提供了一整套丰富的流量控制(traffic control)功能。本文档概述了相应的内核代码设计,描述了其结构,并通过描述一种新的排队策略来说明新元素的添加。 1 引言 最近的Linux内核提供了多种流量控制功能。Alexey Kuznetsov(kuznet…...

分布式 令牌桶算法 总结
前言 相关系列 《分布式 & 目录》《分布式 & 令牌桶算法 & 总结》《分布式 & 令牌桶算法 & 问题》 参考文献 《【算法】令牌桶算法》 概述 简介 TBA Token Bucket Algorithm 令牌桶算法是一种流行于网络通信领域的流量控制/频率限制算法。令牌…...
FFMPEG视频转图片
用FFMPEG视频转图片,并且for循环 import os import subprocess# 输入文件夹和输出文件夹路径 input_folder r"I:\xxx" output_base_folder r"D:\xxx\YOLO\data\video" output_subfolder_name "20240609"# 创建输出子文件夹 output…...

docker入门实践---虚拟机环境配置
文章目录 1.检查内核版本2.确定centos7可以上网3.关闭防火墙4.关闭防火墙5.更换阿里云6.安装gcc7.设置镜像仓库(阿里云)8更新软件包9.安装docket-ce10.启动docker11.普通用户权限设置 1.检查内核版本 2.确定centos7可以上网 3.关闭防火墙 下面的这个表示…...

java要防止重复序列化的问题JSON.toJSONString转义问题
要防止重复序列化的问题JSON.toJSONString(entity) 20241213 10:29 背景: 我在设计业务实现echart图标渲染,业务接口实时性查询耗时很长,为了提高系统可用性和用户体验,采用中间表的方案——即在中间表中存储大JSON。 但是在自测…...

TS的类型守卫、类型约束实践
类型守卫 // 基础类型判断 const arr [30, 50] console.log(typeof arr) // object const set new Set(arr) console.log(typeof set) // object const map new Map() console.log(typeof map) // objectclass Customer {constructor() {}buy(method:string) {console.log(…...

文件转曲,限制PDF文件编辑的最佳方案!
随着数字化进程的推进,PDF文件凭借其多样化的功能和优越的兼容性已经被广泛使用,成为了现代文档交流和存储的重要工具,满足了不同用户和行业的需求。 虽然PDF格式文件的功能很多,常见的比如阅读、编辑、加密、转换、还可用于印刷…...

MySQL系列之数据授权(安全)
导览 前言Q:如何对MySQL数据库进行授权管理一、MySQL的“特权”1. 权限级别2. 权限清单 二、授权操作1. 查看权限2. 分配权限3. 回收权限 结语精彩回放 前言 看过博主上一篇的盆友,可以Get到一个知识点:数据授权(eg:g…...

用 Python 实现经典的 2048 游戏:一步步带你打造属于你的小游戏!
用 Python 实现经典的 2048 游戏:一步步带你打造属于你的小游戏!(结尾附完整代码) 简介 2048 是一个简单而又令人上瘾的数字拼图游戏。玩家通过滑动方块使相同数字的方块合并,目标是创造出数字 2048!在这篇…...
Vue vs. React:两大前端框架的深度对比与分析(一)
前言 在当今快速发展的前端领域中,Vue和React作为两个备受瞩目的前端框架,已经成为许多开发者的首选。这两个框架凭借其出色的设计和强大的功能,在构建现代化、高效性能的Web应用方面扮演着重要角色。 Vue和React都以其独特的特点吸引了众多开…...
React 进阶深入理解核心概念与高阶实践
在上一节中,我们学习了 React 的基础知识,包括组件、状态管理和基本操作。接下来,我们将进一步探索 React 的高级功能和实战技巧,例如 组件间通信、高阶组件、Context API、React Router 等。这些内容将帮助你构建更复杂、功能更丰…...

Linux shell的七大功能 ---自动补齐、管道机制、别名
1、自动补齐---TAB 输入命令的前几个字符,按下tab键,会自动补齐完整的字符,若有多个命令、文件或目录的前几个字符相同,按下tab将会全部列举出来 2、管道机制---| 例如:ls -- help |more 将有关ls的帮助内容传递给“|…...

XML 在线格式化 - 加菲工具
XML 在线格式化 打开网站 加菲工具 选择“XML 在线格式化” 输入XML,点击左上角的“格式化”按钮 得到格式化后的结果...

java_多态的应用
多态数组 应用实例:现有一个继承结构如下:要求创建 1 个 Person 对象、2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中,并调用每个对象 代码 Person类 package com.hspedu.poly_.polyarr_;import javax.swing.*;/*** author:寰愬悏瓒…...
Python+OpenCV系列:模版匹配
文章目录 1. 模板匹配基本原理2. cv2.matchTemplate() 函数函数原型: 3. 模板匹配步骤4. 单目标模板匹配示例5. 多目标模板匹配多目标模板匹配示例代码解析: 6. 多模板匹配多模板匹配示例代码解析 7. 总结 模板匹配是一种在图像中寻找模板的位置的方法。…...

【从零开始入门unity游戏开发之——C#篇10】循环结构——while、do-while、for、foreach的使用
文章目录 一、while 循环1、语法:2、示例: 二、 do-while 循环1、语法:2、示例: 三、for 循环1、语法:2、示例: 四、foreach 循环1、语法:2、示例: 五、总结对比六、注意事项七、使用…...
Spring Boot项目使用虚拟线程
Spring Boot项目启用虚拟线程 开始基本使用先写一个测试方法通过springboot配置项开启虚拟线程 目前存在的问题 开始 虚拟线程正式发布是在JDK21,对于Spring Boot版本选择3以上。 基本使用 关于虚拟线程本身的使用,之前已经介绍过。这里要说的是直接将…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...

《基于Apache Flink的流处理》笔记
思维导图 1-3 章 4-7章 8-11 章 参考资料 源码: https://github.com/streaming-with-flink 博客 https://flink.apache.org/bloghttps://www.ververica.com/blog 聚会及会议 https://flink-forward.orghttps://www.meetup.com/topics/apache-flink https://n…...
Python ROS2【机器人中间件框架】 简介
销量过万TEEIS德国护膝夏天用薄款 优惠券冠生园 百花蜂蜜428g 挤压瓶纯蜂蜜巨奇严选 鞋子除臭剂360ml 多芬身体磨砂膏280g健70%-75%酒精消毒棉片湿巾1418cm 80片/袋3袋大包清洁食品用消毒 优惠券AIMORNY52朵红玫瑰永生香皂花同城配送非鲜花七夕情人节生日礼物送女友 热卖妙洁棉…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...
Bean 作用域有哪些?如何答出技术深度?
导语: Spring 面试绕不开 Bean 的作用域问题,这是面试官考察候选人对 Spring 框架理解深度的常见方式。本文将围绕“Spring 中的 Bean 作用域”展开,结合典型面试题及实战场景,帮你厘清重点,打破模板式回答,…...

c++第七天 继承与派生2
这一篇文章主要内容是 派生类构造函数与析构函数 在派生类中重写基类成员 以及多继承 第一部分:派生类构造函数与析构函数 当创建一个派生类对象时,基类成员是如何初始化的? 1.当派生类对象创建的时候,基类成员的初始化顺序 …...

HubSpot推出与ChatGPT的深度集成引发兴奋与担忧
上周三,HubSpot宣布已构建与ChatGPT的深度集成,这一消息在HubSpot用户和营销技术观察者中引发了极大的兴奋,但同时也存在一些关于数据安全的担忧。 许多网络声音声称,这对SaaS应用程序和人工智能而言是一场范式转变。 但向任何技…...

sshd代码修改banner
sshd服务连接之后会收到字符串: SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢? 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头,…...