视频流式传输时视频被冻结

Video is freezed while video streaming

我在 python2.7 中找到了以下用于通过套接字流式传输视频的代码。当我 运行 它时,视频将在服务器端开始时冻结(它在网络浏览器中显示视频)。我调试了代码并了解到在 streamer.py 中,第三个 while 循环条件创建了一个无限循环,因为条件 while len(data) < msg_size: 始终满足。也就是说,len(data)总是小于msg_size.So,streamer.py不会return图像到server.py。谁能帮我解决这个问题?

server.py是:

     from flask import Flask, render_template, Response
     from streamer import Streamer
     app = Flask(__name__)

     def gen():
       streamer = Streamer('localhost', 8089)
       streamer.start()

       while True:
         if streamer.client_connected():
             yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + 
             streamer.get_jpeg() + b'\r\n\r\n')

    @app.route('/')
    def index():
       return render_template('index.html')

    @app.route('/video_feed')
    def video_feed():
        return Response(gen(), mimetype='multipart/x-mixed-replace; 
        boundary=frame')

    if __name__ == '__main__':
      app.run(host='localhost', threaded=True)

streamer.py是:

import threading
import socket
import struct
import StringIO
import json
import numpy

class Streamer (threading.Thread):
  def __init__(self, hostname, port):
    threading.Thread.__init__(self)

    self.hostname = hostname
    self.port = port
    self.connected = False
    self.jpeg = None

  def run(self):

    self.isRunning = True

    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    print 'Socket created'

    s.bind((self.hostname, self.port))
    print 'Socket bind complete'

    data = ""
    payload_size = struct.calcsize("L")

    s.listen(10)
    print 'Socket now listening'

    while self.isRunning:

      conn, addr = s.accept()
      print 'while 1...'

      while True:

        data = conn.recv(4096)

        print 'while 2...'
        if data:
          packed_msg_size = data[:payload_size]
          data = data[payload_size:]
          msg_size = struct.unpack("L", packed_msg_size)[0]

          while len(data) < msg_size:# the infinite loop is here(my problem)!
            data += conn.recv(4096)
            print ("lenght of data is " , len(data) )
            print ("message size is  " ,  msg_size )

          frame_data = data[:msg_size]
          #frame_data = data[:len(data)]
          memfile = StringIO.StringIO()
          memfile.write(json.loads(frame_data).encode('latin-1'))
          memfile.seek(0)
          frame = numpy.load(memfile)

          ret, jpeg = cv2.imencode('.jpg', frame)
          self.jpeg = jpeg

          self.connected = True
          print 'recieving...'

        else:
          conn.close()
          self.connected = False
          print 'connected=false...'
          break

    self.connected = False

  def stop(self):
    self.isRunning = False

  def client_connected(self):
    return self.connected

  def get_jpeg(self):
    return self.jpeg.tobytes()

Client.py 是:

import socket
import sys
import pickle
import struct
import StringIO
import json
import time

cap=cv2.VideoCapture(0)
clientsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
clientsocket.connect(('localhost',8089))

while(cap.isOpened()):
  ret,frame=cap.read()

  memfile = StringIO.StringIO()
  np.save(memfile, fravidme)
  memfile.seek(0)
  data = json.dumps(memfile.read().decode('latin-1'))

  clientsocket.sendall(struct.pack("L", len(data))+data)
  if cv2.waitKey(1) & 0xFF == ord('q'):
    break

cap.release()

我想在同一网络中的客户端计算机上显示笔记本电脑摄像头拍摄的视频。我期待视频流,但在浏览器中,我只是观看图像,它不会持续更新。

当我分析这段代码时,我注意到通过网络发送 OpenCV 帧的默认实现不起作用。我决定用我用过的 ZeroMQ 实现替换它 . You can check out the linked question for a deeper explanation of how the streaming works. I have neatly packaged it into classes, with unit tests and documentation as SmoothStream 也检查一下。

回到问题,这是工作代码。

client.py
import base64
import cv2
import zmq

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

camera = cv2.VideoCapture(0)  # init the camera

while True:
    try:
        grabbed, frame = camera.read()  # grab the current frame
        frame = cv2.resize(frame, (640, 480))  # resize the frame
        encoded, buffer = cv2.imencode('.jpg', frame)
        jpg_as_text = base64.b64encode(buffer)
        footage_socket.send(jpg_as_text)

    except KeyboardInterrupt:
        camera.release()
        cv2.destroyAllWindows()
        break
server.py
from flask import Flask, render_template, Response
from streamer import Streamer

app = Flask(__name__)


def gen():
    streamer = Streamer('*', 5555)
    streamer.start()

    while True:
        if streamer.client_connected():
            yield (b'--frame\r\n'b'Content-Type: image/jpeg\r\n\r\n' + streamer.get_jpeg() + b'\r\n\r\n')


@app.route('/')
def index():
    return render_template('index.html')


@app.route('/video_feed')
def video_feed():
    return Response(gen(), mimetype='multipart/x-mixed-replace; boundary=frame')


if __name__ == '__main__':
    app.run(host='localhost', threaded=True)
streamer.py
import base64
import threading

import cv2
import numpy as np
import zmq


class Streamer(threading.Thread):
    def __init__(self, hostname, port):
        threading.Thread.__init__(self)

        self.hostname = hostname
        self.port = port
        self.connected = False
        self.jpeg = None

    def run(self):

        self.isRunning = True

        context = zmq.Context()
        footage_socket = context.socket(zmq.SUB)
        footage_socket.bind('tcp://{}:{}'.format(self.hostname, self.port))
        footage_socket.setsockopt_string(zmq.SUBSCRIBE, np.unicode(''))

        while self.isRunning:
            frame = footage_socket.recv_string()
            img = base64.b64decode(frame)
            npimg = np.fromstring(img, dtype=np.uint8)
            source = cv2.imdecode(npimg, 1)

            ret, jpeg = cv2.imencode('.jpg', source)
            self.jpeg = jpeg

            self.connected = True

        self.connected = False

    def stop(self):
        self.isRunning = False

    def client_connected(self):
        return self.connected

    def get_jpeg(self):
        return self.jpeg.tobytes()

I understand that copy-pasting entire .py files are probably not the best way to post an answer here, but this is a complex question with a lot of moving parts and I honestly could not think of a better way to help the OP.