postlund / pyatv

A client library for Apple TV and AirPlay devices
https://pyatv.dev
MIT License
892 stars 99 forks source link

Live audio stream #2526

Open xXxNIKIxXx opened 1 month ago

xXxNIKIxXx commented 1 month ago

What do you need help with?

Is there or will there be a possibility to just feed an pyaudio stream to stream.stream_file? This is to get the stream:

# Parameters
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024

# Initialize PyAudio
p = pyaudio.PyAudio()

# Open stream
stream = p.open(format=FORMAT,
                channels=CHANNELS,
                rate=RATE,
                input=True,
                frames_per_buffer=CHUNK)
postlund commented 1 month ago

That's not something I have considered implementing. But you can pass an up.BufferedIOBase, maybe you can write an adapter that supports pyaudio through that?

xXxNIKIxXx commented 1 month ago

I had the same Idea but always get mediafile.FileTypeError: 'stream': not in a recognized format. Here is the function i use


import pyaudio
import threading
import io

# Parameters
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 44100
CHUNK = 1024

class BufferedAudioStream(io.BufferedIOBase):
    def __init__(self, format, channels, rate, chunk):
        self.p = pyaudio.PyAudio()
        self.stream = self.p.open(format=format,
                                  channels=channels,
                                  rate=rate,
                                  input=True,
                                  frames_per_buffer=chunk)
        self.chunk = chunk
        self.buffer = bytearray()
        self.lock = threading.Lock()
        self.running = True

    def read(self, size=-1):
        with self.lock:
            if size == -1:
                size = len(self.buffer)
            data = self.buffer[:size]
            self.buffer = self.buffer[size:]
            return bytes(data)

    def update_buffer(self):
        while self.running:
            data = self.stream.read(self.chunk)
            with self.lock:
                self.buffer.extend(data)

    def close(self):
        self.running = False
        self.stream.stop_stream()
        self.stream.close()
        self.p.terminate()

# Initialize BufferedAudioStream
audio_stream = BufferedAudioStream(FORMAT, CHANNELS, RATE, CHUNK)

# Start the buffer update thread
buffer_thread = threading.Thread(target=audio_stream.update_buffer)
buffer_thread.start()

await atv.stream.stream_file(audio_stream)
xXxNIKIxXx commented 1 month ago

i have also tried using lameenc to convert the stream into mp3 format. But this also didn't work.

postlund commented 1 month ago

Metadata detection is failing (for some reason). Try passing some bogus metadata to see if that helps (it will disable auto detection).

https://pyatv.dev/development/stream/#custom-metadata

xXxNIKIxXx commented 1 month ago

Unfortunately this didn't work for me

postlund commented 1 month ago

Can you provide a more detailed exception of what is happening?

xXxNIKIxXx commented 1 month ago

Yes. When I use the code above, I get the following error: mediafile.FileTypeError: 'stream': not in a recognized format.

This is probably because the stream doesn't have any header information to indicate what format it is. Then I tried using it as a stream reader, but I got the same error.

With the library lameenc, I tried to convert the stream into an MP3 stream, but it didn't work because the MP3 information was provided in every frame, not just once. I also tried using the metadata to set the information, but that didn’t work either.

xXxNIKIxXx commented 3 weeks ago

Do you have an idea how to implement this? Or have an idea what the problem is?

postlund commented 3 weeks ago

I believe you are on to the problem already. pyatv expects a proper container format (e.g. mp3 or ogg) and cannot decode raw PCM frames at the moment. That could probably be implemented in some intricate way, but I can't say how I would do it right now. I will have to think about it a bit.