link如下

https://github.com/llamerada-jp/webrtc-cpp-sample

该例子总共分成四个部分,有两个类,一个结构体和一个main函数组成,一个一个来,如下

struct Ice

ICE(Interactive Connectivity Establishment) 用于建立连接

struct Ice {
  std::string candidate;
  std::string sdp_mid;    // 返回 "a=mid" 属性的值,以帮助标识 ICE 候						  // 选项所属的媒体流,用于识别媒体流
  int sdp_mline_index;	  // 也是用来标记识别媒体流
};

主要功能如下图所示

在这里插入图片描述

class Connection

本类包含了若干变量及function,还有两个类:

变量

const std::string name;

rtc::scoped_refptr<webrtc::PeerConnectionInterface> 			                                                 peer_connection;
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel;

std::function<void(const std::string &)> on_sdp;
std::function<void()> on_accept_ice;
std::function<void(const Ice &)> on_ice;
std::function<void()> on_success;
std::function<void(const std::string &)> on_message;

提供了两个分别指向两个接口的指针,peer_connection和data_channel。具体用法后面会具体分析。剩下的这几个模板都是针对这两个指针进行的操作,如

// When the status of the DataChannel changes, determine if the connection is complete.
// DataChannelのstateが変化したら、接続が完了したか確かめる。
void on_state_change() {
    std::cout << "state: " << data_channel->state() << std::endl;
    if (data_channel->state() == 	                   webrtc::DataChannelInterface::kOpen && on_success) {
        on_success();
    }
}

PCO(PeerConnectionObserver)

提供了一些针对peer connection的方法,如图

在这里插入图片描述

该类都是虚函数,在此例子中的实现主要是输出当前状态改变:

void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {
    std::cout << parent.name << ":" << std::this_thread::get_id() 		<< ":"<< "PeerConnectionObserver::SignalingChange(" <<           new_state << ")" << std::endl;
};

一个比较重要的函数,接收方获取一个dataChannel实例

void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) override {
    std::cout << parent.name << ":" << std::this_thread::get_id() << ":"
        << "PeerConnectionObserver::DataChannel(" << data_channel << ", " << parent.data_channel.get() << ")"
        << std::endl;
    // The request recipient gets a DataChannel instance in the onDataChannel event.
    parent.data_channel = data_channel;
    parent.data_channel->RegisterObserver(&parent.dco);
};

DCO(DataChannelObserver)

同上

CSDO(CreateSessionDescriptionObserver)

在这里插入图片描述

如图所示,提供两个虚函数,onSuccess和onFailure

SSDO(SetSessionDescriptionObserver)

同上

构造

Connection的构造函数需要以上的四个类

Connection(const std::string &name_) :
    name(name_),
    pco(*this),
    dco(*this),
    csdo(new rtc::RefCountedObject<CSDO>(*this)),
    ssdo(new rtc::RefCountedObject<SSDO>(*this)) {
}

class Wrapper

构造

Wrapper(const std::string name_) : name(name_), connection(name_) {
}

变量

const std::string name;
std::unique_ptr<rtc::Thread> network_thread;
std::unique_ptr<rtc::Thread> worker_thread;
std::unique_ptr<rtc::Thread> signaling_thread;
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> 	                                  peer_connection_factory;
webrtc::PeerConnectionInterface::RTCConfiguration configuration;
Connection connection;

network_thread用于处理网络相关的任务,包括socket的接发等。

worker_thread用于处理工作线程的任务,包括编解码,媒体数据处理,协议处理等,主要执行一些比较耗时的操作,以防止阻塞主线程。

signaling_thread用于处理信令的传输和处理,信令是用于建立对等连接、协商会话描述和处理通信的元数据的消息。

function

init()

