aiortc - how to just send a message over a data channel?

208 Views Asked by At

I just want to send a message... browser to server - button presses/whatever. server to browser - ROS information (other than video, e.g., battery level.)

The examples clearly show how to receive and send a message. In the examples/server.py code:

    # pc is an RTCPeerConnection
    @pc.on("datachannel")
    def on_datachannel(channel):
        @channel.on("message")
        def on_message(message):
            if isinstance(message, str) and message.startswith("ping"):
                channel.send("pong" + message[4:])

I've searched and haven't found any 'send' examples that aren't essentially responding to a received "message" (python side) or "onopen" (js). This of course gives full context and resides inside an async function (python side.)

What I'd like to do is just have a simple, global function akin to this:

var dc = None    # global, set in event "datachannel"
def sendMessage(msg)     # This is what I want!   Both python and JS side
    if (dc != None):
        dc.send(msg)

async def offer(request):
    global dc
    ...
   @pc.on("datachannel")
    def on_datachannel(channel):
        global dc
        dc = channel

# and set dc to None if connection fails or ended

This fails -

Exception in thread Thread-4:
Traceback (most recent call last):
  File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
    self.run()
  File "/usr/lib/python3.10/threading.py", line 1378, in run
    self.function(*self.args, **self.kwargs)
  File "/home/pg/workspaces/isaac_ros-dev/src/pywebrtc/pywebrtc/server.py", line 112, in betterPong
    sendMessage("betterPong")
  File "/home/pg/workspaces/isaac_ros-dev/src/pywebrtc/pywebrtc/server.py", line 107, in sendMessage
    dc.send(msg)
  File "/home/pg/.local/lib/python3.10/site-packages/aiortc/rtcdatachannel.py", line 187, in send
    self.transport._data_channel_send(self, data)
  File "/home/pg/.local/lib/python3.10/site-packages/aiortc/rtcsctptransport.py", line 1808, in _data_channel_send
    asyncio.ensure_future(self._data_channel_flush())
  File "/usr/lib/python3.10/asyncio/tasks.py", line 615, in ensure_future
    return _ensure_future(coro_or_future, loop=loop)
  File "/usr/lib/python3.10/asyncio/tasks.py", line 634, in _ensure_future
    loop = events._get_event_loop(stacklevel=4)
  File "/usr/lib/python3.10/asyncio/events.py", line 656, in get_event_loop
    raise RuntimeError('There is no current event loop in thread %r.'
RuntimeError: There is no current event loop in thread 'Thread-4'.
/usr/lib/python3.10/threading.py:1018: RuntimeWarning: coroutine 'RTCSctpTransport._data_channel_flush' was never awaited

I'm a newb on asyncio in Python. If I put async in front of the function and await on the send, I get

server.py:112: RuntimeWarning: coroutine 'sendMessage' was never awaited

Hopefully, this is simple! :) Best!

1

There are 1 best solutions below

0
Peter Gaston On

Here's a working solution for the Python side... Diving down into asyncio and a stackoverflow answer:

dc = None
async def sendMessage(msg):
    if (dc != None):      # dc set to channel in 'on_datachannel'
        dc.send(msg)

# to call, for now a simple timing loop at the outer level
def betterPong():
    asyncio.run( sendMessage("foo foo") )
    threading.Timer(3, betterPong).start()

betterPong()

Works. Is it efficient to create a new coroutine for each send? to be determined...