Задать вопрос
@valerr007

Как проверить запустилась ли трансляция FFMPEG через Python?

У меня есть микросервис на python который запускает переадресацию видеопотока.

Таким образом я запускаю процесс:

import signal
import subprocess
import sys
import os
from dotenv import load_dotenv
from subprocess import Popen

load_dotenv(".env")

rtsp_server = os.getenv("RTSP_SERVER")
camera_login = os.getenv("CAMERA_LOGIN")
camera_pass = os.getenv("CAMERA_PASS")
camera_host = os.getenv("CAMERA_HOST")
def start_stream():
    args = [
        "ffmpeg",
        "-i", f"rtsp://{camera_login}:{camera_pass}@{camera_host}:554/Streaming/channels/1/",
        "-err_detect", "ignore_err",
        "-reorder_queue_size", "0",
        "-map", "0:v", "-c:v", "copy",
        "-f", "rtsp",
        "-rtsp_transport", "tcp", f"rtsp://{rtsp_server}:8554/live.stream"
    ]
    params = {'cwd': '.'}

    if sys.platform == 'win32':  # винда у нас особенная...
        params['creationflags'] = (
            # subprocess.DETACHED_PROCESS   # если хочешь, чтобы ffmpeg запускался тихо и не спамил в твой stdout
            # subprocess.CREATE_NEW_CONSOLE |  # если хочешь, чтобы открывалась новая консоль для ffmpeg
            subprocess.CREATE_NEW_PROCESS_GROUP  # по докам, это требуется для нормальной работы ctrl-c
        )
    my_subprocess = Popen(args, stdout=subprocess.PIPE,stderr=subprocess.PIPE, **params)

Проблема в том, что FFMPEG при запуске выдаёт следующее:

Input #0, rtsp, from 'rtsp://LOGIN:PASSWORD@11.111.11.111:554/Streaming/channels/1/':
  Metadata:
    title           : Media Presentation
  Duration: N/A, start: 0.080000, bitrate: N/A
  Stream #0:0: Video: h264 (Main), yuvj420p(pc, progressive), 1920x1080 [SAR 1:1 DAR 16:9], 12.50 tbr, 90k tbn
Output #0, rtsp, to 'rtsp://localhost:8554/live.stream':
  Metadata:
    title           : Media Presentation
    encoder         : Lavf60.10.100
  Stream #0:0: Video: h264 (Main), yuvj420p(pc, progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 12.50 tbr, 90k tbn
Stream mapping:
  Stream #0:0 -> #0:0 (copy)
Press [q] to stop, [?] for help

Но не всегда.

Иногда процесс зависает в начале команды и до этого этапа не доходит.

Другими словами, то что я скинул - признак того что переадресация трансляции началась успешно.

Подскажите, как я могу проверить началась ли трансляция успешно после запуска процесса?
Мне это нужно для того, чтобы перезапустить процесс если что-то пойдёт не так.
  • Вопрос задан
  • 203 просмотра
Подписаться 1 Простой 1 комментарий
Пригласить эксперта
Ответы на вопрос 1
trapwalker
@trapwalker Куратор тега Python
Программист, энтузиаст
UPD: Если вы не можете дождаться завершения дочернего процесса, то не обязательно блокировать программу для получения всего его выхлопа:
import subprocess


if __name__ == '__main__':
    cmd = 'py "(time.sleep(1) or print(i) for i in itertools.count())"'
    cmd = 'ping ya.ru'
    cmd = 'ffmpeg'

    with subprocess.Popen([cmd], stdout=subprocess.PIPE, text=True, shell=True) as p:
        for line in p.stdout:
            print(line.strip())

Так вы можете получать выхлоп построчно или вовсе посимвольно:
while 1:
    print(p.stdout.read(1), end='')

Да, это по-прежнему блокирующий вызов, но вы можете читать строки в отдельном треде и по таймауту его закрывать, например.

UPD2:
Ну так что, автор, разобрался, или разжевывать надо решение?
import subprocess
import threading
import logging
import sys


log = logging.getLogger(__name__)


def check_process_output(process: subprocess.Popen, timeout: float, substring: str):
    def fetch_output():
        try:
            while process.poll() is None:
                line = p.stdout.readline()
                log.debug('FETCH: %s', line.strip())
                data.append(line)
        except ValueError as e:
            pass

    data = []
    t = threading.Thread(target=fetch_output)
    t.start()
    log.debug(f'Wait %f s', timeout)
    t.join(timeout)
    result = substring in ''.join(data)
    log.debug('Output is%s contained substring %r', '' if result else ' NOT', substring)
    return result


if __name__ == '__main__':
    logging.basicConfig(stream=sys.stderr, level='DEBUG')
    cmd = 'py "(time.sleep(1) or print(i) for i in itertools.count())"'
    cmd = 'ping ya.ru -c 8'
    # cmd = 'ffmpeg'

    with (subprocess.Popen([cmd], stdout=subprocess.PIPE, text=True, shell=True) as p):
        print('CHECK RESULT:', check_process_output(p, timeout=5, substring='ya.ru'))


Можно сделать потомка от Popen, который будет вычитывать в треде свой stdout и на каждую строчку дёргать коллбэк, а также будет валиться по таймауту, если за какое-то время ни один коллбэк не вернул True.
А можно просто добавить методы readline и read прямо в класс и с таймаутами в аргументах.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы