[vlma-devel] commit: A SAP server. (Adrien Grand )
git version control
git at videolan.org
Mon Dec 28 00:19:18 CET 2009
vlma | branch: master | Adrien Grand <jpountz at videolan.org> | Sun Dec 27 23:39:04 2009 +0100| [d02370cfdf8aa5f333ac5c4a93af23113ba303ce] | committer: Adrien Grand
A SAP server.
> http://git.videolan.org/gitweb.cgi/vlma.git/?a=commit;h=d02370cfdf8aa5f333ac5c4a93af23113ba303ce
---
vlma-watchdog/src/conf.py | 1 +
vlma-watchdog/src/sap.py | 99 ++++++++++++++++++++++++++++++++++++
vlma-watchdog/src/serialization.py | 2 +-
vlma-watchdog/src/streamer/api.py | 8 ++-
4 files changed, 107 insertions(+), 3 deletions(-)
diff --git a/vlma-watchdog/src/conf.py b/vlma-watchdog/src/conf.py
index d91d8e0..ff077f2 100644
--- a/vlma-watchdog/src/conf.py
+++ b/vlma-watchdog/src/conf.py
@@ -3,6 +3,7 @@
import streamer, logging
+NETWORK_ADDRESS = "127.0.0.1"
# Not taken into account on Windows
NETWORK_INTERFACE = "eth0"
diff --git a/vlma-watchdog/src/sap.py b/vlma-watchdog/src/sap.py
new file mode 100644
index 0000000..fe0a1f0
--- /dev/null
+++ b/vlma-watchdog/src/sap.py
@@ -0,0 +1,99 @@
+#! /usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import logging, math, threading, socket, struct, time
+
+MIME_TYPE = "application/sdp"
+SAP_PORT = 9875
+
+
+class Announce:
+
+ def __init__(self, orig, dest, version):
+ self.ip_orig = orig
+ self.ip = self.__guess_ip(dest.ip)
+ self.version = version
+ self.sdp = self.__build_sdp(dest)
+
+ def __guess_ip(self, multicast_address):
+ # TODO
+ return "239.255.255.255"
+
+ def __build_sdp(self, dest):
+ is_ipv6 = self.ip.find(':') > 0
+ ip_version = is_ipv6 and "IP6" or "IP4"
+ now = int(math.floor(time.time() * 1000L))
+ # cf. http://www.ietf.org/rfc/rfc2327.txt
+ # v, o, s, t and m are compulsory
+ result = ["v=0"]
+ result.append("o=VideoLAN %ld 1 IN %s %s" %(now, ip_version, self.ip_orig))
+ result.append("s=%s" %dest.announcing.name)
+ if not dest.announcing.description is None:
+ result.append("i=%s" %dest.announcing.description)
+ result.append("c=IN %s %s%s" %(ip_version, dest.ip, is_ipv6 and "" or "/%d" %dest.ttl))
+ result.append("t=0 0")
+ result.append("a=tool:VLMa")
+ result.append("a=type:broadcast")
+ result.append("a=charset:UTF-8")
+ result.append("a=recvonly") # No interaction required, default for type:broadcast
+ if not dest.announcing.group is None:
+ result.append("a=x-plgroup:%s" %dest.announcing.group)
+ if dest.streaming.type == "rtp":
+ result.append("m=video %d RTP/AVP 33" %dest.port)
+ else:
+ result.append("m=video %d udp mpeg" %dest.port)
+ return "\r\n".join(result)
+
+
+class SAPServer(threading.Thread):
+ """A SAP server"""
+
+ def __init__(self, ip, delay=5):
+ """Create a new SAP server"""
+ self.ip = ip
+ self.delay = delay
+ self.__announces = {}
+ self.__lock = threading.RLock()
+ self.__logger = logging.getLogger("SAP server")
+
+ def add_dest(self, dest):
+ version = int(math.floor(time.time() * 1000)) & 0xffff
+ self.__lock.acquire()
+ self.__announces[dest] = Announce(self.ip, dest, version)
+ self.__lock.release()
+
+ def remove_dest(self, dest):
+ self.__lock.acquire()
+ if self.__announces.has_key(dest):
+ del self.__announces[dest]
+ self.__lock.release()
+
+ def __send_announce(self, announce):
+ is_ipv6 = self.ip.find(":") > 0
+ ip = socket.inet_pton(is_ipv6 and socket.AF_INET6 or socket.AF_INET, self.ip)
+ data = struct.pack("!2BH%ds%ds%ds" %(len(ip), len(MIME_TYPE), len(announce.sdp)),
+ # SAP Header
+ 0x20 | (is_ipv6 and 0x10 or 0x00), # SAP version + IP version
+ 0x00, # Authentication length (none)
+ announce.version, # a version hash, must be changed whenever the content of the announce is changed
+ ip,
+ MIME_TYPE,
+ # Session description
+ announce.sdp) # content
+ self.__sock.sendto(data, (announce.ip, SAP_PORT))
+
+ def run(self):
+ self.__sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP)
+ self.__sock.setsockopt(socket.IPPROTO_IP, socket.IP_MULTICAST_TTL, 2)
+ while True:
+ self.__lock.acquire()
+ for announce in self.__announces.values():
+ try:
+ self.__send_announce(announce)
+ except BaseException, e:
+ self.__logger.warn("Error while sending SAP announce", e)
+ print e
+ self.__lock.release()
+ time.sleep(self.delay)
+ self.__sock.close()
+
diff --git a/vlma-watchdog/src/serialization.py b/vlma-watchdog/src/serialization.py
index 673b7bd..17949d4 100644
--- a/vlma-watchdog/src/serialization.py
+++ b/vlma-watchdog/src/serialization.py
@@ -80,7 +80,7 @@ def order_from_xml(s):
if nodeName == "src":
src = program_content.childNodes[0].nodeValue
elif nodeName == "dest":
- dest = streamer.api.Dest()
+ dest = streamer.api.Dest(order)
for dest_content in program_content.childNodes:
if dest_content.nodeType == xml.TEXT_NODE:
continue
diff --git a/vlma-watchdog/src/streamer/api.py b/vlma-watchdog/src/streamer/api.py
index 22d8232..138d7f0 100644
--- a/vlma-watchdog/src/streamer/api.py
+++ b/vlma-watchdog/src/streamer/api.py
@@ -316,13 +316,16 @@ class Dest:
"""A destination. Describes what to do with the stream (how and where to
stream, transcoding, etc.)"""
- def __init__(self, ip="0.0.0.0", port=1234):
+ def __init__(self, order, ip="0.0.0.0", port=1234):
+ self.__order = order
self.ip = ip
self.port = port
self.streaming = Streaming()
self.transcoding = None
self.announcing = None
+ ttl = property(lambda s: s.__order.ttl)
+
class Streaming:
"""Describes how to stream."""
@@ -349,10 +352,11 @@ class Transcoding:
class Announcing:
"""Describes how the stream should be announced"""
- def __init__(self, type="sap", name="", group=None):
+ def __init__(self, type="sap", name="", group=None, description=None):
self.type = type
self.name = name
self.group = group
+ self.description = None
class DVBOrder(Order):
More information about the vlma-devel
mailing list