通过 omxplayer 从 Python 进程的内存中播放 MP3,而无需写入磁盘



以下代码接收一个MP3,将其写入磁盘并使用OMXPlayer播放。我想消除在播放MP3之前将其写入磁盘的需要。

song = response.content
file = open("temp.mp3", "wb")
file.write(song)
file.close()
response.close()
play_song_subprocess = subprocess.call(['omxplayer', '-o', 'local', '--vol', '-500', 'temp.mp3'])

如何消除file.write()?我想做这样的事情:

song = response.content
play_song_subprocess = subprocess.call(['omxplayer', '-o', 'local', '--vol', '-500', song])

但这会导致以下错误:嵌入式空字节

读者后台

建立在聊天和评论:

  • cat temp.mp3 | omxplayer -o local --vol -500 /dev/stdin会导致segfault
  • omxplayer -o local --vol -500 /dev/fd/3 3< <(cat temp.mp3)工作正常

因此,我们可以在…中传递MP3的数据,但不能在stdin上传递(omxplayer用于控制:暂停、提前退出等(。


方法1:使用外壳进行文件描述符包装

这相当于";方法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希望使用stdin从用户那里获取指令,所以我们需要使用不同的文件描述符来传递其内容。因此,当我们让Python解释器在stdin上传递内容时,我们有一个shell将stdin复制到FD3,并在调用omxplayer之前用句柄或/dev/tty/dev/null替换原始stdin。


方法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:在Python中使用preexec_fn

我们可以使用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)

最新更新