no message
parent
770cda1e03
commit
27b515b763
|
@ -0,0 +1,7 @@
|
|||
cmake_install.cmake
|
||||
CMakeCache.txt
|
||||
Makefile
|
||||
sample
|
||||
build/*
|
||||
CMakeFiles/*
|
||||
xcode/*
|
|
@ -0,0 +1,50 @@
|
|||
cmake_minimum_required(VERSION 2.8)
|
||||
|
||||
include_directories("${LIBWEBRTC_PATH}/include")
|
||||
include_directories("${LIBWEBRTC_PATH}/include/webrtc")
|
||||
|
||||
link_directories("${LIBWEBRTC_PATH}/lib")
|
||||
|
||||
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
|
||||
# macOS
|
||||
file(READ ${LIBWEBRTC_PATH}/exports_libwebrtc.txt webrtc_libs)
|
||||
string(REGEX REPLACE "lib([^.]+).a[\r\n]*" "\\1;" webrtc_libs "${webrtc_libs}")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWEBRTC_MAC=1 -DWEBRTC_POSIX=1")
|
||||
find_library(CORE_FOUNDATION CoreFoundation)
|
||||
list(APPEND webrtc_libs ${CORE_FOUNDATION})
|
||||
find_library(FOUNDATION Foundation)
|
||||
list(APPEND webrtc_libs ${FOUNDATION})
|
||||
find_library(CORE_AUDIO CoreAudio)
|
||||
list(APPEND webrtc_libs ${CORE_AUDIO})
|
||||
find_library(AUDIO_TOOLBOX AudioToolbox)
|
||||
list(APPEND webrtc_libs ${AUDIO_TOOLBOX})
|
||||
find_library(CORE_GRAPHICS CoreGraphics)
|
||||
list(APPEND webrtc_libs ${CORE_GRAPHICS})
|
||||
|
||||
elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux" AND ${CMAKE_SYSTEM_PROCESSOR} MATCHES "^arm")
|
||||
# Linux
|
||||
list(APPEND webrtc_libs "-Wl,--start-group")
|
||||
|
||||
file(READ ${LIBWEBRTC_PATH}/exports_libwebrtc.txt webrtc_read_tmp)
|
||||
string(REGEX REPLACE "lib([^.]+)\.a[\r\n]*" "\\1;" webrtc_read_tmp "${webrtc_read_tmp}")
|
||||
list(APPEND webrtc_libs "${webrtc_read_tmp}")
|
||||
|
||||
list(APPEND webrtc_libs "-Wl,--end-group")
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DWEBRTC_LINUX=1 -DWEBRTC_POSIX=1 -D_GLIBCXX_USE_CXX11_ABI=0")
|
||||
pkg_search_module(X11 REQUIRED x11)
|
||||
list(APPEND webrtc_libs ${X11_LIBRARIES})
|
||||
endif()
|
||||
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -W -Wall -Wno-unused-parameter")
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O0 -g")
|
||||
|
||||
add_executable(sample
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(sample
|
||||
${webrtc_libs}
|
||||
)
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Yuji Ito
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,120 @@
|
|||
# WebRTC C++ sample
|
||||
Sample program for using WebRTC(DataChannel) on C++.
|
||||
|
||||
# Requirement
|
||||
|
||||
* Mac OSX
|
||||
* Please download libwebrtc(It is precompiled chrome's WebRTC) and unarchive it.(https://github.com/llamerada-jp/libwebrtc)
|
||||
|
||||
# Compile
|
||||
|
||||
```sh
|
||||
$ cd <path to work>
|
||||
$ git clone --depth 1 https://github.com/llamerada-jp/webrtc-cpp-sample.git
|
||||
$ cd webrtc-cpp-sample
|
||||
$ git submodule init
|
||||
$ git submodule update
|
||||
$ cmake -DLIBWEBRTC_PATH=<path to unarchived webrtc> .
|
||||
$ make
|
||||
```
|
||||
|
||||
# Run
|
||||
|
||||
This sample use two consoles to try interprocess communication by WebRTC.
|
||||
It maybe cannot communicate over NAT each other, because it does not use ICE server.
|
||||
|
||||
## Connection
|
||||
|
||||
memo : On this sample, Some commands requireing parameter need line of only a semicolon after parameter.
|
||||
|
||||
At CONSOLE-1.
|
||||
|
||||
```sh
|
||||
$ cd <path to work>
|
||||
$ ./sample
|
||||
0x7fff791c9000:Main thread
|
||||
0x700000081000:RTC thread
|
||||
sdp1
|
||||
0x700000081000:PeerConnectionObserver::RenegotiationNeeded
|
||||
0x700000081000:CreateSessionDescriptionObserver::OnSuccess
|
||||
0x700000081000:PeerConnectionObserver::SignalingChange(1)
|
||||
Offer SDP:begin
|
||||
<Copy displayed string to the clipboard as STRING-A.>
|
||||
Offer SDP:end
|
||||
0x700000081000:SetSessionDescriptionObserver::OnSuccess
|
||||
0x700000081000:PeerConnectionObserver::IceGatheringChange(1)
|
||||
0x700000081000:PeerConnectionObserver::IceCandidate
|
||||
0x700000081000:PeerConnectionObserver::IceCandidate
|
||||
0x700000081000:PeerConnectionObserver::IceCandidate
|
||||
0x700000081000:PeerConnectionObserver::IceGatheringChange(2)
|
||||
sdp3
|
||||
<Paste STRING-B that it displayed on CONSOLE-2.>
|
||||
;
|
||||
0x700000081000:PeerConnectionObserver::SignalingChange(0)
|
||||
0x700000081000:PeerConnectionObserver::IceConnectionChange(1)
|
||||
0x700000081000:SetSessionDescriptionObserver::OnSuccess
|
||||
ice1
|
||||
<Copy displayed string to the clipboard as STRING-C.>
|
||||
0x700000081000:PeerConnectionObserver::IceConnectionChange(2)
|
||||
0x700000081000:PeerConnectionObserver::IceConnectionChange(3)
|
||||
0x700000081000:DataChannelObserver::StateChange
|
||||
0x700000081000:PeerConnectionObserver::DataChannel(0x7fd8cb608750, 0x7fd8cb71bef0)
|
||||
ice2
|
||||
<Paste STRING-D that it displayed on CONSOLE-2.>
|
||||
;
|
||||
```
|
||||
|
||||
At CONSOLE-2.
|
||||
|
||||
```sh
|
||||
$ cd <path to work>
|
||||
$ ./sample
|
||||
0x7fff791c9000:Main thread
|
||||
0x700000081000:RTC thread
|
||||
sdp2
|
||||
<Paste STRING-A that it displayed on CONSOLE-1.>
|
||||
;
|
||||
0x700000081000:PeerConnectionObserver::RenegotiationNeeded
|
||||
0x700000081000:PeerConnectionObserver::SignalingChange(3)
|
||||
0x700000081000:SetSessionDescriptionObserver::OnSuccess
|
||||
0x700000081000:CreateSessionDescriptionObserver::OnSuccess
|
||||
0x700000081000:PeerConnectionObserver::SignalingChange(0)
|
||||
Answer SDP:begin
|
||||
<Copy displayed string to the clipboard as STRING-B.>
|
||||
Answer SDP:end
|
||||
0x700000081000:SetSessionDescriptionObserver::OnSuccess
|
||||
0x700000081000:PeerConnectionObserver::IceGatheringChange(1)
|
||||
0x700000081000:PeerConnectionObserver::IceCandidate
|
||||
0x700000081000:PeerConnectionObserver::IceCandidate
|
||||
0x700000081000:PeerConnectionObserver::IceCandidate
|
||||
0x700000081000:PeerConnectionObserver::IceGatheringChange(2)
|
||||
ice2
|
||||
<Paste STRING-C that it displayed on CONSOLE-1.>
|
||||
;
|
||||
0x700000081000:PeerConnectionObserver::IceConnectionChange(1)
|
||||
0x700000081000:PeerConnectionObserver::IceConnectionChange(2)
|
||||
0x700000081000:DataChannelObserver::StateChange
|
||||
0x700000081000:PeerConnectionObserver::DataChannel(0x7fa739e0c0d0, 0x7fa739e08b80)
|
||||
ice1
|
||||
<Copy displayed string to the clipboard as STRING-D.>
|
||||
```
|
||||
|
||||
## Send message
|
||||
|
||||
You can send messages, after connection is enabled.
|
||||
|
||||
```
|
||||
send
|
||||
Hello world.
|
||||
;
|
||||
```
|
||||
|
||||
## Quit
|
||||
|
||||
You can watch sequence of quit by typing of "quit".
|
||||
|
||||
```
|
||||
quit
|
||||
```
|
||||
|
||||
EOD
|
|
@ -0,0 +1,399 @@
|
|||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// 環境に合わせてマクロ定義が必要
|
||||
//#define WEBRTC_ANDROID 1
|
||||
//#define WEBRTC_IOS 1
|
||||
//#define WEBRTC_LINUX 1
|
||||
#define WEBRTC_MAC 1
|
||||
#define WEBRTC_POSIX 1
|
||||
//#define WEBRTC_WIN 1
|
||||
|
||||
// WebRTC関連のヘッダ
|
||||
#include <webrtc/api/audio_codecs/builtin_audio_decoder_factory.h>
|
||||
#include <webrtc/api/audio_codecs/builtin_audio_encoder_factory.h>
|
||||
#include <webrtc/api/peerconnectioninterface.h>
|
||||
#include <webrtc/rtc_base/flags.h>
|
||||
#include <webrtc/rtc_base/physicalsocketserver.h>
|
||||
#include <webrtc/rtc_base/ssladapter.h>
|
||||
#include <webrtc/rtc_base/thread.h>
|
||||
|
||||
// picojsonはコピペ用データ構造を作るために使う
|
||||
#include "picojson/picojson.h"
|
||||
|
||||
class Connection {
|
||||
public:
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection;
|
||||
rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel;
|
||||
std::string sdp_type;
|
||||
picojson::array ice_array;
|
||||
|
||||
// Offer/Answerの作成が成功したら、LocalDescriptionとして設定 & 相手に渡す文字列として表示
|
||||
void onSuccessCSD(webrtc::SessionDescriptionInterface* desc) {
|
||||
peer_connection->SetLocalDescription(ssdo, desc);
|
||||
|
||||
std::string sdp;
|
||||
desc->ToString(&sdp);
|
||||
std::cout << sdp_type << " SDP:begin" << std::endl << sdp << sdp_type << " SDP:end" << std::endl;
|
||||
}
|
||||
|
||||
// ICEを取得したら、表示用JSON配列の末尾に追加
|
||||
void onIceCandidate(const webrtc::IceCandidateInterface* candidate) {
|
||||
picojson::object ice;
|
||||
std::string candidate_str;
|
||||
candidate->ToString(&candidate_str);
|
||||
ice.insert(std::make_pair("candidate", picojson::value(candidate_str)));
|
||||
ice.insert(std::make_pair("sdpMid", picojson::value(candidate->sdp_mid())));
|
||||
ice.insert(std::make_pair("sdpMLineIndex", picojson::value(static_cast<double>(candidate->sdp_mline_index()))));
|
||||
ice_array.push_back(picojson::value(ice));
|
||||
}
|
||||
|
||||
class PCO : public webrtc::PeerConnectionObserver {
|
||||
private:
|
||||
Connection& parent;
|
||||
|
||||
public:
|
||||
PCO(Connection& parent) : parent(parent) {
|
||||
}
|
||||
|
||||
void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "PeerConnectionObserver::SignalingChange(" << new_state << ")" << std::endl;
|
||||
};
|
||||
|
||||
void OnAddStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "PeerConnectionObserver::AddStream" << std::endl;
|
||||
};
|
||||
|
||||
void OnRemoveStream(rtc::scoped_refptr<webrtc::MediaStreamInterface> stream) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "PeerConnectionObserver::RemoveStream" << std::endl;
|
||||
};
|
||||
|
||||
void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> data_channel) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "PeerConnectionObserver::DataChannel(" << data_channel
|
||||
<< ", " << parent.data_channel.get() << ")" << std::endl;
|
||||
// Answer送信側は、onDataChannelでDataChannelの接続を受け付ける
|
||||
parent.data_channel = data_channel;
|
||||
parent.data_channel->RegisterObserver(&parent.dco);
|
||||
};
|
||||
|
||||
void OnRenegotiationNeeded() override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "PeerConnectionObserver::RenegotiationNeeded" << std::endl;
|
||||
};
|
||||
|
||||
void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "PeerConnectionObserver::IceConnectionChange(" << new_state << ")" << std::endl;
|
||||
};
|
||||
|
||||
void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "PeerConnectionObserver::IceGatheringChange(" << new_state << ")" << std::endl;
|
||||
};
|
||||
|
||||
void OnIceCandidate(const webrtc::IceCandidateInterface* candidate) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "PeerConnectionObserver::IceCandidate" << std::endl;
|
||||
parent.onIceCandidate(candidate);
|
||||
};
|
||||
};
|
||||
|
||||
class DCO : public webrtc::DataChannelObserver {
|
||||
private:
|
||||
Connection& parent;
|
||||
|
||||
public:
|
||||
DCO(Connection& parent) : parent(parent) {
|
||||
}
|
||||
|
||||
// 接続状況が変化した時に発火する。切断は発火タイミングで値を確認して検知可能
|
||||
void OnStateChange() override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "DataChannelObserver::StateChange" << std::endl;
|
||||
};
|
||||
|
||||
// メッセージ受信
|
||||
void OnMessage(const webrtc::DataBuffer& buffer) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "DataChannelObserver::Message" << std::endl;
|
||||
std::cout << std::string(buffer.data.data<char>(), buffer.data.size()) << std::endl;
|
||||
};
|
||||
|
||||
void OnBufferedAmountChange(uint64_t previous_amount) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "DataChannelObserver::BufferedAmountChange(" << previous_amount << ")" << std::endl;
|
||||
};
|
||||
};
|
||||
|
||||
class CSDO : public webrtc::CreateSessionDescriptionObserver {
|
||||
private:
|
||||
Connection& parent;
|
||||
|
||||
public:
|
||||
CSDO(Connection& parent) : parent(parent) {
|
||||
}
|
||||
|
||||
void OnSuccess(webrtc::SessionDescriptionInterface* desc) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "CreateSessionDescriptionObserver::OnSuccess" << std::endl;
|
||||
parent.onSuccessCSD(desc);
|
||||
};
|
||||
|
||||
void OnFailure(const std::string& error) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "CreateSessionDescriptionObserver::OnFailure" << std::endl << error << std::endl;
|
||||
};
|
||||
};
|
||||
|
||||
class SSDO : public webrtc::SetSessionDescriptionObserver {
|
||||
private:
|
||||
Connection& parent;
|
||||
|
||||
public:
|
||||
SSDO(Connection& parent) : parent(parent) {
|
||||
}
|
||||
|
||||
void OnSuccess() override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "SetSessionDescriptionObserver::OnSuccess" << std::endl;
|
||||
};
|
||||
|
||||
void OnFailure(const std::string& error) override {
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "SetSessionDescriptionObserver::OnFailure" << std::endl << error << std::endl;
|
||||
};
|
||||
};
|
||||
|
||||
PCO pco;
|
||||
DCO dco;
|
||||
rtc::scoped_refptr<CSDO> csdo;
|
||||
rtc::scoped_refptr<SSDO> ssdo;
|
||||
|
||||
Connection() :
|
||||
pco(*this),
|
||||
dco(*this),
|
||||
csdo(new rtc::RefCountedObject<CSDO>(*this)),
|
||||
ssdo(new rtc::RefCountedObject<SSDO>(*this)) {
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<rtc::Thread> thread;
|
||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> peer_connection_factory;
|
||||
webrtc::PeerConnectionInterface::RTCConfiguration configuration;
|
||||
Connection connection;
|
||||
rtc::PhysicalSocketServer socket_server;
|
||||
|
||||
class CustomRunnable : public rtc::Runnable {
|
||||
public:
|
||||
void Run(rtc::Thread* subthread) override {
|
||||
peer_connection_factory = webrtc::CreatePeerConnectionFactory(
|
||||
webrtc::CreateBuiltinAudioEncoderFactory(),
|
||||
webrtc::CreateBuiltinAudioDecoderFactory());
|
||||
if (peer_connection_factory.get() == nullptr) {
|
||||
std::cout << "Error on CreatePeerConnectionFactory." << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
subthread->Run();
|
||||
}
|
||||
};
|
||||
|
||||
void cmd_sdp1() {
|
||||
connection.peer_connection = peer_connection_factory->CreatePeerConnection(configuration, nullptr, nullptr, &connection.pco);
|
||||
|
||||
webrtc::DataChannelInit config;
|
||||
// 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 << "Error on CreatePeerConnection." << std::endl;
|
||||
return;
|
||||
}
|
||||
connection.sdp_type = "Offer"; // 表示用の文字列、webrtcの動作には関係ない
|
||||
connection.peer_connection->CreateOffer(connection.csdo, nullptr);
|
||||
}
|
||||
|
||||
void cmd_sdp2(const std::string& parameter) {
|
||||
connection.peer_connection = peer_connection_factory->CreatePeerConnection(configuration, nullptr, nullptr, &connection.pco);
|
||||
|
||||
if (connection.peer_connection.get() == nullptr) {
|
||||
peer_connection_factory = nullptr;
|
||||
std::cout << "Error on CreatePeerConnection." << std::endl;
|
||||
return;
|
||||
}
|
||||
webrtc::SdpParseError error;
|
||||
webrtc::SessionDescriptionInterface* session_description(
|
||||
webrtc::CreateSessionDescription("offer", parameter, &error));
|
||||
if (session_description == nullptr) {
|
||||
std::cout << "Error on CreateSessionDescription." << std::endl
|
||||
<< error.line << std::endl
|
||||
<< error.description << std::endl;
|
||||
std::cout << "Offer SDP:begin" << std::endl << parameter << std::endl << "Offer SDP:end" << std::endl;
|
||||
}
|
||||
connection.peer_connection->SetRemoteDescription(connection.ssdo, session_description);
|
||||
|
||||
connection.sdp_type = "Answer"; // 表示用の文字列、webrtcの動作には関係ない
|
||||
connection.peer_connection->CreateAnswer(connection.csdo, nullptr);
|
||||
}
|
||||
|
||||
void cmd_sdp3(const std::string& parameter) {
|
||||
webrtc::SdpParseError error;
|
||||
webrtc::SessionDescriptionInterface* session_description(
|
||||
webrtc::CreateSessionDescription("answer", parameter, &error));
|
||||
if (session_description == nullptr) {
|
||||
std::cout << "Error on CreateSessionDescription." << std::endl
|
||||
<< error.line << std::endl
|
||||
<< error.description << std::endl;
|
||||
std::cout << "Answer SDP:begin" << std::endl << parameter << std::endl << "Answer SDP:end" << std::endl;
|
||||
}
|
||||
connection.peer_connection->SetRemoteDescription(connection.ssdo, session_description);
|
||||
}
|
||||
|
||||
void cmd_ice1() {
|
||||
std::cout << picojson::value(connection.ice_array).serialize(true) << std::endl;
|
||||
connection.ice_array.clear();
|
||||
}
|
||||
|
||||
void cmd_ice2(const std::string& parameter) {
|
||||
picojson::value v;
|
||||
std::string err = picojson::parse(v, parameter);
|
||||
if (!err.empty()) {
|
||||
std::cout << "Error on parse json : " << err << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
webrtc::SdpParseError err_sdp;
|
||||
for (auto& ice_it : v.get<picojson::array>()) {
|
||||
picojson::object& ice_json = ice_it.get<picojson::object>();
|
||||
webrtc::IceCandidateInterface* ice =
|
||||
CreateIceCandidate(ice_json.at("sdpMid").get<std::string>(),
|
||||
static_cast<int>(ice_json.at("sdpMLineIndex").get<double>()),
|
||||
ice_json.at("candidate").get<std::string>(),
|
||||
&err_sdp);
|
||||
if (!err_sdp.line.empty() && !err_sdp.description.empty()) {
|
||||
std::cout << "Error on CreateIceCandidate" << std::endl
|
||||
<< err_sdp.line << std::endl
|
||||
<< err_sdp.description << std::endl;
|
||||
return;
|
||||
}
|
||||
connection.peer_connection->AddIceCandidate(ice);
|
||||
}
|
||||
}
|
||||
|
||||
void cmd_send(const std::string& parameter) {
|
||||
webrtc::DataBuffer buffer(rtc::CopyOnWriteBuffer(parameter.c_str(), parameter.size()), true);
|
||||
std::cout << "Send(" << connection.data_channel->state() << ")" << std::endl;
|
||||
connection.data_channel->Send(buffer);
|
||||
}
|
||||
|
||||
void cmd_quit() {
|
||||
// スレッドを活かしながらCloseしないと、別スレッドからのイベント待ちになり終了できなくなる
|
||||
connection.peer_connection->Close();
|
||||
connection.peer_connection = nullptr;
|
||||
connection.data_channel = nullptr;
|
||||
peer_connection_factory = nullptr;
|
||||
// リソースを開放したらスレッドを止めてOK
|
||||
thread->Quit();
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
// 第三引数にtrueを指定すると、WebRTC関連の引数をargvから削除してくれるらしい
|
||||
rtc::FlagList::SetFlagsFromCommandLine(&argc, argv, true);
|
||||
rtc::FlagList::Print(nullptr, false);
|
||||
|
||||
std::cout << std::this_thread::get_id() << ":"
|
||||
<< "Main thread" << std::endl;
|
||||
|
||||
// GoogleのSTUNサーバを利用
|
||||
webrtc::PeerConnectionInterface::IceServer ice_server;
|
||||
ice_server.uri = "stun:stun.l.google.com:19302";
|
||||
configuration.servers.push_back(ice_server);
|
||||
|
||||
thread.reset(new rtc::Thread(&socket_server));
|
||||
|
||||
rtc::InitializeSSL();
|
||||
|
||||
CustomRunnable runnable;
|
||||
thread->Start(&runnable);
|
||||
|
||||
std::string line;
|
||||
std::string command;
|
||||
std::string parameter;
|
||||
bool is_cmd_mode = true;
|
||||
|
||||
while (std::getline(std::cin, line)) {
|
||||
if (is_cmd_mode) {
|
||||
if (line == "") {
|
||||
continue;
|
||||
|
||||
} else if (line == "sdp1") {
|
||||
cmd_sdp1();
|
||||
|
||||
} else if (line == "sdp2") {
|
||||
command = "sdp2";
|
||||
is_cmd_mode = false;
|
||||
|
||||
} else if (line == "sdp3") {
|
||||
command = "sdp3";
|
||||
is_cmd_mode = false;
|
||||
|
||||
} else if (line == "ice1") {
|
||||
cmd_ice1();
|
||||
|
||||
} else if (line == "ice2") {
|
||||
command = "ice2";
|
||||
is_cmd_mode = false;
|
||||
|
||||
} else if (line == "send") {
|
||||
command = "send";
|
||||
is_cmd_mode = false;
|
||||
|
||||
} else if (line == "quit") {
|
||||
cmd_quit();
|
||||
break;
|
||||
|
||||
} else {
|
||||
std::cout << "?" << line << std::endl;
|
||||
}
|
||||
|
||||
} else {
|
||||
if (line == ";") {
|
||||
if (command == "sdp2") {
|
||||
cmd_sdp2(parameter);
|
||||
|
||||
} else if (command == "sdp3") {
|
||||
cmd_sdp3(parameter);
|
||||
|
||||
} else if (command == "ice2") {
|
||||
cmd_ice2(parameter);
|
||||
|
||||
} else if (command == "send") {
|
||||
cmd_send(parameter);
|
||||
}
|
||||
|
||||
parameter = "";
|
||||
is_cmd_mode = true;
|
||||
|
||||
} else {
|
||||
parameter += line + "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread.reset();
|
||||
rtc::CleanupSSL();
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>WebRTC C++ sample</title>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<textarea id="stdout" cols="100" rows="32" readonly>output</textarea>
|
||||
</div>
|
||||
<div>
|
||||
<button type="button" onclick="sdp1();">sdp1</button>
|
||||
<button type="button" onclick="sdp2();">sdp2</button>
|
||||
<button type="button" onclick="sdp3();">sdp3</button>
|
||||
<button type="button" onclick="ice1();">ice1</button>
|
||||
<button type="button" onclick="ice2();">ice2</button>
|
||||
<button type="button" onclick="send();">send</button>
|
||||
<button type="button" onclick="quit();">quit</button>
|
||||
</div>
|
||||
<div>
|
||||
<textarea id="stdin" cols="100" rows="8"></textarea>
|
||||
</div>
|
||||
</body>
|
||||
<script type="text/javascript">
|
||||
var dataChannel = null;
|
||||
var peerConnection = null;
|
||||
var pcConfig = {
|
||||
iceServers:[
|
||||
{url:'stun:stun.l.google.com:19302'}
|
||||
]
|
||||
};
|
||||
var iceArray = [];
|
||||
|
||||
function output(log) {
|
||||
var stdout = document.getElementById('stdout');
|
||||
stdout.value = stdout.value + log + '\n';
|
||||
}
|
||||
|
||||
function input() {
|
||||
var stdin = document.getElementById('stdin');
|
||||
var input = stdin.value;
|
||||
stdin.value = '';
|
||||
return input;
|
||||
}
|
||||
|
||||
function onDataChannel(evt) {
|
||||
output('onDataChannel');
|
||||
dataChannel = evt.channel;
|
||||
setDataChannelEvents(dataChannel);
|
||||
}
|
||||
|
||||
function onIceCandidate(evt) {
|
||||
if (evt.candidate) {
|
||||
iceArray.push(evt.candidate);
|
||||
} else {
|
||||
output("end of ice candidate" + evt.eventPhase);
|
||||
}
|
||||
}
|
||||
|
||||
function onIceConnectionStateChange(evt) {
|
||||
output("onIceConnectionStateChange:" + peerConnection.iceConnectionState);
|
||||
}
|
||||
|
||||
function setDataChannelEvents(dataChannel) {
|
||||
dataChannel.onerror = function (error) {
|
||||
ouptput('Data Channel onerror:' + error);
|
||||
};
|
||||
|
||||
dataChannel.onmessage = function (event) {
|
||||
output('Data Channel onmessage:' + event.data);
|
||||
output(String.fromCharCode.apply("", new Uint8Array(event.data)));
|
||||
};
|
||||
|
||||
dataChannel.onopen = function () {
|
||||
output('Data Channel onopen');
|
||||
};
|
||||
|
||||
dataChannel.onclose = function () {
|
||||
output('Data Channel onclose');
|
||||
};
|
||||
}
|
||||
|
||||
function sdp1() {
|
||||
function onOfferSuccess(sessionDescription) {
|
||||
peerConnection.setLocalDescription(sessionDescription);
|
||||
output('createOffer -> onOfferSuccess');
|
||||
output('Offer SDP:begin');
|
||||
output(sessionDescription.sdp);
|
||||
output('Offer SDP:end');
|
||||
}
|
||||
|
||||
function onOfferFailure() {
|
||||
output('createOffer -> onOfferFailure');
|
||||
}
|
||||
|
||||
peerConnection = new RTCPeerConnection(pcConfig);
|
||||
peerConnection.onicecandidate = onIceCandidate;
|
||||
peerConnection.ondatachannel = onDataChannel;
|
||||
peerConnection.oniceconnectionstatechange = onIceConnectionStateChange;
|
||||
var dataChannelOptions = {
|
||||
ordered: true, // 順序を保証する
|
||||
maxRetransmitTime: 3000 // ミリ秒
|
||||
};
|
||||
dataChannel = peerConnection.createDataChannel('data_channel', dataChannelOptions);
|
||||
setDataChannelEvents(dataChannel);
|
||||
peerConnection.createOffer(onOfferSuccess, onOfferFailure, null);
|
||||
}
|
||||
|
||||
function sdp2() {
|
||||
function onAnswerSuccess(sessionDescription) {
|
||||
peerConnection.setLocalDescription(sessionDescription);
|
||||
output('createAnswer -> onAnswerSuccess');
|
||||
output('Answer SDP:begin');
|
||||
output(sessionDescription.sdp);
|
||||
output('Answer SDP:end');
|
||||
}
|
||||
|
||||
function onAnswerFailure() {
|
||||
output('createAnswer -> onAnswerFailure');
|
||||
}
|
||||
|
||||
var sdp = new RTCSessionDescription({
|
||||
type: 'offer',
|
||||
sdp: input()
|
||||
});
|
||||
peerConnection = new RTCPeerConnection(pcConfig);
|
||||
peerConnection.onicecandidate = onIceCandidate;
|
||||
peerConnection.ondatachannel = onDataChannel;
|
||||
peerConnection.oniceconnectionstatechange = onIceConnectionStateChange;
|
||||
peerConnection.setRemoteDescription(sdp);
|
||||
peerConnection.createAnswer(onAnswerSuccess, onAnswerFailure, null);
|
||||
}
|
||||
|
||||
function sdp3() {
|
||||
var sdp = new RTCSessionDescription({
|
||||
type: 'answer',
|
||||
sdp: input()
|
||||
});
|
||||
peerConnection.setRemoteDescription(sdp);
|
||||
}
|
||||
|
||||
function ice1() {
|
||||
output('ICE:begin');
|
||||
output(JSON.stringify(iceArray));
|
||||
iceArray = [];
|
||||
output('ICE:end');
|
||||
}
|
||||
|
||||
function ice2() {
|
||||
var ices = JSON.parse(input());
|
||||
for (var ice of ices) {
|
||||
var iceObj = new RTCIceCandidate(ice);
|
||||
peerConnection.addIceCandidate(iceObj);
|
||||
}
|
||||
}
|
||||
|
||||
function send() {
|
||||
var data = input();
|
||||
dataChannel.send(data);
|
||||
}
|
||||
|
||||
function quit() {
|
||||
dataChannel.close();
|
||||
dataChannel = null;
|
||||
peerConnection.close();
|
||||
peerConnection = null;
|
||||
}
|
||||
</script>
|
||||
</html>
|
Loading…
Reference in New Issue