void init() {
    std::cout << name << ":" << std::this_thread::get_id() << ":"
        << "init Main thread" << std::endl;

    // Using Google's STUN server.
    // GoogleのSTUNサーバを利用。
    webrtc::PeerConnectionInterface::IceServer ice_server;
    ice_server.uri = "stun:stun.l.google.com:19302";
    configuration.servers.push_back(ice_server);

    network_thread = rtc::Thread::CreateWithSocketServer();
    network_thread->Start();
    worker_thread = rtc::Thread::Create();
    worker_thread->Start();
    signaling_thread = rtc::Thread::Create();
    signaling_thread->Start();
    webrtc::PeerConnectionFactoryDependencies dependencies;
    dependencies.network_thread   = network_thread.get();
    dependencies.worker_thread    = worker_thread.get();
    dependencies.signaling_thread = signaling_thread.get();
    peer_connection_factory       = webrtc::CreateModularPeerConnectionFactory(std::move(dependencies));

    if (peer_connection_factory.get() == nullptr) {
        std::cout << name << ":" << std::this_thread::get_id() << ":"
            << "Error on CreateModularPeerConnectionFactory." << std::endl;
        exit(EXIT_FAILURE);
    }
}

该函数提供了初始化各个thread的功能,使用stun服务器。

create_offer_sdp() && create_answer_sdp()

创建一个sdp(SessionDescriptionProtocol),用于实现peer_connection

code如下

void create_offer_sdp() {
    std::cout << name << ":" << std::this_thread::get_id() << ":"
        << "create_offer_sdp" << std::endl;

    connection.peer_connection =
        peer_connection_factory->CreatePeerConnection(configuration, nullptr, nullptr, &connection.pco);

    webrtc::DataChannelInit config;

    // Configuring DataChannel.
    // DataChannelの設定。
    connection.data_channel = connection.peer_connection->CreateDataChannel("data_channel", &config);
    connection.data_channel->RegisterObserver(&connection.dco);

    if (connection.peer_connection.get() == nullptr) {
        peer_connection_factory = nullptr;
        std::cout << name << ":" << std::this_thread::get_id() << ":"
            << "Error on CreatePeerConnection." << std::endl;
        exit(EXIT_FAILURE);
    }
    connection.peer_connection->CreateOffer(connection.csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
}

void create_answer_sdp(const std::string &parameter) {
    std::cout << name << ":" << std::this_thread::get_id() << ":"
        << "create_answer_sdp" << std::endl;

    connection.peer_connection =
        peer_connection_factory->CreatePeerConnection(configuration, nullptr, nullptr, &connection.pco);

    if (connection.peer_connection.get() == nullptr) {
        peer_connection_factory = nullptr;
        std::cout << name << ":" << std::this_thread::get_id() << ":"
            << "Error on CreatePeerConnection." << std::endl;
        exit(EXIT_FAILURE);
    }
    webrtc::SdpParseError error;
    webrtc::SessionDescriptionInterface *session_description(
        webrtc::CreateSessionDescription("offer", parameter, &error));
    if (session_description == nullptr) {
        std::cout << name << ":" << std::this_thread::get_id() << ":"
            << "Error on CreateSessionDescription." << std::endl
            << error.line << std::endl
            << error.description << std::endl;
        std::cout << name << ":" << std::this_thread::get_id() << ":"
            << "Offer SDP:begin" << std::endl
            << parameter << std::endl
            << "Offer SDP:end" << std::endl;
        exit(EXIT_FAILURE);
    }
    connection.peer_connection->SetRemoteDescription(connection.ssdo, session_description);
    connection.peer_connection->CreateAnswer(connection.csdo, webrtc::PeerConnectionInterface::RTCOfferAnswerOptions());
}

这两个函数主要负责建立 WebRTC 连接的过程,分别用于本地端的 Offer 和远程端的 Answer。Offer 和 Answer 是 WebRTC 连接建立的关键步骤,它们包含了媒体协商和配置信息,允许两端之间进行媒体传输。

send()

void send(const std::string &parameter) {
    std::cout << name << ":" << std::this_thread::get_id() << ":"
        << "send" << std::endl;

    webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(parameter.c_str(), parameter.size()), true);
    std::cout << name << ":" << std::this_thread::get_id() << ":"
        << "Send(" << connection.data_channel->state() << ")" << std::endl;
    connection.data_channel->Send(buffer);
}
  1. 填充buffer

  2. 通过data_channel向远程发送buffer

