Потоковая передача кадров видеозахвата Opencv с использованием GStreamer в python для веб-камеры

Я пытаюсь передать видеозахват по сети. Я использовал для этого fastapi и uvicorn, и это сработало хорошо, но теперь я перехожу на беспроводную сеть, и сеть не может обрабатывать поток, я получаю 2-3 кадра в секунду с задержкой 5 секунд. Я читал, что gstreamer — лучший способ потоковой передачи кадров, хотя мне понадобится декодер на принимающей стороне потока.

это мой отправитель:

Отправитель.py

import time
import cv2

fps = 52
frame_width = 640
frame_height = 360
flip = 0
camSet='v4l2src device=/dev/video0 ! video/x-raw,width=640,height=360 ! nvvidconv flip-method='+str(flip)+' \
        ! video/x-raw(memory:NVMM), format=I420, width=640, height=360 ! nvvidconv ! video/x-raw, format=BGRx ! videoconvert \
        ! video/x-raw, format=BGR enable-max-performance=1 ! appsink '
cam=cv2.VideoCapture(camSet,cv2.CAP_GSTREAMER)

    gst_str_rtp = " appsrc ! videoconvert ! videoscale ! video/x-raw,format=I420,width=640,height=360,framerate=52/1 !  videoconvert !\
     x264enc tune=zerolatency bitrate=500 speed-preset=superfast ! rtph264pay ! \
udpsink host=0.0.0.0 port=8000"

if cam.isOpened() is not True:
    print("Cannot open camera. Exiting.")
    quit()
    
fourcc = cv2.VideoWriter_fourcc(*'H264')
out = cv2.VideoWriter(gst_str_rtp, fourcc, 52, (frame_width, frame_height), True)

while True:
    ret, frame = cam.read()

    cv2.imshow('webcam',frame)
    out.write(frame)
    cv2.moveWindow('webcam',0,0)
    if cv2.waitKey(1)==ord('q'):
        break

cam.release()
out.release()
cv2.destroyAllWindows()

У меня есть несколько примеров конвейера для выхода, но я не уверен, какой из них вообще работает, как это:

gst_str_rtp ="appsrc ! videoconvert ! video/x-raw,width=1280,height=720 ! queue ! x264enc ! h264parse\
     ! rtph264pay ! udpsink host=127.0.0.1 port=8000"

или

gst_str_rtp = "appsrc ! video/x-raw, format=I420 ! queue ! videoconvert ! \
      width=640,height=360,framerate=52/1 ! nvvidconv ! omxh264enc ! \
          video/x-h264, stream-format=byte-stream ! h264parse ! rtph264pay pt=96 config-interval=1 ! \
        udpsink host=127.0.0.1 port=8000"

мой приемник это: приемник.py

global video_frame
video_frame = None

cv2.namedWindow('stream')

# Use locks for thread-safe viewing of frames in multiple browsers
# locks prevent the program from getting user inputs from multiple sources
# lock helps syncronize the program
global thread_lock 
thread_lock = threading.Lock()

app = FastAPI()

def main():
    global video_frame
    camSet='udpsrc host=0.0.0.0 port=8000 caps = "application/x-rtp, media=(string)video, clock-rate=(int)90000, \
        encoding-name=(string)H264,\
         payload=(int)96" ! rtph264depay ! decodebin ! videoconvert ! appsink'
    video_capture = cv2.VideoCapture(camSet ,cv2.CAP_GSTREAMER)#initalizes our video feed
    # video_capture.set(cv2.CAP_PROP_BUFFERSIZE, 0)#sets camera buffer to 0 meaning we wont get double frames
    print("reading frames")

    while True:
        
        ret, frame = video_capture.read()

        if not ret:
            print('empty frame')
            break

        cv2.imshow(frame,'stream')
        # with thread_lock:
        #     video_frame = frame.copy()



def encodeFrame():#encode frame function takes the frames and encoding them to jpg and encodes them again to bytes so we can stream them
    global thread_lock 
    while True:
        # Acquire thread_lock to access the global video_frame object
        with thread_lock:
            global video_frame
            if video_frame is None:
                continue
            return_key, encoded_image = cv2.imencode(".jpg", video_frame)
            if not return_key:
                continue

        # Output image as a byte array
        yield(b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + 
            bytearray(encoded_image) + b'\r\n')


@app.get("/")
async def video_feed():#our stream function, streams the frames encodeFrame has made to the IP and port we chose
    return StreamingResponse(encodeFrame(), media_type="multipart/x-mixed-replace;boundary=frame")



if __name__ == '__main__':
    # Create a thread and attach the method that captures the image frames, to it
    process_thread = threading.Thread(target=main)

    # Start the thread
    process_thread.start()
    uvicorn.run(app, host="0.0.0.0", port=9000, access_log=True)

Я использую flask и uvicorn для повторной передачи изображений на мой сервер Java, который может их прочитать. либо моя кодировка отправителя, либо декодирование получателя не работают, или, возможно, оба они неверны, и я не уверен, что, или даже если я иду правильным путем, я довольно новичок в gstreamer и конвейерах. Я был бы признателен за любую помощь в этом, и также необходимы предложения, которые не потребуют от меня рестриминга.

Анализ настроения постов в Twitter с помощью Python, Tweepy и Flair
Анализ настроения постов в Twitter с помощью Python, Tweepy и Flair
Анализ настроения текстовых сообщений может быть настолько сложным или простым, насколько вы его сделаете. Как и в любом ML-проекте, вы можете выбрать...
7 лайфхаков для начинающих Python-программистов
7 лайфхаков для начинающих Python-программистов
В этой статье мы расскажем о хитростях и советах по Python, которые должны быть известны разработчику Python.
Установка Apache Cassandra на Mac OS
Установка Apache Cassandra на Mac OS
Это краткое руководство по установке Apache Cassandra.
Сертификатная программа "Кванты Python": Бэктестер ансамблевых методов на основе ООП
Сертификатная программа "Кванты Python": Бэктестер ансамблевых методов на основе ООП
В одном из недавних постов я рассказал о том, как я использую навыки количественных исследований, которые я совершенствую в рамках программы TPQ...
Создание персонального файлового хранилища
Создание персонального файлового хранилища
Вы когда-нибудь хотели поделиться с кем-то файлом, но он содержал конфиденциальную информацию? Многие думают, что электронная почта безопасна, но это...
Создание приборной панели для анализа данных на GCP - часть I
Создание приборной панели для анализа данных на GCP - часть I
Недавно я столкнулся с интересной бизнес-задачей - визуализацией сбоев в цепочке поставок лекарств, которую могут просматривать врачи и...
0
0
26
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Не уверен, что это решит вашу проблему, но может помочь следующее:

  1. Кажется, в захвате камеры есть опечатка, где enable-max-performance=1 не подходит в заголовках видео. Этот пункт скорее свойство плагина (вероятно, от энкодера). Может быть лучше установить частоту кадров, если ваш драйвер камеры обеспечивает другую частоту кадров с этим разрешением, иначе вы столкнетесь с несоответствием частоты кадров записи.
camSet='v4l2src device=/dev/video0 ! video/x-raw,width=640,height=360,framerate=52/1 ! nvvidconv flip-method='+str(flip)+' ! video/x-raw(memory:NVMM), format=I420, width=640, height=360 ! nvvidconv ! video/x-raw, format=BGRx ! videoconvert ! video/x-raw, format=BGR ! queue ! appsink drop=1'
  1. Многоадресная рассылка может перегружать Wi-Fi. Лучше транслировать только на ваш ресивер:
... ! udpsink host=<receiver_IP> port=8000 auto-multicast=0

Вы просто получите на принимающем хосте только:

'udpsrc port=8000 auto-multicast=0 ! application/x-rtp,media=video,encoding-name=H264 ! rtpjitterbuffer latency=300 ! rtph264depay ! decodebin ! videoconvert ! video/x-raw,format=BGR ! appsink drop=1'

