Skip to content

Instantly share code, notes, and snippets.

@pstjuste
Last active May 15, 2017 06:05
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save pstjuste/5855703 to your computer and use it in GitHub Desktop.
Save pstjuste/5855703 to your computer and use it in GitHub Desktop.
WebRTC session creation over command line
#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);
}
}
}
#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;
}
/*
* 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