Last active
May 15, 2017 06:05
-
-
Save pstjuste/5855703 to your computer and use it in GitHub Desktop.
WebRTC session creation over command line
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <unistd.h> | |
#include <stdio.h> | |
#include <string.h> | |
#include <stdlib.h> | |
#include <fcntl.h> | |
#include <errno.h> | |
#include <sys/un.h> | |
#include <sys/ioctl.h> | |
#include <sys/types.h> | |
#include <sys/socket.h> | |
#include <linux/if.h> | |
#include <linux/if_tun.h> | |
#include <arpa/inet.h> | |
#include <pthread.h> | |
#include <semaphore.h> | |
#include <bluetooth/bluetooth.h> | |
#include <bluetooth/l2cap.h> | |
#undef max | |
#define max(x,y) ((x) > (y) ? (x) : (y)) | |
#define MTU 1500 | |
#define MAX_CON 10 | |
static int | |
udp_listen(char *udp_addr, uint16_t port) | |
{ | |
int sock, optval; | |
struct sockaddr_in addr; | |
char buf[MTU]; | |
ssize_t rcount, wcount; | |
sock = socket(PF_INET, SOCK_DGRAM, 0); | |
if (sock < 1) { | |
fprintf(stderr, "socket failed\n"); | |
return -1; | |
} | |
optval = 1; | |
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); | |
socklen_t addr_len = sizeof(addr); | |
memset(&addr, 0, addr_len); | |
addr.sin_family = AF_INET; | |
addr.sin_port = htons(port); | |
addr.sin_addr.s_addr = INADDR_ANY; | |
if (bind(sock, (struct sockaddr*) &addr, addr_len) < 0) { | |
fprintf(stderr, "bind failed\n"); | |
close(sock); | |
return -1; | |
} | |
while (1) { | |
rcount = recvfrom(sock, buf, MTU, 0, (struct sockaddr*) &addr, | |
&addr_len); | |
if (connect(sock, (struct sockaddr *)&addr, addr_len) < 0) { | |
fprintf(stderr, "upd connect failed\n"); | |
} | |
} | |
return 0; | |
} | |
static int | |
bt_create_con(const char *bt_addr, const uint16_t psm) | |
{ | |
int sock; | |
struct sockaddr_l2 addr; | |
sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); | |
if (sock < 0) { | |
fprintf(stderr, "open l2cap failed\n"); | |
return -1; | |
} | |
memset(&addr, 0, sizeof(addr)); | |
addr.l2_family = AF_BLUETOOTH; | |
str2ba(bt_addr, &addr.l2_bdaddr); | |
addr.l2_psm = htobs(psm); | |
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0 && | |
errno != EINTR) { | |
fprintf(stderr, "connect l2cap failed %s\n", strerror(errno)); | |
close(sock); | |
return -1; | |
} | |
return sock; | |
} | |
static int | |
bt_listen(const char *bt_addr, const uint16_t psm) | |
{ | |
int sock, cli_sock; | |
struct sockaddr_l2 addr; | |
struct l2cap_options opts; | |
socklen_t optlen; | |
int rcount; | |
char buf[MTU]; | |
fd_set rdfs; | |
int sock_fds[MAX_CON] = {-1}; | |
int nfds; | |
sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP); | |
if (sock < 0) { | |
fprintf(stderr, "open l2cap failed\n"); | |
close(sock); | |
return -1; | |
} | |
memset(&addr, 0, sizeof(addr)); | |
addr.l2_family = AF_BLUETOOTH; | |
addr.l2_bdaddr = *BDADDR_ANY; | |
addr.l2_psm = htobs(psm); | |
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { | |
fprintf(stderr, "bind rcv l2cap failed %s\n", strerror(errno)); | |
close(sock); | |
return -1; | |
} | |
memset(&opts, 0, sizeof(opts)); | |
optlen = sizeof(opts); | |
if (getsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, &optlen) < 0) { | |
fprintf(stderr, "getsockopt l2cap failed\n"); | |
close(sock); | |
return -1; | |
} | |
opts.imtu = opts.omtu = MTU; | |
if (setsockopt(sock, SOL_L2CAP, L2CAP_OPTIONS, &opts, sizeof(opts)) < 0) { | |
fprintf(stderr, "getsockopt l2cap failed\n"); | |
close(sock); | |
return -1; | |
} | |
if (listen(sock, MAX_CON) < 0) { | |
fprintf(stderr, "listen l2cap failed\n"); | |
return -1; | |
} | |
FD_ZERO(&rdfs); | |
FD_SET(sock, &rdfs); | |
nfds = sock; | |
while (1) { | |
fprintf(stderr, "waiting on select ...\n"); | |
if (select(nfds + 1, &rdfs, NULL, NULL, NULL) < 0) { | |
fprintf(stderr, "select error\n"); | |
break; | |
} | |
if (FD_ISSET(sock, &rdfs)) { | |
memset(&addr, 0, sizeof(addr)); | |
cli_sock = accept(sock, (struct sockaddr *)&addr, &optlen); | |
if (cli_sock < 0) { | |
fprintf(stderr, "accept l2cap failed\n"); | |
break; | |
} | |
int i; | |
for (i = 0; i < MAX_CON; i++) { | |
if (sock_fds[i] == -1) { | |
sock_fds[i] = cli_sock; | |
FD_SET(cli_sock, &rdfs); | |
nfds = max(nfds, cli_sock); | |
break; | |
} | |
} | |
} | |
int i; | |
for (i = 0; i < MAX_CON; i++) { | |
if (sock_fds[i] != -1 && FD_ISSET(sock_fds[i], &rdfs)) { | |
rcount = read(sock_fds[i], buf, sizeof(buf)); | |
if (rcount > 0) { | |
printf("rcv packet of size %d\n", rcount); | |
} | |
else { | |
fprintf(stderr, "error on fd\n"); | |
close(sock_fds[i]); | |
sock_fds[i] = -1; | |
} | |
} | |
} | |
} | |
close(sock); | |
return 0; | |
} | |
int main(int argc, char *argv[]) | |
{ | |
if (argc < 2) { | |
bt_listen(NULL, 0x1001); | |
} | |
else { | |
int sock; | |
sock = bt_create_con(argv[1], 0x1001); | |
char *line = NULL; | |
size_t len = 0; | |
ssize_t read = 0; | |
while ((read = getline(&line, &len, stdin)) != -1) { | |
send(sock, line, read, 0); | |
} | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <sys/socket.h> | |
#include <arpa/inet.h> | |
#include <string.h> | |
#include <unistd.h> | |
#include <stdlib.h> | |
#define MAXBUF 1024 | |
static int | |
udp_reflect(uint16_t port) | |
{ | |
int sock, optval, n; | |
struct sockaddr_in s_addr, c_addr; | |
socklen_t len; | |
char buf[MAXBUF]; | |
sock = socket(AF_INET, SOCK_DGRAM, 0); | |
if (sock < 1) { | |
fprintf(stderr, "socket failed\n"); | |
return -1; | |
} | |
optval = 1; | |
setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval)); | |
len = sizeof(s_addr); | |
memset(&s_addr, 0, len); | |
s_addr.sin_family = AF_INET; | |
s_addr.sin_port = htons(port); | |
s_addr.sin_addr.s_addr = INADDR_ANY; | |
if (bind(sock, (struct sockaddr*) &s_addr, len) < 0) { | |
fprintf(stderr, "bind failed\n"); | |
close(sock); | |
return -1; | |
} | |
while (1) { | |
len = sizeof(c_addr); | |
n = recvfrom(sock, buf, MAXBUF, 0, (struct sockaddr*) &c_addr, &len); | |
if (n < 0) { | |
fprintf(stderr, "read failed\n"); | |
return -1; | |
} | |
n = sprintf(buf, "%s:%d", inet_ntoa(c_addr.sin_addr), | |
ntohs(c_addr.sin_port)); | |
if (n < 0) { | |
fprintf(stderr, "sprintf error\n"); | |
} | |
sendto(sock, buf, n, 0, (struct sockaddr*) &c_addr, len); | |
fprintf(stdout, "%s\n", buf); | |
} | |
} | |
int main(int argc, char *argv[]) | |
{ | |
uint16_t port = atoi(argv[1]); | |
udp_reflect(port); | |
return 0; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright 2013, Pierre St Juste | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the following disclaimer. | |
* 2. Redistributions in binary form must reproduce the above copyright notice, | |
* this list of conditions and the following disclaimer in the documentation | |
* and/or other materials provided with the distribution. | |
* 3. The name of the author may not be used to endorse or promote products | |
* derived from this software without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED | |
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO | |
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, | |
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; | |
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, | |
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR | |
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF | |
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
*/ | |
#include <iostream> | |
#include <string> | |
#include "talk/app/webrtc/peerconnectioninterface.h" | |
#include "talk/app/webrtc/jsep.h" | |
#include "talk/app/webrtc/datachannelinterface.h" | |
#include "talk/app/webrtc/test/fakeconstraints.h" | |
#include "talk/base/json.h" | |
#include "talk/base/logging.h" | |
class WebRtcConnectionManager | |
: public webrtc::PeerConnectionObserver, | |
public webrtc::CreateSessionDescriptionObserver { | |
public: | |
WebRtcConnectionManager(); | |
bool InitConnection(); | |
void CreateOffer(); | |
void OnOfferRequest(webrtc::SessionDescriptionInterface* desc); | |
void OnOfferReply(webrtc::SessionDescriptionInterface* desc); | |
static webrtc::SessionDescriptionInterface* StringToDescription( | |
const std::string string_desc); | |
static std::string DescriptionToString( | |
const webrtc::SessionDescriptionInterface* desc); | |
virtual const webrtc::SessionDescriptionInterface* | |
local_description() const { | |
return peer_connection_->local_description(); | |
} | |
protected: | |
//~WebRtcConnectionManager(); | |
// inherited from PeerConnectionObserver | |
virtual void OnError(); | |
virtual void OnStateChange( | |
webrtc::PeerConnectionObserver::StateType state_changed); | |
virtual void OnAddStream(webrtc::MediaStreamInterface* stream); | |
virtual void OnRemoveStream(webrtc::MediaStreamInterface* stream); | |
virtual void OnIceCandidate(const webrtc::IceCandidateInterface* candidate); | |
// inherited from CreateSessionDescriptionObserver | |
virtual void OnSuccess(webrtc::SessionDescriptionInterface* desc); | |
virtual void OnFailure(const std::string &error); | |
virtual int AddRef() { return 1; } | |
virtual int Release() { return 0; } | |
private: | |
talk_base::scoped_refptr<webrtc::PeerConnectionFactoryInterface> | |
peer_connection_factory_; | |
webrtc::PeerConnectionInterface::IceServers servers_; | |
webrtc::PeerConnectionInterface::IceServer server_; | |
webrtc::FakeConstraints constraints_; | |
talk_base::scoped_refptr<webrtc::PeerConnectionInterface> peer_connection_; | |
talk_base::scoped_refptr<webrtc::AudioTrackInterface> audio_track_; | |
talk_base::scoped_refptr<webrtc::MediaStreamInterface> stream_; | |
}; | |
const char kStunServerUri[] = "stun:stun.l.google.com:19302"; | |
const char kAudioLabel[] = "audio_label"; | |
const char kStreamLabel[] = "stream_label"; | |
class DummySetSessionDescriptionObserver | |
: public webrtc::SetSessionDescriptionObserver { | |
public: | |
static DummySetSessionDescriptionObserver* Create() { | |
return | |
new talk_base::RefCountedObject<DummySetSessionDescriptionObserver>(); | |
} | |
virtual void OnSuccess() { | |
LOG(INFO) << __FUNCTION__; | |
} | |
virtual void OnFailure(const std::string& error) { | |
LOG(INFO) << __FUNCTION__ << " " << error; | |
} | |
protected: | |
DummySetSessionDescriptionObserver() {} | |
~DummySetSessionDescriptionObserver() {} | |
}; | |
WebRtcConnectionManager::WebRtcConnectionManager() { | |
peer_connection_factory_ = webrtc::CreatePeerConnectionFactory(); | |
server_.uri = kStunServerUri; | |
servers_.push_back(server_); | |
constraints_.SetMandatoryReceiveAudio(false); | |
constraints_.SetMandatoryReceiveVideo(false); | |
constraints_.SetAllowRtpDataChannels(); | |
} | |
bool WebRtcConnectionManager::InitConnection() { | |
peer_connection_ = peer_connection_factory_->CreatePeerConnection(servers_, | |
&constraints_, this); | |
audio_track_ = peer_connection_factory_->CreateAudioTrack(kAudioLabel, | |
NULL); | |
stream_ = peer_connection_factory_->CreateLocalMediaStream(kStreamLabel); | |
stream_->AddTrack(audio_track_); | |
peer_connection_->AddStream(stream_, &constraints_); | |
return true; | |
} | |
void WebRtcConnectionManager::CreateOffer() { | |
peer_connection_->CreateOffer(this, &constraints_); | |
} | |
void WebRtcConnectionManager::OnOfferRequest( | |
webrtc::SessionDescriptionInterface* desc) { | |
peer_connection_->SetRemoteDescription( | |
DummySetSessionDescriptionObserver::Create(), desc); | |
peer_connection_->CreateAnswer(this, &constraints_); | |
} | |
void WebRtcConnectionManager::OnOfferReply( | |
webrtc::SessionDescriptionInterface* desc) { | |
peer_connection_->SetRemoteDescription( | |
DummySetSessionDescriptionObserver::Create(), desc); | |
} | |
void WebRtcConnectionManager::OnError() { | |
std::cout << "PEERCONNECTION ERROR" << std::endl; | |
} | |
void WebRtcConnectionManager::OnStateChange( | |
webrtc::PeerConnectionObserver::StateType state_changed) { | |
} | |
void WebRtcConnectionManager::OnAddStream( | |
webrtc::MediaStreamInterface* stream) { | |
} | |
void WebRtcConnectionManager::OnRemoveStream( | |
webrtc::MediaStreamInterface* stream) { | |
} | |
void WebRtcConnectionManager::OnIceCandidate( | |
const webrtc::IceCandidateInterface* candidate) { | |
std::cout << "ICE ICE baby" << std::endl; | |
} | |
void WebRtcConnectionManager::OnSuccess( | |
webrtc::SessionDescriptionInterface* desc) { | |
std::cout << "SETTING LOCAL" << std::endl; | |
peer_connection_->SetLocalDescription( | |
DummySetSessionDescriptionObserver::Create(), desc); | |
} | |
void WebRtcConnectionManager::OnFailure(const std::string &error) { | |
std::cout << error << std::endl; | |
} | |
webrtc::SessionDescriptionInterface* | |
WebRtcConnectionManager::StringToDescription(const std::string string_desc) { | |
Json::Reader reader; | |
Json::Value jdesc; | |
if (!reader.parse(string_desc, jdesc)) { | |
LOG(WARNING) << "Unknown string desc " << string_desc; | |
return NULL; | |
} | |
// replace with global constants | |
std::string type = jdesc["type"].asString(); | |
std::string sdp = jdesc["sdp"].asString(); | |
return webrtc::CreateSessionDescription(type, sdp); | |
} | |
std::string WebRtcConnectionManager::DescriptionToString( | |
const webrtc::SessionDescriptionInterface* desc) { | |
Json::FastWriter writer; | |
Json::Value jdesc; | |
jdesc["type"] = desc->type(); | |
std::string sdp; | |
desc->ToString(&sdp); | |
jdesc["sdp"] = sdp; | |
return writer.write(jdesc); | |
} | |
int main(int argc, char **argv) { | |
WebRtcConnectionManager manager; | |
while (1) { | |
std::string input; | |
std::cout << "Enter command:" << std::endl; | |
std::cin >> input; | |
if (input.compare("init") == 0) { | |
manager.InitConnection(); | |
} | |
else if (input.compare("offer") == 0) { | |
manager.CreateOffer(); | |
} | |
else if (input.compare("print") == 0) { | |
std::string string_desc = manager.DescriptionToString( | |
manager.local_description()); | |
std::cout << string_desc << std::endl; | |
} | |
else if (input.compare("request") == 0) { | |
std::string string_desc; | |
std::cout << "Enter request:" << std::endl; | |
std::getline(std::cin, string_desc); | |
std::getline(std::cin, string_desc); | |
webrtc::SessionDescriptionInterface* desc = | |
manager.StringToDescription(string_desc); | |
manager.OnOfferRequest(desc); | |
} | |
else if (input.compare("reply") == 0) { | |
std::string string_desc; | |
std::cout << "Enter reply:" << std::endl; | |
std::getline(std::cin, string_desc); | |
std::getline(std::cin, string_desc); | |
webrtc::SessionDescriptionInterface* desc = | |
manager.StringToDescription(string_desc); | |
manager.OnOfferReply(desc); | |
} | |
} | |
return 0; | |
} | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment