Skip to content

Commit

Permalink
Merge pull request ppettit#43 from SussexLearningSystems/40_gc2x_plugin
Browse files Browse the repository at this point in the history
add new plugin and documentation for a galicaster 2.x capture agent
  • Loading branch information
andiempettJISC authored Jul 31, 2017
2 parents 9aa8174 + 484abe9 commit 175c542
Show file tree
Hide file tree
Showing 6 changed files with 744 additions and 0 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,3 +71,7 @@ docker-compose stop
docker-compose build peakaboo
docker-compose up -d
```
Client Side
-----------

To send/recieve data from the peakaboo app you will need to use a client that uses the Distributed data protocol from Meteor. You could write your own, or check out the examples in the docs directory: [Peakaboo Plugins](docs/capture_agent_plugins/examples)
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
*.*~### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec

# Installer logs
pip-log.txt
pip-delete-this-directory.txt

# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*,cover
.hypothesis/

# Translations
*.mo
*.pot

# Django stuff:
*.log
local_settings.py

# Flask instance folder
instance/

# Scrapy stuff:
.scrapy

# Sphinx documentation
docs/_build/

# PyBuilder
target/

# IPython Notebook
.ipynb_checkpoints

# pyenv
.python-version

# celery beat schedule file
celerybeat-schedule

# dotenv
.env

# virtualenv
venv/
ENV/

# Spyder project settings
.spyderproject

# Rope project settings
.ropeproject

# Pycharm project settings
.idea
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
Peakaboo Plugins for Galicaster
===================================

Two plugins that enables two-way communication between An Opencast compatible capture agent running Galicaster and a Peakaboo instance. This plugin leverages the Distributed Data Protocol (DDP) to communicate with the Meteor framework. The Audiostream plugin sets up a small audio server on the capture agent using gstreamer.

Things to note
--------------
* See https://github.com/hharnisc/python-ddp on more information for developing using ddp
* When using SSL with peakaboo the websocket url must use 'wss' rather than 'ws'
* When using a self signed SSL certificate on peakaboo images will not be Posted
* This version of the peakaboo plugin does not have `audiofaders` due to compatibility issues with how sound cards have various `alsa` audio settings. Please look at the sussex plugin if you want to implement audio faders.
* Figuring out the alsa/pulse audio sources can be difficult depending on your audio hardware


# ddp.py
Loading
-------

To activate the plugin, add the line in the `plugins` section of your configuration file

[plugins]
ddp = True

True: Enables plugin.
False: Disables plugin.

Plugin Options
--------------

[ddp]
meteor = ws://localhost/websocket
room_name = local-room
cam_available = 0
cam_labels = local-camera-1,local-camera-2
user = [email protected]
password = galicaster
http_host = http://localhost
take_screenshot = True
hq_snapshot = False
existing_stream_host =
existing_stream_port =
existing_stream_key =
extra_params =
existing_screenshot =
token =


| Option | Type | Parameter | Description |
|----------------------|---------|---------------------------|--------------------------------------------------------------------------------------|
| meteor | string | [ws, wss]://uri/websocket | the meteor ddp websocket interface (wss when using SSL) |
| room_name | string | any | the room name |
| cam_available | integer | 0 | if cameras feeds are available in the room |
| cam_labels | string | list: string,string,... | list of camera names |
| user | string | any | username for sending image POST requests to peakaboo |
| password | string | any | password for sending image POST requests to peakaboo |
| http_host | string | [http, https]://uri | URL for sending image POST requests to peakaboo |
| take_screenshot | boolean | True, False | Have the plugin take the screenshot |
| hq_snapshot | boolean | True,False | Use a high quality screenshot |
| existing_stream_host | string | | The host for an existing audio stream service e.g. icecast server |
| existing_stream_port | integer | | The port for an existing audio stream service e.g. icecast server |
| existing_stream_key | string | | The URI for an existing audio stream service e.g. icecast server |
| extra_params | string | list:of;extra:parameters | Extra params you want to send to peakaboo that will make it into the database to use |
| existing_screenshot | boolean | | Path to an existing screenshot on the filesystem /path/to/screenshot.jpg |
| token | string | | The token that will be obtained automatically from peakaboo |

# audiostream.py
Loading
-------

To activate the plugin, add the line in the `plugins` section of your configuration file


[plugins]
audiostream = True

True: Enables plugin.
False: Disables plugin.

Plugin Options
--------------

[audiostream]
port = 31337
src = alsasrc
device =

| Option | Type | Parameter | Description |
|--------|---------|---------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| port | integer | 31337 | The port to set up the audio stream from. Can be any non-secure port |
| src | string | [alsasrc, pulsesrc] | The audio source to select from, alsasrc by default but can also use pulsesrc |
| device | string | any | This is the device you will stream from the `src` parameter. you may not need this if using alsasrc alone. `pactl list sources` and `arecord -l` may help if you need to specify a custom audio source |
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
# AudioStream galicaster plugin
#
# Copyright (c) 2016 University of Sussex
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to
# permit persons to whom the Software is furnished to do so, subject to
# the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
import os
import requests
from SocketServer import ThreadingMixIn
import subprocess
from threading import Thread
from galicaster.core import context

conf = context.get_conf()
dispatcher = context.get_dispatcher()
_http_host = conf.get('ddp', 'http_host')
_id = conf.get('ingest', 'hostname')
_port = conf.get_int('audiostream', 'port') or 31337
src = conf.get('audiostream', 'src') or 'alsasrc'
device = conf.get('audiostream', 'device') or None
if device:
device_params = 'device=' + device
else:
device_params = ''


def init():
audiostream = AudioStream()
audiostream.start()


class AudioStream(Thread):

def __init__(self):
Thread.__init__(self)
serveraddr = ('', _port)
server = ThreadedHTTPServer(serveraddr, AudioStreamer)
server.allow_reuse_address = True
server.timeout = 30
self.server = server
dispatcher.connect('action-quit', self.shutdown)

def run(self):
self.server.serve_forever()

def shutdown(self, whatever):
self.server.shutdown()


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""


class AudioStreamer(BaseHTTPRequestHandler):
def _writeheaders(self):

self.send_response(200) # 200 OK http response
self.send_header('Content-type', 'audio/mpeg')
self.end_headers()

def _not_allowed(self):
self.send_response(403) # 200 OK http response
self.end_headers()

def do_HEAD(self):
self._writeheaders()

def do_GET(self):
data = {'_id': _id, 'streamKey': self.path[1:]}
r = requests.post(_http_host + '/stream_key', data=data)
# key
try:
self._writeheaders()
DataChunkSize = 10000
devnull = open(os.devnull, 'wb')
command = 'gst-launch-1.0 {} {} ! '.format(src, device_params) + \
'lamemp3enc bitrate=128 cbr=true ! ' + \
'filesink location=/dev/stdout'
p = subprocess.Popen(
command,
stdout=subprocess.PIPE,
stderr=devnull,
bufsize=-1,
shell=True)
while(p.poll() is None):
stdoutdata = p.stdout.read(DataChunkSize)
self.wfile.write(stdoutdata)
stdoutdata = p.stdout.read(DataChunkSize)
self.wfile.write(stdoutdata)

except Exception:
pass
p.kill()

try:
self.wfile.flush()
self.wfile.close()
except:
pass

def handle_one_request(self):
try:
BaseHTTPRequestHandler.handle_one_request(self)
except:
self.close_connection = 1
self.rfile = None
self.wfile = None

def finish(self):
try:
BaseHTTPRequestHandler.finish(self)
except:
pass
Loading

0 comments on commit 175c542

Please sign in to comment.