Как блокировать меньше при потоковой передаче видео с торнадо - PullRequest
2 голосов
/ 17 марта 2020

Я хочу знать, как сделать так, чтобы блок кода потокового видео Tornado был меньше, чтобы я не влиял на других обработчиков в моем веб-приложении.

У меня есть устаревший код, который я использую в Tornado для потоковой передачи входящих сообщений. JPEG и показать их как видео на странице html. Мой обработчик выглядит так. Это работало достаточно хорошо в течение нескольких лет, но в последнее время я заметил, что это на самом деле большой источник ресурсов. Когда что-то вызывает этот обработчик, все остальные запросы резко замедляются.

class VideoAPI(tornado.web.RequestHandler):
    '''
    Serves a MJPEG of the images posted from the vehicle.
    '''

    @tornado.gen.coroutine
    def get(self):
        host = self.get_argument("host")
        port = int(self.get_argument("port"))
        ioloop = tornado.ioloop.IOLoop.current()
        self.set_header("Content-type", "multipart/x-mixed-replace;boundary=--frame")
        self.served_image_timestamp = time.time()
        my_boundary = "--frame"
        for frame in live_video_stream(host,port=port):
            interval = .1
            if self.served_image_timestamp + interval < time.time():
                img = cv2.imencode('.jpg', frame)[1].tostring()
                self.write(my_boundary)
                self.write("Content-type: image/jpeg\r\n")
                self.write("Content-length: %s\r\n\r\n" % len(img))
                # Serve the image
                self.write(img)
                self.served_image_timestamp = time.time()
                yield tornado.gen.Task(self.flush)
            else:
                yield tornado.gen.Task(ioloop.add_timeout, ioloop.time() + interval)

В другом месте моего приложения у меня есть код, который несколько дороже в вычислительном отношении, который я могу запустить, не перегружая процессор. Это соответствует следующему шаблону:

class Example(tornado.web.RequestHandler):

    executor = ThreadPoolExecutor(3)

    @tornado.concurrent.run_on_executor
    def do_some_stuff(self):
        """Do something that's not asyncio-friendly, and computationally slow"""
        return something

    @tornado.gen.coroutine
    def get(self):
        result = yield self.do_some_stuff()
        self.write(result)

Предполагая, что этот второй пример является наиболее эффективным шаблоном для потокового видео, как я могу преобразовать обработчик видео, чтобы он выглядел больше так? Я унаследовал код обработчика видео от кого-то другого, и я не знаю, как воспроизвести часть l oop. Например, я должен оставить l oop в функции, которая выполняется на исполнителе? Должен ли я держать это в @tornado.gen.coroutine украшенном методе? Я должен сделать что-то совершенно другое?

1 Ответ

1 голос
/ 20 марта 2020

Вам необходимо идентифицировать дорогие части кода и переместить их в другие потоки; оставьте оставшуюся часть кода в сопрограмме (которая требуется для вызовов Торнадо, например self.write). Например, я предполагаю, что основная проблема - это строка

img = cv2.imencode('.jpg', frame)[1].tostring()

Чтобы переместить это в другой поток (используя более современные идиомы торнадо, чем декоратор run_on_executor), поместите его в функцию и запустите ее следующим образом :

def expensive_fn():
    return cv2.imencode('.jpg', frame)[1].tostring()

img = yield ioloop.run_in_executor(None, expensive_fn)

Если проблема for frame in live_video_stream(host,port=port), вам потребуется выполнить более обширный рефакторинг, чтобы отойти от шаблона генератора, чтобы его можно было повторить в отдельном потоке.

...