Ejemplo n.º 1
0
 def __init__(self, link_name, redis_host):
     """
         Set up a new LinkConfig instance - needs to know the link name and
         configuration host.
     """
     self.int_properties = [
         'port', 'jitter_buffer', 'opus_framesize', 'opus_complexity',
         'bitrate', 'opus_loss_expectation'
     ]
     self.bool_properties = ['opus_dtx', 'opus_fec', 'multicast']
     self.link_name = link_name
     self.redis_host = redis_host
     self.logger_factory = LoggerFactory()
     self.logger = self.logger_factory.getLogger('link.%s.config' %
                                                 self.link_name)
     self.logger.info("Connecting to configuration host %s" %
                      self.redis_host)
     self.redis = None
     while True:
         try:
             self.redis = redis.StrictRedis(host=self.redis_host,
                                            charset="utf-8",
                                            decode_responses=True)
             break
         except Exception as e:
             self.logger.error(
                 "Unable to connect to configuration host! Retrying. (%s)" %
                 e)
             time.sleep(0.1)
Ejemplo n.º 2
0
 def __init__(self, node_name, interface_name='default'):
     self.interface_name = interface_name
     self.node_name = node_name
     self.logger_factory = LoggerFactory()
     self.logger = self.logger_factory.getLogger(
         'node.%s.audio_interface.%s' %
         (self.node_name, self.interface_name))
     self.config = dict()
Ejemplo n.º 3
0
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP receiver"""
    
        self.link_config = link_config
        self.audio_interface = audio_interface

        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.link.%s.%s' % (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info('Creating reception pipeline')

        self.build_pipeline()
Ejemplo n.º 4
0
class LinkConfig(object):
    """
        The LinkConfig class encapsulates link configuration. It's genderless;
        a TX node should be able to set up a new link and an RX node should be
        able (once the TX node has specified the port caps) to configure itself
        to receive the stream using the data and methods in this config.
    """
    def __init__(self, link_name, redis_host):
        """
            Set up a new LinkConfig instance - needs to know the link name and
            configuration host.
        """
        self.link_name = link_name
        self.redis_host = redis_host
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('link.%s.config' %
                                                    self.link_name)
        self.logger.info("Connecting to configuration host %s" %
                         self.redis_host)
        self.redis = None
        while True:
            try:
                self.redis = redis.StrictRedis(self.redis_host)
                break
            except Exception, e:
                self.logger.error(
                    "Unable to connect to configuration host! Retrying. (%s)" %
                    e)
                time.sleep(0.1)
Ejemplo n.º 5
0
 def __init__(self, node_name, interface_name='default'):
     self.interface_name = interface_name
     self.node_name = node_name
     self.logger_factory = LoggerFactory()
     self.logger = self.logger_factory.getLogger('node.%s.audio_interface.%s' 
                                                 % (self.node_name, self.interface_name))
     self.config = dict()
Ejemplo n.º 6
0
class LinkConfig(object):

    """
        The LinkConfig class encapsulates link configuration. It's genderless;
        a TX node should be able to set up a new link and an RX node should be
        able (once the TX node has specified the port caps) to configure itself
        to receive the stream using the data and methods in this config.
    """

    def __init__(self, link_name, redis_host):
        """
            Set up a new LinkConfig instance - needs to know the link name and
            configuration host.
        """
        self.link_name = link_name
        self.redis_host = redis_host
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('link.%s.config' % self.link_name)
        self.logger.info("Connecting to configuration host %s" % self.redis_host)
        self.redis = None
        while True:
            try:
                self.redis = redis.StrictRedis(self.redis_host)
                break
            except Exception, e:
                self.logger.error(
                    "Unable to connect to configuration host! Retrying. (%s)"
                    % e
                )
                time.sleep(0.1)
Ejemplo n.º 7
0
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP transmitter"""
        
        self.link_config = link_config
        self.audio_interface = audio_interface

        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.link.%s.%s' % (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info('Creating transmission pipeline')

        self.build_pipeline()
Ejemplo n.º 8
0
 def __init__(self, link_name, redis_host):
     """
         Set up a new LinkConfig instance - needs to know the link name and
         configuration host.
     """
     self.link_name = link_name
     self.redis_host = redis_host
     self.logger_factory = LoggerFactory()
     self.logger = self.logger_factory.getLogger('link.%s.config' %
                                                 self.link_name)
     self.logger.info("Connecting to configuration host %s" %
                      self.redis_host)
     self.redis = None
     while True:
         try:
             self.redis = redis.StrictRedis(self.redis_host)
             break
         except Exception, e:
             self.logger.error(
                 "Unable to connect to configuration host! Retrying. (%s)" %
                 e)
             time.sleep(0.1)
Ejemplo n.º 9
0
class AudioInterface(object):

    """
        The AudioInterface class describes an audio interface on a Node.
        The configuration is not shared across the network. The type property of
        an AudioInterface should define the mode of link operation.
    """

    def __init__(self, node_name, interface_name='default'):
        self.interface_name = interface_name
        self.node_name = node_name
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.audio_interface.%s' 
                                                    % (self.node_name, self.interface_name))
        self.config = dict()

    def set(self, key, value):
        """Set a config value"""
        self.logger.debug("Set %s to %s" % (key, value))
        self.config[key] = value

    def get(self, key):
        """Get a config value"""
        value = self.config[key]
        self.logger.debug("Fetched %s, got %s" % (key, value))
        return value

    def __getattr__(self, key):
        """Convenience method to access get"""
        return self.get(key)

    def set_from_argparse(self, opts):
        """Set up the audio interface from argparse options"""
        self.set("mode", opts.mode)

        if opts.mode == "tx":
            self.set("type", opts.audio_input)
            self.set("samplerate", opts.samplerate)
        elif opts.mode == "rx":
            self.set("type", opts.audio_output)

        if self.get("type") == "alsa":
            self.set("alsa_device", opts.alsa_device)
        elif self.get("type") == "jack":
            if opts.jack_auto is not False:
                self.set("jack_auto", opts.jack_auto)
            if opts.jack_name is not None:
                self.set("jack_name", opts.jack_name)
            else:
                self.set("jack_name", "openob")
Ejemplo n.º 10
0
class AudioInterface(object):
    """
        The AudioInterface class describes an audio interface on a Node.
        The configuration is not shared across the network. The type property of
        an AudioInterface should define the mode of link operation.
    """
    def __init__(self, node_name, interface_name='default'):
        self.interface_name = interface_name
        self.node_name = node_name
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger(
            'node.%s.audio_interface.%s' %
            (self.node_name, self.interface_name))
        self.config = dict()

    def set(self, key, value):
        """Set a config value"""
        self.logger.debug("Set %s to %s" % (key, value))
        self.config[key] = value

    def get(self, key):
        """Get a config value"""
        value = self.config[key]
        self.logger.debug("Fetched %s, got %s" % (key, value))
        return value

    def __getattr__(self, key):
        """Convenience method to access get"""
        return self.get(key)

    def set_from_argparse(self, opts):
        """Set up the audio interface from argparse options"""
        self.set("mode", opts.mode)

        if opts.mode == "tx":
            self.set("type", opts.audio_input)
            self.set("samplerate", opts.samplerate)
        elif opts.mode == "rx":
            self.set("type", opts.audio_output)
        if self.get("type") == "alsa":
            self.set("alsa_device", opts.alsa_device)
        elif self.get("type") == "jack":
            self.set("jack_auto", opts.jack_auto)
            if opts.jack_name is not None:
                self.set("jack_name", opts.jack_name)
            else:
                self.set("jack_name", "openob")
            if opts.jack_port_pattern is not None:
                self.set("jack_port_pattern", opts.jack_port_pattern)
Ejemplo n.º 11
0
 def __init__(self, link_name, redis_host):
     """
         Set up a new LinkConfig instance - needs to know the link name and
         configuration host.
     """
     self.link_name = link_name
     self.redis_host = redis_host
     self.logger_factory = LoggerFactory()
     self.logger = self.logger_factory.getLogger('link.%s.config' % self.link_name)
     self.logger.info("Connecting to configuration host %s" % self.redis_host)
     self.redis = None
     while True:
         try:
             self.redis = redis.StrictRedis(self.redis_host)
             break
         except Exception, e:
             self.logger.error(
                 "Unable to connect to configuration host! Retrying. (%s)"
                 % e
             )
             time.sleep(0.1)
Ejemplo n.º 12
0
 def __init__(self, link_name, redis_host):
     """
         Set up a new LinkConfig instance - needs to know the link name and
         configuration host.
     """
     self.int_properties = ['port', 'jitter_buffer', 'opus_framesize', 'opus_complexity', 'bitrate', 'opus_loss_expectation']
     self.bool_properties = ['opus_dtx', 'opus_fec', 'multicast']
     self.link_name = link_name
     self.redis_host = redis_host
     self.logger_factory = LoggerFactory()
     self.logger = self.logger_factory.getLogger('link.%s.config' % self.link_name)
     self.logger.info("Connecting to configuration host %s" % self.redis_host)
     self.redis = None
     while True:
         try:
             self.redis = redis.StrictRedis(self.redis_host)
             break
         except Exception as e:
             self.logger.error(
                 "Unable to connect to configuration host! Retrying. (%s)"
                 % e
             )
             time.sleep(0.1)
