当前位置: 首页 > news >正文

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库)

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

C语言刷题

1. 题目描述 根据给出的三角形3条边a:b.c(a.b,c<100.000)&#xff0c;计算三角形的周长和面积。 输入描述: 一行&#xff0c;三角形3条边(能构成三角形)&#xff0c;中间用一个空格隔开. 输出描述: 一行&#xff0c;三角形周长和面积保留两位小数&#xff0c;中问用一个空…...

LabVIEW实现RFID通信

目录 1、RFID通信原理 2、硬件环境部署 3、程序架构 4、前面板设计 5、程序框图设计 6、测试验证 本专栏以LabVIEW为开发平台,讲解物联网通信组网原理与开发方法,覆盖RS232、TCP、MQTT、蓝牙、Wi-Fi、NB-IoT等协议。 结合实际案例,展示如何利用LabVIEW和常用模块实现物联网系…...

Linux 网络流量控制 - 实现概述

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

分布式 令牌桶算法 总结

前言 相关系列 《分布式 & 目录》《分布式 & 令牌桶算法 & 总结》《分布式 & 令牌桶算法 & 问题》 参考文献 《【算法】令牌桶算法》 概述 简介 TBA Token Bucket Algorithm 令牌桶算法是一种流行于网络通信领域的流量控制/频率限制算法。令牌…...

FFMPEG视频转图片

用FFMPEG视频转图片&#xff0c;并且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.设置镜像仓库&#xff08;阿里云&#xff09;8更新软件包9.安装docket-ce10.启动docker11.普通用户权限设置 1.检查内核版本 2.确定centos7可以上网 3.关闭防火墙 下面的这个表示…...

java要防止重复序列化的问题JSON.toJSONString转义问题

要防止重复序列化的问题JSON.toJSONString(entity) 20241213 10:29 背景&#xff1a; 我在设计业务实现echart图标渲染&#xff0c;业务接口实时性查询耗时很长&#xff0c;为了提高系统可用性和用户体验&#xff0c;采用中间表的方案——即在中间表中存储大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文件编辑的最佳方案!

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

MySQL系列之数据授权(安全)

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

用 Python 实现经典的 2048 游戏:一步步带你打造属于你的小游戏!

用 Python 实现经典的 2048 游戏&#xff1a;一步步带你打造属于你的小游戏&#xff01;&#xff08;结尾附完整代码&#xff09; 简介 2048 是一个简单而又令人上瘾的数字拼图游戏。玩家通过滑动方块使相同数字的方块合并&#xff0c;目标是创造出数字 2048&#xff01;在这篇…...

Vue vs. React:两大前端框架的深度对比与分析(一)

前言 在当今快速发展的前端领域中&#xff0c;Vue和React作为两个备受瞩目的前端框架&#xff0c;已经成为许多开发者的首选。这两个框架凭借其出色的设计和强大的功能&#xff0c;在构建现代化、高效性能的Web应用方面扮演着重要角色。 Vue和React都以其独特的特点吸引了众多开…...

React 进阶深入理解核心概念与高阶实践

在上一节中&#xff0c;我们学习了 React 的基础知识&#xff0c;包括组件、状态管理和基本操作。接下来&#xff0c;我们将进一步探索 React 的高级功能和实战技巧&#xff0c;例如 组件间通信、高阶组件、Context API、React Router 等。这些内容将帮助你构建更复杂、功能更丰…...

Linux shell的七大功能 ---自动补齐、管道机制、别名

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

XML 在线格式化 - 加菲工具

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

java_多态的应用

多态数组 应用实例:现有一个继承结构如下&#xff1a;要求创建 1 个 Person 对象、2 个 Student 对象和 2 个 Teacher 对象, 统一放在数组中&#xff0c;并调用每个对象 代码 Person类 package com.hspedu.poly_.polyarr_;import javax.swing.*;/*** author:寰愬悏瓒&#xf…...

Python+OpenCV系列:模版匹配

文章目录 1. 模板匹配基本原理2. cv2.matchTemplate() 函数函数原型&#xff1a; 3. 模板匹配步骤4. 单目标模板匹配示例5. 多目标模板匹配多目标模板匹配示例代码解析&#xff1a; 6. 多模板匹配多模板匹配示例代码解析 7. 总结 模板匹配是一种在图像中寻找模板的位置的方法。…...

【从零开始入门unity游戏开发之——C#篇10】循环结构——while、do-while、for、foreach的使用

文章目录 一、while 循环1、语法&#xff1a;2、示例&#xff1a; 二、 do-while 循环1、语法&#xff1a;2、示例&#xff1a; 三、for 循环1、语法&#xff1a;2、示例&#xff1a; 四、foreach 循环1、语法&#xff1a;2、示例&#xff1a; 五、总结对比六、注意事项七、使用…...