quit()

void quit() {
    std::cout << name << ":" << std::this_thread::get_id() << ":"
        << "quit" << std::endl;

    // Close with the thread running.
    // スレッドが起動した状態でCloseする。
    connection.peer_connection->Close();
    connection.peer_connection = nullptr;
    connection.data_channel    = nullptr;
    peer_connection_factory    = nullptr;

    network_thread->Stop();
    worker_thread->Stop();
    signaling_thread->Stop();
}

stop各个线程,用于释放资源

push_reply_sdp && push_ice

void push_reply_sdp(const std::string &parameter) {
    std::cout << name << ":" << std::this_thread::get_id() << ":"
        << "push_reply_sdp" << std::endl;

    webrtc::SdpParseError error;
    webrtc::SessionDescriptionInterface *session_description(
        webrtc::CreateSessionDescription("answer", parameter, &error));
    if (session_description == nullptr) {
        std::cout << name << ":" << std::this_thread::get_id() << ":"
            << "Error on CreateSessionDescription." << std::endl
            << error.line << std::endl
            << error.description << std::endl;
        std::cout << name << ":" << std::this_thread::get_id() << ":"
            << "Answer SDP:begin" << std::endl
            << parameter << std::endl
            << "Answer SDP:end" << std::endl;
        exit(EXIT_FAILURE);
    }
    connection.peer_connection->SetRemoteDescription(connection.ssdo, session_description);
}

void push_ice(const Ice &ice_it) {
    std::cout << name << ":" << std::this_thread::get_id() << ":"
        << "push_ice" << std::endl;

    webrtc::SdpParseError err_sdp;
    webrtc::IceCandidateInterface *ice =
        CreateIceCandidate(ice_it.sdp_mid, ice_it.sdp_mline_index, ice_it.candidate, &err_sdp);
    if (!err_sdp.line.empty() && !err_sdp.description.empty()) {
        std::cout << name << ":" << std::this_thread::get_id() << ":"
            << "Error on CreateIceCandidate" << std::endl
            << err_sdp.line << std::endl
            << err_sdp.description << std::endl;
        exit(EXIT_FAILURE);
    }
    connection.peer_connection->AddIceCandidate(ice);
}
  1. push_reply_sdp 函数:
    • 接收 Answer SDP 参数:该函数接收 Answer SDP 参数,然后通过解析该参数来创建 Session Description(会话描述)。
    • 设置远程描述:使用 SetRemoteDescription 将远程端的描述信息(Answer SDP)设置到当前连接中。这是连接建立的一部分,以便双方能够了解如何进行媒体传输。
  2. push_ice 函数:
    • 接收 ICE 参数:这个函数用于接收 ICE 候选(Candidate)参数,包括 sdp_midsdp_mline_indexcandidate
    • 创建 ICE 候选:通过调用 CreateIceCandidate 创建 ICE 候选对象。
    • 添加 ICE 候选:使用 AddIceCandidate 将 ICE 候选添加到 WebRTC 连接中。ICE 候选用于建立对等连接的网络通信路径,以确保数据能够传输。

这些函数是 WebRTC 连接建立和数据传输的重要步骤。它们在实时通信应用程序中用于配置连接和处理媒体传输所需的描述和候选信息。

main

具体实现步骤如下:

  1. 初始化, 初始化SSL,声明相关变量
  2. Wrapper webrtc
  3. 根据输入来进行相应的操作

小结

本例子主要是实现了对webrtc库的简单使用,通过此了解到了一些基本实现,主要是ICE协议,用于建立连接,connect类负责peer_connection和data_channel,wrapper类则封装了一些用到的函数,尤其是对三个线程的划分(network, worker, signaling),实现多线程,避免阻塞,但这一块应该还需要一些优化。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