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以上。 基本使用 关于虚拟线程本身的使用,之前已经介绍过。这里要说的是直接将…...
conda相比python好处
Conda 作为 Python 的环境和包管理工具,相比原生 Python 生态(如 pip 虚拟环境)有许多独特优势,尤其在多项目管理、依赖处理和跨平台兼容性等方面表现更优。以下是 Conda 的核心好处: 一、一站式环境管理:…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
【解密LSTM、GRU如何解决传统RNN梯度消失问题】
解密LSTM与GRU:如何让RNN变得更聪明? 在深度学习的世界里,循环神经网络(RNN)以其卓越的序列数据处理能力广泛应用于自然语言处理、时间序列预测等领域。然而,传统RNN存在的一个严重问题——梯度消失&#…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序
一、开发准备 环境搭建: 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 项目创建: File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...
转转集团旗下首家二手多品类循环仓店“超级转转”开业
6月9日,国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解,“超级…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
AI病理诊断七剑下天山,医疗未来触手可及
一、病理诊断困局:刀尖上的医学艺术 1.1 金标准背后的隐痛 病理诊断被誉为"诊断的诊断",医生需通过显微镜观察组织切片,在细胞迷宫中捕捉癌变信号。某省病理质控报告显示,基层医院误诊率达12%-15%,专家会诊…...
