Skip to content

Instantly share code, notes, and snippets.

@n3wtron
Last active July 8, 2024 17:49
Show Gist options
  • Save n3wtron/4624820 to your computer and use it in GitHub Desktop.
Save n3wtron/4624820 to your computer and use it in GitHub Desktop.
Simple Python Motion Jpeg (mjpeg server) from webcam. Using: OpenCV,BaseHTTPServer
#!/usr/bin/python
'''
Author: Igor Maculan - [email protected]
A Simple mjpg stream http server
'''
import cv2
import Image
import threading
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
from SocketServer import ThreadingMixIn
import StringIO
import time
capture=None
class CamHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path.endswith('.mjpg'):
self.send_response(200)
self.send_header('Content-type','multipart/x-mixed-replace; boundary=--jpgboundary')
self.end_headers()
while True:
try:
rc,img = capture.read()
if not rc:
continue
imgRGB=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
jpg = Image.fromarray(imgRGB)
tmpFile = StringIO.StringIO()
jpg.save(tmpFile,'JPEG')
self.wfile.write("--jpgboundary")
self.send_header('Content-type','image/jpeg')
self.send_header('Content-length',str(tmpFile.len))
self.end_headers()
jpg.save(self.wfile,'JPEG')
time.sleep(0.05)
except KeyboardInterrupt:
break
return
if self.path.endswith('.html'):
self.send_response(200)
self.send_header('Content-type','text/html')
self.end_headers()
self.wfile.write('<html><head></head><body>')
self.wfile.write('<img src="http://127.0.0.1:8080/cam.mjpg"/>')
self.wfile.write('</body></html>')
return
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
"""Handle requests in a separate thread."""
def main():
global capture
capture = cv2.VideoCapture(0)
capture.set(cv2.cv.CV_CAP_PROP_FRAME_WIDTH, 320);
capture.set(cv2.cv.CV_CAP_PROP_FRAME_HEIGHT, 240);
capture.set(cv2.cv.CV_CAP_PROP_SATURATION,0.2);
global img
try:
server = ThreadedHTTPServer(('localhost', 8080), CamHandler)
print "server started"
server.serve_forever()
except KeyboardInterrupt:
capture.release()
server.socket.close()
if __name__ == '__main__':
main()
@DickingAround
Copy link

Great stuff. BTW, if you're viewing this on a pi (i.e. localhost) at the moment this appears not to work on raspberri pi chromium (broken pipe after a few renders). But it works on luakit browser

@Ajinkz
Copy link

Ajinkz commented Apr 22, 2019

Not able to stream video and facing this error. I have made some changes in above code but still not working with Python 3.6.x
Please help me know whats going on wrong

(face) F:\FR\IGL\flask server>python simple_mjpeg_streamer_http_server.py
server started
127.0.0.1 - - [23/Apr/2019 00:20:34] "GET /cam.mjpg HTTP/1.1" 200 -
----------------------------------------
Exception happened during processing of request from ('127.0.0.1', 60377)
Traceback (most recent call last):
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\site-packages\PIL\ImageFile.py", line 485, in _save
    fh = fp.fileno()
io.UnsupportedOperation: fileno

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\socketserver.py", line 651, in process_request_thread
    self.finish_request(request, client_address)
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\socketserver.py", line 361, in finish_request
    self.RequestHandlerClass(request, client_address, self)
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\socketserver.py", line 721, in __init__
    self.handle()
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\http\server.py", line 418, in handle
    self.handle_one_request()
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\http\server.py", line 406, in handle_one_request
    method()
  File "simple_mjpeg_streamer_http_server.py", line 24, in do_GET
    jpg.save(tmpFile,'JPEG')
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\site-packages\PIL\Image.py", line 1969, in save
    save_handler(self, fp, filename)
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\site-packages\PIL\JpegImagePlugin.py", line 761, in _save
    ImageFile._save(im, fp, [("jpeg", (0, 0)+im.size, 0, rawmode)], bufsize)
  File "C:\Users\Ajinkya\Miniconda3\envs\face\lib\site-packages\PIL\ImageFile.py", line 500, in _save
    fp.write(d)
TypeError: string argument expected, got 'bytes'

This is the code

import cv2
from PIL import Image
import threading
from http.server import BaseHTTPRequestHandler,HTTPServer
from socketserver import ThreadingMixIn
from io import StringIO
import time
capture=None

