0

我读过已回答的类似问题:

这是我的窗口:

播放进度.gif

问题是当计数器还有 20% 的时候歌曲结束。我知道原因主要是由于系统调用检查进程是否仍然pgrep ffplay每秒运行 10 次。次要原因仅仅是 Python 和 Tkinter 开销。

为了“创可贴修复”问题,我使用了1.24分秒而不是1每分秒,正如我的代码现在说明的那样:

def play_to_end(self):
    ''' 
    Play single song, checking status every decisecond
    Called from:
        self.play_forever() to start a new song
        self.pp_toggle() to restart song after pausing
    '''
    while True:
        if not self.top2_is_active: return          # Play window closed?
        root.update()                               # Process other events
        if self.pp_state is "Paused": 
            time.sleep(.1)                          # Wait until playing
            continue

        PID = os.popen("pgrep ffplay").read()       # Get PID for ffplay
        if len(PID) < 2:                            # Has song ended?
            return                                  # Song has ended

        #self.current_song_time += .1                # Add decisecond
        self.current_song_time += .124              # Add 1.24 deciseconds
                                                    #  compensatation .24
        self.current_progress.set(str('%.1f' % self.current_song_time) + \
                    " seconds of: " + str(self.DurationSecs))
        root.update()                               # Process other events
        root.after(100)                             # Sleep 1 decisecond

这个创可贴修复的问题是它高度依赖机器。例如,我的机器是 Skylake。此外,它高度依赖于同时运行的其他进程。测试我的机器负载相对较轻:

播放进度开销.gif

我如何以编程方式计算损失的时间,以便准确地增加经过的时间?

也许有更好的方法来简单地查询ffplay歌曲进度?

顺便说一句(我知道一次问两个问题是不受欢迎的)为什么我不能简单地检查是否PID为空?我尝试过检查等于.rstrip().strip()之后.read()无济于事。如果每个程序下都有一个进程 ID,则会行为不端。PID""Noneffplay10

4

1 回答 1

1

您可以使用subprocess.Popen()执行ffplay并重定向stderr到 PIPE,然后您可以从中读取进度stderr并更新进度标签。

下面是一个例子:

import tkinter as tk
import subprocess as subp
import threading

class MediaPlayer(tk.Tk):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.proc = None
        self.protocol('WM_DELETE_WINDOW', self.quit)

    def init_ui(self):
        self.current_progress = tk.StringVar()
        self.progress = tk.Label(self, textvariable=self.current_progress)
        self.progress.grid(row=0, column=0, columnspan=2)

        btn_close = tk.Button(self, text='Stop', width=20, command=self.stop_playing)
        btn_close.grid(row=1, column=0, sticky='ew')

        btn_play = tk.Button(self, text='Play', width=20, command=self.play_song)
        btn_play.grid(row=1, column=1, sticky='ew')

    def play_to_end(self):
        self.proc = subp.Popen(
            ['ffplay', '-nodisp', '-hide_banner', '-autoexit', self.current_song_path],
            stderr=subp.PIPE, bufsize=1, text=1
        )
        duration = ''
        while self.proc.poll() is None:
            msg = self.proc.stderr.readline().strip()
            if msg:
                if msg.startswith('Duration'):
                    duration = msg.split(',')[0].split(': ')[1]
                else:
                    msg = msg.split()[0]
                    if '.' in msg:
                        elapsed = float(msg)
                        mins, secs = divmod(elapsed, 60)
                        hrs, mins = divmod(mins, 60)
                        self.current_progress.set('Play Progress: {:02d}:{:02d}:{:04.1f} / {}'.format(int(hrs), int(mins), secs, duration))
        print('done')
        self.proc = None

    def play_song(self):
        self.current_song_path = '/path/to/song.mp3'
        if self.proc is None:
            threading.Thread(target=self.play_to_end, daemon=True).start()

    def stop_playing(self):
        if self.proc:
            self.proc.terminate()

    def quit(self):
        self.stop_playing()
        self.destroy()

app = MediaPlayer()
app.mainloop()
于 2020-07-27T11:32:25.740 回答