I'm generating a MJPEG stream using Flask and flask-restful. For reasons, I want to catch this stream in another Python program, for which I use OpenCV(3). Problem is that the first frame that is requested comes in well. On the other hand, the second frame that is requested (after a delay) is not received properly, and throws the error:
[mpjpeg @ 0000017a86f524a0] Expected boundary '--' not found, instead found a line of 82 bytes
Multiple times.
I believe this happens because the boundary of a frame is set manually. I will put the offending code below.
MJPEG Stream generation:
## Controller for the streaming of content.
class StreamContent(Resource):
@classmethod
def setVis(self, vis):
self.savedVis = vis
def get(self):
return Response(gen(VideoCamera(self.savedVis)),
mimetype='multipart/x-mixed-replace; boundary=frame')
## Generate a new VideoCamera and stream the retrieved frames.
def gen(camera):
frame = camera.getFrame()
while frame != None:
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n')
time.sleep(0.07)
frame = camera.getFrame()
## Allows for the reading of video frames.
class VideoCamera(object):
def __init__(self, vis):
#object we retrieve the frame from.
self.vis = vis
## Get the current frame.
def getFrame(self):
image = self.vis.mat_frame_with_overlay
# We are using Motion JPEG, but OpenCV defaults to capture raw images,
# so we must encode it into JPEG in order to correctly display the
# video/image stream.
ret, jpeg = cv2.imencode('.jpg', image)
return jpeg.tobytes()
MJPEG Stream retrieval:
"""
Get a single frame from the camera.
"""
class Image(Resource):
def get(self):
camera = VideoCamera()
return Response(camera.getSingleFrame(), mimetype='image/jpeg')
"""
Contains methods for retrieving video information from a source.
"""
class VideoCamera(object):
def __del__(self):
self.video.release()
@classmethod
def setVideo(self, video):
self.video = video
## Get the current frame.
def getSingleFrame(self):
self.startVideoFromSource(self.video)
ret, image = self.video.read()
time.sleep(0.5)
ret, image = self.video.read()
# We are using Motion JPEG, but OpenCV defaults to capture raw images,
# so we must encode it into JPEG in order to correctly display the
# video/image stream.
ret, jpeg = cv2.imencode('.jpg', image)
self.stopVideo()
return jpeg.tobytes()
def stopVideo(self):
self.video.release()
Changing the frame generator worked for me:
yield (b'--frame\r\n'
b'Content-Type:image/jpeg\r\n'
b'Content-Length: ' + f"{len(frame)}".encode() + b'\r\n'
b'\r\n' + frame + b'\r\n')
I'm new to this but it's definitely a multithreading issue, as it only happens sometimes when I reload the page. Easy fix:
camera.py
import cv2, threading
lock = threading.Lock()
camIpLink = 'http://user:[email protected]/with/video/footage'
cap = cv2.VideoCapture(camIpLink)
def getFrame():
global cap
with lock:
while True:
try:
return bytes(cv2.imencode('.jpg', cap.read()[1])[1])
except:
print("Frame exception")
cap.release()
cap = cv2.VideoCapture(camIpLink)
server.py
from camera import getFrame
def gen():
while True:
frame = getFrame()
try:
yield (b'--frame\r\n'
b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')
except:
print("Yield Exception")
return
@app.route("/cam", methods=['GET']) # todo authentication
def camVideoFootage():
return Response(gen(),
mimetype='multipart/x-mixed-replace; boundary=frame')
I did some error handling through trial and error. Hope that helps!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With