Events

Connections, channels, tracks and transports fire the events of the specification. Every one of them is an EventTarget.

Handlers

on() registers a handler, called with the event object. It can be a plain function or a coroutine function, and on works as a decorator too:

@pc.on('track')
def on_track(event):
    print('new track:', event.track.kind)


@pc.on('icecandidate')
async def on_candidate(event):
    if event.candidate is not None:
        await signaling.send(event.candidate.to_json())


channel.on('message', lambda event: print(event.data))

The names are the ones of the specification, without the on prefix. A name the object doesnโ€™t fire raises ValueError, which lists the names it does.

Handlers run on the event loop they were registered from, so they have to be registered from a running loop. The native threads only schedule events on that loop. A slow handler delays the events after it, and the media keeps flowing.

One-shot handlers

once() registers a handler that is removed after its first call. To wait for an event inline, resolve a future from it:

opened = asyncio.get_running_loop().create_future()
channel.once('open', lambda event: opened.set_result(event))
await opened

Removing handlers

pc.remove_listener('track', on_track)  # one handler
pc.remove_all_listeners('track')  # every handler of an event
pc.off()  # every handler of every event

listeners() returns the handlers of an event, and event_names() the events that have some. Registering the same handler twice has no effect.

Errors

An exception in a handler doesnโ€™t stop the other handlers. It goes to the exception handler of the loop, which logs it by default. Use asyncio.loop.set_exception_handler() to handle them yourself.