From 6a6f80d5e91d1a1231ae75cf93716a54a88e5380 Mon Sep 17 00:00:00 2001 From: Axel Isouard Date: Sun, 25 Sep 2016 19:24:44 +0200 Subject: [PATCH] Add Samples subdirectory, with a very basic sample program --- CMakeLists.txt | 6 +- Samples/CMakeLists.txt | 1 + Samples/PeerConnection/CMakeLists.txt | 35 ++++ Samples/PeerConnection/Core.cpp | 76 +++++++ Samples/PeerConnection/Core.h | 28 +++ .../PeerConnection/CreateSessionObserver.cpp | 24 +++ .../PeerConnection/CreateSessionObserver.h | 26 +++ .../PeerConnection/DataChannelObserver.cpp | 31 +++ Samples/PeerConnection/DataChannelObserver.h | 22 +++ Samples/PeerConnection/Peer.cpp | 186 ++++++++++++++++++ Samples/PeerConnection/Peer.h | 62 ++++++ .../PeerConnection/PeerConnectionObserver.cpp | 5 + .../PeerConnection/PeerConnectionObserver.h | 14 ++ .../SetLocalSessionDescriptionObserver.cpp | 30 +++ .../SetLocalSessionDescriptionObserver.h | 27 +++ .../SetRemoteSessionDescriptionObserver.cpp | 27 +++ .../SetRemoteSessionDescriptionObserver.h | 28 +++ Samples/PeerConnection/UnixConsole.cpp | 153 ++++++++++++++ Samples/PeerConnection/UnixConsole.h | 20 ++ Samples/PeerConnection/main.cpp | 140 +++++++++++++ 20 files changed, 940 insertions(+), 1 deletion(-) create mode 100644 Samples/CMakeLists.txt create mode 100644 Samples/PeerConnection/CMakeLists.txt create mode 100644 Samples/PeerConnection/Core.cpp create mode 100644 Samples/PeerConnection/Core.h create mode 100644 Samples/PeerConnection/CreateSessionObserver.cpp create mode 100644 Samples/PeerConnection/CreateSessionObserver.h create mode 100644 Samples/PeerConnection/DataChannelObserver.cpp create mode 100644 Samples/PeerConnection/DataChannelObserver.h create mode 100644 Samples/PeerConnection/Peer.cpp create mode 100644 Samples/PeerConnection/Peer.h create mode 100644 Samples/PeerConnection/PeerConnectionObserver.cpp create mode 100644 Samples/PeerConnection/PeerConnectionObserver.h create mode 100644 Samples/PeerConnection/SetLocalSessionDescriptionObserver.cpp create mode 100644 Samples/PeerConnection/SetLocalSessionDescriptionObserver.h create mode 100644 Samples/PeerConnection/SetRemoteSessionDescriptionObserver.cpp create mode 100644 Samples/PeerConnection/SetRemoteSessionDescriptionObserver.h create mode 100644 Samples/PeerConnection/UnixConsole.cpp create mode 100644 Samples/PeerConnection/UnixConsole.h create mode 100644 Samples/PeerConnection/main.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 6ce9d52..841cd95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -66,4 +66,8 @@ set(LIBWEBRTC_LIBRARY ${CMAKE_STATIC_LIBRARY_PREFIX}webrtc${CMAKE_STATIC_LIBRARY set(LIBWEBRTC_BUILD_ROOT ${CMAKE_SOURCE_DIR}/src/out/Default) set(WEBRTC_CHROMIUM_DEPS git@github.com:aisouard/libwebrtc-chromium-deps.git) -add_subdirectory(Targets) \ No newline at end of file +add_subdirectory(Targets) + +if(BUILD_SAMPLES) + add_subdirectory(Samples) +endif(BUILD_SAMPLES) \ No newline at end of file diff --git a/Samples/CMakeLists.txt b/Samples/CMakeLists.txt new file mode 100644 index 0000000..8a79773 --- /dev/null +++ b/Samples/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(PeerConnection) \ No newline at end of file diff --git a/Samples/PeerConnection/CMakeLists.txt b/Samples/PeerConnection/CMakeLists.txt new file mode 100644 index 0000000..4556989 --- /dev/null +++ b/Samples/PeerConnection/CMakeLists.txt @@ -0,0 +1,35 @@ +project(PeerConnection) + +set(THREADS_PREFER_PTHREAD_FLAG ON) +find_package(Threads REQUIRED) +find_package(X11 REQUIRED) + +set(PEERCONNECTION_SOURCE_FILES + main.cpp + Core.cpp + CreateSessionObserver.cpp + DataChannelObserver.cpp + Peer.cpp + PeerConnectionObserver.cpp + SetLocalSessionDescriptionObserver.cpp + SetRemoteSessionDescriptionObserver.cpp +) + +include_directories(${CMAKE_SOURCE_DIR}/out/src) + +if(WIN) + add_definitions(-DWEBRTC_WIN) +else(WIN) + add_definitions(-DWEBRTC_POSIX -std=c++11 -fno-rtti -D_GLIBCXX_USE_CXX11_ABI=0) + set(PEERCONNECTION_SOURCE_FILES ${PEERCONNECTION_SOURCE_FILES} UnixConsole.cpp) +endif(WIN) + +add_executable(PeerConnection ${PEERCONNECTION_SOURCE_FILES}) + +set(PEERCONNECTION_LIBRARIES ${CMAKE_BINARY_DIR}/${LIBWEBRTC_LIBRARY} Threads::Threads) + +if(UNIX AND NOT APPLE) + set(PEERCONNECTION_LIBRARIES ${PEERCONNECTION_LIBRARIES} ${X11_LIBRARIES} ${CMAKE_DL_LIBS}) +endif(UNIX AND NOT APPLE) + +target_link_libraries(PeerConnection ${PEERCONNECTION_LIBRARIES}) \ No newline at end of file diff --git a/Samples/PeerConnection/Core.cpp b/Samples/PeerConnection/Core.cpp new file mode 100644 index 0000000..7dbf6fa --- /dev/null +++ b/Samples/PeerConnection/Core.cpp @@ -0,0 +1,76 @@ +// +// Created by ax on 24/09/16. +// + +#include "webrtc/base/ssladapter.h" +#include "Core.h" + +rtc::Thread *Core::_signalingThread = NULL; +rtc::Thread *Core::_workerThread = NULL; +webrtc::PeerConnectionFactoryInterface *Core::_peerConnectionFactory = NULL; + +bool Core::Init() { + rtc::InitializeSSL(); + rtc::InitRandom(rtc::Time()); + rtc::ThreadManager::Instance()->WrapCurrentThread(); + + _signalingThread = new rtc::Thread(); + _workerThread = new rtc::Thread(); + + _signalingThread->SetName("signaling_thread", NULL); + _workerThread->SetName("worker_thread", NULL); + + if (!_signalingThread->Start() || !_workerThread->Start()) { + return false; + } + + _peerConnectionFactory = + webrtc::CreatePeerConnectionFactory(_signalingThread, + _workerThread, + NULL, NULL, NULL).release(); + + return true; +} + +bool Core::Update() { + return rtc::Thread::Current()->ProcessMessages(0); +} + +bool Core::Cleanup() { + _peerConnectionFactory->Release(); + _peerConnectionFactory = NULL; + + _signalingThread->Stop(); + _workerThread->Stop(); + + delete _signalingThread; + delete _workerThread; + + _signalingThread = NULL; + _workerThread = NULL; + + return rtc::CleanupSSL(); +} + +rtc::Thread *Core::GetSignalingThread() { + return _signalingThread; +} + +rtc::Thread *Core::GetWorkerThread() { + return _workerThread; +} + +webrtc::PeerConnectionFactoryInterface *Core::GetPeerConnectionFactory() { + return _peerConnectionFactory; +} + + + + + + + + + + + diff --git a/Samples/PeerConnection/Core.h b/Samples/PeerConnection/Core.h new file mode 100644 index 0000000..006ec82 --- /dev/null +++ b/Samples/PeerConnection/Core.h @@ -0,0 +1,28 @@ +// +// Created by ax on 24/09/16. +// + +#ifndef LIBWEBRTC_CORE_H +#define LIBWEBRTC_CORE_H + +#include "webrtc/api/peerconnectioninterface.h" +#include "webrtc/base/thread.h" + +class Core { +public: + static bool Init(); + static bool Update(); + static bool Cleanup(); + + static rtc::Thread *GetSignalingThread(); + static rtc::Thread *GetWorkerThread(); + static webrtc::PeerConnectionFactoryInterface *GetPeerConnectionFactory(); + +private: + static rtc::Thread *_signalingThread; + static rtc::Thread *_workerThread; + static webrtc::PeerConnectionFactoryInterface *_peerConnectionFactory; +}; + + +#endif //LIBWEBRTC_CORE_H diff --git a/Samples/PeerConnection/CreateSessionObserver.cpp b/Samples/PeerConnection/CreateSessionObserver.cpp new file mode 100644 index 0000000..c53d36f --- /dev/null +++ b/Samples/PeerConnection/CreateSessionObserver.cpp @@ -0,0 +1,24 @@ +// +// Created by ax on 25/09/16. +// + +#include "CreateSessionObserver.h" +#include "UnixConsole.h" +#include "Peer.h" +#include "SetLocalSessionDescriptionObserver.h" + +using namespace webrtc; + +CreateSessionObserver::CreateSessionObserver(Peer *peer): _peer(peer) { +} + +void CreateSessionObserver::OnSuccess(SessionDescriptionInterface* desc) { + rtc::scoped_refptr observer = + new rtc::RefCountedObject(desc); + + _peer->SetLocalSessionDescription(desc, observer); +} + +void CreateSessionObserver::OnFailure(const std::string& error) { + Console::Print("[CreateSessionObserver::OnFailure] %s", error.c_str()); +} \ No newline at end of file diff --git a/Samples/PeerConnection/CreateSessionObserver.h b/Samples/PeerConnection/CreateSessionObserver.h new file mode 100644 index 0000000..51c9cae --- /dev/null +++ b/Samples/PeerConnection/CreateSessionObserver.h @@ -0,0 +1,26 @@ +// +// Created by ax on 25/09/16. +// + +#ifndef LIBWEBRTC_CREATEOFFEROBSERVER_H +#define LIBWEBRTC_CREATEOFFEROBSERVER_H + +#include +#include "Peer.h" + +class CreateSessionObserver: public webrtc::CreateSessionDescriptionObserver { +public: + CreateSessionObserver(Peer *peer); + + void OnSuccess(webrtc::SessionDescriptionInterface* desc); + void OnFailure(const std::string& error); + +private: + Peer *_peer; + +protected: + ~CreateSessionObserver() {} +}; + + +#endif //LIBWEBRTC_CREATEOFFEROBSERVER_H diff --git a/Samples/PeerConnection/DataChannelObserver.cpp b/Samples/PeerConnection/DataChannelObserver.cpp new file mode 100644 index 0000000..fde3c5e --- /dev/null +++ b/Samples/PeerConnection/DataChannelObserver.cpp @@ -0,0 +1,31 @@ +// +// Created by ax on 25/09/16. +// + +#include "DataChannelObserver.h" +#include "UnixConsole.h" + + +DataChannelObserver::DataChannelObserver( + webrtc::DataChannelInterface *dataChannel): _dataChannel(dataChannel) { +} + +void DataChannelObserver::OnStateChange() { + Console::Print("[DataChannelObserver::OnStateChange] %s", + webrtc::DataChannelInterface::DataStateString(_dataChannel->state())); +} + +void DataChannelObserver::OnMessage(const webrtc::DataBuffer& buffer) { + size_t len = buffer.size(); + const unsigned char *data = buffer.data.cdata(); + char *message = new char[len + 1]; + + memcpy(message, data, len); + message[len] = '\0'; + + Console::Print(" %s", message); + delete[] message; +} + +void DataChannelObserver::OnBufferedAmountChange(uint64_t previous_amount) { +} diff --git a/Samples/PeerConnection/DataChannelObserver.h b/Samples/PeerConnection/DataChannelObserver.h new file mode 100644 index 0000000..607b116 --- /dev/null +++ b/Samples/PeerConnection/DataChannelObserver.h @@ -0,0 +1,22 @@ +// +// Created by ax on 25/09/16. +// + +#ifndef LIBWEBRTC_DATACHANNELOBSERVER_H +#define LIBWEBRTC_DATACHANNELOBSERVER_H + +#include + +class DataChannelObserver: public webrtc::DataChannelObserver { +public: + DataChannelObserver(webrtc::DataChannelInterface *dataChannel); + + void OnStateChange(); + void OnMessage(const webrtc::DataBuffer& buffer); + void OnBufferedAmountChange(uint64_t previous_amount); + +private: + webrtc::DataChannelInterface *_dataChannel; +}; + +#endif //LIBWEBRTC_DATACHANNELOBSERVER_H diff --git a/Samples/PeerConnection/Peer.cpp b/Samples/PeerConnection/Peer.cpp new file mode 100644 index 0000000..7845ce1 --- /dev/null +++ b/Samples/PeerConnection/Peer.cpp @@ -0,0 +1,186 @@ +// +// Created by ax on 24/09/16. +// + +#include +#include +#include "Peer.h" +#include "Core.h" +#include "UnixConsole.h" +#include "DataChannelObserver.h" + +using webrtc::PeerConnectionInterface; +using webrtc::MediaConstraintsInterface; +using webrtc::DataChannelInit; +using webrtc::MediaStreamInterface; +using webrtc::DataChannelInterface; +using webrtc::IceCandidateInterface; +using webrtc::SessionDescriptionInterface; + +Peer::Peer() { + PeerConnectionInterface::RTCConfiguration config; + PeerConnectionInterface::IceServer googleIceServer; + + googleIceServer.uri = "stun:stun.l.google.com:19302"; + googleIceServer.urls.push_back("stun:stun.l.google.com:19302"); + googleIceServer.urls.push_back("stun:stun1.l.google.com:19302"); + googleIceServer.urls.push_back("stun:stun2.l.google.com:19302"); + googleIceServer.urls.push_back("stun:stun3.l.google.com:19302"); + googleIceServer.urls.push_back("stun:stun4.l.google.com:19302"); + + config.servers.push_back(googleIceServer); + + _dataChannel = NULL; + _dataChannelObserver = NULL; + _peerConnection = Core::GetPeerConnectionFactory()-> + CreatePeerConnection(config, &_mediaConstraints, + NULL, NULL, this); + + _mediaConstraints.AddOptional( + MediaConstraintsInterface::kEnableDtlsSrtp, + MediaConstraintsInterface::kValueTrue); + + _mediaConstraints.AddMandatory( + MediaConstraintsInterface::kOfferToReceiveAudio, + MediaConstraintsInterface::kValueFalse); + + _mediaConstraints.AddMandatory( + MediaConstraintsInterface::kOfferToReceiveVideo, + MediaConstraintsInterface::kValueFalse); +} + +Peer::~Peer() { + if (_dataChannel) { + _dataChannel->Close(); + _dataChannel = NULL; + } + + _peerConnection->Close(); + _peerConnection = NULL; + + if (_dataChannelObserver) { + _dataChannelObserver = NULL; + } +} + +void Peer::CreateOffer(webrtc::CreateSessionDescriptionObserver *createSDPObserver) { + DataChannelInit init; + + init.reliable = true; + _dataChannel = _peerConnection->CreateDataChannel("MyDataChannel", &init); + _dataChannelObserver = new DataChannelObserver(_dataChannel); + _dataChannel->RegisterObserver(_dataChannelObserver); + + _peerConnection->CreateOffer(createSDPObserver, &_mediaConstraints); +} + +void Peer::CreateAnswer(webrtc::CreateSessionDescriptionObserver *createSDPObserver) { + _peerConnection->CreateAnswer(createSDPObserver, &_mediaConstraints); +} + +bool Peer::AddIceCandidate(webrtc::IceCandidateInterface *candidate) { + return _peerConnection->AddIceCandidate(candidate); +} + +void Peer::SetLocalSessionDescription(SessionDescriptionInterface* desc, + webrtc::SetSessionDescriptionObserver *obs) { + _peerConnection->SetLocalDescription(obs, desc); +} + +void Peer::SetRemoteSessionDescription(SessionDescriptionInterface* desc, + webrtc::SetSessionDescriptionObserver *obs) { + _peerConnection->SetRemoteDescription(obs, desc); +} + +bool Peer::IsConnected() { + if (!_dataChannel) { + return false; + } + + return _dataChannel->state() == webrtc::DataChannelInterface::kOpen; +} + +void Peer::SendMessage(const std::string& message) { + webrtc::DataBuffer buffer(message); + + _dataChannel->Send(buffer); +} + +/* + * webrtc::PeerConnectionObserver methods + */ +void Peer::OnSignalingChange( + PeerConnectionInterface::SignalingState new_state) { + Console::Print("[Peer::OnSignalingChange] new signaling state: %d", + new_state); +} + +void Peer::OnAddStream(rtc::scoped_refptr stream) { + Console::Print("[Peer::OnAddStream]"); +} + +void Peer::OnRemoveStream(rtc::scoped_refptr stream) { + Console::Print("[Peer::OnRemoveStream]"); +} + +void Peer::OnDataChannel( + rtc::scoped_refptr data_channel) { + Console::Print("[Peer::OnDataChannel] %s", data_channel->label().c_str()); + _dataChannel = data_channel; + + _dataChannelObserver = new DataChannelObserver(_dataChannel); + _dataChannel->RegisterObserver(_dataChannelObserver); +} + +void Peer::OnRenegotiationNeeded() { +} + +void Peer::OnIceConnectionChange( + PeerConnectionInterface::IceConnectionState new_state) { + if (new_state == PeerConnectionInterface::kIceConnectionCompleted) { + Console::Print("Connected!"); + } else if (new_state > PeerConnectionInterface::kIceConnectionCompleted) { + Console::Print("Disconnected."); + } +} + +void Peer::OnIceGatheringChange( + PeerConnectionInterface::IceGatheringState new_state) { + if (new_state == PeerConnectionInterface::kIceGatheringGathering) { + Console::Print("Gathering ICE candidates, please wait."); + return; + } + + if (new_state != PeerConnectionInterface::kIceGatheringComplete) { + return; + } + + Json::FastWriter writer; + writer.write(_iceCandidates); + + Console::Print("Done, paste this array of ICE candidates once requested." \ + "\n\n%s", writer.write(_iceCandidates).c_str()); +} + +void Peer::OnIceCandidate(const IceCandidateInterface* candidate) { + Json::Value jmessage; + + jmessage["sdpMid"] = candidate->sdp_mid(); + jmessage["sdpMLineIndex"] = candidate->sdp_mline_index(); + std::string sdp; + if (!candidate->ToString(&sdp)) { + Console::Print("[Peer::OnIceCandidate] Failed to serialize candidate"); + return; + } + jmessage["candidate"] = sdp; + _iceCandidates.append(jmessage); +} + +void Peer::OnIceCandidatesRemoved( + const std::vector& candidates) { + Console::Print("[Peer::OnIceCandidatesRemoved]"); + +} + +void Peer::OnIceConnectionReceivingChange(bool receiving) { +} \ No newline at end of file diff --git a/Samples/PeerConnection/Peer.h b/Samples/PeerConnection/Peer.h new file mode 100644 index 0000000..73d96dd --- /dev/null +++ b/Samples/PeerConnection/Peer.h @@ -0,0 +1,62 @@ +// +// Created by ax on 24/09/16. +// + +#ifndef LIBWEBRTC_PEER_H +#define LIBWEBRTC_PEER_H + +#include +#include "webrtc/api/test/fakeconstraints.h" +#include "webrtc/api/peerconnectioninterface.h" + +class Peer : public webrtc::PeerConnectionObserver { +public: + Peer(); + ~Peer(); + + void CreateOffer(webrtc::CreateSessionDescriptionObserver *createSDPObserver); + void CreateAnswer(webrtc::CreateSessionDescriptionObserver *createSDPObserver); + + bool AddIceCandidate(webrtc::IceCandidateInterface *candidate); + + void SetLocalSessionDescription(webrtc::SessionDescriptionInterface* desc, + webrtc::SetSessionDescriptionObserver *setSDPObserver); + void SetRemoteSessionDescription(webrtc::SessionDescriptionInterface* desc, + webrtc::SetSessionDescriptionObserver *setSDPObserver); + + bool IsConnected(); + void SendMessage(const std::string& message); + + /* + * webrtc::PeerConnectionObserver methods + */ + void OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state); + + void OnAddStream(rtc::scoped_refptr stream); + void OnRemoveStream(rtc::scoped_refptr stream); + + void OnDataChannel( + rtc::scoped_refptr data_channel); + + void OnRenegotiationNeeded(); + + void OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state); + void OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state); + + void OnIceCandidate(const webrtc::IceCandidateInterface* candidate); + void OnIceCandidatesRemoved( + const std::vector& candidates); + void OnIceConnectionReceivingChange(bool receiving); + +private: + rtc::scoped_refptr _peerConnection; + webrtc::FakeConstraints _mediaConstraints; + rtc::scoped_refptr _dataChannel; + webrtc::DataChannelObserver *_dataChannelObserver; + Json::Value _iceCandidates; +}; + +#endif //LIBWEBRTC_PEER_H diff --git a/Samples/PeerConnection/PeerConnectionObserver.cpp b/Samples/PeerConnection/PeerConnectionObserver.cpp new file mode 100644 index 0000000..c8b13de --- /dev/null +++ b/Samples/PeerConnection/PeerConnectionObserver.cpp @@ -0,0 +1,5 @@ +// +// Created by ax on 25/09/16. +// + +#include "PeerConnectionObserver.h" diff --git a/Samples/PeerConnection/PeerConnectionObserver.h b/Samples/PeerConnection/PeerConnectionObserver.h new file mode 100644 index 0000000..8f9d6b4 --- /dev/null +++ b/Samples/PeerConnection/PeerConnectionObserver.h @@ -0,0 +1,14 @@ +// +// Created by ax on 25/09/16. +// + +#ifndef LIBWEBRTC_PEERCONNECTIONOBSERVER_H +#define LIBWEBRTC_PEERCONNECTIONOBSERVER_H + + +class PeerConnectionObserver { + +}; + + +#endif //LIBWEBRTC_PEERCONNECTIONOBSERVER_H diff --git a/Samples/PeerConnection/SetLocalSessionDescriptionObserver.cpp b/Samples/PeerConnection/SetLocalSessionDescriptionObserver.cpp new file mode 100644 index 0000000..46897bd --- /dev/null +++ b/Samples/PeerConnection/SetLocalSessionDescriptionObserver.cpp @@ -0,0 +1,30 @@ +// +// Created by ax on 25/09/16. +// + +#include +#include "SetLocalSessionDescriptionObserver.h" +#include "UnixConsole.h" + +SetLocalSessionDescriptionObserver::SetLocalSessionDescriptionObserver( + webrtc::SessionDescriptionInterface* desc): _desc(desc) { +} + +void SetLocalSessionDescriptionObserver::OnSuccess() { + std::string sdp; + + _desc->ToString(&sdp); + + Json::FastWriter writer; + Json::Value jmessage; + jmessage["type"] = _desc->type(); + jmessage["sdp"] = sdp; + + Console::Print("Here is the SDP, paste it to the remote client and paste " \ + "their answer here.\n\n%s", writer.write(jmessage).c_str()); +} + +void SetLocalSessionDescriptionObserver::OnFailure(const std::string& error) { + Console::Print("[SetLocalSessionDescriptionObserver::OnFailure] %s", + error.c_str()); +} \ No newline at end of file diff --git a/Samples/PeerConnection/SetLocalSessionDescriptionObserver.h b/Samples/PeerConnection/SetLocalSessionDescriptionObserver.h new file mode 100644 index 0000000..49a3cc2 --- /dev/null +++ b/Samples/PeerConnection/SetLocalSessionDescriptionObserver.h @@ -0,0 +1,27 @@ +// +// Created by ax on 25/09/16. +// + +#ifndef LIBWEBRTC_SETLOCALSESSIONDESCRIPTIONOBSERVER_H +#define LIBWEBRTC_SETLOCALSESSIONDESCRIPTIONOBSERVER_H + + +#include + +class SetLocalSessionDescriptionObserver: + public webrtc::SetSessionDescriptionObserver { +public: + SetLocalSessionDescriptionObserver(webrtc::SessionDescriptionInterface* desc); + + void OnSuccess(); + void OnFailure(const std::string& error); + +private: + webrtc::SessionDescriptionInterface* _desc; + +protected: + ~SetLocalSessionDescriptionObserver() {} +}; + + +#endif //LIBWEBRTC_SETLOCALSESSIONDESCRIPTIONOBSERVER_H diff --git a/Samples/PeerConnection/SetRemoteSessionDescriptionObserver.cpp b/Samples/PeerConnection/SetRemoteSessionDescriptionObserver.cpp new file mode 100644 index 0000000..9fd9715 --- /dev/null +++ b/Samples/PeerConnection/SetRemoteSessionDescriptionObserver.cpp @@ -0,0 +1,27 @@ +// +// Created by ax on 25/09/16. +// + +#include "SetRemoteSessionDescriptionObserver.h" +#include "CreateSessionObserver.h" +#include "UnixConsole.h" + + +SetRemoteSessionDescriptionObserver::SetRemoteSessionDescriptionObserver( + Peer *peer, webrtc::SessionDescriptionInterface* desc): + _peer(peer), _desc(desc) { +} + +void SetRemoteSessionDescriptionObserver::OnSuccess() { + if (_desc->type() == webrtc::SessionDescriptionInterface::kOffer) { + rtc::scoped_refptr createAnswerObserver = + new rtc::RefCountedObject(_peer); + + _peer->CreateAnswer(createAnswerObserver); + } +} + +void SetRemoteSessionDescriptionObserver::OnFailure(const std::string& error) { + Console::Print("[SetRemoteSessionDescriptionObserver::OnFailure] %s", + error.c_str()); +} diff --git a/Samples/PeerConnection/SetRemoteSessionDescriptionObserver.h b/Samples/PeerConnection/SetRemoteSessionDescriptionObserver.h new file mode 100644 index 0000000..3530030 --- /dev/null +++ b/Samples/PeerConnection/SetRemoteSessionDescriptionObserver.h @@ -0,0 +1,28 @@ +// +// Created by ax on 25/09/16. +// + +#ifndef LIBWEBRTC_SETREMOTESESSIONDESCRIPTIONOBSERVER_H +#define LIBWEBRTC_SETREMOTESESSIONDESCRIPTIONOBSERVER_H + +#include +#include "Peer.h" + +class SetRemoteSessionDescriptionObserver: + public webrtc::SetSessionDescriptionObserver { +public: + SetRemoteSessionDescriptionObserver(Peer *peer, webrtc::SessionDescriptionInterface* desc); + + void OnSuccess(); + void OnFailure(const std::string& error); + +private: + Peer *_peer; + webrtc::SessionDescriptionInterface* _desc; + +protected: + ~SetRemoteSessionDescriptionObserver() {} +}; + + +#endif //LIBWEBRTC_SETREMOTESESSIONDESCRIPTIONOBSERVER_H diff --git a/Samples/PeerConnection/UnixConsole.cpp b/Samples/PeerConnection/UnixConsole.cpp new file mode 100644 index 0000000..b49fa9e --- /dev/null +++ b/Samples/PeerConnection/UnixConsole.cpp @@ -0,0 +1,153 @@ +// +// Created by ax on 25/09/16. +// + +#include +#include +#include +#include +#include +#include +#include +#include +#include "UnixConsole.h" + +static int ttyErase; +static int ttyEof; +static struct termios ttyTc; + +static size_t cursor; +static std::string buffer; + +bool Console::Init() { + struct termios tc; + const char* term = getenv("TERM"); + + signal(SIGTTIN, SIG_IGN); + signal(SIGTTOU, SIG_IGN); + signal(SIGCONT, Console::Reset); + fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) | O_NONBLOCK ); + + if (!(isatty(STDIN_FILENO) && + !(term && (!strcmp(term, "raw") || !strcmp(term, "dumb"))))) { + std::cerr << "Input is not a tty." << std::endl; + return false; + } + + tcgetattr (STDIN_FILENO, &ttyTc); + ttyErase = ttyTc.c_cc[VERASE]; + ttyEof = ttyTc.c_cc[VEOF]; + + tc = ttyTc; + tc.c_lflag &= ~(ECHO | ICANON); + tc.c_iflag &= ~(ISTRIP | INPCK); + tc.c_cc[VMIN] = 1; + tc.c_cc[VTIME] = 0; + tcsetattr (STDIN_FILENO, TCSADRAIN, &tc); + + cursor = 0; + buffer.clear(); + return true; +} + +bool Console::Update(std::string &input) { + char key; + ssize_t avail = read(STDIN_FILENO, &key, 1); + + input.clear(); + if (avail == -1) { + return true; + } + + if (key == ttyEof) { + return false; + } + + if (((key == ttyErase) || (key == 127) || (key == 8)) && cursor > 0) + { + buffer.erase(--cursor, 1); + Console::Back(); + return true; + } + + if (key == '\n') { + input = buffer; + cursor = 0; + buffer.clear(); + + write(STDOUT_FILENO, &key, 1); + key = '>'; + write(STDOUT_FILENO, &key, 1); + key = ' '; + write(STDOUT_FILENO, &key, 1); + return true; + } + + if (key < ' ') { + return true; + } + + cursor++; + buffer.append(1, key); + write(STDOUT_FILENO, &key, 1); + return true; +} + +void Console::Cleanup() { + tcsetattr (STDIN_FILENO, TCSADRAIN, &ttyTc); + fcntl(STDIN_FILENO, F_SETFL, fcntl(STDIN_FILENO, F_GETFL, 0) & ~O_NONBLOCK); +} + +void Console::Reset(int num) { + Console::Init(); +} + +void Console::Show() { + char key; + + key = '>'; + write(STDOUT_FILENO, &key, 1); + key = ' '; + write(STDOUT_FILENO, &key, 1); + + + for (size_t i = 0; i < cursor; i++) { + key = buffer.at(i); + write(STDOUT_FILENO, &key, 1); + } +} + +void Console::Hide() { + for (int i = 0; i < cursor + 2; i++) { + Console::Back(); + } +} + +void Console::Print(const std::string &fmt, ...) { + va_list argptr; + char string[1024]; + + if (!fmt.length()) { + return; + } + + va_start(argptr, fmt); + vsnprintf(string, sizeof(string), fmt.c_str(), argptr); + va_end(argptr); + + Console::Hide(); + std::cout << string << std::endl; + Console::Show(); +} + +void Console::Back() +{ + char key; + + key = '\b'; + write(STDOUT_FILENO, &key, 1); + key = ' '; + write(STDOUT_FILENO, &key, 1); + key = '\b'; + write(STDOUT_FILENO, &key, 1); +} \ No newline at end of file diff --git a/Samples/PeerConnection/UnixConsole.h b/Samples/PeerConnection/UnixConsole.h new file mode 100644 index 0000000..ba2fb29 --- /dev/null +++ b/Samples/PeerConnection/UnixConsole.h @@ -0,0 +1,20 @@ +// +// Created by ax on 25/09/16. +// + +#ifndef LIBWEBRTC_UNIXCONSOLE_H +#define LIBWEBRTC_UNIXCONSOLE_H + +class Console { +public: + static bool Init(); + static bool Update(std::string &input); + static void Cleanup(); + static void Reset(int num); + static void Print(const std::string &line, ...); + static void Show(); + static void Hide(); + static void Back(); +}; + +#endif //LIBWEBRTC_UNIXCONSOLE_H diff --git a/Samples/PeerConnection/main.cpp b/Samples/PeerConnection/main.cpp new file mode 100644 index 0000000..357758a --- /dev/null +++ b/Samples/PeerConnection/main.cpp @@ -0,0 +1,140 @@ +#include +#include +#include "Core.h" +#include "CreateSessionObserver.h" +#include "DataChannelObserver.h" +#include "Peer.h" +#include "SetRemoteSessionDescriptionObserver.h" +#include "UnixConsole.h" + +enum { + STATE_EXCHANGE = 0, + STATE_CHAT +}; + +static Peer *peer = NULL; +static int state = STATE_EXCHANGE; + +void HandleSDP(Json::Value object) { + std::string type = object["type"].asString(); + std::string sdp = object["sdp"].asString(); + + webrtc::SdpParseError error; + webrtc::SessionDescriptionInterface* desc( + webrtc::CreateSessionDescription(type, sdp, &error)); + + if (!desc) { + Console::Print("Can't parse the SDP: %s", error.description); + return; + } + + rtc::scoped_refptr observer = + new rtc::RefCountedObject(peer, desc); + + peer->SetRemoteSessionDescription(desc, observer); +} + +void HandleICECandidate(Json::Value object) { + std::string sdp_mid = object["sdpMid"].asString(); + int sdp_mlineindex = object["sdpMLineIndex"].asInt(); + std::string sdp = object["candidate"].asString(); + + webrtc::SdpParseError error; + std::unique_ptr candidate( + webrtc::CreateIceCandidate(sdp_mid, sdp_mlineindex, sdp, &error)); + + if (!candidate.get()) { + Console::Print("Can't parse the ICE candidate: %s", error.description); + return; + } + + if (!peer->AddIceCandidate(candidate.get())) { + Console::Print("Failed to add the ICE candidate."); + return; + } +} + +void HandleObject(Json::Value object) { + if (object.isMember("type") && object["type"].isString() && + object.isMember("sdp") && object["sdp"].isString()) { + HandleSDP(object); + return; + } + + if (object.isMember("candidate") && object["candidate"].isString() && + object.isMember("sdpMLineIndex") && object["sdpMLineIndex"].isNumeric() && + object.isMember("sdpMid") && object["sdpMid"].isString()) { + HandleICECandidate(object); + return; + } + + Console::Print("Unknown object."); +} + +void HandleCommand(const std::string& input) { + Json::Reader reader; + Json::Value jmessage; + + if (peer->IsConnected()) { + peer->SendMessage(input); + return; + } + + if (!reader.parse(input, jmessage)) { + Console::Print("Invalid JSON string."); + return; + } + + if (jmessage.isArray()) { + for (Json::ValueIterator it = jmessage.begin(); + it != jmessage.end(); it++) { + if (!(*it).isObject()) { + continue; + } + + HandleObject(*it); + } + return; + } + + if (jmessage.isObject()) { + HandleObject(jmessage); + return; + } + + Console::Print("Must be an array or object."); +} + +int main(int argc, char **argv) { + std::string input; + + if (!Console::Init()) { + return EXIT_FAILURE; + } + + Core::Init(); + + peer = new Peer(); + + if (argc == 1) { + rtc::scoped_refptr createOfferObserver = + new rtc::RefCountedObject(peer); + + peer->CreateOffer(createOfferObserver); + } else { + Console::Print("Recipient mode. Paste the offer made by the emitter.\n"); + } + + while (Console::Update(input)) { + if (input.length()) { + HandleCommand(input); + } + Core::Update(); + } + + delete peer; + + Core::Cleanup(); + Console::Cleanup(); + return EXIT_SUCCESS; +} \ No newline at end of file