| | import subprocess |
| |
|
| | import numpy as np |
| |
|
| |
|
| | def ffmpeg_stream(youtube_url, sampling_rate=16_000, chunk_duration_ms=5000, pad_duration_ms=200): |
| | """ |
| | Helper function to read an audio file through ffmpeg. |
| | """ |
| | chunk_len = int(sampling_rate * chunk_duration_ms / 1000) |
| | pad_len = int(sampling_rate * pad_duration_ms / 1000) |
| | read_chunk_len = chunk_len + pad_len * 2 |
| |
|
| | ar = f"{sampling_rate}" |
| | ac = "1" |
| | format_for_conversion = "f32le" |
| | dtype = np.float32 |
| | size_of_sample = 4 |
| |
|
| | ffmpeg_command = [ |
| | "ffmpeg", |
| | "-i", |
| | "pipe:", |
| | "-ac", |
| | ac, |
| | "-ar", |
| | ar, |
| | "-f", |
| | format_for_conversion, |
| | "-hide_banner", |
| | "-loglevel", |
| | "quiet", |
| | "pipe:1", |
| | ] |
| |
|
| | ytdl_command = ["yt-dlp", "-f", "bestaudio", youtube_url, "--quiet", "-o", "-"] |
| |
|
| | try: |
| | ffmpeg_process = subprocess.Popen(ffmpeg_command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, bufsize=-1) |
| | ytdl_process = subprocess.Popen(ytdl_command, stdout=ffmpeg_process.stdin) |
| | except FileNotFoundError: |
| | raise ValueError("ffmpeg was not found but is required to stream audio files from filename") |
| |
|
| | acc = b"" |
| | leftover = np.zeros((0,), dtype=np.float32) |
| | while ytdl_process.poll() is None: |
| | buflen = read_chunk_len * size_of_sample |
| |
|
| | raw = ffmpeg_process.stdout.read(buflen) |
| | if raw == b"": |
| | break |
| |
|
| | if len(acc) + len(raw) > buflen: |
| | acc = raw |
| | else: |
| | acc += raw |
| |
|
| | audio = np.frombuffer(acc, dtype=dtype) |
| | audio = np.concatenate([leftover, audio]) |
| | if len(audio) < pad_len * 2: |
| | |
| | break |
| | yield audio |
| |
|
| | leftover = audio[-pad_len * 2 :] |
| | read_chunk_len = chunk_len |