|
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 |