Create a gist now

Instantly share code, notes, and snippets.

What would you like to do?
Simple Python Motion Jpeg (mjpeg server) from webcam. Using: OpenCV,BaseHTTPServer
#!/usr/bin/python
'''
Author: Igor Maculan - n3wtron@gmail.com
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()
@wpoet

This comment has been minimized.

Show comment Hide comment
@wpoet

wpoet Jan 9, 2016

Igor, this is a terrific example! Wonderful work!!!!

When I run it on my Ubuntu 15.10 system, it works, but initially I get this error message:
server started

127.0.0.1 - - [09/Jan/2016 16:54:47] "GET /cam.mjpg HTTP/1.1" 200 -

Exception happened during processing of request from ('127.0.0.1', 42188)
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 295, in _handle_request_noblock
self.process_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 321, in process_request
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python2.7/SocketServer.py", line 657, in init
self.finish()
File "/usr/lib/python2.7/SocketServer.py", line 716, in finish
self.wfile.close()
File "/usr/lib/python2.7/socket.py", line 283, in close
self.flush()
File "/usr/lib/python2.7/socket.py", line 307, in flush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe

The program still serves the mjpeg stream, but this message comes on every re/connect. Do you have an idea why this happens?

br Wolfgang

wpoet commented Jan 9, 2016

Igor, this is a terrific example! Wonderful work!!!!

When I run it on my Ubuntu 15.10 system, it works, but initially I get this error message:
server started

127.0.0.1 - - [09/Jan/2016 16:54:47] "GET /cam.mjpg HTTP/1.1" 200 -

Exception happened during processing of request from ('127.0.0.1', 42188)
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 295, in _handle_request_noblock
self.process_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 321, in process_request
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python2.7/SocketServer.py", line 657, in init
self.finish()
File "/usr/lib/python2.7/SocketServer.py", line 716, in finish
self.wfile.close()
File "/usr/lib/python2.7/socket.py", line 283, in close
self.flush()
File "/usr/lib/python2.7/socket.py", line 307, in flush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe

The program still serves the mjpeg stream, but this message comes on every re/connect. Do you have an idea why this happens?

br Wolfgang

@saif-data

This comment has been minimized.

Show comment Hide comment
@saif-data

saif-data Jan 14, 2016

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.

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.

@beyondszine

This comment has been minimized.

Show comment Hide comment
@beyondszine

beyondszine Jan 16, 2016

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

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

@bluelemonade

This comment has been minimized.

Show comment Hide comment
@bluelemonade

bluelemonade Feb 16, 2016

hi,

when connecting a flash mjepg netstream reader I got
192.168.178.54 - - [16/Feb/2016 22:19:44] "GET /cam.mjpg HTTP/1.1" 200 -
and the cam stops working. where my fault?

hi,

when connecting a flash mjepg netstream reader I got
192.168.178.54 - - [16/Feb/2016 22:19:44] "GET /cam.mjpg HTTP/1.1" 200 -
and the cam stops working. where my fault?

@n3wtron

This comment has been minimized.

Show comment Hide comment
@n3wtron

n3wtron Feb 23, 2016

Hi bluelemonade,
I've made the program for my raspberry robot, I've never used it with a "flash mjpeg netstream reader" ( not even know what is it a flash mjpeg reader).
Is it work in a browser?
If in a browser works, you could check the network traffic (i.e. with wireshark) to check the difference between a browser and "flash mjpeg reader"

Owner

n3wtron commented Feb 23, 2016

Hi bluelemonade,
I've made the program for my raspberry robot, I've never used it with a "flash mjpeg netstream reader" ( not even know what is it a flash mjpeg reader).
Is it work in a browser?
If in a browser works, you could check the network traffic (i.e. with wireshark) to check the difference between a browser and "flash mjpeg reader"

@njlochner

This comment has been minimized.

Show comment Hide comment
@njlochner

njlochner Mar 2, 2016

Hello Igor, I have adapted your code for a project of mine. Thanks for sharing this!

I noticed you did not include a license with this, and I want to be respectful of your ownership of this code. Am I free to use my modified version of this gist in my project under any license?

Thanks!

Hello Igor, I have adapted your code for a project of mine. Thanks for sharing this!

I noticed you did not include a license with this, and I want to be respectful of your ownership of this code. Am I free to use my modified version of this gist in my project under any license?

Thanks!

@tuf11244

This comment has been minimized.

Show comment Hide comment
@tuf11244

tuf11244 Mar 19, 2016

hello ...people...I used this code ...it works for me ...like it doesn't give any error...but I cannot see the web cam stream in browser for some reason ??.....help is much appreciated.

Thanks

hello ...people...I used this code ...it works for me ...like it doesn't give any error...but I cannot see the web cam stream in browser for some reason ??.....help is much appreciated.

Thanks

@n3wtron

This comment has been minimized.

Show comment Hide comment
@n3wtron

n3wtron Mar 21, 2016

Hi @njlochner you are free to use it and modify it (GPLv3 License) when I've shared this code I forgot to add the licence, I'm sorry.
@tuf11244, if you post your code somewhere I can help you better.

Owner

n3wtron commented Mar 21, 2016

Hi @njlochner you are free to use it and modify it (GPLv3 License) when I've shared this code I forgot to add the licence, I'm sorry.
@tuf11244, if you post your code somewhere I can help you better.

@njlochner

This comment has been minimized.

Show comment Hide comment
@njlochner

njlochner Mar 30, 2016

Hi @njlochner you are free to use it and modify it (GPLv3 License) when I've shared this code I forgot to add the licence, I'm sorry.
@tuf11244, if you post your code somewhere I can help you better.

Thanks!

Hi @njlochner you are free to use it and modify it (GPLv3 License) when I've shared this code I forgot to add the licence, I'm sorry.
@tuf11244, if you post your code somewhere I can help you better.

Thanks!

@Asymptote

This comment has been minimized.

Show comment Hide comment
@Asymptote

Asymptote Mar 30, 2016

Hi
It works fine, but I can't open the stream from multiple browsers.

Hi
It works fine, but I can't open the stream from multiple browsers.

@njlochner

This comment has been minimized.

Show comment Hide comment
@njlochner

njlochner May 3, 2016

@Asymptote, in order to do this you need to adapt this into a threaded HTTP server. I have done so in my code. See the accepted answer from this thread: http://stackoverflow.com/questions/14088294/multithreaded-web-server-in-python

@Asymptote, in order to do this you need to adapt this into a threaded HTTP server. I have done so in my code. See the accepted answer from this thread: http://stackoverflow.com/questions/14088294/multithreaded-web-server-in-python

@n3wtron

This comment has been minimized.

Show comment Hide comment
@n3wtron

n3wtron May 11, 2016

@Asymptote thanks to @njlochner I've adjust it for multi thread.

Owner

n3wtron commented May 11, 2016

@Asymptote thanks to @njlochner I've adjust it for multi thread.

@Benoit-Besnier

This comment has been minimized.

Show comment Hide comment
@Benoit-Besnier

Benoit-Besnier May 19, 2016

Hi !

I wanted to see what we can do with this. It seems to work fine, because i don't get any error and the infinite loop is running, but when i want to access at "localhost:8080" with my browser, i receive an error : "Message corrupt". Any idea about it ?

For those who get the error "Cannot import Image", you should replace
import Image
by
import PIL as pillow
from PIL import Image

Anyway, thank you for sharing this =)

Benoit-Besnier commented May 19, 2016

Hi !

I wanted to see what we can do with this. It seems to work fine, because i don't get any error and the infinite loop is running, but when i want to access at "localhost:8080" with my browser, i receive an error : "Message corrupt". Any idea about it ?

For those who get the error "Cannot import Image", you should replace
import Image
by
import PIL as pillow
from PIL import Image

Anyway, thank you for sharing this =)

@Kiolali

This comment has been minimized.

Show comment Hide comment
@Kiolali

Kiolali Jun 8, 2016

Hey!
When i try to access "http://127.0.0.1:8080/cam.mjpg" with my browser I get the following exception:

Exception happened during processing of request from ('127.0.0.1', 50752) Traceback (most recent call last): File "C:\Python27\lib\SocketServer.py", line 599, in process_request_thread self.finish_request(request, client_address) File "C:\Python27\lib\SocketServer.py", line 334, in finish_request self.RequestHandlerClass(request, client_address, self) File "C:\Python27\lib\SocketServer.py", line 655, in __init__ self.handle() File "C:\Python27\lib\BaseHTTPServer.py", line 340, in handle self.handle_one_request() File "C:\Python27\lib\BaseHTTPServer.py", line 328, in handle_one_request method() File "C:\coding\mpeg streamer example\simple_mjpeg_streamer_http_server.py", line 31, in do_GET jpg.save(tmpFile,'JPEG') File "C:\Python27\lib\site-packages\PIL\Image.py", line 1439, in save save_handler(self, fp, filename) File "C:\Python27\lib\site-packages\PIL\JpegImagePlugin.py", line 471, in _save ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)]) File "C:\Python27\lib\site-packages\PIL\ImageFile.py", line 480, in _save for e, b, o, a in tile: ValueError: Not a valid number of quantization tables. Should be between 1 and 4.

The problem seems to be this line:
jpg.save(tmpFile,'JPEG')

Any idea?

Best,
Hanna

Kiolali commented Jun 8, 2016

Hey!
When i try to access "http://127.0.0.1:8080/cam.mjpg" with my browser I get the following exception:

Exception happened during processing of request from ('127.0.0.1', 50752) Traceback (most recent call last): File "C:\Python27\lib\SocketServer.py", line 599, in process_request_thread self.finish_request(request, client_address) File "C:\Python27\lib\SocketServer.py", line 334, in finish_request self.RequestHandlerClass(request, client_address, self) File "C:\Python27\lib\SocketServer.py", line 655, in __init__ self.handle() File "C:\Python27\lib\BaseHTTPServer.py", line 340, in handle self.handle_one_request() File "C:\Python27\lib\BaseHTTPServer.py", line 328, in handle_one_request method() File "C:\coding\mpeg streamer example\simple_mjpeg_streamer_http_server.py", line 31, in do_GET jpg.save(tmpFile,'JPEG') File "C:\Python27\lib\site-packages\PIL\Image.py", line 1439, in save save_handler(self, fp, filename) File "C:\Python27\lib\site-packages\PIL\JpegImagePlugin.py", line 471, in _save ImageFile._save(im, fp, [("jpeg", (0,0)+im.size, 0, rawmode)]) File "C:\Python27\lib\site-packages\PIL\ImageFile.py", line 480, in _save for e, b, o, a in tile: ValueError: Not a valid number of quantization tables. Should be between 1 and 4.

The problem seems to be this line:
jpg.save(tmpFile,'JPEG')

Any idea?

Best,
Hanna

@Kiolali

This comment has been minimized.

Show comment Hide comment
@Kiolali

Kiolali Jun 8, 2016

i fixed the error above by uninstalling pil and pillow (seem to cause conflicts if both are installed). Then I reinstalled only pillow.

Now I get a popup window "python.exe has stopped working.." when accessing the mjpg via browser

caused by this line:
jpg.save(self.wfile,'JPEG')

Any ideas?

Best, Hanna

Kiolali commented Jun 8, 2016

i fixed the error above by uninstalling pil and pillow (seem to cause conflicts if both are installed). Then I reinstalled only pillow.

Now I get a popup window "python.exe has stopped working.." when accessing the mjpg via browser

caused by this line:
jpg.save(self.wfile,'JPEG')

Any ideas?

Best, Hanna

@Vinggui

This comment has been minimized.

Show comment Hide comment
@Vinggui

Vinggui Aug 19, 2016

Nice, worked like a charm.... Thank you!
The only trouble I had was related to python OpenCV, which I needed to install, beside the OpenCV library, the python-opencv (sudo apt-get install python-opencv)
But working great now...

Vinggui commented Aug 19, 2016

Nice, worked like a charm.... Thank you!
The only trouble I had was related to python OpenCV, which I needed to install, beside the OpenCV library, the python-opencv (sudo apt-get install python-opencv)
But working great now...

@plumgeek

This comment has been minimized.

Show comment Hide comment
@plumgeek

plumgeek Sep 6, 2016

Hello Igor, this looks very promising . I have a couple questions maybe someone could help with. Please forgive me I'm a bit of a noob at this.

I'm presently using SimpleCV, which includes an mjpeg streaming server that seems to work in a similar way to this code, however with that server I often have to re-load the page a few times to get the image feed to appear, and sometimes it disappears after it's been streaming for a while. I thought maybe it would be more reliable if I were to somehow get the stream to write to a location and then use Apache to actually serve it up. There is a post here about streaming OpenCV images via Apache. I discovered your code above when starting to work through that tutorial.

I don't understand how to write the code to get the components to talk to eachother. Would you mind giving a bit more detail and example of how to actually use the above code? I assume I would kick it off in a terminal and let it run, at which time I could access the stream via a browser, but I'm not sure how to actually save images from OpenCV/SimpleCV to the server.

And a followup, is there a reason you're not just using Apache? I'm sure there was a good reason, it just seems it's already installed with most Linux distros so I'm not sure why one would write another server.

Thanks so much!

plumgeek commented Sep 6, 2016

Hello Igor, this looks very promising . I have a couple questions maybe someone could help with. Please forgive me I'm a bit of a noob at this.

I'm presently using SimpleCV, which includes an mjpeg streaming server that seems to work in a similar way to this code, however with that server I often have to re-load the page a few times to get the image feed to appear, and sometimes it disappears after it's been streaming for a while. I thought maybe it would be more reliable if I were to somehow get the stream to write to a location and then use Apache to actually serve it up. There is a post here about streaming OpenCV images via Apache. I discovered your code above when starting to work through that tutorial.

I don't understand how to write the code to get the components to talk to eachother. Would you mind giving a bit more detail and example of how to actually use the above code? I assume I would kick it off in a terminal and let it run, at which time I could access the stream via a browser, but I'm not sure how to actually save images from OpenCV/SimpleCV to the server.

And a followup, is there a reason you're not just using Apache? I'm sure there was a good reason, it just seems it's already installed with most Linux distros so I'm not sure why one would write another server.

Thanks so much!

@n3wtron

This comment has been minimized.

Show comment Hide comment
@n3wtron

n3wtron Sep 7, 2016

Hi @plumgeek,
I've written this code for my raspberryPi robot (performance and lightweight).
Using apache it would be foolish.
If you want to save every frame with my script you have to extract the infinite loop at line 21 and save the frames into a folder instead of using tmpFile = StringIO.StringIO()

I hope that I have been useful to you

Owner

n3wtron commented Sep 7, 2016

Hi @plumgeek,
I've written this code for my raspberryPi robot (performance and lightweight).
Using apache it would be foolish.
If you want to save every frame with my script you have to extract the infinite loop at line 21 and save the frames into a folder instead of using tmpFile = StringIO.StringIO()

I hope that I have been useful to you

@andresR8

This comment has been minimized.

Show comment Hide comment
@andresR8

andresR8 Oct 28, 2016

I have the same issue that @Kiolali

I have the same issue that @Kiolali

@n3wtron

This comment has been minimized.

Show comment Hide comment
@n3wtron

n3wtron Nov 9, 2016

Hi @andresR8,
I'm sorry, but I don't have any Windows to try, the only thing that I can say is try to check windows firewall on python.exe, maybe (really maybe) the problem is access to the network port.
I'm saying that by the description of wfile : "Contains the output stream for writing a response back to the client. Proper adherence to the HTTP protocol must be used when writing to this stream."

Owner

n3wtron commented Nov 9, 2016

Hi @andresR8,
I'm sorry, but I don't have any Windows to try, the only thing that I can say is try to check windows firewall on python.exe, maybe (really maybe) the problem is access to the network port.
I'm saying that by the description of wfile : "Contains the output stream for writing a response back to the client. Proper adherence to the HTTP protocol must be used when writing to this stream."

@nagarashish

This comment has been minimized.

Show comment Hide comment
@nagarashish

nagarashish Dec 14, 2016

I am having issue when multiple clients connect to the server. When first client connects, streaming works perfectly. As soon as I second client connects, get following dump many times and server crashes:

[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 127 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 8 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 98 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 127 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 127 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 12 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 50 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 3 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 41 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 32 bytes
[mjpeg @ 0x560aa30faae0] error count: 64
[mjpeg @ 0x560aa30faae0] error y=19 x=8
[mjpeg @ 0x560aa30faae0] overread 8
[mjpeg @ 0x560aa30faae0] No JPEG data found in image

Any idea how to fix multi client access issue?

Thanks

I am having issue when multiple clients connect to the server. When first client connects, streaming works perfectly. As soon as I second client connects, get following dump many times and server crashes:

[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 127 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 8 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 98 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 127 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 127 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 12 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 50 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 3 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 41 bytes
[mpjpeg @ 0x560aa30ffae0] Expected boundary '--' not found, instead found a line of 32 bytes
[mjpeg @ 0x560aa30faae0] error count: 64
[mjpeg @ 0x560aa30faae0] error y=19 x=8
[mjpeg @ 0x560aa30faae0] overread 8
[mjpeg @ 0x560aa30faae0] No JPEG data found in image

Any idea how to fix multi client access issue?

Thanks

@pauls06

This comment has been minimized.

Show comment Hide comment
@pauls06

pauls06 Dec 30, 2016

I had the same issue as @Kiolali and @andresR8.

Replacing the problem line
jpg.save(self.wfile,'JPEG')
with
self.wfile.write( tmpFile.getvalue() )
avoids the problem.

pauls06 commented Dec 30, 2016

I had the same issue as @Kiolali and @andresR8.

Replacing the problem line
jpg.save(self.wfile,'JPEG')
with
self.wfile.write( tmpFile.getvalue() )
avoids the problem.

@Kiolali

This comment has been minimized.

Show comment Hide comment
@Kiolali

Kiolali Jan 6, 2017

I have the same issue as @wpoet ("broken pipe")
Any ideas how to solve it?

accessing the mjpeg stream via browser works - but if I use a test client like "insomnia" (GET url_mjpeg_stream) I don't get any response..

Kiolali commented Jan 6, 2017

I have the same issue as @wpoet ("broken pipe")
Any ideas how to solve it?

accessing the mjpeg stream via browser works - but if I use a test client like "insomnia" (GET url_mjpeg_stream) I don't get any response..

@limtingfei

This comment has been minimized.

Show comment Hide comment
@limtingfei

limtingfei Jan 19, 2017

Thank you for the code. I got an output "server started" . Is it considered successful ?
Because I was expecting to stream it in opencv just like how I used to stream the webcam directly into my pc.

Thank you for the code. I got an output "server started" . Is it considered successful ?
Because I was expecting to stream it in opencv just like how I used to stream the webcam directly into my pc.

@awood85

This comment has been minimized.

Show comment Hide comment
@awood85

awood85 Feb 13, 2017

Hi, thanks very much for this code, exactly what I was after.

I had it working locally on the rpi but couldn't access it from either my phone or PC when trying to access it across the network. The Python output didn't show the 200 response message.

I adjusted lines 44 and 60 to point to the local IP of the rpi (which I've got acting as an access point if that makes any difference) and could get the 200 response while trying to access from both phone and PC although I never saw any output. This also stopped me viewing the stream locally from the pi. Reverted back and still no images locally or over the network.

I'll delete the code and start again and see if that fixes it but any ideas in the mean time why it might not be working. Ultimately I want to view the processed output after some openCV manipulation on my phone with the rpi being headless.

Thanks

Adam

awood85 commented Feb 13, 2017

Hi, thanks very much for this code, exactly what I was after.

I had it working locally on the rpi but couldn't access it from either my phone or PC when trying to access it across the network. The Python output didn't show the 200 response message.

I adjusted lines 44 and 60 to point to the local IP of the rpi (which I've got acting as an access point if that makes any difference) and could get the 200 response while trying to access from both phone and PC although I never saw any output. This also stopped me viewing the stream locally from the pi. Reverted back and still no images locally or over the network.

I'll delete the code and start again and see if that fixes it but any ideas in the mean time why it might not be working. Ultimately I want to view the processed output after some openCV manipulation on my phone with the rpi being headless.

Thanks

Adam

@amvlr

This comment has been minimized.

Show comment Hide comment
@amvlr

amvlr Mar 9, 2017

Traceback (most recent call last):
File "/home/pi/simple_mjpeg_streamer_http_server", line 6, in
import cv2
ImportError: No module named 'cv2'

Please help me out!

amvlr commented Mar 9, 2017

Traceback (most recent call last):
File "/home/pi/simple_mjpeg_streamer_http_server", line 6, in
import cv2
ImportError: No module named 'cv2'

Please help me out!

@RichLewis007

This comment has been minimized.

Show comment Hide comment
@RichLewis007

RichLewis007 Mar 16, 2017

I got it working on macOS by doing the following:
note: it's written for Python 2.

  • install OpenCV 3.x for python
  • install pillow for Python2:
    pip2 install pillow
  • removed the unneeded lines that are causing errors:
	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);
  • run it:
    python2 motion-jpeg-server.py

  • see it:
    in browser, go to:
    http://127.0.0.1:8080/cam.mjpg
    to see video.

RichLewis007 commented Mar 16, 2017

I got it working on macOS by doing the following:
note: it's written for Python 2.

  • install OpenCV 3.x for python
  • install pillow for Python2:
    pip2 install pillow
  • removed the unneeded lines that are causing errors:
	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);
  • run it:
    python2 motion-jpeg-server.py

  • see it:
    in browser, go to:
    http://127.0.0.1:8080/cam.mjpg
    to see video.

@adham95

This comment has been minimized.

Show comment Hide comment
@adham95

adham95 Jun 1, 2017

how can i save the video streaming on my hard ?

adham95 commented Jun 1, 2017

how can i save the video streaming on my hard ?

@Yegorko

This comment has been minimized.

Show comment Hide comment
@Yegorko

Yegorko Jun 6, 2017

Thank you for the code. I got an output "server started", but i see nothing in browser from 127.0.0.1:8080/cam.mjpg.
What it can be?

Yegorko commented Jun 6, 2017

Thank you for the code. I got an output "server started", but i see nothing in browser from 127.0.0.1:8080/cam.mjpg.
What it can be?

@Morfonio

This comment has been minimized.

Show comment Hide comment
@Morfonio

Morfonio Aug 14, 2017

Hello!

I have the same problem as @wpoet and @Kiolali with the broken pipe.

How did you fix it?

127.0.0.1 - - [14/Aug/2017 11:29:15] "GET /cam.mjpg HTTP/1.1" 200 -

Exception happened during processing of request from ('127.0.0.1', 43004)
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 593, in process_request_thread
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python2.7/SocketServer.py", line 651, in init
self.finish()
File "/usr/lib/python2.7/SocketServer.py", line 710, in finish
self.wfile.close()
File "/usr/lib/python2.7/socket.py", line 279, in close
self.flush()
File "/usr/lib/python2.7/socket.py", line 303, in flush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe

Hello!

I have the same problem as @wpoet and @Kiolali with the broken pipe.

How did you fix it?

127.0.0.1 - - [14/Aug/2017 11:29:15] "GET /cam.mjpg HTTP/1.1" 200 -

Exception happened during processing of request from ('127.0.0.1', 43004)
Traceback (most recent call last):
File "/usr/lib/python2.7/SocketServer.py", line 593, in process_request_thread
self.finish_request(request, client_address)
File "/usr/lib/python2.7/SocketServer.py", line 334, in finish_request
self.RequestHandlerClass(request, client_address, self)
File "/usr/lib/python2.7/SocketServer.py", line 651, in init
self.finish()
File "/usr/lib/python2.7/SocketServer.py", line 710, in finish
self.wfile.close()
File "/usr/lib/python2.7/socket.py", line 279, in close
self.flush()
File "/usr/lib/python2.7/socket.py", line 303, in flush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
error: [Errno 32] Broken pipe

@charmed12

This comment has been minimized.

Show comment Hide comment
@charmed12

charmed12 Oct 2, 2017

@Morfonio , i have the same pb when I tried to connect via Firefox. But the error disappears and the code work when I used chrome

@Morfonio , i have the same pb when I tried to connect via Firefox. But the error disappears and the code work when I used chrome

@ashish1405

This comment has been minimized.

Show comment Hide comment
@ashish1405

ashish1405 Dec 13, 2017

modified for Python 3

#!/usr/bin/python
'''
	Author: Igor Maculan - n3wtron@gmail.com
	A Simple mjpg stream http server