Ejemplo n.º 13
0
class RTPTransmitter(object):
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP transmitter"""
        self.started = False
        self.caps = 'None'
        self.pipeline = gst.Pipeline("tx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger(
            'node.%s.link.%s.%s' %
            (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info("Creating RTP transmission pipeline")
        # Audio input
        if self.audio_interface.type == 'auto':
            self.source = gst.element_factory_make('autoaudiosrc')
        elif self.audio_interface.type == 'alsa':
            self.source = gst.element_factory_make('alsasrc')
            self.source.set_property('device',
                                     self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            self.source = gst.element_factory_make("jackaudiosrc")
            if self.audio_interface.jack_auto:
                self.source.set_property('connect', 'auto')
            else:
                self.source.set_property('connect', 'none')
            self.source.set_property('buffer-time', 50000)
            self.source.set_property('name', self.audio_interface.jack_name)
            self.source.set_property('client-name',
                                     self.audio_interface.jack_name)
        # Audio resampling and conversion
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample.set_property('quality', 9)  # SRC

        # Encoding and payloading
        if self.link_config.encoding == 'opus':
            self.encoder = gst.element_factory_make("opusenc", "encoder")
            self.encoder.set_property('bitrate',
                                      int(self.link_config.bitrate) * 1000)
            self.encoder.set_property('tolerance', 80000000)
            self.encoder.set_property('frame-size',
                                      self.link_config.opus_framesize)
            self.encoder.set_property('complexity',
                                      int(self.link_config.opus_complexity))
            self.encoder.set_property('inband-fec', self.link_config.opus_fec)
            self.encoder.set_property(
                'packet-loss-percentage',
                int(self.link_config.opus_loss_expectation))
            self.encoder.set_property('dtx', self.link_config.opus_dtx)
            print(self.encoder.get_properties('bitrate', 'dtx', 'inband-fec'))
            self.payloader = gst.element_factory_make("rtpopuspay",
                                                      "payloader")
        elif self.link_config.encoding == 'pcm':
            # we have no encoder for PCM operation
            self.payloader = gst.element_factory_make("rtpL16pay", "payloader")
        else:
            self.logger.critical("Unknown encoding type %s" %
                                 self.link_config.encoding)
        # TODO: Add a tee here, and sort out creating multiple UDP sinks for multipath
        # Now the RTP bits
        # We'll send audio out on this
        self.udpsink_rtpout = gst.element_factory_make("udpsink",
                                                       "udpsink_rtp")
        self.udpsink_rtpout.set_property('host',
                                         self.link_config.receiver_host)
        self.udpsink_rtpout.set_property('port', self.link_config.port)
        self.logger.info(
            'Set receiver to %s:%i' %
            (self.link_config.receiver_host, self.link_config.port))

        if self.link_config.multicast:
            self.udpsink_rtpout.set_property('auto_multicast', True)
            self.logger.info('Multicast mode enabled')

        # Our RTP manager
        self.rtpbin = gst.element_factory_make("gstrtpbin", "gstrtpbin")
        self.rtpbin.set_property('latency', 0)
        # Our level monitor
        self.level = gst.element_factory_make("level")
        self.level.set_property('message', True)
        self.level.set_property('interval', 1000000000)

        # Add a capsfilter to allow specification of input sample rate
        self.capsfilter = gst.element_factory_make("capsfilter")

        # Add to the pipeline
        self.pipeline.add(self.source, self.capsfilter, self.audioresample,
                          self.audioconvert, self.payloader,
                          self.udpsink_rtpout, self.rtpbin, self.level)

        if self.link_config.encoding != 'pcm':
            # Only add the encoder if we're not in PCM mode
            self.pipeline.add(self.encoder)

        # Decide which format to apply to the capsfilter (Jack uses float)
        if self.audio_interface.type == 'jack':
            data_type = 'audio/x-raw-float'
        else:
            data_type = 'audio/x-raw-int'

        # if audio_rate has been specified, then add that to the capsfilter
        if self.audio_interface.samplerate != 0:
            self.capsfilter.set_property(
                "caps",
                gst.Caps('%s, channels=2, rate=%d' %
                         (data_type, self.audio_interface.samplerate)))
        else:
            self.capsfilter.set_property(
                "caps", gst.Caps('%s, channels=2' % data_type))

        # Then continue linking the pipeline together
        gst.element_link_many(self.source, self.capsfilter, self.level,
                              self.audioresample, self.audioconvert)

        # Now we get to link this up to our encoder/payloader
        if self.link_config.encoding != 'pcm':
            gst.element_link_many(self.audioconvert, self.encoder,
                                  self.payloader)
        else:
            gst.element_link_many(self.audioconvert, self.payloader)

        # And now the RTP bits
        self.payloader.link_pads('src', self.rtpbin, 'send_rtp_sink_0')
        self.rtpbin.link_pads('send_rtp_src_0', self.udpsink_rtpout, 'sink')
        # self.udpsrc_rtcpin.link_pads('src', self.rtpbin, 'recv_rtcp_sink_0')
        # # RTCP SRs
        # self.rtpbin.link_pads('send_rtcp_src_0', self.udpsink_rtcpout, 'sink')
        # Connect our bus up
        self.bus.add_signal_watch()
        self.bus.connect('message', self.on_message)

    def run(self):
        self.pipeline.set_state(gst.STATE_PLAYING)
        while self.caps == 'None':
            self.logger.debug(self.udpsink_rtpout.get_state())
            self.caps = str(
                self.udpsink_rtpout.get_pad('sink').get_property('caps'))
            # Fix for gstreamer bug in rtpopuspay fixed in GST-plugins-bad
            # 50140388d2b62d32dd9d0c071e3051ebc5b4083b, bug 686547
            if self.link_config.encoding == 'opus':
                self.caps = re.sub(r'(caps=.+ )', '', self.caps)

            if self.caps == 'None':
                self.logger.warn("Waiting for audio interface/caps")

            time.sleep(0.1)

    def loop(self):
        try:
            self.loop = gobject.MainLoop()
            self.loop.run()
        except Exception as e:
            self.logger.exception(
                "Encountered a problem in the MainLoop, tearing down the pipeline: %s"
                % e)
            self.pipeline.set_state(gst.STATE_NULL)

    def on_message(self, bus, message):
        if message.type == gst.MESSAGE_ELEMENT:
            if message.structure.get_name() == 'level':
                if self.started is False:
                    self.started = True
                    #gst.DEBUG_BIN_TO_DOT_FILE(self.pipeline, gst.DEBUG_GRAPH_SHOW_ALL, 'tx-graph')
                    #self.logger.debug(self.source.get_property('actual-buffer-time'))
                    if len(message.structure['peak']) == 1:
                        self.logger.info("Started mono audio transmission")
                    else:
                        self.logger.info("Started stereo audio transmission")
        return True

    def get_caps(self):
        return self.caps
Ejemplo n.º 14
0
class LinkConfig(object):

    """
        The LinkConfig class encapsulates link configuration. It's genderless;
        a TX node should be able to set up a new link and an RX node should be
        able (once the TX node has specified the port caps) to configure itself
        to receive the stream using the data and methods in this config.
    """

    def __init__(self, link_name, redis_host):
        """
            Set up a new LinkConfig instance - needs to know the link name and
            configuration host.
        """
        self.int_properties = ['port', 'jitter_buffer', 'opus_framesize', 'opus_complexity', 'bitrate', 'opus_loss_expectation']
        self.bool_properties = ['opus_dtx', 'opus_fec', 'multicast']
        self.link_name = link_name
        self.redis_host = redis_host
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('link.%s.config' % self.link_name)
        self.logger.info("Connecting to configuration host %s" % self.redis_host)
        self.redis = None
        while True:
            try:
                self.redis = redis.StrictRedis(self.redis_host)
                break
            except Exception as e:
                self.logger.error(
                    "Unable to connect to configuration host! Retrying. (%s)"
                    % e
                )
                time.sleep(0.1)

    def blocking_get(self, key):
        """Get a value, blocking until it's not None if needed"""
        while True:
            value = self.get(key)
            if value is not None:
                self.logger.debug("Fetched (blocking) %s, got %s" % (key, value))
                return value
            time.sleep(0.1)

    def set(self, key, value):
        """Set a value in the config store"""
        scoped_key = self.scoped_key(key)
        self.redis.set(scoped_key, value)
        self.logger.debug("Set %s to %s" % (scoped_key, value))
        return value

    def get(self, key):
        """Get a value from the config store"""
        scoped_key = self.scoped_key(key)
        value = self.redis.get(scoped_key)
        
        # Do some typecasting
        if key in self.int_properties:
            value = int(value)
        if key in self.bool_properties:
            value = (value == 'True')
        self.logger.debug("Fetched %s, got %s" % (scoped_key, value))
        return value

    def unset(self, key):
        scoped_key = self.scoped_key(key)
        self.redis.delete(scoped_key)
        self.logger.debug("Unset %s" % scoped_key)

    def __getattr__(self, key):
        """Convenience method to access get"""
        return self.get(key)

    def scoped_key(self, key):
        """Return an appropriate key name scoped to a link"""
        return ("openob:%s:%s" % (self.link_name, key))

    def set_from_argparse(self, opts):
        """Given an optparse object from bin/openob, configure this link"""
        self.set("name", opts.link_name)
        if opts.mode == "tx":
            self.set("port", opts.port)
            self.set("jitter_buffer", opts.jitter_buffer)
            self.set("encoding", opts.encoding)
            self.set("bitrate", opts.bitrate)
            self.set("multicast", opts.multicast)
            self.set("input_samplerate", opts.samplerate)
            self.set("receiver_host", opts.receiver_host)
            self.set("opus_framesize", opts.framesize)
            self.set("opus_complexity", opts.complexity)
            self.set("opus_fec", opts.fec)
            self.set("opus_loss_expectation", opts.loss)
            self.set("opus_dtx", opts.dtx)

    def commit_changes(self, restart=False):
        """
            To be called after calls to set() on a running link to signal
            a reconfiguration event for that link. If restart is True, the link
            should simply terminate itself so it can be restarted with the new
            parameters. If restart is False, the link should set all parameters
            it can which do not involve a restart.
        """
        raise(NotImplementedError, "Link reconfiguration is not yet implemented")
