金歌 发表于 2024-6-18 18:43:33

C++ webrtc开辟(非原生开辟,linux上利用libdatachannel库)

提示:文章写完后,目次可以自动天生,怎样天生可参考右边的帮助文档


前言

利用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库都行。
https://img-blog.csdnimg.cn/direct/0ee4d21a25894d91bd43899d10a04e13.png
2.引入库

https://img-blog.csdnimg.cn/direct/85da993c9d58488b97640a8fc9824003.png
就像这样引入这些库吧。最重要的是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后却不会产生自己的sdp
       
        ws.onMessage([](auto data) {
                // data holds either std::string or rtc::binary
                if (!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: \n" << 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: \n" << 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:\n" << 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);

        //简单的媒体track
        rtc::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 << "" << std::endl;
        });
       
        //本地sdp
        pc->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 :\n" << 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(() {
        //                 // 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((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;
                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兼容

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: C++ webrtc开辟(非原生开辟,linux上利用libdatachannel库)