'''
import cv2
from PIL import Image
import threading
from http.server import BaseHTTPRequestHandler,HTTPServer
from socketserver import ThreadingMixIn
from io import StringIO,BytesIO
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 = BytesIO()
					jpg.save(tmpFile,'JPEG')
					self.wfile.write("--jpgboundary".encode())
					self.send_header('Content-type','image/jpeg')
					self.send_header('Content-length',str(tmpFile.getbuffer().nbytes))
					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>'.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):
	"""Handle requests in a separate thread."""

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

if __name__ == '__main__':
	main()

modified for Python 3

#!/usr/bin/python
'''
	Author: Igor Maculan - n3wtron@gmail.com
	A Simple mjpg stream http server
'''
import cv2
from PIL import Image
import threading
from http.server import BaseHTTPRequestHandler,HTTPServer
from socketserver import ThreadingMixIn
from io import StringIO,BytesIO
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 = BytesIO()
					jpg.save(tmpFile,'JPEG')
					self.wfile.write("--jpgboundary".encode())
					self.send_header('Content-type','image/jpeg')
					self.send_header('Content-length',str(tmpFile.getbuffer().nbytes))
					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>'.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):
	"""Handle requests in a separate thread."""

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

if __name__ == '__main__':
	main()
@amakukha

This comment has been minimized.

Show comment Hide comment
@amakukha

amakukha Dec 26, 2017

I does work in Chrome, but it doesn't in Firefox for some reason. I get "[Errno 32] Broken pipe".

What worked for me in Mozilla Firefox was pymjpeg project. I merged the two solutions for my purpose.

amakukha commented Dec 26, 2017

I does work in Chrome, but it doesn't in Firefox for some reason. I get "[Errno 32] Broken pipe".

What worked for me in Mozilla Firefox was pymjpeg project. I merged the two solutions for my purpose.

@marcokuchla

This comment has been minimized.

Show comment Hide comment
@marcokuchla

marcokuchla Feb 28, 2018

I modified a little bit the code to get the video_capture_path by command line (it works with file/webcam/rtsp as input). Also, since it's multithreaded, I create a lock to avoid race condition on camera's read() method. I've dropped the dependency to PIL since OpenCV can encode to JPEG and the dependency to StringIO, BytesIO, due to use numpy "tobytes()" method. One limitation with the following code is that multiple threads cannot read the same frame, so using a video file as input, more viewers implies in "fast" video because between consecutive reads in one thread, some frames may have already been skipped (read) by other threads.

#!/usr/bin/python3
"""
   Author: Igor Maculan - n3wtron@gmail.com
   A Simple mjpg stream http server
"""
import cv2
import threading
import http
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
import time
import sys


class CamHandler(BaseHTTPRequestHandler):
    
    def __init__(self, request, client_address, server):
        img_src = 'http://{}:{}/cam.mjpg'.format(server.server_address[0], server.server_address[1])
        self.html_page = """
            <html>
                <head></head>
                <body>
                    <img src="{}"/>
                </body>
            </html>""".format(img_src)
        self.html_404_page = """
            <html>
                <head></head>
                <body>
                    <h1>NOT FOUND</h1>
                </body>
            </html>"""
        BaseHTTPRequestHandler.__init__(self, request, client_address, server)

    def do_GET(self):
        if self.path.endswith('.mjpg'):
            self.send_response(http.HTTPStatus.OK)
            self.send_header('Content-type', 'multipart/x-mixed-replace; boundary=--jpgboundary')
            self.end_headers()
            while True:
                try:
                    img = self.server.read_frame()
                    retval, jpg = cv2.imencode('.jpg', img)
                    if not retval:
                        raise RuntimeError('Could not encode img to JPEG')
                    jpg_bytes = jpg.tobytes()
                    self.wfile.write("--jpgboundary\r\n".encode())
                    self.send_header('Content-type', 'image/jpeg')
                    self.send_header('Content-length', len(jpg_bytes))
                    self.end_headers()
                    self.wfile.write(jpg_bytes)
                    time.sleep(self.server.read_delay)
                except (IOError, ConnectionError):
                    break
        elif self.path.endswith('.html'):
            self.send_response(http.HTTPStatus.OK)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(self.html_page.encode())
        else:
            self.send_response(http.HTTPStatus.NOT_FOUND)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(self.html_404_page.encode())


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""
    def __init__(self, capture_path, server_address, RequestHandlerClass, bind_and_activate=True):
        HTTPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate)
        ThreadingMixIn.__init__(self)
        try:
            # verifies whether is a webcam
            capture_path = int(capture_path)
        except TypeError:
            pass
        self._capture_path = capture_path
        fps = 30
        self.read_delay = 1. / fps
        self._lock = threading.Lock()
        self._camera = cv2.VideoCapture()

    def open_video(self):
        if not self._camera.open(self._capture_path):
            raise IOError('Could not open Camera {}'.format(self._capture_path))

    def read_frame(self):
        with self._lock:
            retval, img = self._camera.read()
            if not retval:
                self.open_video()
        return img

    def serve_forever(self, poll_interval=0.5):
        self.open_video()
        try:
            super().serve_forever(poll_interval)
        except KeyboardInterrupt:
            self._camera.release()


def main():
    server = ThreadedHTTPServer(sys.argv[1], ('127.0.0.1', 8080), CamHandler)
    print("server started")
    server.serve_forever()


if __name__ == '__main__':
    main()

marcokuchla commented Feb 28, 2018

I modified a little bit the code to get the video_capture_path by command line (it works with file/webcam/rtsp as input). Also, since it's multithreaded, I create a lock to avoid race condition on camera's read() method. I've dropped the dependency to PIL since OpenCV can encode to JPEG and the dependency to StringIO, BytesIO, due to use numpy "tobytes()" method. One limitation with the following code is that multiple threads cannot read the same frame, so using a video file as input, more viewers implies in "fast" video because between consecutive reads in one thread, some frames may have already been skipped (read) by other threads.

#!/usr/bin/python3
"""
   Author: Igor Maculan - n3wtron@gmail.com
   A Simple mjpg stream http server
"""
import cv2
import threading
import http
from http.server import BaseHTTPRequestHandler, HTTPServer
from socketserver import ThreadingMixIn
import time
import sys


class CamHandler(BaseHTTPRequestHandler):
    
    def __init__(self, request, client_address, server):
        img_src = 'http://{}:{}/cam.mjpg'.format(server.server_address[0], server.server_address[1])
        self.html_page = """
            <html>
                <head></head>
                <body>
                    <img src="{}"/>
                </body>
            </html>""".format(img_src)
        self.html_404_page = """
            <html>
                <head></head>
                <body>
                    <h1>NOT FOUND</h1>
                </body>
            </html>"""
        BaseHTTPRequestHandler.__init__(self, request, client_address, server)

    def do_GET(self):
        if self.path.endswith('.mjpg'):
            self.send_response(http.HTTPStatus.OK)
            self.send_header('Content-type', 'multipart/x-mixed-replace; boundary=--jpgboundary')
            self.end_headers()
            while True:
                try:
                    img = self.server.read_frame()
                    retval, jpg = cv2.imencode('.jpg', img)
                    if not retval:
                        raise RuntimeError('Could not encode img to JPEG')
                    jpg_bytes = jpg.tobytes()
                    self.wfile.write("--jpgboundary\r\n".encode())
                    self.send_header('Content-type', 'image/jpeg')
                    self.send_header('Content-length', len(jpg_bytes))
                    self.end_headers()
                    self.wfile.write(jpg_bytes)
                    time.sleep(self.server.read_delay)
                except (IOError, ConnectionError):
                    break
        elif self.path.endswith('.html'):
            self.send_response(http.HTTPStatus.OK)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(self.html_page.encode())
        else:
            self.send_response(http.HTTPStatus.NOT_FOUND)
            self.send_header('Content-type', 'text/html')
            self.end_headers()
            self.wfile.write(self.html_404_page.encode())


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer):
    """Handle requests in a separate thread."""
    def __init__(self, capture_path, server_address, RequestHandlerClass, bind_and_activate=True):
        HTTPServer.__init__(self, server_address, RequestHandlerClass, bind_and_activate)
        ThreadingMixIn.__init__(self)
        try:
            # verifies whether is a webcam
            capture_path = int(capture_path)
        except TypeError:
            pass
        self._capture_path = capture_path
        fps = 30
        self.read_delay = 1. / fps
        self._lock = threading.Lock()
        self._camera = cv2.VideoCapture()

    def open_video(self):
        if not self._camera.open(self._capture_path):
            raise IOError('Could not open Camera {}'.format(self._capture_path))

    def read_frame(self):
        with self._lock:
            retval, img = self._camera.read()
            if not retval:
                self.open_video()
        return img

    def serve_forever(self, poll_interval=0.5):
        self.open_video()
        try:
            super().serve_forever(poll_interval)
        except KeyboardInterrupt:
            self._camera.release()


def main():
    server = ThreadedHTTPServer(sys.argv[1], ('127.0.0.1', 8080), CamHandler)
    print("server started")
    server.serve_forever()


if __name__ == '__main__':
    main()

@menymp

This comment has been minimized.

Show comment Hide comment
@menymp

menymp Mar 5, 2018

Nice work, im using it for a iot project it works very well

menymp commented Mar 5, 2018

Nice work, im using it for a iot project it works very well

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