繁体   English   中英

python 多线程重定向标准输出

[英]python multiple threads redirecting stdout

我正在建立一个 icecast2 广播电台,它将以较低的质量重新播放现有电台。 该程序将生成多个 FFmpeg 进程 24/7 重新流式传输。 出于故障排除的目的,我希望将每个 FFmpeg 进程的 output 重定向到单独的文件。

import ffmpeg, csv
from threading import Thread

def run(name, mount, source):
icecast = "icecast://"+ICECAST2_USER+":"+ICECAST2_PASS+"@localhost:"+ICECAST2_PORT+"/"+mount
stream = (
        ffmpeg
        .input(source)
        .output(
            icecast,
            audio_bitrate=BITRATE, sample_rate=SAMPLE_RATE, format=FORMAT, acodec=CODEC,
            reconnect="1", reconnect_streamed="1", reconnect_at_eof="1", reconnect_delay_max="120",
            ice_name=name, ice_genre=source
            )
        )
return stream

with open('stations.csv', mode='r') as data:
for station in csv.DictReader(data):
    stream = run(station['name'], station['mount'], station['url'])
    thread = Thread(target=stream.run)
    thread.start()

据我了解,我不能单独重定向每个线程的标准输出,我也不能使用仅由环境变量配置的 ffmpeg 报告。 我还有其他选择吗?

你需要自己创建一个线程 function


def stream_runner(stream,id):
    # open a stream-specific log file to write to
    with open(f'stream_{id}.log','wt') as f:
        # block until ffmpeg is done
        sp.run(stream.compile(),stderr=f)

for i, station in enumerate(csv.DictReader(data)):
    stream = run(station['name'], station['mount'], station['url'])
    thread = Thread(target=stream_runner,args=(stream,i))
    thread.start()

像这样的东西应该工作。

ffmpeg-python 并没有完全为您提供执行此操作的工具 - 您想控制 arguments 之一到子进程stderr ,但 ffmpeg 对此没有参数。

但是,ffmpeg-python 确实具有显示它会使用的命令行 arguments 的能力。 之后,您可以自己调用 subprocess 。

您也不需要使用线程来执行此操作 - 您可以设置每个 ffmpeg 子进程,而无需等待它完成,并每秒检查一次。 此示例并行启动两个 ffmpeg 实例,并通过每秒从每个实例中打印出最新的 output 行来监控每个实例,并跟踪它们是否已退出。

我为测试做了两个更改:

  1. 它从字典而不是 CSV 文件中获取电台。
  2. 它转码 MP4 文件而不是音频 stream,因为我没有 Icecast 服务器。 如果你想测试它,它希望在同一目录中有一个名为“sample.mp4”的文件。

两者都应该很容易改回来。

import ffmpeg
import subprocess
import os
import time

stations = [
    {'name': 'foo1', 'input': 'sample.mp4', 'output': 'output.mp4'},
    {'name': 'foo2', 'input': 'sample.mp4', 'output': 'output2.mp4'},
]

class Transcoder():
    def __init__(self, arguments):
        self.arguments = arguments

    def run(self):
        stream = (
            ffmpeg
            .input(self.arguments['input'])
            .output(self.arguments['output'])
        )
        args = stream.compile(overwrite_output=True)
        with open(self.log_name(), 'ab') as logfile:
            self.subproc = subprocess.Popen(
                args,
                stdin=None,
                stdout=None,
                stderr=logfile,
            )
    
    def log_name(self):
        return self.arguments['name'] + "-ffmpeg.log"

    def still_running(self):
        return self.subproc.poll() is None
    
    def last_log_line(self):
        with open(self.log_name(), 'rb') as f:
            try:  # catch OSError in case of a one line file 
                f.seek(-2, os.SEEK_END)
                while f.read(1) not in [b'\n', 'b\r']:
                    f.seek(-2, os.SEEK_CUR)
            except OSError:
                f.seek(0)
            last_line = f.readline().decode()
        last_line = last_line.split('\n')[-1]
        return last_line

    def name(self):
        return self.arguments['name']


transcoders = []
for station in stations:
    t = Transcoder(station)
    t.run()
    transcoders.append(t)

while True:
    for t in list(transcoders):
        if not t.still_running():
            print(f"{t.name()} has exited")
            transcoders.remove(t)
        print(t.name(), repr(t.last_log_line()))
    if len(transcoders) == 0:
        break
    time.sleep(1)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM