diff --git a/config_files/osc2cam_final.conf b/config_files/osc2cam_final.conf new file mode 100644 index 0000000..d2918a7 --- /dev/null +++ b/config_files/osc2cam_final.conf @@ -0,0 +1,3 @@ +192.168.1.51, 9000 +192.168.1.52, 9000 +192.168.1.53, 9000 diff --git a/config_files/osc2cam_local_testing.conf b/config_files/osc2cam_local_testing.conf new file mode 100644 index 0000000..29a688c --- /dev/null +++ b/config_files/osc2cam_local_testing.conf @@ -0,0 +1,3 @@ +localhost, 9001 +localhost, 9002 +localhost, 9003 diff --git a/osc2cam/osc2cam/main.py b/osc2cam/osc2cam/main.py index 3af2e90..3cc77a2 100644 --- a/osc2cam/osc2cam/main.py +++ b/osc2cam/osc2cam/main.py @@ -6,8 +6,15 @@ import re import sys import httplib +from datetime import datetime from chaosc.simpleOSCServer import SimpleOSCServer +try: + from chaosc.c_osc_lib import OSCMessage +except ImportError, e: + print e + from chaosc.osc_lib import OSCMessage + class OSC2CamServer(SimpleOSCServer): """OSC filtering/transcoding middleware @@ -27,37 +34,67 @@ class OSC2CamServer(SimpleOSCServer): SimpleOSCServer.__init__(self, ("", args.port)) + self.args = args self.set_url = "/cgi-bin/admin/" self.get_url = "/cgi-bin/view/" self.ptz_ctl_url = "/cgi-bin/operator/ptzset" self.ptz_config_url = "/cgi-bin/operator/ptzconfig" + self.parse_cam_config() self.connections = [httplib.HTTPConnection(host, port) for host, port in cams] self.resetCams() + self.targets = list() + + + def parse_cam_config(self): + lines = open(self.args.cam_config_file).readlines() + cams = list() + for line in lines: + host, port = line.split(",") + host = host.strip() + port = int(port.strip()) + cams.append((host, port)) + return cams + + + def handleConnResult(self, connection, client_address=None, cmd=None): + conn_result = connection.getresponse() + if client_address is not None: + response = OSCMessage("/Response") + response.appendTypedArg(cmd, "s") + response.appendTypedArg(conn_result.status, "i") + response.appendTypedArg(conn_result.reason, "s") + self.socket.sendto(response.encode_osc(), client_address) + + if conn_result.status != 200: + print "%s. Error: %d, %s" % (datetime.now().strftime("%x %X"), conn_result.status, conn_result.reason) + + def resetCams(self): """ configures each ip cam""" - for connection in self.connections: + now = datetime.now().strftime("%x %X") + for ix, connection in enumerate(self.connections): + print "%s: resetting camera %d to: resolution 640x480, 75%% compression and 25 fps" % (now, ix) connection.request("GET", "%sparam?action=update&Image.I0.MJPEG.Resolution=640x480" % self.set_url) - conn_result = connection.getresponse() - print conn_result.status, conn_result.reason + self.handleConnResult(connection, None) connection.request("GET", "%sparam?action=update&Image.I0.Appearance.Compression=75" % self.set_url) - conn_result = connection.getresponse() - print conn_result.status, conn_result.reason + self.handleConnResult(connection, None) connection.request("GET", "%sparam?action=update&Image.I0.MJPEG.FPS=25" % self.set_url) - conn_result = connection.getresponse() - print conn_result.status, conn_result.reason + self.handleConnResult(connection, None) - def move_cam(self, cam_id, args): + def move_cam(self, cam_id, args, client_address): """ moves given ip cam""" direction = args[0] + now = datetime.now().strftime("%x %X") if direction in ("home", "up", "down", "left", "right", "upleft", "upright", "downleft", "downright", "repeat", "stop"): + print "%s: move camera %d to: dir %r" % (now, cam_id, direction) connection = self.connections[cam_id] connection.request("GET", "%s?move=%s" % (self.ptz_ctl_url, direction)) - conn_result = connection.getresponse() - print conn_result.status, conn_result.reason + self.handleConnResult(connection, client_address, "moveCam") + def use_cam_preset(self, cam_id, args): @@ -65,9 +102,11 @@ class OSC2CamServer(SimpleOSCServer): presetno = args[0] connection = self.connections[cam_id] + now = datetime.now().strftime("%x %X") + print "%s: use camera %d preset %d" % (now, cam_id, presetno) + connection.request("GET", "%s?gotoserverpresetno=%d" % (self.ptz_ctl_url, presetno)) - conn_result = connection.getresponse() - print conn_result.status, conn_result.reason + self.handleConnResult(connection, client_address, "useCamPreset") def set_cam_preset(self, cam_id, args): @@ -76,8 +115,7 @@ class OSC2CamServer(SimpleOSCServer): presetno = args[0] connection = self.connections[cam_id] connection.request("GET", "%s?setserverpresetno=%d&home=yes" % (self.ptz_config_url, presetno)) - conn_result = connection.getresponse() - print conn_result.status, conn_result.reason + self.handleConnResult(connection, client_address, "setCamPreset") def zoom_cam(self, cam_id, args): @@ -91,8 +129,7 @@ class OSC2CamServer(SimpleOSCServer): direction = 1 connection = self.connections[cam_id] connection.request("GET", "%s?zoom=%s" % (self.ptz_ctl_url, direction)) - conn_result = connection.getresponse() - print conn_result.status, conn_result.reason + self.handleConnResult(connection, client_address, "zoomCam") def toggle_night_view(self, cam_id, args): @@ -107,16 +144,16 @@ class OSC2CamServer(SimpleOSCServer): connection = self.connections[cam_id] connection.request("GET", "%sparam?action=update&Image.I0.Appearance.NightMode=%s" % (self.set_url, state)) - conn_result = connection.getresponse() - print conn_result.status, conn_result.reason + self.handleConnResult(connection, client_address, "toggleNightView") def dispatchMessage(self, osc_address, typetags, args, packet, client_address): """ dispatches parsed osc messages to the ip cam command methods""" + print "client_address", client_address cam_id = args.pop(0) if osc_address == "/moveCam": - self.move_cam(cam_id, args) + self.move_cam(cam_id, args, client_address) elif osc_address == "/setCamPreset": self.set_cam_preset(cam_id, args) elif osc_address == "/useCamPreset": @@ -125,12 +162,49 @@ class OSC2CamServer(SimpleOSCServer): self.zoom_cam(cam_id, args) elif osc_address == "/toggleNightView": self.toggle_night_view(cam_id, args) + elif osc_address == "/subscribe": + self.__subscription_handler(osc_address, typetags, args, packet, client_address) + + def __subscription_handler(self, addr, typetags, args, client_address): + """handles a target subscription. + + The provided 'typetags' equals ["s", "i"] and + 'args' contains [host, portnumber] + + only subscription requests with valid host will be granted. + """ + + address = args + try: + r = socket.getaddrinfo(address[0], address[1], socket.AF_INET6, socket.SOCK_DGRAM, 0, socket.AI_V4MAPPED | socket.AI_ALL) + print "addrinfo", r + if len(r) == 2: + address = r[1][4] + try: + print "%s: subscribe %r (%s:%d) by %s:%d" % ( + datetime.now().strftime("%x %X"), args[3], address[0], + address[1], client_address[0], client_address[1]) + self.targets[tuple(address)] = args[3] + except IndexError: + self.targets[tuple(address)] = "" + print "%s: subscribe (%s:%d) by %s:%d" % ( + datetime.now().strftime("%x %X"), address[0], address[1], + client_address[0], client_address[1]) + except socket.error, error: + print error + print "subscription attempt from %r: host %r not usable" % ( + client_address, address[0]) + def main(): parser = argparse.ArgumentParser(prog='osc2cam') parser.add_argument('-p', "--port", required=True, type=int, help='my port') + parser.add_argument('-C', "--client_port", required=True, + type=int, help='client port to send reponse to') + parser.add_argument('-c', "--cam-config-file", required=True, + type=str, help='txt file for cam configuration, each line should be of the form "host, port"') args = parser.parse_args(sys.argv[1:]) diff --git a/osc2cam/osc2cam/testing.py b/osc2cam/osc2cam/testing.py new file mode 100644 index 0000000..96990a5 --- /dev/null +++ b/osc2cam/osc2cam/testing.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- + +# This file is part of chaosc +# +# chaosc is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# chaosc is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with chaosc. If not, see . +# +# Copyright (C) 2012-2013 Stefan Kögl + + +import socket, argparse, sys + +from chaosc.simpleOSCServer import SimpleOSCServer + + +try: + from chaosc.c_osc_lib import OSCMessage, decode_osc +except ImportError, e: + print e + from chaosc.osc_lib import OSCMessage, decode_osc + + + + +def test_moveCam(args, cam_id, sock): + directions = ("home", "up", "down", "left", "right", "upleft", "upright", "downleft", "downright", "repeat", "stop") + for direction in directions: + moveCam = OSCMessage("/moveCam") + moveCam.appendTypedArg(cam_id, "i") + moveCam.appendTypedArg(direction, "s") + binary = moveCam.encode_osc() + + try: + sent = sock.sendto(binary, (args.osc2cam_host, args.osc2cam_port)) + except socket.error, e: + if e[0] in (7, 65): # 7 = 'no address associated with nodename', 65 = 'no route to host' + raise e + else: + raise Exception("while sending OSCMessage 'moveCam' for cam_id %r with direction %r to %r:%r: %s" % (cam_id, direction, args.osc2cam_host, args.osc2cam_port, str(e))) + response = sock.recv(4096) + if response: + osc_address, typetags, arguments = decode_osc(response, 0, len(response)) + print osc_address, arguments + + + +def parse_cam_config(path): + lines = open(path).readlines() + cams = list() + for line in lines: + host, port = line.split(",") + host = host.strip() + port = int(port.strip()) + cams.append((host, port)) + return cams + + + +def main(): + parser = argparse.ArgumentParser(prog='osc2cam-test') + parser.add_argument("-o", '--osc2cam_host', required=True, + type=str, help='host of osc2cam instance to control') + parser.add_argument("-p", '--osc2cam_port', required=True, + type=int, help='port of osc2cam instance to control') + parser.add_argument('-c', "--cam-config-file", required=True, + type=str, help='txt file for cam configuration, each line should be of the form "host, port"') + + args = parser.parse_args(sys.argv[1:]) + + cams = parse_cam_config(args.cam_config_file) + + sock = socket.socket(2, 2, 17) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 4096) + sock.setsockopt(socket.SOL_SOCKET, socket.SO_RCVBUF, 4096) + sock.settimeout(1) + sock.bind(("localhost", 10000)) + #sock.connect((args.osc2cam_host, args.osc2cam_port)) + + for ix, (host, port) in enumerate(cams): + test_moveCam(args, ix, sock) + +main()