Ejemplo n.º 15
0
Archivo: tx.py Proyecto: iha/openob
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP transmitter"""
        self.started = False
        self.caps = 'None'
        self.pipeline = gst.Pipeline("tx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.link.%s.%s' % (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info("Creating RTP transmission pipeline")
        # Audio input
        if self.audio_interface.type == 'auto':
            self.source = gst.element_factory_make('autoaudiosrc')
        elif self.audio_interface.type == 'alsa':
            self.source = gst.element_factory_make('alsasrc')
            self.source.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            self.source = gst.element_factory_make("jackaudiosrc")
            if self.audio_interface.jack_auto:
                self.source.set_property('connect', 'auto')
            else:
                self.source.set_property('connect', 'none')
            self.source.set_property('buffer-time', 50000)
            self.source.set_property('name', self.audio_interface.jack_name)
            self.source.set_property('client-name', self.audio_interface.jack_name)
        # Audio resampling and conversion
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample.set_property('quality', 9)  # SRC

        # Encoding and payloading
        if self.link_config.encoding == 'opus':
            self.encoder = gst.element_factory_make("opusenc", "encoder")
            self.encoder.set_property('bitrate', int(self.link_config.bitrate) * 1000)
            self.encoder.set_property('tolerance', 80000000)
            self.encoder.set_property('frame-size', self.link_config.opus_framesize)
            self.encoder.set_property('complexity', int(self.link_config.opus_complexity))
            self.encoder.set_property('inband-fec', self.link_config.opus_fec)
            self.encoder.set_property('packet-loss-percentage', int(self.link_config.opus_loss_expectation))
            self.encoder.set_property('dtx', self.link_config.opus_dtx)
            print(self.encoder.get_properties('bitrate', 'dtx', 'inband-fec'))
            self.payloader = gst.element_factory_make("rtpopuspay", "payloader")
        elif self.link_config.encoding == 'pcm':
            # we have no encoder for PCM operation
            self.payloader = gst.element_factory_make("rtpL16pay", "payloader")
        else:
            self.logger.critical("Unknown encoding type %s" % self.link_config.encoding)
        # TODO: Add a tee here, and sort out creating multiple UDP sinks for multipath
        # Now the RTP bits
        # We'll send audio out on this
        self.udpsink_rtpout = gst.element_factory_make("udpsink", "udpsink_rtp")
        self.udpsink_rtpout.set_property('host', self.link_config.receiver_host)
        self.udpsink_rtpout.set_property('port', self.link_config.port)
        self.logger.info('Set receiver to %s:%i' % (self.link_config.receiver_host, self.link_config.port))

        if self.link_config.multicast:
            self.udpsink_rtpout.set_property('auto_multicast', True)
            self.logger.info('Multicast mode enabled')

        # Our RTP manager
        self.rtpbin = gst.element_factory_make("gstrtpbin", "gstrtpbin")
        self.rtpbin.set_property('latency', 0)
        # Our level monitor
        self.level = gst.element_factory_make("level")
        self.level.set_property('message', True)
        self.level.set_property('interval', 1000000000)

        # Add a capsfilter to allow specification of input sample rate
        self.capsfilter = gst.element_factory_make("capsfilter")

        # Add to the pipeline
        self.pipeline.add(
            self.source, self.capsfilter, self.audioresample, self.audioconvert,
            self.payloader, self.udpsink_rtpout, self.rtpbin,
            self.level)

        if self.link_config.encoding != 'pcm':
            # Only add the encoder if we're not in PCM mode
            self.pipeline.add(self.encoder)

        # Decide which format to apply to the capsfilter (Jack uses float)
        if self.audio_interface.type == 'jack':
            data_type = 'audio/x-raw-float'
        else:
            data_type = 'audio/x-raw-int'

        # if audio_rate has been specified, then add that to the capsfilter
        if self.audio_interface.samplerate != 0:
            self.capsfilter.set_property(
                "caps", gst.Caps('%s, channels=2, rate=%d' % (data_type, self.audio_interface.samplerate)))
        else:
            self.capsfilter.set_property(
                "caps", gst.Caps('%s, channels=2' % data_type))

        # Then continue linking the pipeline together
        gst.element_link_many(
            self.source, self.capsfilter, self.level, self.audioresample, self.audioconvert)

        # Now we get to link this up to our encoder/payloader
        if self.link_config.encoding != 'pcm':
            gst.element_link_many(
                self.audioconvert, self.encoder, self.payloader)
        else:
            gst.element_link_many(self.audioconvert, self.payloader)

        # And now the RTP bits
        self.payloader.link_pads('src', self.rtpbin, 'send_rtp_sink_0')
        self.rtpbin.link_pads('send_rtp_src_0', self.udpsink_rtpout, 'sink')
        # self.udpsrc_rtcpin.link_pads('src', self.rtpbin, 'recv_rtcp_sink_0')
        # # RTCP SRs
        # self.rtpbin.link_pads('send_rtcp_src_0', self.udpsink_rtcpout, 'sink')
        # Connect our bus up
        self.bus.add_signal_watch()
        self.bus.connect('message', self.on_message)
Ejemplo n.º 16
0
class RTPTransmitter(object):

    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP transmitter"""
        
        self.link_config = link_config
        self.audio_interface = audio_interface

        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.link.%s.%s' % (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info('Creating transmission pipeline')

        self.build_pipeline()

    def run(self):
        self.pipeline.set_state(Gst.State.PLAYING)
        # Gst.debug_bin_to_dot_file(self.pipeline, Gst.DebugGraphDetails.ALL, 'tx-graph')
        
        while self.caps == None:
            caps = self.transport.get_by_name('udpsink').get_static_pad('sink').get_property('caps')

            if caps == None:
                self.logger.warn('Waiting for audio interface/caps')
                time.sleep(0.1)
            else:
                self.caps = caps.to_string()

    def loop(self):
        try:
            loop = GLib.MainLoop()
            loop.run()
        except Exception as e:
            self.logger.exception('Encountered a problem in the MainLoop, tearing down the pipeline: %s' % e)
            self.pipeline.set_state(Gst.State.NULL)

    def build_pipeline(self):
        self.pipeline = Gst.Pipeline.new('tx')

        self.started = False
        self.caps = None

        bus = self.pipeline.get_bus()

        self.source = self.build_audio_interface()
        self.encoder = self.build_encoder()
        self.transport = self.build_transport()
        
        self.pipeline.add(self.source)
        self.pipeline.add(self.encoder)
        self.pipeline.add(self.transport)
        self.source.link(self.encoder)
        self.encoder.link(self.transport)

        # Connect our bus up
        bus.add_signal_watch()
        bus.connect('message', self.on_message)

    def build_audio_interface(self):
        self.logger.debug('Building audio input bin')
        bin = Gst.Bin.new('audio')

        # Audio input
        if self.audio_interface.type == 'auto':
            source = Gst.ElementFactory.make('autoaudiosrc')
        elif self.audio_interface.type == 'alsa':
            source = Gst.ElementFactory.make('alsasrc')
            source.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            source = Gst.ElementFactory.make('jackaudiosrc')
            if self.audio_interface.jack_auto:
                source.set_property('connect', 'auto')
            else:
                source.set_property('connect', 'none')
            source.set_property('buffer-time', 50000)
            source.set_property('name', self.audio_interface.jack_name)
            source.set_property('client-name', self.audio_interface.jack_name)
            if self.audio_interface.jack_port_pattern:
                source.set_property('port-pattern', self.audio_interface.jack_port_pattern)

        elif self.audio_interface.type == 'test':
            source = Gst.ElementFactory.make('audiotestsrc')

        bin.add(source)

        # Our level monitor
        level = Gst.ElementFactory.make('level')
        level.set_property('message', True)
        level.set_property('interval', 1000000000)
        bin.add(level)

        # Audio resampling and conversion
        resample = Gst.ElementFactory.make('audioresample')
        resample.set_property('quality', 9)  # SRC
        bin.add(resample)

        convert = Gst.ElementFactory.make('audioconvert')
        bin.add(convert)

        # Add a capsfilter to allow specification of input sample rate
        capsfilter = Gst.ElementFactory.make('capsfilter')

        caps = Gst.Caps.new_empty_simple('audio/x-raw')

        # if audio_rate has been specified, then add that to the capsfilter
        if self.audio_interface.samplerate != 0:
            caps.set_value('rate', self.audio_interface.samplerate)
        
        self.logger.debug(caps.to_string())
        capsfilter.set_property('caps', caps)
        bin.add(capsfilter)

        source.link(level)
        level.link(resample)
        resample.link(convert)
        convert.link(capsfilter)

        bin.add_pad(Gst.GhostPad.new('src', capsfilter.get_static_pad('src')))

        return bin

    def build_encoder(self):
        self.logger.debug('Building encoder bin')
        bin = Gst.Bin.new('encoder')

        # Encoding and payloading
        if self.link_config.encoding == 'opus':
            encoder = Gst.ElementFactory.make('opusenc', 'encoder')
            encoder.set_property('bitrate', int(self.link_config.bitrate) * 1000)
            encoder.set_property('tolerance', 80000000)
            encoder.set_property('frame-size', self.link_config.opus_framesize)
            encoder.set_property('complexity', int(self.link_config.opus_complexity))
            encoder.set_property('inband-fec', self.link_config.opus_fec)
            encoder.set_property('packet-loss-percentage', int(self.link_config.opus_loss_expectation))
            encoder.set_property('dtx', self.link_config.opus_dtx)

            payloader = Gst.ElementFactory.make('rtpopuspay', 'payloader')
        elif self.link_config.encoding == 'pcm':
            # we have no encoder for PCM operation
            payloader = Gst.ElementFactory.make('rtpL16pay', 'payloader')
        else:
            self.logger.critical('Unknown encoding type %s' % self.link_config.encoding)

        bin.add(payloader)

        if 'encoder' in locals():
            bin.add(encoder)
            encoder.link(payloader)
            bin.add_pad(Gst.GhostPad.new('sink', encoder.get_static_pad('sink')))
        else:
            bin.add_pad(Gst.GhostPad.new('sink', payloader.get_static_pad('sink')))

        bin.add_pad(Gst.GhostPad.new('src', payloader.get_static_pad('src')))

        return bin

    def build_transport(self):
        self.logger.debug('Building RTP transport bin')
        bin = Gst.Bin.new('transport')

        # Our RTP manager
        rtpbin = Gst.ElementFactory.make('rtpbin', 'rtpbin')
        rtpbin.set_property('latency', 0)
        bin.add(rtpbin)

        # TODO: Add a tee here, and sort out creating multiple UDP sinks for multipath
        udpsink = Gst.ElementFactory.make('udpsink', 'udpsink')
        udpsink.set_property('host', self.link_config.receiver_host)
        udpsink.set_property('port', self.link_config.port)
        self.logger.info('Set receiver to %s:%i' % (self.link_config.receiver_host, self.link_config.port))

        if self.link_config.multicast:
            udpsink.set_property('auto_multicast', True)
            self.logger.info('Multicast mode enabled')
        bin.add(udpsink)

        bin.add_pad(Gst.GhostPad.new('sink', rtpbin.get_request_pad('send_rtp_sink_0')))

        rtpbin.link_pads('send_rtp_src_0', udpsink, 'sink')

        return bin

    def on_message(self, bus, message):
        if message.type == Gst.MessageType.ELEMENT:
            struct = message.get_structure()
            if struct != None:
                if struct.get_name() == 'level':
                    if self.started is False:
                        self.started = True
                        if len(struct.get_value('peak')) == 1:
                            self.logger.info('Started mono audio transmission')
                        else:
                            self.logger.info('Started stereo audio transmission')
                    else:
                        if len(struct.get_value('peak')) == 1:
                            self.logger.debug('Level: %.2f', struct.get_value('peak')[0])
                        else:
                            self.logger.debug('Levels: L %.2f R %.2f' % (struct.get_value('peak')[0], struct.get_value('peak')[1]))
        return True

    def get_caps(self):
        return self.caps
Ejemplo n.º 17
0
 def __init__(self, node_name):
     """Set up a new node."""
     self.node_name = node_name
     self.logger_factory = LoggerFactory()
     self.logger = self.logger_factory.getLogger('node.%s' % self.node_name)
Ejemplo n.º 18
0
 def __init__(self, node_name):
     """Set up a new node."""
     self.node_name = node_name
     self.logger_factory = LoggerFactory()
     self.logger = self.logger_factory.getLogger('node.%s' % self.node_name)
