读者的背景故事
在聊天和评论中建立:
cat temp.mp3 | omxplayer -o local --vol -500 /dev/stdin
导致段错误。
omxplayer -o local --vol -500 /dev/fd/3 3< <(cat temp.mp3)
工作正常。
因此,我们可以在...中传递 MP3 的数据,但不能在标准输入上(omxplayer
用于控制:暂停、提前退出等)。
方法 1:使用 Shell 进行文件描述符整理
这相当于“方法 3”,但不是使用非常新的和现代的 Python 功能来进行进程内的 FD 争吵,而是启动一个副本/bin/sh
来完成工作(因此将与更旧的 Python 版本一起使用)。
play_from_stdin_sh = '''
exec 3<&0 # copy stdin to FD 3
exec </dev/tty || exec </dev/null # make stdin now be /dev/tty or /dev/null
exec omxplayer -o local --vol -500 /dev/fd/3 # play data from FD 3
'''
p = subprocess.Popen(['sh', '-c', play_from_stdin_sh], stdin=subprocess.POPEN)
p.communicate(song) # passes data in "song" as stdin to the copy of sh
因为omxplayer
期望使用标准输入从它的用户那里获取指令,我们需要使用不同的文件描述符来传递它的内容。因此,当我们让 Python 解释器在 stdin 上传递内容时,我们有一个 shell 将 stdin 复制到 FD 3 并用句柄替换原始 stdin,/dev/tty
或者/dev/null
在调用omxplayer
.
方法 2:使用命名管道
关于这是否在“不写入磁盘”约束上作弊存在一些问题。它不会将任何 MP3 数据写入磁盘,但它确实创建了一个文件系统对象,两个进程都可以打开该文件系统对象作为相互连接的一种方式,即使写入该对象的数据直接在进程之间流动,而不是写入磁盘。
import tempfile, os, os.path, shutil, subprocess
fifo_dir = None
try:
fifo_dir = tempfile.mkdtemp('mp3-fifodir')
fifo_name = os.path.join(fifo_dir, 'fifo.mp3')
os.mkfifo(fifo_name)
# now, we start omxplayer, and tell it to read from the FIFO
# as long as it opens it in read mode, it should just hang until something opens
# ...the write side of the FIFO, writes content to it, and then closes it.
p = subprocess.Popen(['omxplayer', '-o', 'local', '--vol', '-500', fifo_name])
# this doesn't actually write content to a file on disk! instead, it's written directly
# ...to the omxplayer process's handle on the other side of the FIFO.
fifo_fd = open(fifo_name, 'w')
fifo_fd.write(song)
fifo_fd.close()
p.wait()
finally:
shutil.rmtree(fifo_dir)
方法 3:preexec_fn
在 Python 中使用 A
Popen
我们可以使用对象的preexec_fn
参数来实现方法 1 在本机 Python 中使用 shell 的文件描述符整理。考虑:
import os, subprocess
def move_stdin():
os.dup2(0, 3) # copy our stdin -- FD 0 -- to FD 3
try:
newstdin = open('/dev/tty', 'r') # open /dev/tty...
os.dup2(newstdin.fileno(), 0) # ...and attach it to FD 0.
except IOError:
newstdin = open('/dev/null', 'r') # Couldn't do that? Open /dev/null...
os.dup2(newstdin.fileno(), 0) # ...and attach it to FD 0.
p = subprocess.Popen(['omxplayer', '-o', 'local', '--vol', '-500', '/dev/fd/3'],
stdin=subprocess.PIPE, preexec_fn=move_stdin, pass_fds=[0,1,2,3])
p.communicate(song)