I prepared an asynchronous python-script using trio to receive internet-radio and playback audio using VLC. The receiver part is based on httpx and is running well. The audio part uses cvlc - the VLC-commandline-version and is started using "await trio.lowlevel.open_process(cmd, stdin=PIPE)". The receiver feeds the audio-data to stdin of cvlc and cvlc is playing audio. After a few seconds audio starts stuttering. And when player and receiver are stopped I get an error at the end of the program:
Exception ignored in: <function _FdHolder.del at 0x7f07f753e170> Traceback (most recent call last): File "/home/tester/anaconda3/lib/python3.10/site-packages/trio/_unix_pipes.py", line 68, in del File "/home/tester/anaconda3/lib/python3.10/site-packages/trio/_unix_pipes.py", line 64, in _raw_close TypeError: 'NoneType' object is not callable
Here is my script test_playback.py:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" Test """
# import multiprocessing
import re
from subprocess import PIPE
import httpx
import trio
VAR = {'Player': None, 'Stop': False, 'audio': []}
CH = 'http://icecast.ndr.de/ndr/ndr2/niedersachsen/mp3/128/stream.mp3'
# --------------------------------------------------------------------------------------------------
async def main():
""" Start all tasks. """
async with trio.open_nursery() as nrs:
nrs.start_soon(receiver)
nrs.start_soon(play_audio)
# --------------------------------------------------------------------------------------------------
async def play_audio():
""" Start the VLC-Player as commandline-tool. """
cmd = 'cvlc --quiet -'.split()
VAR['Player'] = await trio.lowlevel.open_process(cmd, stdin=PIPE)
await trio.sleep(20) # stop after 20 seconds
VAR['Stop'] = True
VAR['Player'].terminate()
# --------------------------------------------------------------------------------------------------
async def receiver():
""" Start receiving internetradio. """
await trio.sleep(1)
btitle = b'\x53\x74\x72\x65\x61\x6d\x54\x69\x74\x6c\x65\x3d' # searchstring: 'StreamTitle='
pos = 0
data = b''
radio = None
await trio.lowlevel.wait_writable(VAR['Player'].stdin) # wait for VLC
while not VAR['Player'] is None and VAR['Player'].poll() is None:
async with httpx.AsyncClient() as client:
if VAR['Stop']:
break
while radio is None:
try:
async with client.stream("GET", CH, headers={'Icy-MetaData': '1'},
follow_redirects=True) as radio:
encoding = radio.headers.get('encoding', 'utf-8')
metaint = int(radio.headers.get('icy-metaint', '0'))
stream = radio.stream
async for chunk in stream:
if VAR['Stop']:
await stream.aclose() # close radiodatastream
break
data += chunk # get new radiodata
while pos < len(data) - metaint: # extract audiodata
try:
await VAR['Player'].stdin.send_all(data[pos : pos + metaint])
except trio.BrokenResourceError:
pass
pos += metaint
metalen = data[pos] * 16
pos += 1
meta_data = data[pos : pos + metalen].rstrip(b'\0')
if metalen and data[pos : pos + 12] == btitle: # streamtitle found
title = re.search(br"StreamTitle='([^']*)';", meta_data)
if not title is None:
title = title.group(1).decode(encoding, errors='replace')
print(f"Title: '{title}'")
pos += metalen
data = data[pos : ] # data for further processing
pos = 0
except (httpx.ConnectTimeout, httpx.ReadTimeout, httpx.ConnectError):
if not stream is None:
await stream.aclose() # close radiodatastream
stream = None
# --------------------------------------------------------------------------------------------------
if __name__ == "__main__":
trio.run(main)
I have prepared a virtual machine in VirtualBox to isolate the issue.
- OS: Linux Mint 21.1 - clean install and all current updates
- VLC installed via application manager
- following installed in terminal: sudo apt install python3-pip pip install httpx pip install trio
After these steps I started the script: python3 test_playback.py
Further information: The error message disappears, if I uncomment the line "# import multiprocesssing". The stuttering is minimal or disappears on a 'real' system, but I still get the error message.
Can anyone help solving the issue?