Skip to content

Commit

Permalink
Initial code commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Gene Hallman committed Nov 26, 2014
1 parent aefee13 commit aacb030
Show file tree
Hide file tree
Showing 14 changed files with 1,266 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.pyc
66 changes: 66 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
# Pykurento

Pykurento is a [Kurento](http://www.kurento.org/docs/5.0.3/what_is_kurento.html) client written in python and uses the [websocket-client](https://github.com/liris/websocket-client) library for its transport layer.

## Installing

```
pip install git+https://github.com/minervaproject/pykurento.git#egg=pykurento
```

## Usage

Here's a simple example of a loopback pipeline created in a tornado request handler.

```python
from pykurento import KurentoClient

kurento = KurentoClient("ws://localhost:8888/kurento")

class LoopbackHandler(tornado.web.RequestHandler):
def get(self):
with open("loopback.html","r") as f:
self.finish(f.read())

def post(self):
sdp_offer = self.request.body
pipeline = kurento.createPipeline()
wrtc_pub = pipeline.createWebRtcEndpoint()
sdp_answer = wrtc_pub.processOffer(sdp_offer)
wrtc_pub.connect(wrtc_pub)
self.finish(str(sdp_answer))
```

[Source for loopback.html](https://github.com/minervaproject/pykurento/blob/master/example/loopback.html)


## Developing

### Source and deps

```
git clone https://github.com/minervaproject/pykurento
cd ./pykurento
pip install -r requirements.txt
```

### Running the example

```
cd ./example
./app.py
```

There are a couple of assumptions the example makes.
* You want to run on port 8080 - its hardcoded in there at the moment
* Your KMS address is localhost:8888 - again, hardcoded

For making the 2nd bullet work, the easiest way during development is to setup an ssh tunnel to your media server.

```
ssh -nNT -i <identity file> -L 8888:localhost:8888 <user>@<server address>
```

## License
As with Kurento, this client is released under the terms of [LGPL version 2.1](http://www.gnu.org/licenses/lgpl-2.1.html) license.

139 changes: 139 additions & 0 deletions example/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
#!/usr/bin/env python

import uuid
import os
import sys
import logging

sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
logging.getLogger().setLevel(logging.DEBUG)

import tornado.ioloop
import tornado.web

from pykurento import KurentoClient

kurento = KurentoClient("ws://localhost:8888/kurento")


class Participant:
def __init__(self, room, offer):
self.participant_id = str(uuid.uuid4())
self.room = room
self.offer = offer
self.incoming = self.room.pipeline.createWebRtcEndpoint()
self.outgoings = {}
self.answer = None

def get_answer(self):
if not self.answer:
self.answer = self.incoming.processOffer(self.offer)

return self.answer

def connect(self, participant, offer):
if participant.participant_id not in self.outgoings:
self.outgoings[participant.participant_id] = self.room.pipeline.createWebRtcEndpoint()
self.incoming.connect(self.outgoings[participant.participant_id])

outgoing = self.outgoings[participant.participant_id]
return outgoing.processOffer(offer)


class Room:
rooms = {}

@classmethod
def get(cls, room_id):
if room_id not in cls.rooms:
cls.rooms[room_id] = Room(room_id)
return cls.rooms[room_id]

def __init__(self, room_id):
self.room_id = room_id
self.participants = {}
self.pipeline = kurento.createPipeline()

def addParticipant(self, participant):
self.participants[participant.participant_id] = participant
return participant

def getParticipant(self, participant_id):
return self.participants[participant_id] if participant_id in self.participants else None


class RoomHandler(tornado.web.RequestHandler):
def get(self, room_id=None):
room = Room.get(room_id)
self.finish({"participants": [k for k in room.participants]})

def post(self, room_id):
room = Room.get(room_id)
sdp_offer = self.request.body
participant = room.addParticipant(Participant(room, sdp_offer))
sdp_answer = participant.get_answer()

self.finish({
"participant_id": participant.participant_id,
"answer": sdp_answer
})


class SubscribeToParticipantHandler(tornado.web.RequestHandler):
def post(self, room_id, from_participant_id, to_participant_id):
room = Room.get(room_id)
sdp_offer = self.request.body
from_participant = room.getParticipant(from_participant_id)
to_participant = room.getParticipant(to_participant_id)

if from_participant and to_participant:
sdp_answer = from_participant.connect(to_participant, sdp_offer)
self.finish({ "answer": sdp_answer })
return
else:
self.set_status(409)
self.finish({ "error": sdp_answer })


class IndexHandler(tornado.web.RequestHandler):
def get(self):
with open("index.html","r") as f:
self.finish(f.read())


class RoomIndexHandler(tornado.web.RequestHandler):
def get(self, room_id=None):
with open("room.html","r") as f:
self.finish(f.read())


class LoopbackHandler(tornado.web.RequestHandler):
def get(self):
with open("loopback.html","r") as f:
self.finish(f.read())

def post(self):
sdp_offer = self.request.body
pipeline = kurento.createPipeline()
wrtc_pub = pipeline.createWebRtcEndpoint()
sdp_answer = wrtc_pub.processOffer(sdp_offer)
wrtc_pub.connect(wrtc_pub)
self.finish(str(sdp_answer))


application = tornado.web.Application([
(r"/", IndexHandler),
(r"/loopback", LoopbackHandler),
(r"/room", RoomIndexHandler),
(r"/room/(?P<room_id>\d*)", RoomHandler),
(r"/room/(?P<room_id>[^/]*)/subscribe/(?P<from_participant_id>[^/]*)/(?P<to_participant_id>[^/]*)", SubscribeToParticipantHandler),
(r'/static/(.*)', tornado.web.StaticFileHandler, {'path': os.path.join(os.path.dirname(__file__), "static")}),
#(r"/join/{room_id}", JoinRoomHandler),
], debug=True) #, autoreload=False)

if __name__ == "__main__":
application.listen(8080)
print "Webserver now listening on port 8080"
tornado.ioloop.IOLoop.instance().start()
kurento.close()

21 changes: 21 additions & 0 deletions example/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="expires" content="0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Pykurento Examples</title>
</head>


<body>
<h1>Pykurento Examples</h1>
<ul>
<li><a href="/room">Room</a></li>
<li><a href="/loopback">Loopback</a></li>
</ul>

</body>
</html>
87 changes: 87 additions & 0 deletions example/loopback.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="cache-control" content="no-cache">
<meta http-equiv="pragma" content="no-cache">
<meta http-equiv="expires" content="0">
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<script src="http://code.jquery.com/jquery-2.1.1.min.js"></script>
<script src="https://rawgit.com/GoogleChrome/webrtc/master/samples/web/js/adapter.js"></script>
<script src="/static/js/kurento-utils.js"></script>
<title>Pykurento Loopback</title>
</head>


<body>
<div class="container">
<div style="float:left;">
<h3>Local stream</h3>
<video id="videoInput" autoplay width="480px" height="360px"></video>
</div>
<div style="float:right;">
<h3>Remote stream</h3>
<video id="videoOutput" autoplay width="480px" height="360px" muted="muted"></video>
</div>
</div>
</div>


<script type="text/javascript">
var webRtcPeer;
var videoInput;
var videoOutput;

var mediaConstraints = {
audio: true,
video: {
mandatory: {
maxWidth: 640,
maxFrameRate: 60,
minFrameRate: 30
}
}
};

window.onload = function() {
videoInput = document.getElementById('videoInput');
videoOutput = document.getElementById('videoOutput');
console.log("Loading complete ...");
start();
}

function start() {
console.log("Starting video call ...");
webRtcPeer = kurentoUtils.WebRtcPeer.startSendRecv(videoInput, videoOutput, onOffer, console.error, mediaConstraints);
}

function stop() {
if (webRtcPeer) {
console.log("Stopping video call ...");
webRtcPeer.dispose();
webRtcPeer = null;
}
}

function onOffer(sdpOffer) {
console.info('Invoking SDP offer callback function ' + location.host);
$.ajax({
url: '/loopback',
type: 'POST',
dataType: 'text',
contentType: 'application/sdp',
data: sdpOffer,
success: function(sdpAnswer) {
console.log("Received sdpAnswer from server. Processing ...");
webRtcPeer.processSdpAnswer(sdpAnswer);
},
error: function(jqXHR, textStatus, error) {
console.error(error);
}
});
}

</script>

</body>
</html>
Loading

0 comments on commit aacb030

Please sign in to comment.