Ejemplo n.º 19
0
class RTPReceiver(object):
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP receiver"""
        self.started = False
        self.pipeline = gst.Pipeline("rx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger(
            'node.%s.link.%s.%s' %
            (node_name, self.link_config.name, self.audio_interface.mode))
        caps = self.link_config.get("caps")
        # Audio output
        if self.audio_interface.type == 'auto':
            self.sink = gst.element_factory_make("autoaudiosink")
        elif self.audio_interface.type == 'alsa':
            self.sink = gst.element_factory_make("alsasink")
            self.sink.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            self.sink = gst.element_factory_make("jackaudiosink")
            if self.audio_interface.jack_auto:
                self.sink.set_property('connect', 'auto')
            else:
                self.sink.set_property('connect', 'none')
            self.sink.set_property('name', self.audio_interface.jack_name)
        # Audio conversion and resampling
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioresample.set_property('quality', 9)
        self.audiorate = gst.element_factory_make("audiorate")

        # Decoding and depayloading
        if self.link_config.encoding == 'opus':
            self.decoder = gst.element_factory_make("opusdec", "decoder")
            self.decoder.set_property('use-inband-fec', True)  # FEC
            self.decoder.set_property('plc', True)  # Packet loss concealment
            self.depayloader = gst.element_factory_make(
                "rtpopusdepay", "depayloader")
        elif self.link_config.encoding == 'pcm':
            self.depayloader = gst.element_factory_make(
                "rtpL16depay", "depayloader")

        # RTP stuff
        self.rtpbin = gst.element_factory_make('gstrtpbin')
        self.rtpbin.set_property('latency', self.link_config.jitter_buffer)
        self.rtpbin.set_property('autoremove', True)
        self.rtpbin.set_property('do-lost', True)
        # Where audio comes in
        self.udpsrc_rtpin = gst.element_factory_make('udpsrc')
        self.udpsrc_rtpin.set_property('port', self.link_config.port)
        if self.link_config.multicast:
            self.udpsrc_rtpin.set_property('auto_multicast', True)
            self.udpsrc_rtpin.set_property('multicast_group',
                                           self.link_config.receiver_host)
        caps = caps.replace('\\', '')
        # Fix for gstreamer bug in rtpopuspay fixed in GST-plugins-bad
        # 50140388d2b62d32dd9d0c071e3051ebc5b4083b, bug 686547
        if self.link_config.encoding == 'opus':
            caps = re.sub(r'(caps=.+ )', '', caps)
        udpsrc_caps = gst.caps_from_string(caps)
        self.udpsrc_rtpin.set_property('caps', udpsrc_caps)
        self.udpsrc_rtpin.set_property('timeout', 3000000)

        # Our level monitor, also used for continuous audio
        self.level = gst.element_factory_make("level")
        self.level.set_property('message', True)
        self.level.set_property('interval', 1000000000)

        # And now we've got it all set up we need to add the elements
        self.pipeline.add(self.audiorate, self.audioresample,
                          self.audioconvert, self.sink, self.level,
                          self.depayloader, self.rtpbin, self.udpsrc_rtpin)
        if self.link_config.encoding != 'pcm':
            self.pipeline.add(self.decoder)
            gst.element_link_many(self.depayloader, self.decoder,
                                  self.audioconvert)
        else:
            gst.element_link_many(self.depayloader, self.audioconvert)
        gst.element_link_many(self.audioconvert, self.audioresample,
                              self.audiorate, self.level, self.sink)
        # Now the RTP pads
        self.udpsrc_rtpin.link_pads('src', self.rtpbin, 'recv_rtp_sink_0')

        # Attach callbacks for dynamic pads (RTP output) and busses
        self.rtpbin.connect('pad-added', self.rtpbin_pad_added)
        self.bus.add_signal_watch()

    # Our RTPbin won't give us an audio pad till it receives, so we need to
    # attach it here
    def rtpbin_pad_added(self, obj, pad):
        # Unlink first.
        self.rtpbin.unlink(self.depayloader)
        # Relink
        self.rtpbin.link(self.depayloader)

    def on_message(self, bus, message):
        if message.type == gst.MESSAGE_ELEMENT:
            if message.structure.get_name() == 'level':
                if self.started is False:
                    self.started = True
                    if len(message.structure['peak']) == 1:
                        self.logger.info("Receiving mono audio transmission")
                    else:
                        self.logger.info("Receiving stereo audio transmission")

            if message.structure.get_name() == 'GstUDPSrcTimeout':
                # Only UDP source configured to emit timeouts is the audio
                # input
                self.logger.critical("No data received for 3 seconds!")
                if self.started:
                    self.logger.critical("Shutting down receiver for restart")
                    self.pipeline.set_state(gst.STATE_NULL)
                    self.loop.quit()
        return True

    def run(self):
        self.pipeline.set_state(gst.STATE_PLAYING)

    def loop(self):
        self.loop = gobject.MainLoop()
        self.loop.run()
Ejemplo n.º 20
0
class LinkConfig(object):
    """
        The LinkConfig class encapsulates link configuration. It's genderless;
        a TX node should be able to set up a new link and an RX node should be
        able (once the TX node has specified the port caps) to configure itself
        to receive the stream using the data and methods in this config.
    """
    def __init__(self, link_name, redis_host):
        """
            Set up a new LinkConfig instance - needs to know the link name and
            configuration host.
        """
        self.int_properties = [
            'port', 'jitter_buffer', 'opus_framesize', 'opus_complexity',
            'bitrate', 'opus_loss_expectation'
        ]
        self.bool_properties = ['opus_dtx', 'opus_fec', 'multicast']
        self.link_name = link_name
        self.redis_host = redis_host
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('link.%s.config' %
                                                    self.link_name)
        self.logger.info("Connecting to configuration host %s" %
                         self.redis_host)
        self.redis = None
        while True:
            try:
                self.redis = redis.StrictRedis(host=self.redis_host,
                                               charset="utf-8",
                                               decode_responses=True)
                break
            except Exception as e:
                self.logger.error(
                    "Unable to connect to configuration host! Retrying. (%s)" %
                    e)
                time.sleep(0.1)

    def blocking_get(self, key):
        """Get a value, blocking until it's not None if needed"""
        while True:
            value = self.get(key)
            if value is not None:
                self.logger.debug("Fetched (blocking) %s, got %s" %
                                  (key, value))
                return value
            time.sleep(0.1)

    def set(self, key, value):
        """Set a value in the config store"""
        scoped_key = self.scoped_key(key)

        if key in self.bool_properties:
            value = int(value)

        self.redis.set(scoped_key, value)
        self.logger.debug("Set %s to %s" % (scoped_key, value))
        return value

    def get(self, key):
        """Get a value from the config store"""
        scoped_key = self.scoped_key(key)
        value = self.redis.get(scoped_key)

        # Do some typecasting
        if key in self.int_properties:
            value = int(value)
        if key in self.bool_properties:
            value = (value == 'True')
        self.logger.debug("Fetched %s, got %s" % (scoped_key, value))
        return value

    def unset(self, key):
        scoped_key = self.scoped_key(key)
        self.redis.delete(scoped_key)
        self.logger.debug("Unset %s" % scoped_key)

    def __getattr__(self, key):
        """Convenience method to access get"""
        return self.get(key)

    def scoped_key(self, key):
        """Return an appropriate key name scoped to a link"""
        return ("openob:%s:%s" % (self.link_name, key))

    def set_from_argparse(self, opts):
        """Given an optparse object from bin/openob, configure this link"""
        self.set("name", opts.link_name)
        if opts.mode == "tx":
            self.set("port", opts.port)
            self.set("jitter_buffer", opts.jitter_buffer)
            self.set("encoding", opts.encoding)
            self.set("bitrate", opts.bitrate)
            self.set("multicast", opts.multicast)
            self.set("input_samplerate", opts.samplerate)
            self.set("receiver_host", opts.receiver_host)
            self.set("opus_framesize", opts.framesize)
            self.set("opus_complexity", opts.complexity)
            self.set("opus_fec", opts.fec)
            self.set("opus_loss_expectation", opts.loss)
            self.set("opus_dtx", opts.dtx)

    def commit_changes(self, restart=False):
        """
            To be called after calls to set() on a running link to signal
            a reconfiguration event for that link. If restart is True, the link
            should simply terminate itself so it can be restarted with the new
            parameters. If restart is False, the link should set all parameters
            it can which do not involve a restart.
        """
        raise (NotImplementedError,
               "Link reconfiguration is not yet implemented")