Spring Boot项目使用虚拟线程

Spring Boot项目启用虚拟线程 开始基本使用先写一个测试方法通过springboot配置项开启虚拟线程 目前存在的问题 开始 虚拟线程正式发布是在JDK21&#xff0c;对于Spring Boot版本选择3以上。 基本使用 关于虚拟线程本身的使用&#xff0c;之前已经介绍过。这里要说的是直接将…...

chatgpt-mirai-qq-bot内存持久化:文件和Redis存储方案对比

chatgpt-mirai-qq-bot内存持久化&#xff1a;文件和Redis存储方案对比 你是否在为聊天机器人的记忆管理而烦恼&#xff1f;在多轮对话中&#xff0c;如何确保机器人能够记住上下文&#xff0c;同时保证数据的安全性和性能&#xff1f;chatgpt-mirai-qq-bot提供了两种内存持久化…...

ODT怎么转PDF?2026年实测5种转换方法与在线工具对比

ODT&#xff08;OpenDocument Text&#xff09;是开源办公软件默认的文档格式&#xff0c;但在实际工作和分享中&#xff0c;PDF的通用性和防篡改特性让它成为更优选择。很多人拿到ODT文件后都会面临同一个问题&#xff1a;怎样才能快速转成PDF&#xff1f;本文将从多个角度展示…...

一个真实网工的一天

很多人对网络工程师的印象,还停留在“敲命令、配交换机、修Wi-Fi”。 但真正干过这行的人都知道,网络工程师这个职业,有时候像消防员,有时候像急诊医生。平时看起来风平浪静,一旦出问题,电话、消息、会议能在5分钟内同时炸开。 有人天天996,也有人慢慢开始“只做分内事…...

如何快速掌握LuaJIT字节码还原:面向开发者的完整指南

如何快速掌握LuaJIT字节码还原&#xff1a;面向开发者的完整指南 【免费下载链接】luajit-decompiler https://gitlab.com/znixian/luajit-decompiler 项目地址: https://gitcode.com/gh_mirrors/lu/luajit-decompiler LuaJIT反编译器&#xff08;LuaJIT Raw-Bytecode D…...

如何快速掌握智能电源管理:macOS用户的完整配置指南

如何快速掌握智能电源管理&#xff1a;macOS用户的完整配置指南 【免费下载链接】SleeperX MacBook prevent idle/lid sleep! Hackintosh sleep on low battery capacity. 项目地址: https://gitcode.com/gh_mirrors/sl/SleeperX SleeperX是一款专为macOS用户设计的开源…...

【Midjourney宝丽来风格终极指南】:20年AI影像专家亲授3步调参法,97%用户忽略的胶片颗粒校准秘钥

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;宝丽来风格的视觉基因解码 宝丽来&#xff08;Polaroid&#xff09;成像并非仅关乎化学显影&#xff0c;其独特视觉语言根植于物理光学、色彩衰减模型与模拟噪声的协同作用。理解这一“视觉基因”&#xff0c…...

宝丽来胶片模拟不等于加噪点!深度拆解Polaroid SX-70光学特性与MJ v6渲染引擎的4层映射偏差,附12组可直接复用的--sref哈希值

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;宝丽来SX-70胶片的光学本质与历史语境 宝丽来SX-70胶片并非传统意义上的“静态感光材料”&#xff0c;而是一套高度集成的自显影光学化学系统。其核心在于多层涂布结构中嵌入的镜面反射层、碱性催化剂囊…...

拟态设计革命来了,你还在用老版MJ?2024Q2官方未披露的3类新拟态纹理权重算法首度解密

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;拟态设计革命的底层逻辑与时代必然性 拟态设计并非视觉层面的风格迁移&#xff0c;而是一场由安全范式迁移、计算环境异构化与攻击面指数级扩张共同驱动的系统性重构。其底层逻辑根植于“动态异构冗余”&…...

企业微信SCRM与客户管理系统推荐:2026年这12家值得关注

2026年&#xff0c;一个企业要选客户管理系统&#xff0c;第一个要回答的问题是&#xff1a;你的客户在哪里&#xff1f;如果答案是"微信"&#xff0c;那企业微信SCRM就是最直接的路径——而在这个领域&#xff0c;微盛企微管家作为企业微信最大ISV&#xff0c;服务了…...

如何快速上手UndertaleModTool:游戏修改的完整指南

如何快速上手UndertaleModTool&#xff1a;游戏修改的完整指南 【免费下载链接】UndertaleModTool The most complete tool for modding, decompiling and unpacking Undertale (and other GameMaker games!) 项目地址: https://gitcode.com/gh_mirrors/un/UndertaleModTool …...