Python Opencv和Sockets - 以h264编码的视频流传输

3

我希望制作一个流媒体播放器,能够在我的局域网内从一台电脑传输视频到另一台电脑(或同一台电脑)。为了尽可能地节省带宽,我需要使用h264进行编码。但是我在这方面遇到了麻烦,不知道从哪里开始。目前视频是以jpg格式进行编码,并且逐帧发送。然而,我知道这种方式非常低效并占用大量带宽。这是我的当前接收端代码。

import cv2
import socket
import _pickle
import time

host = "192.168.1.196"
port = 25565
boo = True

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # declares s object with two parameters
s.bind((host, port)) # tells my socket object to connect to this host & port "binds it to it"
s.listen(10) # tells the socket how much data it will be receiving.

conn, addr = s.accept()
buf = ''
while boo:
        pictures = conn.recv(128000) # creates a pictures variable that receives the pictures with a max amount of 128000 data it can receive
        decoded = _pickle.loads(pictures) # decodes the pictures
        frame = cv2.imdecode(decoded, cv2.IMREAD_COLOR) # translates decoded into frames that we can see!
        cv2.imshow("recv", frame)
        if cv2.waitKey(1) & 0xFF == ord("q"):  # wait until q key was pressed once and
            break

以下是我的当前客户端代码(发送者):

import cv2
import numpy as np
import socket
import _pickle
from cv2 import *

host = "192.168.1.196"
port = 25565

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)  # declares s object with two parameters
s.connect((host, port))  # connects to the host & port
cap = cv2.VideoCapture(1)
cv2.cv.CV_FOURCC('H','2','6','4')
while cap.isOpened(): # while camera is being used
    ret, frame = cap.read()  # reads each frame from webcam
    cv2.imshow("client", frame)
    if ret:
        encoded = _pickle.dumps(cv2.imencode("fourcc", frame)[1]) # encoding each frame, instead of sending live video it is sending pictures one by one
        s.sendall(encoded)
    if cv2.waitKey(1) & 0xFF == ord("q"): # wait until key was pressed once and
        break
cap.release()
cv2.destroyAllWindows()

我只需要一些关于如何对视频进行H264编码和解码的帮助。
1个回答

3
你可以使用 pyzmq 和基于 base64 字符串编解码的发布/订阅模式来实现这一点。在服务器端,大致思路如下:
  • 从摄像头流获取帧
  • 使用 cv2.imencode 从内存缓冲区读取图像
  • ndarray 转换为 base64 编码的 str 类型并通过 socket 发送

客户端

在客户端,我们只需要按照相反的方式进行即可:
  • 从 socket 中读取图像字符串
  • 使用 base64 将 str 转换为 bytes
  • 使用 np.frombuffer + cv2.imdecodebytes 转换为 ndarray
由于此方法仅在套接字间发送字符串,因此不会占用太多带宽。请注意保留 HTML 标记。
import base64
import cv2
import zmq

context = zmq.Context()
socket = context.socket(zmq.PUB)
socket.connect('tcp://localhost:7777')

camera = cv2.VideoCapture(0)

while True:
    try:
        ret, frame = camera.read()
        frame = cv2.resize(frame, (640, 480))
        encoded, buf = cv2.imencode('.jpg', frame)
        image = base64.b64encode(buf)
        socket.send(image)
    except KeyboardInterrupt:
        camera.release()
        cv2.destroyAllWindows()
        break

客户端

import cv2
import zmq
import base64
import numpy as np

context = zmq.Context()
socket = context.socket(zmq.SUB)
socket.bind('tcp://*:7777')
socket.setsockopt_string(zmq.SUBSCRIBE, np.unicode(''))

while True:
    try:
        image_string = socket.recv_string()
        raw_image = base64.b64decode(image_string)
        image = np.frombuffer(raw_image, dtype=np.uint8)
        frame = cv2.imdecode(image, 1)
        cv2.imshow("frame", frame)
        cv2.waitKey(1)
    except KeyboardInterrupt:
        cv2.destroyAllWindows()
        break

谢谢。我明天会尝试一下,然后再告诉你效果如何。出于某种规定的原因,我的流编码器最高容量是4兆比特/秒,希望这样可以表现得很好。 - Coke
1
谢谢,我继续使用套接字模块而不是ZeroMQ,并且使用base64编码,但仍然只有9mbps。我需要它小于4mbps。有没有想法如何使用h.264对视频进行编码? - Coke
调整帧大小以减少带宽是否有效?我不确定如何对视频进行H.264编码。 - nathancy
没问题,我现在获得了500 kbps的速度,对我的需求来说已经很惊人了。 - Coke
当然没问题。现在我只需要让opencv在我的树莓派4上运行,这很有趣... - Coke
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接