Ejemplo n.º 21
0
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP receiver"""
        self.started = False
        self.pipeline = gst.Pipeline("rx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger(
            'node.%s.link.%s.%s' %
            (node_name, self.link_config.name, self.audio_interface.mode))
        caps = self.link_config.get("caps")
        # Audio output
        if self.audio_interface.type == 'auto':
            self.sink = gst.element_factory_make("autoaudiosink")
        elif self.audio_interface.type == 'alsa':
            self.sink = gst.element_factory_make("alsasink")
            self.sink.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            self.sink = gst.element_factory_make("jackaudiosink")
            if self.audio_interface.jack_auto:
                self.sink.set_property('connect', 'auto')
            else:
                self.sink.set_property('connect', 'none')
            self.sink.set_property('name', self.audio_interface.jack_name)
        # Audio conversion and resampling
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioresample.set_property('quality', 9)
        self.audiorate = gst.element_factory_make("audiorate")

        # Decoding and depayloading
        if self.link_config.encoding == 'opus':
            self.decoder = gst.element_factory_make("opusdec", "decoder")
            self.decoder.set_property('use-inband-fec', True)  # FEC
            self.decoder.set_property('plc', True)  # Packet loss concealment
            self.depayloader = gst.element_factory_make(
                "rtpopusdepay", "depayloader")
        elif self.link_config.encoding == 'pcm':
            self.depayloader = gst.element_factory_make(
                "rtpL16depay", "depayloader")

        # RTP stuff
        self.rtpbin = gst.element_factory_make('gstrtpbin')
        self.rtpbin.set_property('latency', self.link_config.jitter_buffer)
        self.rtpbin.set_property('autoremove', True)
        self.rtpbin.set_property('do-lost', True)
        # Where audio comes in
        self.udpsrc_rtpin = gst.element_factory_make('udpsrc')
        self.udpsrc_rtpin.set_property('port', self.link_config.port)
        if self.link_config.multicast:
            self.udpsrc_rtpin.set_property('auto_multicast', True)
            self.udpsrc_rtpin.set_property('multicast_group',
                                           self.link_config.receiver_host)
        caps = caps.replace('\\', '')
        # Fix for gstreamer bug in rtpopuspay fixed in GST-plugins-bad
        # 50140388d2b62d32dd9d0c071e3051ebc5b4083b, bug 686547
        if self.link_config.encoding == 'opus':
            caps = re.sub(r'(caps=.+ )', '', caps)
        udpsrc_caps = gst.caps_from_string(caps)
        self.udpsrc_rtpin.set_property('caps', udpsrc_caps)
        self.udpsrc_rtpin.set_property('timeout', 3000000)

        # Our level monitor, also used for continuous audio
        self.level = gst.element_factory_make("level")
        self.level.set_property('message', True)
        self.level.set_property('interval', 1000000000)

        # And now we've got it all set up we need to add the elements
        self.pipeline.add(self.audiorate, self.audioresample,
                          self.audioconvert, self.sink, self.level,
                          self.depayloader, self.rtpbin, self.udpsrc_rtpin)
        if self.link_config.encoding != 'pcm':
            self.pipeline.add(self.decoder)
            gst.element_link_many(self.depayloader, self.decoder,
                                  self.audioconvert)
        else:
            gst.element_link_many(self.depayloader, self.audioconvert)
        gst.element_link_many(self.audioconvert, self.audioresample,
                              self.audiorate, self.level, self.sink)
        # Now the RTP pads
        self.udpsrc_rtpin.link_pads('src', self.rtpbin, 'recv_rtp_sink_0')

        # Attach callbacks for dynamic pads (RTP output) and busses
        self.rtpbin.connect('pad-added', self.rtpbin_pad_added)
        self.bus.add_signal_watch()
Ejemplo n.º 22
0
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP receiver"""
        self.started = False
        self.pipeline = gst.Pipeline("rx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.link.%s.%s' % (node_name, self.link_config.name, self.audio_interface.mode))
        caps = self.link_config.get("caps")
        # Audio output
        if self.audio_interface.type == 'auto':
            self.sink = gst.element_factory_make("autoaudiosink")
        elif self.audio_interface.type == 'alsa':
            self.sink = gst.element_factory_make("alsasink")
            self.sink.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            self.sink = gst.element_factory_make("jackaudiosink")
            if self.audio_interface.jack_auto:
                self.sink.set_property('connect', 'auto')
            else:
                self.sink.set_property('connect', 'none')
            self.sink.set_property('name', self.audio_interface.jack_name)
        # Audio conversion and resampling
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioresample.set_property('quality', 9)
        self.audiorate = gst.element_factory_make("audiorate")

        # Decoding and depayloading
        if self.link_config.encoding == 'opus':
            self.decoder = gst.element_factory_make("opusdec", "decoder")
            self.decoder.set_property('use-inband-fec', True)  # FEC
            self.decoder.set_property('plc', True)  # Packet loss concealment
            self.depayloader = gst.element_factory_make(
                "rtpopusdepay", "depayloader")
        elif self.link_config.encoding == 'pcm':
            self.depayloader = gst.element_factory_make(
                "rtpL16depay", "depayloader")

        # RTP stuff
        self.rtpbin = gst.element_factory_make('gstrtpbin')
        self.rtpbin.set_property('latency', self.link_config.jitter_buffer)
        self.rtpbin.set_property('autoremove', True)
        self.rtpbin.set_property('do-lost', True)
        # Where audio comes in
        self.udpsrc_rtpin = gst.element_factory_make('udpsrc')
        self.udpsrc_rtpin.set_property('port', self.link_config.port)
        if self.link_config.multicast:
            self.udpsrc_rtpin.set_property('auto_multicast', True)
            self.udpsrc_rtpin.set_property('multicast_group', self.link_config.receiver_host)
        caps = caps.replace('\\', '')
        # Fix for gstreamer bug in rtpopuspay fixed in GST-plugins-bad
        # 50140388d2b62d32dd9d0c071e3051ebc5b4083b, bug 686547
        if self.link_config.encoding == 'opus':
            caps = re.sub(r'(caps=.+ )', '', caps)
        udpsrc_caps = gst.caps_from_string(caps)
        self.udpsrc_rtpin.set_property('caps', udpsrc_caps)
        self.udpsrc_rtpin.set_property('timeout', 3000000)

        # Our level monitor, also used for continuous audio
        self.level = gst.element_factory_make("level")
        self.level.set_property('message', True)
        self.level.set_property('interval', 1000000000)

        # And now we've got it all set up we need to add the elements
        self.pipeline.add(
            self.audiorate, self.audioresample, self.audioconvert, self.sink,
            self.level, self.depayloader, self.rtpbin, self.udpsrc_rtpin)
        if self.link_config.encoding != 'pcm':
            self.pipeline.add(self.decoder)
            gst.element_link_many(
                self.depayloader, self.decoder, self.audioconvert)
        else:
            gst.element_link_many(self.depayloader, self.audioconvert)
        gst.element_link_many(
            self.audioconvert, self.audioresample, self.audiorate, self.level,
            self.sink)
        # Now the RTP pads
        self.udpsrc_rtpin.link_pads('src', self.rtpbin, 'recv_rtp_sink_0')

        # Attach callbacks for dynamic pads (RTP output) and busses
        self.rtpbin.connect('pad-added', self.rtpbin_pad_added)
        self.bus.add_signal_watch()
Ejemplo n.º 23
0
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP transmitter"""
        self.started = False
        self.caps = 'None'
        self.pipeline = gst.Pipeline("tx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger(
            'node.%s.link.%s.%s' %
            (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info("Starting up RTP transmitter")
        # Audio input
        if self.audio_interface.type == 'auto':
            self.source = gst.element_factory_make('autoaudiosrc')
        elif self.audio_interface.type == 'alsa':
            self.source = gst.element_factory_make('alsasrc')
            self.source.set_property('device',
                                     self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            self.source = gst.element_factory_make("jackaudiosrc")
            if self.audio_interface.jack_auto:
                self.source.set_property('connect', 'auto')
            else:
                self.source.set_property('connect', 'none')
            self.source.set_property('name', self.audio_interface.jack_name)
        # Audio conversion and resampling
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioresample.set_property('quality', 9)  # SRC
        self.audiorate = gst.element_factory_make("audiorate")

        # Encoding and payloading
        if self.link_config.encoding == 'opus':
            self.encoder = gst.element_factory_make("opusenc", "encoder")
            self.encoder.set_property('bitrate',
                                      int(self.link_config.bitrate) * 1000)
            self.encoder.set_property('frame-size',
                                      self.link_config.opus_framesize)
            self.encoder.set_property('complexity',
                                      int(self.link_config.opus_complexity))
            self.encoder.set_property('inband-fec', self.link_config.opus_fec)
            self.encoder.set_property(
                'packet-loss-percentage',
                int(self.link_config.opus_loss_expectation))
            self.encoder.set_property('dtx', self.link_config.opus_dtx)
            self.payloader = gst.element_factory_make("rtpopuspay",
                                                      "payloader")
        elif self.link_config.encoding == 'pcm':
            # we have no encoder for PCM operation
            self.payloader = gst.element_factory_make("rtpL16pay", "payloader")
        else:
            self.logger.critical("Unknown encoding type %s" %
                                 self.link_config.encoding)
        # TODO: Add a tee here, and sort out creating multiple UDP sinks for multipath
        # Now the RTP bits
        # We'll send audio out on this
        self.udpsink_rtpout = gst.element_factory_make("udpsink",
                                                       "udpsink_rtp")
        self.udpsink_rtpout.set_property('host',
                                         self.link_config.receiver_host)
        self.udpsink_rtpout.set_property('port', self.link_config.port)
        if self.link_config.multicast:
            self.udpsink_rtpout.set_property('auto_multicast', True)

        # Our RTP manager
        self.rtpbin = gst.element_factory_make("gstrtpbin", "gstrtpbin")

        # Our level monitor
        self.level = gst.element_factory_make("level")
        self.level.set_property('message', True)
        self.level.set_property('interval', 1000000000)

        # Add a capsfilter to allow specification of input sample rate
        self.capsfilter = gst.element_factory_make("capsfilter")

        # Add to the pipeline
        self.pipeline.add(self.source, self.capsfilter, self.audioconvert,
                          self.audioresample, self.audiorate, self.payloader,
                          self.udpsink_rtpout, self.rtpbin, self.level)

        if self.link_config.encoding != 'pcm':
            # Only add the encoder if we're not in PCM mode
            self.pipeline.add(self.encoder)

        # Decide which format to apply to the capsfilter (Jack uses float)
        if self.audio_interface.type == 'jack':
            type = 'audio/x-raw-float'
        else:
            type = 'audio/x-raw-int'

        # if audio_rate has been specified, then add that to the capsfilter
        if self.audio_interface.samplerate != 0:
            self.capsfilter.set_property(
                "caps",
                gst.Caps('%s, channels=2, rate=%d' %
                         (type, self.audio_interface.samplerate)))
        else:
            self.capsfilter.set_property("caps",
                                         gst.Caps('%s, channels=2' % type))

        # Then continue linking the pipeline together
        gst.element_link_many(self.source, self.capsfilter, self.level,
                              self.audioresample, self.audiorate,
                              self.audioconvert)

        # Now we get to link this up to our encoder/payloader
        if self.link_config.encoding != 'pcm':
            gst.element_link_many(self.audioconvert, self.encoder,
                                  self.payloader)
        else:
            gst.element_link_many(self.audioconvert, self.payloader)

        # And now the RTP bits
        self.payloader.link_pads('src', self.rtpbin, 'send_rtp_sink_0')
        self.rtpbin.link_pads('send_rtp_src_0', self.udpsink_rtpout, 'sink')
        # self.udpsrc_rtcpin.link_pads('src', self.rtpbin, 'recv_rtcp_sink_0')
        # # RTCP SRs
        # self.rtpbin.link_pads('send_rtcp_src_0', self.udpsink_rtcpout, 'sink')
        # Connect our bus up
        self.bus.add_signal_watch()
        self.bus.connect('message', self.on_message)
Ejemplo n.º 24
0
argv = sys.argv
sys.argv = []
from openob.logger import LoggerFactory
from openob.node import Node
from openob.link_config import LinkConfig
from openob.audio_interface import AudioInterface

sys.argv = argv

Config = ConfigParser.ConfigParser()
Config.read("/home/pi/openob-gui/outstreamer.ini")

if len(sys.argv) > 1 and sys.argv[1] == 'autostart':
    if Config.get("outstreamer", "Boot") != '1':
        print "Autostart off"
        sys.exit()

Log_Level = logging.INFO  #INFO / DEBUG
logger_factory = LoggerFactory(level=Log_Level)
link_config = LinkConfig("transmission", Config.get("outstreamer",
                                                    "Encoder_IP"))
link_config.set("jitter_buffer", Config.get("outstreamer", "Jitter_Buffer"))
audio_interface = AudioInterface("recepteur")
link_config.set("port", Config.get("outstreamer", "Listen_Port"))
audio_interface.set("mode", "rx")
audio_interface.set("type", "alsa")
audio_interface.set("alsa_device",
                    "hw:" + Config.get("outstreamer", "Soundcard_ID"))
node = Node("recepteur")
node.run_link(link_config, audio_interface)
Ejemplo n.º 25
0
class Node(object):
    """
        OpenOB node instance.

        Nodes run links. Each Node looks after its end of a link, ensuring
        that it remains running and tries to recover from failures, as well as
        responding to configuration changes.

        Nodes have a name; everything else is link specific.

        For instance, a node might be the 'studio' node, which would run a
        'tx' end for the 'stl' link.

        Nodes have a config host which is where they store their inter-Node
        data and communicate with other Nodes.
    """
    def __init__(self, node_name):
        """Set up a new node."""
        self.node_name = node_name
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s' % self.node_name)

    def run_link(self, link_config, audio_interface):
        """
          Run a new TX or RX node.
        """
        # We're now entering the realm where we should desperately try and
        # maintain a link under all circumstances forever.
        self.logger.info("Link %s initial setup start on %s" %
                         (link_config.name, self.node_name))
        link_logger = self.logger_factory.getLogger(
            'node.%s.link.%s' % (self.node_name, link_config.name))
        while True:
            try:
                if audio_interface.mode == 'tx':
                    try:
                        link_logger.info("Starting up transmitter")
                        transmitter = RTPTransmitter(self.node_name,
                                                     link_config,
                                                     audio_interface)
                        transmitter.run()
                        caps = transmitter.get_caps()
                        link_logger.debug(
                            "Got caps from transmitter, setting config")
                        link_config.set("caps", caps)
                        transmitter.loop()
                    except ElementNotFoundError as e:
                        link_logger.critical(
                            "GStreamer element missing: %s - will now exit" %
                            e)
                        sys.exit(1)
                    except Exception as e:
                        link_logger.exception(
                            "Transmitter crashed for some reason! Restarting..."
                        )
                        time.sleep(0.5)
                elif audio_interface.mode == 'rx':
                    link_logger.info("Waiting for transmitter capabilities...")
                    caps = link_config.blocking_get("caps")
                    link_logger.info("Got caps from transmitter")
                    try:
                        link_logger.info("Starting up receiver")
                        receiver = RTPReceiver(self.node_name, link_config,
                                               audio_interface)
                        receiver.run()
                        receiver.loop()
                    except ElementNotFoundError as e:
                        link_logger.critical(
                            "GStreamer element missing: %s - will now exit" %
                            e)
                        sys.exit(1)
                    except Exception as e:
                        link_logger.exception(
                            "Receiver crashed for some reason! Restarting...")
                        time.sleep(0.1)
                else:
                    link_logger.critical("Unknown audio interface mode (%s)!" %
                                         audio_interface.mode)
                    sys.exit(1)
            except Exception as e:
                link_logger.exception(
                    "Unknown exception thrown - please report this as a bug! %s"
                    % e)
                raise
Ejemplo n.º 26
0
class RTPTransmitter(object):
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP transmitter"""

        self.link_config = link_config
        self.audio_interface = audio_interface

        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger(
            'node.%s.link.%s.%s' %
            (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info('Creating transmission pipeline')

        self.build_pipeline()

    def run(self):
        self.pipeline.set_state(Gst.State.PLAYING)
        # Gst.debug_bin_to_dot_file(self.pipeline, Gst.DebugGraphDetails.ALL, 'tx-graph')

        while self.caps == None:
            caps = self.transport.get_by_name('udpsink').get_static_pad(
                'sink').get_property('caps')

            if caps == None:
                self.logger.warn('Waiting for audio interface/caps')
                time.sleep(0.1)
            else:
                self.caps = caps.to_string()

    def loop(self):
        try:
            loop = GLib.MainLoop()
            loop.run()
        except Exception as e:
            self.logger.exception(
                'Encountered a problem in the MainLoop, tearing down the pipeline: %s'
                % e)
            self.pipeline.set_state(Gst.State.NULL)

    def build_pipeline(self):
        self.pipeline = Gst.Pipeline.new('tx')

        self.started = False
        self.caps = None

        bus = self.pipeline.get_bus()

        self.source = self.build_audio_interface()
        self.encoder = self.build_encoder()
        self.transport = self.build_transport()

        self.pipeline.add(self.source)
        self.pipeline.add(self.encoder)
        self.pipeline.add(self.transport)
        self.source.link(self.encoder)
        self.encoder.link(self.transport)

        # Connect our bus up
        bus.add_signal_watch()
        bus.connect('message', self.on_message)

    def build_audio_interface(self):
        self.logger.debug('Building audio input bin')
        bin = Gst.Bin.new('audio')

        # Audio input
        if self.audio_interface.type == 'auto':
            source = Gst.ElementFactory.make('autoaudiosrc')
        elif self.audio_interface.type == 'alsa':
            source = Gst.ElementFactory.make('alsasrc')
            source.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            source = Gst.ElementFactory.make('jackaudiosrc')
            if self.audio_interface.jack_auto:
                source.set_property('connect', 'auto')
            else:
                source.set_property('connect', 'none')
            source.set_property('buffer-time', 50000)
            source.set_property('name', self.audio_interface.jack_name)
            source.set_property('client-name', self.audio_interface.jack_name)
            if self.audio_interface.jack_port_pattern:
                source.set_property('port-pattern',
                                    self.audio_interface.jack_port_pattern)

        elif self.audio_interface.type == 'test':
            source = Gst.ElementFactory.make('audiotestsrc')

        bin.add(source)

        # Our level monitor
        level = Gst.ElementFactory.make('level')
        level.set_property('message', True)
        level.set_property('interval', 1000000000)
        bin.add(level)

        # Audio resampling and conversion
        resample = Gst.ElementFactory.make('audioresample')
        resample.set_property('quality', 9)  # SRC
        bin.add(resample)

        convert = Gst.ElementFactory.make('audioconvert')
        bin.add(convert)

        # Add a capsfilter to allow specification of input sample rate
        capsfilter = Gst.ElementFactory.make('capsfilter')

        caps = Gst.Caps.new_empty_simple('audio/x-raw')

        # if audio_rate has been specified, then add that to the capsfilter
        if self.audio_interface.samplerate != 0:
            caps.set_value('rate', self.audio_interface.samplerate)

        self.logger.debug(caps.to_string())
        capsfilter.set_property('caps', caps)
        bin.add(capsfilter)

        source.link(level)
        level.link(resample)
        resample.link(convert)
        convert.link(capsfilter)

        bin.add_pad(Gst.GhostPad.new('src', capsfilter.get_static_pad('src')))

        return bin

    def build_encoder(self):
        self.logger.debug('Building encoder bin')
        bin = Gst.Bin.new('encoder')

        # Encoding and payloading
        if self.link_config.encoding == 'opus':
            encoder = Gst.ElementFactory.make('opusenc', 'encoder')
            encoder.set_property('bitrate',
                                 int(self.link_config.bitrate) * 1000)
            encoder.set_property('tolerance', 80000000)
            encoder.set_property('frame-size', self.link_config.opus_framesize)
            encoder.set_property('complexity',
                                 int(self.link_config.opus_complexity))
            encoder.set_property('inband-fec', self.link_config.opus_fec)
            encoder.set_property('packet-loss-percentage',
                                 int(self.link_config.opus_loss_expectation))
            encoder.set_property('dtx', self.link_config.opus_dtx)

            payloader = Gst.ElementFactory.make('rtpopuspay', 'payloader')
        elif self.link_config.encoding == 'pcm':
            # we have no encoder for PCM operation
            payloader = Gst.ElementFactory.make('rtpL16pay', 'payloader')
        else:
            self.logger.critical('Unknown encoding type %s' %
                                 self.link_config.encoding)

        bin.add(payloader)

        if 'encoder' in locals():
            bin.add(encoder)
            encoder.link(payloader)
            bin.add_pad(
                Gst.GhostPad.new('sink', encoder.get_static_pad('sink')))
        else:
            bin.add_pad(
                Gst.GhostPad.new('sink', payloader.get_static_pad('sink')))

        bin.add_pad(Gst.GhostPad.new('src', payloader.get_static_pad('src')))

        return bin

    def build_transport(self):
        self.logger.debug('Building RTP transport bin')
        bin = Gst.Bin.new('transport')

        # Our RTP manager
        rtpbin = Gst.ElementFactory.make('rtpbin', 'rtpbin')
        rtpbin.set_property('latency', 0)
        bin.add(rtpbin)

        # TODO: Add a tee here, and sort out creating multiple UDP sinks for multipath
        udpsink = Gst.ElementFactory.make('udpsink', 'udpsink')
        udpsink.set_property('host', self.link_config.receiver_host)
        udpsink.set_property('port', self.link_config.port)
        self.logger.info(
            'Set receiver to %s:%i' %
            (self.link_config.receiver_host, self.link_config.port))

        if self.link_config.multicast:
            udpsink.set_property('auto_multicast', True)
            self.logger.info('Multicast mode enabled')
        bin.add(udpsink)

        bin.add_pad(
            Gst.GhostPad.new('sink',
                             rtpbin.get_request_pad('send_rtp_sink_0')))

        rtpbin.link_pads('send_rtp_src_0', udpsink, 'sink')

        return bin

    def on_message(self, bus, message):
        if message.type == Gst.MessageType.ELEMENT:
            struct = message.get_structure()
            if struct != None:
                if struct.get_name() == 'level':
                    if self.started is False:
                        self.started = True
                        if len(struct.get_value('peak')) == 1:
                            self.logger.info('Started mono audio transmission')
                        else:
                            self.logger.info(
                                'Started stereo audio transmission')
                    else:
                        if len(struct.get_value('peak')) == 1:
                            self.logger.debug('Level: %.2f',
                                              struct.get_value('peak')[0])
                        else:
                            self.logger.debug('Levels: L %.2f R %.2f' %
                                              (struct.get_value('peak')[0],
                                               struct.get_value('peak')[1]))
        return True

    def get_caps(self):
        return self.caps
Ejemplo n.º 27
0
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP receiver"""
        self.started = False
        self.pipeline = gst.Pipeline("rx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger(
            "node.%s.link.%s.%s" % (node_name, self.link_config.name, self.audio_interface.mode)
        )
        self.logger.info("Creating RTP reception pipeline")
        caps = self.link_config.get("caps")
        # Audio output
        if self.audio_interface.type == "auto":
            self.sink = gst.element_factory_make("autoaudiosink")
        elif self.audio_interface.type == "alsa":
            self.sink = gst.element_factory_make("alsasink")
            self.sink.set_property("device", self.audio_interface.alsa_device)
        elif self.audio_interface.type == "jack":
            self.sink = gst.element_factory_make("jackaudiosink")
            if self.audio_interface.jack_auto:
                self.sink.set_property("connect", "auto")
            else:
                self.sink.set_property("connect", "none")
            self.sink.set_property("name", self.audio_interface.jack_name)
            self.sink.set_property("client-name", self.audio_interface.jack_name)

        # Audio resampling and conversion
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample.set_property("quality", 6)

        # Decoding and depayloading
        if self.link_config.encoding == "opus":
            self.decoder = gst.element_factory_make("opusdec", "decoder")
            self.decoder.set_property("use-inband-fec", True)  # FEC
            self.decoder.set_property("plc", True)  # Packet loss concealment
            self.depayloader = gst.element_factory_make("rtpopusdepay", "depayloader")
        elif self.link_config.encoding == "pcm":
            self.depayloader = gst.element_factory_make("rtpL16depay", "depayloader")

        # RTP stuff
        self.rtpbin = gst.element_factory_make("gstrtpbin")
        self.rtpbin.set_property("latency", self.link_config.jitter_buffer)
        self.rtpbin.set_property("autoremove", True)
        self.rtpbin.set_property("do-lost", True)
        # self.rtpbin.set_property('buffer-mode', 1)
        # Where audio comes in
        self.udpsrc_rtpin = gst.element_factory_make("udpsrc")
        self.udpsrc_rtpin.set_property("port", self.link_config.port)
        if self.link_config.multicast:
            self.udpsrc_rtpin.set_property("auto_multicast", True)
            self.udpsrc_rtpin.set_property("multicast_group", self.link_config.receiver_host)
            self.logger.info("Multicast mode enabled")
        caps = caps.replace("\\", "")
        # Fix for gstreamer bug in rtpopuspay fixed in GST-plugins-bad
        # 50140388d2b62d32dd9d0c071e3051ebc5b4083b, bug 686547
        if self.link_config.encoding == "opus":
            caps = re.sub(r"(caps=.+ )", "", caps)
        udpsrc_caps = gst.caps_from_string(caps)
        self.udpsrc_rtpin.set_property("caps", udpsrc_caps)
        self.udpsrc_rtpin.set_property("timeout", 3000000)

        # Our level monitor, also used for continuous audio
        self.level = gst.element_factory_make("level")
        self.level.set_property("message", True)
        self.level.set_property("interval", 1000000000)

        # And now we've got it all set up we need to add the elements
        self.pipeline.add(
            self.audioconvert,
            self.audioresample,
            self.sink,
            self.level,
            self.depayloader,
            self.rtpbin,
            self.udpsrc_rtpin,
        )
        if self.link_config.encoding != "pcm":
            self.pipeline.add(self.decoder)
            gst.element_link_many(self.depayloader, self.decoder, self.audioconvert)
        else:
            gst.element_link_many(self.depayloader, self.audioconvert)
        gst.element_link_many(self.audioconvert, self.audioresample, self.level, self.sink)
        self.logger.debug(self.sink)
        # Now the RTP pads
        self.udpsrc_rtpin.link_pads("src", self.rtpbin, "recv_rtp_sink_0")

        # Attach callbacks for dynamic pads (RTP output) and busses
        self.rtpbin.connect("pad-added", self.rtpbin_pad_added)
        self.bus.add_signal_watch()
Ejemplo n.º 28
0
Archivo: tx.py Proyecto: deedos/openob
class RTPTransmitter(object):

    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP transmitter"""
        self.started = False
        self.caps = 'None'
        self.pipeline = gst.Pipeline("tx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.link.%s.%s' % (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info("Starting up RTP transmitter")
        # Audio input
        if self.audio_interface.type == 'auto':
            self.source = gst.element_factory_make('autoaudiosrc')
        elif self.audio_interface.type == 'alsa':
            self.source = gst.element_factory_make('alsasrc')
            self.source.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            self.source = gst.element_factory_make("jackaudiosrc")
            if self.audio_interface.jack_auto:
                self.source.set_property('connect', 'auto')
            else:
                self.source.set_property('connect', 'none')
            self.source.set_property('name', self.audio_interface.jack_name)
        # Audio conversion and resampling
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioresample.set_property('quality', 9)  # SRC
        self.audiorate = gst.element_factory_make("audiorate")

        # Encoding and payloading
        if self.link_config.encoding == 'opus':
            self.encoder = gst.element_factory_make("opusenc", "encoder")
            self.encoder.set_property('bitrate', int(self.link_config.bitrate) * 1000)
            self.encoder.set_property('frame-size', self.link_config.opus_framesize)
            self.encoder.set_property('complexity', int(self.link_config.opus_complexity))
            self.encoder.set_property('inband-fec', self.link_config.opus_fec)
            self.encoder.set_property('packet-loss-percentage', int(self.link_config.opus_loss_expectation))
            self.encoder.set_property('dtx', self.link_config.opus_dtx)
            self.payloader = gst.element_factory_make("rtpopuspay", "payloader")
        elif self.link_config.encoding == 'pcm':
            # we have no encoder for PCM operation
            self.payloader = gst.element_factory_make("rtpL16pay", "payloader")
        else:
            self.logger.critical("Unknown encoding type %s" % self.link_config.encoding)
        # TODO: Add a tee here, and sort out creating multiple UDP sinks for multipath
        # Now the RTP bits
        # We'll send audio out on this
        self.udpsink_rtpout = gst.element_factory_make(
            "udpsink", "udpsink_rtp")
        self.udpsink_rtpout.set_property('host', self.link_config.receiver_host)
        self.udpsink_rtpout.set_property('port', self.link_config.port)
        if self.link_config.multicast:
            self.udpsink_rtpout.set_property('auto_multicast', True)

        # Our RTP manager
        self.rtpbin = gst.element_factory_make("gstrtpbin", "gstrtpbin")

        # Our level monitor
        self.level = gst.element_factory_make("level")
        self.level.set_property('message', True)
        self.level.set_property('interval', 1000000000)

        # Add a capsfilter to allow specification of input sample rate
        self.capsfilter = gst.element_factory_make("capsfilter")

        # Add to the pipeline
        self.pipeline.add(
            self.source, self.capsfilter, self.audioconvert, self.audioresample,
            self.audiorate, self.payloader, self.udpsink_rtpout, self.rtpbin,
            self.level)

        if self.link_config.encoding != 'pcm':
            # Only add the encoder if we're not in PCM mode
            self.pipeline.add(self.encoder)

        # Decide which format to apply to the capsfilter (Jack uses float)
        if self.audio_interface.type == 'jack':
            type = 'audio/x-raw-float'
        else:
            type = 'audio/x-raw-int'

        # if audio_rate has been specified, then add that to the capsfilter
        if self.audio_interface.samplerate != 0:
            self.capsfilter.set_property(
                "caps", gst.Caps('%s, channels=2, rate=%d' % (type, self.audio_interface.samplerate)))
        else:
            self.capsfilter.set_property(
                "caps", gst.Caps('%s, channels=2' % type))

        # Then continue linking the pipeline together
        gst.element_link_many(
            self.source, self.capsfilter, self.level, self.audioresample,
            self.audiorate, self.audioconvert)

        # Now we get to link this up to our encoder/payloader
        if self.link_config.encoding != 'pcm':
            gst.element_link_many(
                self.audioconvert, self.encoder, self.payloader)
        else:
            gst.element_link_many(self.audioconvert, self.payloader)

        # And now the RTP bits
        self.payloader.link_pads('src', self.rtpbin, 'send_rtp_sink_0')
        self.rtpbin.link_pads('send_rtp_src_0', self.udpsink_rtpout, 'sink')
        # self.udpsrc_rtcpin.link_pads('src', self.rtpbin, 'recv_rtcp_sink_0')
        # # RTCP SRs
        # self.rtpbin.link_pads('send_rtcp_src_0', self.udpsink_rtcpout, 'sink')
        # Connect our bus up
        self.bus.add_signal_watch()
        self.bus.connect('message', self.on_message)

    def run(self):
        self.pipeline.set_state(gst.STATE_PLAYING)
        while self.caps == 'None':
            self.logger.warn("Waiting for audio interface/caps")
            self.logger.debug(self.udpsink_rtpout.get_state())
            self.caps = str(
                self.udpsink_rtpout.get_pad('sink').get_property('caps'))
            # Fix for gstreamer bug in rtpopuspay fixed in GST-plugins-bad
            # 50140388d2b62d32dd9d0c071e3051ebc5b4083b, bug 686547
            if self.link_config.encoding == 'opus':
                self.caps = re.sub(r'(caps=.+ )', '', self.caps)
            time.sleep(0.1)

    def loop(self):
        try:
            self.loop = gobject.MainLoop()
            self.loop.run()
        except Exception, e:
            self.logger.exception("Encountered a problem in the MainLoop, tearing down the pipeline")
            self.pipeline.set_state(gst.STATE_NULL)
Ejemplo n.º 29
0
class RTPReceiver(object):
    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP receiver"""
        self.started = False
        self.pipeline = gst.Pipeline("rx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger(
            "node.%s.link.%s.%s" % (node_name, self.link_config.name, self.audio_interface.mode)
        )
        self.logger.info("Creating RTP reception pipeline")
        caps = self.link_config.get("caps")
        # Audio output
        if self.audio_interface.type == "auto":
            self.sink = gst.element_factory_make("autoaudiosink")
        elif self.audio_interface.type == "alsa":
            self.sink = gst.element_factory_make("alsasink")
            self.sink.set_property("device", self.audio_interface.alsa_device)
        elif self.audio_interface.type == "jack":
            self.sink = gst.element_factory_make("jackaudiosink")
            if self.audio_interface.jack_auto:
                self.sink.set_property("connect", "auto")
            else:
                self.sink.set_property("connect", "none")
            self.sink.set_property("name", self.audio_interface.jack_name)
            self.sink.set_property("client-name", self.audio_interface.jack_name)

        # Audio resampling and conversion
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample.set_property("quality", 6)

        # Decoding and depayloading
        if self.link_config.encoding == "opus":
            self.decoder = gst.element_factory_make("opusdec", "decoder")
            self.decoder.set_property("use-inband-fec", True)  # FEC
            self.decoder.set_property("plc", True)  # Packet loss concealment
            self.depayloader = gst.element_factory_make("rtpopusdepay", "depayloader")
        elif self.link_config.encoding == "pcm":
            self.depayloader = gst.element_factory_make("rtpL16depay", "depayloader")

        # RTP stuff
        self.rtpbin = gst.element_factory_make("gstrtpbin")
        self.rtpbin.set_property("latency", self.link_config.jitter_buffer)
        self.rtpbin.set_property("autoremove", True)
        self.rtpbin.set_property("do-lost", True)
        # self.rtpbin.set_property('buffer-mode', 1)
        # Where audio comes in
        self.udpsrc_rtpin = gst.element_factory_make("udpsrc")
        self.udpsrc_rtpin.set_property("port", self.link_config.port)
        if self.link_config.multicast:
            self.udpsrc_rtpin.set_property("auto_multicast", True)
            self.udpsrc_rtpin.set_property("multicast_group", self.link_config.receiver_host)
            self.logger.info("Multicast mode enabled")
        caps = caps.replace("\\", "")
        # Fix for gstreamer bug in rtpopuspay fixed in GST-plugins-bad
        # 50140388d2b62d32dd9d0c071e3051ebc5b4083b, bug 686547
        if self.link_config.encoding == "opus":
            caps = re.sub(r"(caps=.+ )", "", caps)
        udpsrc_caps = gst.caps_from_string(caps)
        self.udpsrc_rtpin.set_property("caps", udpsrc_caps)
        self.udpsrc_rtpin.set_property("timeout", 3000000)

        # Our level monitor, also used for continuous audio
        self.level = gst.element_factory_make("level")
        self.level.set_property("message", True)
        self.level.set_property("interval", 1000000000)

        # And now we've got it all set up we need to add the elements
        self.pipeline.add(
            self.audioconvert,
            self.audioresample,
            self.sink,
            self.level,
            self.depayloader,
            self.rtpbin,
            self.udpsrc_rtpin,
        )
        if self.link_config.encoding != "pcm":
            self.pipeline.add(self.decoder)
            gst.element_link_many(self.depayloader, self.decoder, self.audioconvert)
        else:
            gst.element_link_many(self.depayloader, self.audioconvert)
        gst.element_link_many(self.audioconvert, self.audioresample, self.level, self.sink)
        self.logger.debug(self.sink)
        # Now the RTP pads
        self.udpsrc_rtpin.link_pads("src", self.rtpbin, "recv_rtp_sink_0")

        # Attach callbacks for dynamic pads (RTP output) and busses
        self.rtpbin.connect("pad-added", self.rtpbin_pad_added)
        self.bus.add_signal_watch()

    # Our RTPbin won't give us an audio pad till it receives, so we need to
    # attach it here
    def rtpbin_pad_added(self, obj, pad):
        # Unlink first.
        self.rtpbin.unlink(self.depayloader)
        # Relink
        self.rtpbin.link(self.depayloader)

    def on_message(self, bus, message):
        if message.type == gst.MESSAGE_ELEMENT:
            if message.structure.get_name() == "level":
                if self.started is False:
                    self.started = True
                    # gst.DEBUG_BIN_TO_DOT_FILE(self.pipeline, gst.DEBUG_GRAPH_SHOW_ALL, 'rx-graph')
                    if len(message.structure["peak"]) == 1:
                        self.logger.info("Receiving mono audio transmission")
                    else:
                        self.logger.info("Receiving stereo audio transmission")

            if message.structure.get_name() == "GstUDPSrcTimeout":
                # Only UDP source configured to emit timeouts is the audio
                # input
                self.logger.critical("No data received for 3 seconds!")
                if self.started:
                    self.logger.critical("Shutting down receiver for restart")
                    self.pipeline.set_state(gst.STATE_NULL)
                    self.loop.quit()
        return True

    def run(self):
        self.pipeline.set_state(gst.STATE_PLAYING)
        self.logger.info("Listening for stream on %s:%i" % (self.link_config.receiver_host, self.link_config.port))

    def loop(self):
        self.loop = gobject.MainLoop()
        self.loop.run()
Ejemplo n.º 30
0
class Node(object):

    """
        OpenOB node instance.

        Nodes run links. Each Node looks after its end of a link, ensuring
        that it remains running and tries to recover from failures, as well as
        responding to configuration changes.

        Nodes have a name; everything else is link specific.

        For instance, a node might be the 'studio' node, which would run a
        'tx' end for the 'stl' link.

        Nodes have a config host which is where they store their inter-Node
        data and communicate with other Nodes.
    """

    def __init__(self, node_name):
        """Set up a new node."""
        self.node_name = node_name
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s' % self.node_name)

    def run_link(self, link_config, audio_interface):
        """
          Run a new TX or RX node.
        """
        # We're now entering the realm where we should desperately try and
        # maintain a link under all circumstances forever.
        self.logger.info("Link %s initial setup start on %s" % (link_config.name, self.node_name))
        link_logger = self.logger_factory.getLogger('node.%s.link.%s' % (self.node_name, link_config.name))
        while True:
            try:
                if audio_interface.mode == 'tx':
                    try:
                        link_logger.info("Starting up transmitter")
                        transmitter = RTPTransmitter(self.node_name, link_config, audio_interface)
                        transmitter.run()
                        caps = transmitter.get_caps()
                        link_logger.debug("Got caps from transmitter, setting config")
                        link_config.set("caps", caps)
                        transmitter.loop()
                    except ElementNotFoundError as e:
                        link_logger.critical("GStreamer element missing: %s - will now exit" % e)
                        sys.exit(1)
                    except Exception as e:
                        link_logger.exception("Transmitter crashed for some reason! Restarting...")
                        time.sleep(0.5)
                elif audio_interface.mode == 'rx':
                    link_logger.info("Waiting for transmitter capabilities...")
                    caps = link_config.blocking_get("caps")
                    link_logger.info("Got caps from transmitter")
                    try:
                        link_logger.info("Starting up receiver")
                        receiver = RTPReceiver(self.node_name, link_config, audio_interface)
                        receiver.run()
                        receiver.loop()
                    except ElementNotFoundError as e:
                        link_logger.critical("GStreamer element missing: %s - will now exit" % e)
                        sys.exit(1)
                    except Exception as e:
                        link_logger.exception("Receiver crashed for some reason! Restarting...")
                        time.sleep(0.1)
                else:
                    link_logger.critical("Unknown audio interface mode (%s)!" % audio_interface.mode)
                    sys.exit(1)
            except Exception as e:
                link_logger.exception("Unknown exception thrown - please report this as a bug! %s" % e)
                raise
Ejemplo n.º 31
0
sys.argv = []
from openob.logger import LoggerFactory
from openob.node import Node
from openob.link_config import LinkConfig
from openob.audio_interface import AudioInterface
sys.argv = argv

Config = configparser.ConfigParser()
Config.read("/home/pi/openob-gui/instreamer.ini")

if len(sys.argv) > 1 and sys.argv[1] == 'autostart':
    if Config.get("instreamer", "Boot") != '1':
        print("Autostart off")
        sys.exit()

logger_factory = LoggerFactory(level=logging.INFO)
link_config = LinkConfig("transmission", Config.get("instreamer",
                                                    "Encoder_IP"))
audio_interface = AudioInterface("emetteur")

link_config.set("port", Config.get("instreamer", "Listen_Port"))
link_config.set("bitrate", int(Config.get("instreamer", "Bitrate")))
link_config.set("encoding", Config.get("instreamer", "Encoding"))
link_config.set("opus_framesize", "20")
link_config.set("opus_complexity", "9")
link_config.set("opus_loss_expectation", "0")
link_config.set("jitter_buffer", "40")
link_config.set("receiver_host", Config.get("instreamer", "Receiver_IP"))
audio_interface.set("mode", "tx")
audio_interface.set("samplerate", int(Config.get("instreamer", "Samplerate")))
audio_interface.set("type", "alsa")
Ejemplo n.º 32
0
class RTPReceiver(object):

    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP receiver"""
        self.started = False
        self.pipeline = gst.Pipeline("rx")
        self.bus = self.pipeline.get_bus()
        self.bus.connect("message", self.on_message)
        self.link_config = link_config
        self.audio_interface = audio_interface
        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.link.%s.%s' % (node_name, self.link_config.name, self.audio_interface.mode))
        caps = self.link_config.get("caps")
        # Audio output
        if self.audio_interface.type == 'auto':
            self.sink = gst.element_factory_make("autoaudiosink")
        elif self.audio_interface.type == 'alsa':
            self.sink = gst.element_factory_make("alsasink")
            self.sink.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            self.sink = gst.element_factory_make("jackaudiosink")
            if self.audio_interface.jack_auto:
                self.sink.set_property('connect', 'auto')
            else:
                self.sink.set_property('connect', 'none')
            self.sink.set_property('name', self.audio_interface.jack_name)
        # Audio conversion and resampling
        self.audioconvert = gst.element_factory_make("audioconvert")
        self.audioresample = gst.element_factory_make("audioresample")
        self.audioresample.set_property('quality', 9)
        self.audiorate = gst.element_factory_make("audiorate")

        # Decoding and depayloading
        if self.link_config.encoding == 'opus':
            self.decoder = gst.element_factory_make("opusdec", "decoder")
            self.decoder.set_property('use-inband-fec', True)  # FEC
            self.decoder.set_property('plc', True)  # Packet loss concealment
            self.depayloader = gst.element_factory_make(
                "rtpopusdepay", "depayloader")
        elif self.link_config.encoding == 'pcm':
            self.depayloader = gst.element_factory_make(
                "rtpL16depay", "depayloader")

        # RTP stuff
        self.rtpbin = gst.element_factory_make('gstrtpbin')
        self.rtpbin.set_property('latency', self.link_config.jitter_buffer)
        self.rtpbin.set_property('autoremove', True)
        self.rtpbin.set_property('do-lost', True)
        # Where audio comes in
        self.udpsrc_rtpin = gst.element_factory_make('udpsrc')
        self.udpsrc_rtpin.set_property('port', self.link_config.port)
        if self.link_config.multicast:
            self.udpsrc_rtpin.set_property('auto_multicast', True)
            self.udpsrc_rtpin.set_property('multicast_group', self.link_config.receiver_host)
        caps = caps.replace('\\', '')
        # Fix for gstreamer bug in rtpopuspay fixed in GST-plugins-bad
        # 50140388d2b62d32dd9d0c071e3051ebc5b4083b, bug 686547
        if self.link_config.encoding == 'opus':
            caps = re.sub(r'(caps=.+ )', '', caps)
        udpsrc_caps = gst.caps_from_string(caps)
        self.udpsrc_rtpin.set_property('caps', udpsrc_caps)
        self.udpsrc_rtpin.set_property('timeout', 3000000)

        # Our level monitor, also used for continuous audio
        self.level = gst.element_factory_make("level")
        self.level.set_property('message', True)
        self.level.set_property('interval', 1000000000)

        # And now we've got it all set up we need to add the elements
        self.pipeline.add(
            self.audiorate, self.audioresample, self.audioconvert, self.sink,
            self.level, self.depayloader, self.rtpbin, self.udpsrc_rtpin)
        if self.link_config.encoding != 'pcm':
            self.pipeline.add(self.decoder)
            gst.element_link_many(
                self.depayloader, self.decoder, self.audioconvert)
        else:
            gst.element_link_many(self.depayloader, self.audioconvert)
        gst.element_link_many(
            self.audioconvert, self.audioresample, self.audiorate, self.level,
            self.sink)
        # Now the RTP pads
        self.udpsrc_rtpin.link_pads('src', self.rtpbin, 'recv_rtp_sink_0')

        # Attach callbacks for dynamic pads (RTP output) and busses
        self.rtpbin.connect('pad-added', self.rtpbin_pad_added)
        self.bus.add_signal_watch()

    # Our RTPbin won't give us an audio pad till it receives, so we need to
    # attach it here
    def rtpbin_pad_added(self, obj, pad):
        # Unlink first.
        self.rtpbin.unlink(self.depayloader)
        # Relink
        self.rtpbin.link(self.depayloader)

    def on_message(self, bus, message):
        if message.type == gst.MESSAGE_ELEMENT:
            if message.structure.get_name() == 'level':
                if self.started is False:
                    self.started = True
                    if len(message.structure['peak']) == 1:
                        self.logger.info("Receiving mono audio transmission")
                    else:
                        self.logger.info("Receiving stereo audio transmission")

            if message.structure.get_name() == 'GstUDPSrcTimeout':
                # Only UDP source configured to emit timeouts is the audio
                # input
                self.logger.critical("No data received for 3 seconds!")
                if self.started:
                    self.logger.critical("Shutting down receiver for restart")
                    self.pipeline.set_state(gst.STATE_NULL)
                    self.loop.quit()
        return True

    def run(self):
        self.pipeline.set_state(gst.STATE_PLAYING)

    def loop(self):
        self.loop = gobject.MainLoop()
        self.loop.run()
Ejemplo n.º 33
0
class RTPReceiver(object):

    def __init__(self, node_name, link_config, audio_interface):
        """Sets up a new RTP receiver"""
    
        self.link_config = link_config
        self.audio_interface = audio_interface

        self.logger_factory = LoggerFactory()
        self.logger = self.logger_factory.getLogger('node.%s.link.%s.%s' % (node_name, self.link_config.name, self.audio_interface.mode))
        self.logger.info('Creating reception pipeline')

        self.build_pipeline()

    def run(self):
        self.pipeline.set_state(Gst.State.PLAYING)
        self.logger.info('Listening for stream on %s:%i' % (self.link_config.receiver_host, self.link_config.port))

    def loop(self):
        try:
            self.main_loop = GLib.MainLoop()
            self.main_loop.run()
        except Exception as e:
            self.logger.exception('Encountered a problem in the MainLoop, tearing down the pipeline: %s' % e)
            self.pipeline.set_state(Gst.State.NULL)

    def build_pipeline(self):
        self.pipeline = Gst.Pipeline.new('rx')
        
        self.started = False
        bus = self.pipeline.get_bus()
        
        self.transport = self.build_transport()
        self.decoder = self.build_decoder()
        self.output = self.build_audio_interface()

        self.pipeline.add(self.transport)
        self.pipeline.add(self.decoder)
        self.pipeline.add(self.output)
        self.transport.link(self.decoder)
        self.decoder.link(self.output)

        bus.add_signal_watch()
        bus.connect('message', self.on_message)

    def build_audio_interface(self):
        self.logger.debug('Building audio output bin')
        bin = Gst.Bin.new('audio')

        # Audio output
        if self.audio_interface.type == 'auto':
            sink = Gst.ElementFactory.make('autoaudiosink')
        elif self.audio_interface.type == 'alsa':
            sink = Gst.ElementFactory.make('alsasink')
            sink.set_property('device', self.audio_interface.alsa_device)
        elif self.audio_interface.type == 'jack':
            sink = Gst.ElementFactory.make('jackaudiosink')
            if self.audio_interface.jack_auto:
                sink.set_property('connect', 'auto')
            else:
                sink.set_property('connect', 'none')
            sink.set_property('name', self.audio_interface.jack_name)
            sink.set_property('client-name', self.audio_interface.jack_name)
            if self.audio_interface.jack_port_pattern:
                sink.set_property('port-pattern', self.audio_interface.jack_port_pattern)
        elif self.audio_interface.type == 'test':
            sink = Gst.ElementFactory.make('fakesink')

        bin.add(sink)
        
        # Audio resampling and conversion
        resample = Gst.ElementFactory.make('audioresample')
        resample.set_property('quality', 9)
        bin.add(resample)

        convert = Gst.ElementFactory.make('audioconvert')
        bin.add(convert)

        # Our level monitor, also used for continuous audio
        level = Gst.ElementFactory.make('level')
        level.set_property('message', True)
        level.set_property('interval', 1000000000)
        bin.add(level)

        resample.link(convert)
        convert.link(level)
        level.link(sink)

        bin.add_pad(Gst.GhostPad.new('sink', resample.get_static_pad('sink')))

        return bin

    def build_decoder(self):
        self.logger.debug('Building decoder bin')
        bin = Gst.Bin.new('decoder')

        # Decoding and depayloading
        if self.link_config.encoding == 'opus':
            decoder = Gst.ElementFactory.make('opusdec', 'decoder')
            decoder.set_property('use-inband-fec', True)  # FEC
            decoder.set_property('plc', True)  # Packet loss concealment
            depayloader = Gst.ElementFactory.make(
                'rtpopusdepay', 'depayloader')
        elif self.link_config.encoding == 'pcm':
            depayloader = Gst.ElementFactory.make(
                'rtpL16depay', 'depayloader')
        else:
            self.logger.critical('Unknown encoding type %s' % self.link_config.encoding)
        
        bin.add(depayloader)

        bin.add_pad(Gst.GhostPad.new('sink', depayloader.get_static_pad('sink')))

        if 'decoder' in locals():
            bin.add(decoder)
            depayloader.link(decoder)
            bin.add_pad(Gst.GhostPad.new('src', decoder.get_static_pad('src')))
        else:
            bin.add_pad(Gst.GhostPad.new('src', depayloader.get_static_pad('src')))

        return bin

    def build_transport(self):
        self.logger.debug('Building RTP transport bin')
        bin = Gst.Bin.new('transport')

        caps = self.link_config.get('caps').replace('\\', '')
        udpsrc_caps = Gst.Caps.from_string(caps)
        
        # Where audio comes in
        udpsrc = Gst.ElementFactory.make('udpsrc', 'udpsrc')
        udpsrc.set_property('port', self.link_config.port)
        udpsrc.set_property('caps', udpsrc_caps)
        udpsrc.set_property('timeout', 3000000000)
        if self.link_config.multicast:
            udpsrc.set_property('auto_multicast', True)
            udpsrc.set_property('multicast_group', self.link_config.receiver_host)
            self.logger.info('Multicast mode enabled')
        bin.add(udpsrc)

        rtpbin = Gst.ElementFactory.make('rtpbin', 'rtpbin')
        rtpbin.set_property('latency', self.link_config.jitter_buffer)
        rtpbin.set_property('autoremove', True)
        rtpbin.set_property('do-lost', True)
        bin.add(rtpbin)

        udpsrc.link_pads('src', rtpbin, 'recv_rtp_sink_0')

        valve = Gst.ElementFactory.make('valve', 'valve')
        bin.add(valve)
        
        bin.add_pad(Gst.GhostPad.new('src', valve.get_static_pad('src')))
        # Attach callbacks for dynamic pads (RTP output) and busses
        rtpbin.connect('pad-added', self.rtpbin_pad_added)

        return bin

    # Our RTPbin won't give us an audio pad till it receives, so we need to
    # attach it here
    def rtpbin_pad_added(self, obj, pad):
        valve = self.transport.get_by_name('valve')
        rtpbin = self.transport.get_by_name('rtpbin')

        # Unlink first.
        rtpbin.unlink(valve)
        # Relink
        rtpbin.link(valve)

    def on_message(self, bus, message):
        if message.type == Gst.MessageType.ELEMENT:
            struct = message.get_structure()
            if struct != None:
                if struct.get_name() == 'level':
                    if self.started is False:
                        self.started = True
                        if len(struct.get_value('peak')) == 1:
                            self.logger.info('Receiving mono audio transmission')
                        else:
                            self.logger.info('Receiving stereo audio transmission')
                    else:
                        if len(struct.get_value('peak')) == 1:
                            self.logger.debug('Level: %.2f', struct.get_value('peak')[0])
                        else:
                            self.logger.debug('Levels: L %.2f R %.2f' % (struct.get_value('peak')[0], struct.get_value('peak')[1]))

                if struct.get_name() == 'GstUDPSrcTimeout':
                    # Gst.debug_bin_to_dot_file(self.pipeline, Gst.DebugGraphDetails.ALL, 'rx-graph')                    
                    # Only UDP source configured to emit timeouts is the audio input
                    self.logger.critical('No data received for 3 seconds!')
                    if self.started:
                        self.logger.critical('Shutting down receiver for restart')
                        self.pipeline.set_state(Gst.State.NULL)
                        self.main_loop.quit()
        return True