Python pipes库废弃后,如何通过命名管道控制mpg123远程服务?
解决Python控制mpg123命名管道无音频输出问题
背景
之前用Python的pipes库配合mpg123的-R(远程控制)接口实现音频播放,因pipes在Python 3.13+被弃用,改用os.mkfifo创建命名管道,但新代码无法正常播放,无音频输出且无报错。
旧代码逻辑(依赖已弃用的pipes库):
import subprocess,pipes mpgpipe = "/tmp/mpg123remote.cmd" mpgout = "/tmp/mpg123.out" fmpgout=open(mpgout,"w") mpg123proc = subprocess.Popen(["/usr/bin/mpg123", "-vR", "--fifo", mpgpipe ], stdin=subprocess.PIPE, stdout=fmpgout) t = pipes.Template() t.append("echo load \"%s\"" % "test.mp3", '--') f = t.open(mpgpipe, 'w') f.close()
尝试的新代码(用os.mkfifo):
import os, io, subprocess mpgpipe = "/tmp/mpg123remote.cmd" try: os.mkfifo(mpgpipe) print("Named pipe created successfully!") except FileExistsError: print("Named pipe already exists!") except OSError as e: print(f"Named pipe creation failed: {e}") fmpgout=open("/tmp/mpg123.out","w") mpg123proc = subprocess.Popen(["/usr/bin/mpg123", "-a", "default", "-vR", "--fifo", mpgpipe ], stdout=fmpgout) pipfd = os.open(mpgpipe, os.O_RDWR | os.O_NONBLOCK) songname = "test.mp3" command=f"load \"{songname}\"\n" with io.FileIO(pipfd, 'wb') as f: f.write(command.encode())
问题现象:
运行后仅输出以下内容,无音频:
High Performance MPEG 1.0/2.0/2.5 Audio Player for Layers 1, 2 and 3 version 1.32.9; written and copyright by Michael Hipp and others free software (LGPL) without any warranty but with best wishes Decoder: x86-64 (AVX) Trying output module: alsa, device: default
注:直接在终端执行mpg123 test.mp3可正常播放,音频设备无问题。
解决方案
方案1:修复命名管道的使用问题
问题出在非阻塞模式和管道打开方式:
- 非阻塞模式下,写入命令后mpg123可能还没来得及读取,管道就被关闭,导致命令未被处理
- 不需要
O_RDWR,只用O_WRONLY即可
修正后的代码:
import os, subprocess mpgpipe = "/tmp/mpg123remote.cmd" # 确保管道不存在时创建,存在则先删除(避免旧管道残留问题) try: os.remove(mpgpipe) except OSError: pass try: os.mkfifo(mpgpipe) print("Named pipe created successfully!") except OSError as e: print(f"Named pipe creation failed: {e}") exit(1) # 启动mpg123,绑定命名管道作为控制输入 fmpgout = open("/tmp/mpg123.out", "w") mpg123proc = subprocess.Popen( ["/usr/bin/mpg123", "-a", "default", "-vR", "--fifo", mpgpipe], stdout=fmpgout, stderr=subprocess.STDOUT ) # 以阻塞模式打开管道,写入命令 songname = "test.mp3" command = f"load \"{songname}\"\n" # 用普通open打开管道(默认阻塞模式),替代os.open+FileIO with open(mpgpipe, 'w') as f: f.write(command) # 可选:保持进程运行,否则主程序退出可能导致mpg123终止 # mpg123proc.wait()
方案2:直接用subprocess的stdin发送命令(更简洁)
mpg123的-R模式默认从标准输入读取命令,无需额外创建命名管道,直接通过subprocess.Popen的stdin发送命令即可:
import subprocess # 启动mpg123,开启远程控制模式 fmpgout = open("/tmp/mpg123.out", "w") mpg123proc = subprocess.Popen( ["/usr/bin/mpg123", "-a", "default", "-vR"], stdin=subprocess.PIPE, stdout=fmpgout, stderr=subprocess.STDOUT, text=True # 用文本模式,无需手动编码 ) # 发送播放命令 songname = "test.mp3" mpg123proc.stdin.write(f"load \"{songname}\"\n") mpg123proc.stdin.flush() # 确保命令被发送到mpg123 # 可选:保持进程运行 # mpg123proc.wait()
说明
- 方案2无需处理命名管道的创建和同步问题,更稳定简洁
- 若需要多进程控制mpg123,方案1的命名管道更合适,但需注意管道的读写同步
内容的提问来源于stack exchange,提问作者alessandro
相关产品推荐
相关产品推荐