class CamHandler(BaseHTTPRequestHandler):
	def do_GET(self):
		if self.path.endswith('.mjpg'):
			self.send_response(200)
			self.send_header('Content-type','multipart/x-mixed-replace; boundary=--jpgboundary')
			self.end_headers()
			while True:
				try:
					rc,img = capture.read()
					if not rc:
						continue
					imgRGB=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
					jpg = Image.fromarray(imgRGB)
					tmpFile = StringIO() #out_s = StringIO()
					jpg.save(tmpFile,'JPEG')
					self.wfile.write("--jpgboundary")
					self.send_header('Content-type','image/jpeg')
					self.send_header('Content-length',str(tmpFile.len))
					self.end_headers()
					jpg.save(self.wfile,'JPEG')
					time.sleep(0.05)
				except KeyboardInterrupt:
					break
			return
		if self.path.endswith('.html'):
			self.send_response(200)
			self.send_header('Content-type','text/html')
			self.end_headers()
			self.wfile.write('<html><head></head><body>')
			self.wfile.write('<img src="http://127.0.0.1:8081/cam.mjpg"/>')
			self.wfile.write('</body></html>')
			return


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
	"""Handle requests in a separate thread."""

def main():
	global capture
	capture = cv2.VideoCapture(0)
	capture.set(cv2.CAP_PROP_FRAME_WIDTH, 640); 
	capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 480);
	capture.set(cv2.CAP_PROP_SATURATION,0.2);
	global img
	try:
		server = ThreadedHTTPServer(('localhost', 8081), CamHandler)
		print("server started")
		server.serve_forever()
	except KeyboardInterrupt:
		capture.release()
		server.socket.close()

if __name__ == '__main__':
	main()

@muka
Copy link

muka commented Oct 22, 2019

This one works

import cv2
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn

capture = None


class CamHandler(BaseHTTPRequestHandler):

    def do_GET(self):
        if self.path.endswith('.mjpg'):
            self.send_response(200)
            self.send_header(
                'Content-type',
                'multipart/x-mixed-replace; boundary=--jpgboundary'
            )
            self.end_headers()
            while True:
                try:

                    rc, img = capture.read()
                    if not rc:
                        continue

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

                    self.send_header('Content-type', 'image/jpeg')
                    self.send_header('Content-length', len(img_str))
                    self.end_headers()

                    self.wfile.write(img_str)
                    self.wfile.write(b"\r\n--jpgboundary\r\n")

                except KeyboardInterrupt:
                    self.wfile.write(b"\r\n--jpgboundary--\r\n")
                    break
                except BrokenPipeError:
                    continue
            return

        if self.path.endswith('.html'):
            self.send_response(200)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(b'<html><head></head><body>')
            self.wfile.write(b'<img src="http://127.0.0.1:8081/cam.mjpg"/>')
            self.wfile.write(b'</body></html>')
            return


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""


def main():

    global capture
    capture = cv2.VideoCapture(0)
    capture.set(cv2.CAP_PROP_FRAME_WIDTH, 1280)
    capture.set(cv2.CAP_PROP_FRAME_HEIGHT, 720)

    global img
    try:
        server = ThreadedHTTPServer(('localhost', 8081), CamHandler)
        print("server started at http://127.0.0.1:8081/cam.html")
        server.serve_forever()
    except KeyboardInterrupt:
        capture.release()
        server.socket.close()

if __name__ == '__main__':
    main()

@foilandwater
Copy link

Nice man!! exactly I was looking for on web. Saved my time 👍 )
I am doing this for rtsp stream.
And this way RTSP gets embedded in a web page.
Thanks

Hi,

Please can you tell me how you did it with an RTSP IP camera onto a web page? I've been researching for weeks to get this done!!

@wennycooper
Copy link

Great work, Igor!

