PyQt线程。动态获取输出

问题描述 投票:1回答:1

我使用PyQt线程通过ffmpeg将mp3文件并行转换为aac。这是我的代码:

class SubprocessThread(QThread):
    signal = pyqtSignal('PyQt_PyObject')

    def __init__(self, command, args):
        QThread.__init__(self)
        self.command = command
        self.args = args

    def __del__(self):
        self.wait()

    def run(self):
        output = subprocess.check_output('{0} {1}'.format(self.command, self.args), shell=True).split()
        self.signal.emit(output)

这是用法示例:

threads = []

for part in parts.keys():
    args = "-i \'{0}.mp3\' -c:a aac -b:a {1}k \'{2}.m4a\'".format(
        os.path.join(tmp_dir, str(part)),
        int(self.bitrate_cbx.currentText()),
        os.path.join(tmp_dir, str(part)))
    print(args)  # debug
    ffmpeg_thread = SubprocessThread('ffmpeg', args)
    ffmpeg_thread.signal.connect(self.on_data_ready)
    threads.append(ffmpeg_thread)
    ffmpeg_thread.start()
    self.threads_count += 1

我想根据转换创建进度条,但是ffmpeg总是在其输出中更新最后一个字符串(转换正在进行中)。这是文件转换时ffmpeg输出的示例:

user@host$ ffmpeg -i '/home/user/001.mp3' -c:a aac -b:a 128k -vn '/home/user/test.m4a' 
ffmpeg version n4.2.1 Copyright (c) 2000-2019 the FFmpeg developers
  built with gcc 9.2.0 (GCC)
  configuration: --prefix=/usr --disable-debug --disable-static --disable-stripping --enable-fontconfig --enable-gmp --enable-gnutls --enable-gpl --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libdav1d --enable-libdrm --enable-libfreetype --enable-libfribidi --enable-libgsm --enable-libiec61883 --enable-libjack --enable-libmodplug --enable-libmp3lame --enable-libopencore_amrnb --enable-libopencore_amrwb --enable-libopenjpeg --enable-libopus --enable-libpulse --enable-libsoxr --enable-libspeex --enable-libssh --enable-libtheora --enable-libv4l2 --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwebp --enable-libx264 --enable-libx265 --enable-libxcb --enable-libxml2 --enable-libxvid --enable-nvdec --enable-nvenc --enable-omx --enable-shared --enable-version3
  libavutil      56. 31.100 / 56. 31.100
  libavcodec     58. 54.100 / 58. 54.100
  libavformat    58. 29.100 / 58. 29.100
  libavdevice    58.  8.100 / 58.  8.100
  libavfilter     7. 57.100 /  7. 57.100
  libswscale      5.  5.100 /  5.  5.100
  libswresample   3.  5.100 /  3.  5.100
  libpostproc    55.  5.100 / 55.  5.100
Input #0, mp3, from '/home/user/001.mp3':
  Metadata:
    encoder         : Lavf57.41.100
    title           : test
    artist          : test
    album_artist    : test
    album           : test
    composer        : test
    genre           : test
    date            : 2018
  Duration: 00:12:38.02, start: 0.025056, bitrate: 192 kb/s
    Stream #0:0: Audio: mp3, 44100 Hz, stereo, fltp, 192 kb/s
    Metadata:
      encoder         : Lavc57.48
    Stream #0:1: Video: mjpeg (Baseline), yuvj420p(pc, bt470bg/unknown/unknown), 500x500 [SAR 1:1 DAR 1:1], 90k tbr, 90k tbn, 90k tbc (attached pic)
    Metadata:
      comment         : Cover (front)
Stream mapping:
  Stream #0:0 -> #0:0 (mp3 (mp3float) -> aac (native))
Press [q] to stop, [?] for help
Output #0, ipod, to '/home/user/test.m4a':
  Metadata:
    date            : test
    title           : test
    artist          : test
    album_artist    : test
    album           : test
    composer        : test
    genre           : test
    encoder         : Lavf58.29.100
    Stream #0:0: Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s
    Metadata:
      encoder         : Lavc58.54.100 aac
size=   12107kB time=00:12:38.01 bitrate= 130.8kbits/s speed=79.2x 

我如何从并行QThreads接收此数据(字符串,从“ size = ...”开始的字符串)以计算总体进度?

python ffmpeg pyqt pyqt5 qthread
1个回答
0
投票

您不必使用subprocess.check_output() + QThread,因为如您所见,您仅在执行完成时获得日志。而是使用QProcess在转换仍在运行时通知日志。

class Converter(QObject):
    dataChanged = pyqtSignal(object)

    def __init__(self, parent=None):
        super().__init__(parent)

        self._process = QProcess()

        self._process.setProcessChannelMode(QProcess.MergedChannels)
        self._process.readyReadStandardOutput.connect(self.on_readyReadStandardOutput)

        self._process.setProgram("ffmpeg")

    def convert(self, source, destination, bitrate):
        # remove if destination exist
        QFile.remove(destination)
        args = ["-i", source, "-c:a", "aac", "-b:a", bitrate, "-vn", destination]
        self._process.setArguments(args)
        self._process.start()

    @pyqtSlot()
    def on_readyReadStandardOutput(self):
        keys = ("size", "time", "bitrate", "speed")
        data = self._process.readAllStandardOutput()
        msg = data.data().decode()
        for line in msg.splitlines():
            line = line.replace(" ", "").replace("=", "")
            if all(key in line for key in keys):
                values = []
                for left, right in zip(keys[:-1], keys[1:]):
                    # https://stackoverflow.com/a/51456576/6622587
                    value = line[line.index(left) + len(left) : line.index(right)]
                    values.append(value)
                value = line[line.index(keys[-1]) + len(keys[-1]) :]
                values.append(value)
                d = dict(zip(keys, values))
                self.dataChanged.emit(d)

这是用法示例:

    # ...
    converters = []

    for part in parts.keys():
        converter = Converter()
        converter.dataChanged.connect(self.on_data_ready)
        converter.convert(
            "{}.mp3".format(part),
            "{}.m4a".format(part),
            "{}k".format(self.bitrate_cbx.currentText()),
        )
        converters.append(converter)
        # ...
# ...

@pyqtSlot(object)
def on_data_ready(self, data):
    print(data)

输出:

{'size': '256kB', 'time': '00:00:17.94', 'bitrate': '116.9kbits/s', 'speed': '35.9x'}
{'size': '512kB', 'time': '00:00:44.62', 'bitrate': '94.0kbits/s', 'speed': '44.6x'}
{'size': '1024kB', 'time': '00:01:11.21', 'bitrate': '117.8kbits/s', 'speed': '47.5x'}
{'size': '1280kB', 'time': '00:01:37.66', 'bitrate': '107.4kbits/s', 'speed': '48.8x'}
{'size': '1941kB', 'time': '00:02:02.46', 'bitrate': '129.8kbits/s', 'speed': '49.6x'}
© www.soinside.com 2019 - 2024. All rights reserved.