Python中使用vlc库制作播放器,如何获取音频数据并生成音量柱?
最近在使用 wxpython+python-vlc 来做一个直播轮询的软件,中间遇到个问题,有些直播是纯音频内容,那么直播是否正常就需要有音量柱来体现,目前一直卡在这个地方,segmentfault 上提问了,没人解答,想看看 V2EX 又没有大神能指点下。因为 wxpython 比较多,只贴下 vlc 播放的简单代码。
import vlc
import time
player = vlc.MediaPlayer(‘you-video-url’)
player.play()
time.sleep(20)
Python中使用vlc库制作播放器,如何获取音频数据并生成音量柱?
1 回复
要获取VLC播放器的音频数据并生成音量柱,可以用libvlc_audio_set_callbacks配合numpy和matplotlib。下面是个完整示例:
import sys
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.colors import LinearSegmentedColormap
import vlc
class AudioVisualizer:
def __init__(self):
# 初始化VLC实例和播放器
self.instance = vlc.Instance('--no-xlib')
self.player = self.instance.media_player_new()
# 音频参数
self.sample_rate = 44100
self.channels = 2
self.buffer_size = 1024 # 每次回调的数据量
# 存储音频数据
self.audio_data = np.zeros(self.buffer_size)
self.lock = False
# 设置音频回调
self.player.audio_set_callbacks(self._play_cb, self._pause_cb,
self._resume_cb, self._flush_cb,
self._drain_cb)
self.player.audio_set_format_callback(self._setup_cb, self._cleanup_cb)
# 创建图形界面
self.fig, self.ax = plt.subplots(figsize=(10, 4))
self.fig.patch.set_facecolor('#2b2b2b')
self.ax.set_facecolor('#1e1e1e')
# 创建自定义颜色映射(从绿到红)
colors = ['#00ff00', '#ffff00', '#ff0000']
self.cmap = LinearSegmentedColormap.from_list('volume', colors, N=100)
# 初始化柱状图
self.bars = self.ax.bar(range(50), [0]*50,
color=self.cmap(0),
edgecolor='white',
linewidth=0.5)
# 美化图形
self.ax.set_ylim(0, 1)
self.ax.set_xlim(0, 50)
self.ax.set_xticks([])
self.ax.set_yticks([])
self.ax.spines['top'].set_visible(False)
self.ax.spines['right'].set_visible(False)
self.ax.spines['bottom'].set_visible(False)
self.ax.spines['left'].set_visible(False)
self.ax.text(0.5, 1.05, 'Volume Visualization',
transform=self.ax.transAxes,
ha='center', fontsize=14, color='white',
fontweight='bold')
def _setup_cb(self, opaque, format_ptr, rate_ptr, channels_ptr):
"""设置音频格式回调"""
format_ptr.contents.value = vlc.AudioFormat.S16N
rate_ptr.contents.value = self.sample_rate
channels_ptr.contents.value = self.channels
return 0
def _cleanup_cb(self, opaque):
"""清理回调"""
return 0
def _play_cb(self, opaque, samples, count, pts):
"""播放回调 - 在这里获取音频数据"""
if self.lock or count == 0:
return 0
self.lock = True
try:
# 将字节数据转换为numpy数组
audio_array = np.frombuffer(samples[:count*4], dtype=np.int16)
# 转换为浮点数并归一化
if len(audio_array) > 0:
audio_float = audio_array.astype(np.float32) / 32768.0
# 取左右声道的平均值
if self.channels == 2:
audio_float = audio_float.reshape(-1, 2).mean(axis=1)
# 更新音频数据(取最新数据)
self.audio_data = audio_float[:self.buffer_size]
if len(self.audio_data) < self.buffer_size:
self.audio_data = np.pad(self.audio_data,
(0, self.buffer_size - len(self.audio_data)))
finally:
self.lock = False
return count
def _pause_cb(self, opaque, pts):
return 0
def _resume_cb(self, opaque, pts):
return 0
def _flush_cb(self, opaque):
return 0
def _drain_cb(self, opaque):
return 0
def update_plot(self, frame):
"""更新柱状图动画"""
if not self.lock and len(self.audio_data) > 0:
# 计算RMS音量
rms = np.sqrt(np.mean(self.audio_data**2))
# 生成频谱数据(简化版)
fft_data = np.abs(np.fft.rfft(self.audio_data)[:50])
fft_normalized = fft_data / np.max(fft_data) if np.max(fft_data) > 0 else fft_data
# 更新每个柱子的高度和颜色
for i, bar in enumerate(self.bars):
height = fft_normalized[i] if i < len(fft_normalized) else 0
bar.set_height(height)
# 根据音量和位置设置颜色
color_value = min(rms * 2 + i/100, 1.0)
bar.set_color(self.cmap(color_value))
bar.set_edgecolor(self.cmap(color_value))
return self.bars
def play(self, media_path):
"""播放媒体文件"""
media = self.instance.media_new(media_path)
self.player.set_media(media)
self.player.play()
# 启动动画
ani = animation.FuncAnimation(self.fig, self.update_plot,
interval=50, blit=True)
plt.show()
# 使用示例
if __name__ == "__main__":
visualizer = AudioVisualizer()
visualizer.play("your_audio_file.mp3") # 替换为你的音频文件路径
关键点说明:
- 音频回调设置:通过
audio_set_callbacks注册回调函数,_play_cb是获取原始PCM数据的关键 - 数据处理:将16位整数转换为浮点,计算RMS音量,用FFT生成频谱数据
- 可视化:用matplotlib创建动画柱状图,颜色随音量变化
- 线程安全:用锁防止数据竞争
注意:需要先安装python-vlc, numpy, matplotlib。这个方案直接操作音频缓冲区,比用audio_get_volume获取的音量数据更精确。
简单说就是注册回调拿原始音频数据,算RMS和频谱,用matplotlib画动态柱子。

