-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Gene Hallman
committed
Nov 26, 2014
1 parent
aefee13
commit aacb030
Showing
14 changed files
with
1,266 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
*.pyc |
This file contains 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
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. | ||
|
This file contains 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
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() | ||
|
This file contains 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
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> |
This file contains 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
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> |
Oops, something went wrong.