Wolfgang, the program uses pipes (http://www.python-course.eu/pipes.php) to communicate the output to the server. When you disconnect from the port, it breaks the existing pipe, and when you reconnect, it breaks the previous pipe and starts a new one.

If you were running the script locally and the opencv output was displayed in a window, you could use the 'Keyboard Interrupt' to break the loop without any errors. That doesn't work in the browser because you've already used a pipe to get there.

tl;dr : that error is nothing to worry about, you'll pretty much see it every time you disconnect or reconnect.

Hi saif-data, Do you have any idea how to remove those broken pipe error messages? Try/Exception not work..

@ashwin2401
Copy link

import cv2
import numpy as np
import time
from PIL import Image
from io import BytesIO
import threading
from multiprocessing import Process
import config_ip_vid
try:
from http.server import BaseHTTPRequestHandler,HTTPServer
from socketserver import ThreadingMixIn
except ImportError:
from BaseHTTPServer import BaseHTTPRequestHandler,HTTPServer
from SocketServer import ThreadingMixIn

debug = False
gracefulexit=False
TFrame = None
videoserver = None

port = config_ip_vid.port_number

vid = config_ip_vid.vid_number

for i,j in zip(port,vid):

print("all file and ports******",i,j)

#print('port number -------',port)
#print('video number -------',vid)

#importing all the ports and video from config file
port_8081 = config_ip_vid.port_number1
vid1 = str(config_ip_vid.vid_number1)
print("@@@@@$$$$$$$",str(config_ip_vid.vid_number1))
port_8082 = config_ip_vid.port_number2
vid2 = config_ip_vid.vid_number2

port_8083 = config_ip_vid.port_number3
vid3 = config_ip_vid.vid_number3

port_8084 = config_ip_vid.port_number4
vid4 = config_ip_vid.vid_number4

port_8085 = config_ip_vid.port_number5
vid5 = config_ip_vid.vid_number5

class CamHandler(BaseHTTPRequestHandler):

def do_GET(self):
    global gracefulexit
    if self.path.endswith('.mjpg'):
        self.send_response(200)
        self.send_header('Content-Type','multipart/x-mixed-replace; boundary=--jpgboundary')
        self.send_header('Cache-Control', 'no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0')
        self.end_headers()
        dummy=np.zeros((100,100,3), np.uint8)
        if debug:
            print("vidserver: got new connection")
        while True:
            if gracefulexit:
                break
            try:
                img=TFrame
                if img is None:
                    if debug:
                        print("vidserver: sending dummy")
                    img = dummy

                imgRGB=cv2.cvtColor(img,cv2.COLOR_BGR2RGB)
                jpg = Image.fromarray(imgRGB)
                tmpFile = BytesIO()
                jpg.save(tmpFile,'JPEG')
                self.wfile.write("--jpgboundary".encode())
                self.send_header('Content-type','image/jpeg')
                self.send_header('Content-length',str(len(tmpFile.getvalue())))
                self.end_headers()
                jpg.save(self.wfile,'JPEG')
                time.sleep(0.05)
            except KeyboardInterrupt:
                print("vidserver: got interrupt")
                break
            except Exception as e:
                print("vidserver error:",e)
                break
        print("Exiting cam thread")
        return
    elif self.path.endswith('.html'):
        self.send_response(200)
        self.send_header('Content-type','text/html')
        self.end_headers()
        self.wfile.write('<html><head></head><body>'.encode())
        self.wfile.write('<img src="http://127.0.0.1:8087/cam.mjpg"/>'.encode())
        self.wfile.write('</body></html>'.encode())
        return

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
daemon_threads=True
pass
"""Handle requests in a separate thread."""

def startVideoServer(video_transmit_ip,video_transmit_port):
print("starting video server on %s" % video_transmit_ip)
global gracefulexit

    try:
        videoserver = ThreadedHTTPServer((video_transmit_ip, video_transmit_port), CamHandler)
        # videoserver.anprObj=self
        videoserver.serve_forever()
    except KeyboardInterrupt:
        pass
    print("Exiting videoserver thread")
    gracefulexit=True
    videoserver.shutdown()

    # cap = cv2.VideoCapture(vid)
    # print("############$$$$$$$$$$$$$$$$$ Cap in process",cap)
    # if (cap.isOpened() == False):
    #     print("Error opening video stream or file")
    #
    # while (cap.isOpened()):
    #     ret, TFrame = cap.read()
    #     print("Tframe---=======",TFrame)
    #     if ret != True: break
    # cap.release()
    # if videoserver is not None:
    #     print("Shutting down video server")
    #     videoserver.shutdown()
    #     videoserver.server_close()
    #     videoserver.socket.close()
    # cv2.destroyAllWindows()

print("port 1%%%%%%",port[0])

print("port 2%%%%%%",port[1])

print("video 1%%%%%%",vid[0])

print("video 2%%%%%%",vid[1])

#for i,j in zip(port,vid):

video_transmit_ip = "localhost"
video_transmit_port = int(port_8081)#int(i)
video_transmit_port2 = int(port_8082)
video_transmit_port3 = int(port_8083)
video_transmit_port4 = int(port_8084)
video_transmit_port5 = int(port_8085)
#print("==========",i)
#print("==========",j)
vthread=threading.Thread(target=startVideoServer,args=(video_transmit_ip,video_transmit_port))
vthread.start()

vthread2=threading.Thread(target=startVideoServer,args=(video_transmit_ip,video_transmit_port2))
vthread2.start()

cap = cv2.VideoCapture(vid1)
cap2 = cv2.VideoCapture(vid2)
#print("############$$$$$$$$$$$$$$$$$ Cap in process",cap)
if (cap.isOpened() == False):
print("Error opening video stream or file")
if (cap2.isOpened() == False):
print("Error opening video stream or file")

while (cap.isOpened()|cap2.isOpened()):
ret, TFrame = cap.read()
ret1, img1 = cap2.read()
#print("Tframe---=======",TFrame)
if ret != True: break
cap.release()
cap2.release()
if videoserver is not None:
print("Shutting down video server")
videoserver.shutdown()
videoserver.server_close()
videoserver.socket.close()
cv2.destroyAllWindows()

**I want add multiple video input source to the web but it only displays one of the starting can any of you guys can help me through it i want to play multiple videos the above code is attached for the reference
**

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment