0

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?

Victor Silva
  • 723
  • 4
  • 17
Peter
  • 3
  • 3

0 Answers0