# Variant for NVIDIA:
'udpsrc port=8000 auto-multicast=0 ! application/x-rtp,media=video,encoding-name=H264 ! rtpjitterbuffer latency=300 ! rtph264depay ! decodebin ! nvvidconv ! video/x-raw,format=BGRx ! videoconvert ! video/x-raw,format=BGR ! appsink drop=1'

  1. При использовании бэкенда gstreamer обратите внимание, что код 4cc бесполезен. Вы можете использовать кодировщик с ускорением HW, например:
gst_str_rtp = "appsrc ! video/x-raw,format=BGR ! queue ! videoconvert ! video/x-raw,format=BGRx ! nvvidconv ! video/x-raw(memory:NVMM),format=NV12,width=640,height=360,framerate=52/1 ! nvv4l2h264enc insert-sps-pps=1 insert-vui=1 idrinterval=30 ! h264parse ! rtph264pay ! udpsink host=<receiver_IP> port=8000 auto-multicast=0"
out = cv2.VideoWriter(gst_str_rtp, cv2.CAP_GSTREAMER, 0, float(52), (frame_width, frame_height), True)

привет, я получил ошибку gstreamer с предложенным вами набором камер: предупреждение GStreamer: ошибка при открытии конвейера записи: нет свойства «автоматическая многоадресная передача» в элементе «udpsink0». Открытие в РЕЖИМЕ БЛОКИРОВКИ. и при удалении многоадресной рассылки я получил это на принимающей стороне, а не видео: Открытие в РЕЖИМЕ БЛОКИРОВКИ NvMMLiteOpen : Block : BlockType = 261 NVMEDIA: Reading vendor.tegra.display-size : status: 6 NvMMLiteBlockCreate : Block : BlockType = 261

KTBM 10.04.2022 16:21

Извините, это была опечатка. Отредактировал мой ответ.

SeB 10.04.2022 16:24

мой принимающий конец застрял на: Открытие в РЕЖИМЕ БЛОКИРОВКИ NvMMLiteOpen: Блок: BlockType = 261 NVMEDIA: Чтение vendor.tegra.display-size: статус: 6 NvMMLiteBlockCreate: Блок: BlockType = 261

KTBM 10.04.2022 16:38

Похоже, что приемник использует NVDEC. Вы пробовали конвейер NVIDIA в моем редактировании? Обратите внимание, что установка может занять некоторое время, дайте ей не менее 10 секунд.

SeB 10.04.2022 17:16

конвейер nvidia работает отлично!! спасибо, теперь только для того, чтобы заставить сервер узла получать его и транслировать с использованием html. я буду работать над этим сейчас. после этого я буду больше изучать gstreamer и пойму все, что вы использовали.

KTBM 10.04.2022 17:31

еще один небольшой вопрос: при попытке потоковой передачи через Ethernet на другой хост-компьютер хост не получает кадры, хотя соединение хорошее и пинг возвращается, есть ли что-то, что нужно изменить при создании приемника на окнах, когда отправитель есть на ubuntu(мой случай на jetson NX)?

KTBM 10.04.2022 19:07

Если удаленный приемник не использует декодер NVIDIA, вы должны использовать первый конвейер приемника. Также убедитесь, что ни один брандмауэр не препятствует передаче UDP/8000 от отправителя к получателю.

SeB 10.04.2022 19:23

да, по какой-то причине получатель не хочет правильно читать захват, я проверил с помощью wirehark, и поток работает нормально, и все брандмауэры не работают, единственным отличием в коде является хост, и захват возвращает None

KTBM 10.04.2022 19:44

На какой ОС работает ресивер? Также вы можете запустить прием и отображение конвейера gstreamer перед использованием opencv?

SeB 10.04.2022 20:30

приемник находится в Windows 10, сервер, созданный в Windows, идентичен серверу в Ubuntu, за исключением аддона хоста, я пробовал все способы хостинга, такие как хостинг в Ubuntu и использование его IP на сервере Windows, и другой способ размещение на IP-адресе Windows в отправителе Ubuntu и размещение на локальном хосте на сервере Windows. я не пробовал gst-launch, но мой сервер ubuntu работает отлично, сервер Windows, похоже, не может читать из конвейера

KTBM 10.04.2022 20:46

Другие вопросы по теме