[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