Video Streaming with Flask

Posted by
on under

I'm sure by now you know that I have released a book and a couple of videos on Flask in cooperation with O'Reilly Media. While the coverage of the Flask framework in these is fairly complete, there are a small number of features that for one reason or another did not get mentioned much, so I thought it would be a good idea to write articles about them here.

This article is dedicated to streaming, an interesting feature that gives Flask applications the ability to provide large responses efficiently partitioned in small chunks, potentially over a long period of time. To illustrate the topic I'm going to show you how to build a live video streaming server!

NOTE: there is now a follow-up to this article, Flask Video Streaming Revisited, in which I describe some improvements to the streaming server introduced here.

What is Streaming?

Streaming is a technique in which the server provides the response to a request in chunks. I can think of a couple of reasons why this might be useful:

  • Very large responses. Having to assemble a response in memory only to return it to the client can be inefficient for very large responses. An alternative would be to write the response to disk and then return the file with flask.send_file(), but that adds I/O to the mix. Providing the response in small portions is a much better solution, assuming the data can be generated in chunks.
  • Real time data. For some applications a request may need to return data that comes from a real time source. A pretty good example of this is a real time video or audio feed. A lot of security cameras use this technique to stream video to web browsers.

Implementing Streaming With Flask

Flask provides native support for streaming responses through the use of generator functions. A generator is a special function that can be interrupted and resumed. Consider the following function:

def gen():
    yield 1
    yield 2
    yield 3

This is a function that runs in three steps, each returning a value. Describing how generator functions are implemented is outside the scope of this article, but if you are a bit curious the following shell session will give you an idea of how generators are used:

>>> x = gen()
>>> x
<generator object gen at 0x7f06f3059c30>
>>> next(x)
1
>>> next(x)
2
>>> next(x)
3
>>> next(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration

You can see in this simple example that a generator function can return multiple results in sequence. Flask uses this characteristic of generator functions to implement streaming.

The example below shows how using streaming it is possible to generate a large data table, without having to assemble the entire table in memory:

from flask import Response, render_template
from app.models import Stock

def generate_stock_table():
    yield render_template('stock_header.html')
    for stock in Stock.query.all():
        yield render_template('stock_row.html', stock=stock)
    yield render_template('stock_footer.html')

@app.route('/stock-table')
def stock_table():
    return Response(generate_stock_table())

In this example you can see how Flask works with generator functions. A route that returns a streamed response needs to return a Response object that is initialized with the generator function. Flask then takes care of invoking the generator and sending all the partial results as chunks to the client.

For this particular example if you assume Stock.query.all() returns the result of a database query as an iterable, then you can generate a potentially large table one row at a time, so regardless of the number of elements in the query the memory consumption in the Python process will not grow larger and larger due to having to assemble a large response string.

Multipart Responses

The table example above generates a traditional page in small portions, with all the parts concatenated into the final document. This is a good example of how to generate large responses, but something a little bit more exciting is to work with real time data.

An interesting use of streaming is to have each chunk replace the previous one in the page, as this enables streams to "play" or animate in the browser window. With this technique you can have each chunk in the stream be an image, and that gives you a cool video feed that runs in the browser!

The secret to implement in-place updates is to use a multipart response. Multipart responses consist of a header that includes one of the multipart content types, followed by the parts, separated by a boundary marker and each having its own part specific content type.

There are several multipart content types for different needs. For the purpose of having a stream where each part replaces the previous part the multipart/x-mixed-replace content type must be used. To help you get an idea of how this looks, here is the structure of a multipart video stream:

HTTP/1.1 200 OK
Content-Type: multipart/x-mixed-replace; boundary=frame

--frame
Content-Type: image/jpeg

<jpeg data here>
--frame
Content-Type: image/jpeg

<jpeg data here>
...

As you see above, the structure is pretty simple. The main Content-Type header is set to multipart/x-mixed-replace and a boundary string is defined. Then each part is included, prefixed by two dashes and the part boundary string in their own line. The parts have their own Content-Type header, and each part can optionally include a Content-Length header with the length in bytes of the part payload, but at least for images browsers are able to deal with the stream without the length.

Building a Live Video Streaming Server

There's been enough theory in this article, now it is time to build a complete application that streams live video to web browsers.

There are many ways to stream video to browsers, and each method has its benefits and disadvantages. The method that works well with the streaming feature of Flask is to stream a sequence of independent JPEG pictures. This is called Motion JPEG, and is used by many IP security cameras. This method has low latency, but quality is not the best, since JPEG compression is not very efficient for motion video.

Below you can see a surprisingly simple, yet complete web application that can serve a Motion JPEG stream:

#!/usr/bin/env python
from flask import Flask, render_template, Response
from camera import Camera

app = Flask(__name__)

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

def gen(camera):
    while True:
        frame = camera.get_frame()
        yield (b'--frame\r\n'
               b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n')

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

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

This application imports a Camera class that is in charge of providing the sequence of frames. Putting the camera control portion in a separate module is a good idea in this case, this way the web application remains clean, simple and generic.

The application has two routes. The / route serves the main page, which is defined in the index.html template. Below you can see the contents of this template file:

<html>
  <head>
    <title>Video Streaming Demonstration</title>
  </head>
  <body>
    <h1>Video Streaming Demonstration</h1>
    <img src="{{ url_for('video_feed') }}">
  </body>
</html>

This is a simple HTML page with just a heading and an image tag. Note that the image tag's src attribute points to the second route of this application, and this is where the magic happens.

The /video_feed route returns the streaming response. Because this stream returns the images that are to be displayed in the web page, the URL to this route is in the src attribute of the image tag. The browser will automatically keep the image element updated by displaying the stream of JPEG images in it, since multipart responses are supported in most/all browsers (let me know if you find a browser that doesn't like this).

The generator function used in the /video_feed route is called gen(), and takes as an argument an instance of the Camera class. The mimetype argument is set as shown above, with the multipart/x-mixed-replace content type and a boundary set to the string "frame".

The gen() function enters a loop where it continuously returns frames from the camera as response chunks. The function asks the camera to provide a frame by calling the camera.get_frame() method, and then it yields with this frame formatted as a response chunk with a content type of image/jpeg, as shown above.

Obtaining Frames from a Video Camera

Now all that is left is to implement the Camera class, which will have to connect to the camera hardware and download live video frames from it. The nice thing about encapsulating the hardware dependent part of this application in a class is that this class can have different implementations for different people, but the rest of the application remains the same. You can think of this class as a device driver, which provides a uniform implementation regardless of the actual hardware device in use.

The other advantage of having the Camera class separated from the rest of the application is that it is easy to fool the application into thinking there is a camera when in reality there is not, since the camera class can be implemented to emulate a camera without real hardware. In fact, while I was working on this application, the easiest way for me to test the streaming was to do that and not have to worry about the hardware until I had everything else running. Below you can see the simple emulated camera implementation that I used:

from time import time

class Camera(object):
    def __init__(self):
        self.frames = [open(f + '.jpg', 'rb').read() for f in ['1', '2', '3']]

    def get_frame(self):
        return self.frames[int(time()) % 3]

This implementation reads three images from disk called 1.jpg, 2.jpg and 3.jpg and then returns them one after another repeatedly, at a rate of one frame per second. The get_frame() method uses the current time in seconds to determine which of the three frames to return at any given moment. Pretty simple, right?

To run this emulated camera I needed to create the three frames. Using gimp I've made the following images:

Frame 1 Frame 2 Frame 3

Because the camera is emulated, this application runs on any environment, so you can run this right now! I have this application all ready to go on GitHub. If you are familiar with git you can clone it with the following command:

$ git clone https://github.com/miguelgrinberg/flask-video-streaming.git

If you prefer to download it, then you can get a zip file here.

Once you have the application installed, create a virtual environment and install Flask in it. Then you can run the application as follows:

$ python app.py

After you start the application enter http://localhost:5000 in your web browser and you will see the emulated video stream playing the 1, 2 and 3 images over and over. Pretty cool, right?

Once I had everything working I fired up my Raspberry Pi with its camera module and implemented a new Camera class that converts the Pi into a video streaming server, using the picamera package to control the hardware. I will not discuss this camera implementation here, but you can find it in the source code in file camera_pi.py.

If you have a Raspberry Pi and a camera module you can edit app.py to import the Camera class from this module and then you will be able to live stream the Pi camera, like I'm doing in the following screenshot:

Frame 1

If you want to make this streaming application work with a different camera, then all you need to do is write another implementation of the Camera class. If you end up writing one I would appreciate it if you contribute it to my GitHub project.

Limitations of Streaming

When the Flask application serves regular requests the request cycle is short. The web worker receives the request, invokes the handler function and finally returns the response. Once the response is sent back to the client the worker is free and ready to take on another request.

When a request that uses streaming is received, the worker remains attached to the client for the duration of the stream. When working with long, never ending streams such as a video stream from a camera, a worker will stay locked to the client until the client disconnects. This effectively means that unless specific measures are taken, the application can only serve as many clients as there are web workers. When working with the Flask application in debug mode that means just one, so you will not be able to connect a second browser window to watch the stream from two places at the same time.

There are ways to overcome this important limitation. The best solution in my opinion is to use a coroutine based web server such as gevent, which Flask fully supports. With the use of coroutines gevent is able to handle multiple clients on a single worker thread, as gevent modifies the Python I/O functions to issue context switches as necessary.

Conclusion

In case you missed it above, the code that supports this article is this GitHub repository: https://github.com/miguelgrinberg/flask-video-streaming/tree/v1. Here you can find a generic implementation of video streaming that does not require a camera, and also an implementation for the Raspberry Pi camera module. This follow-up article describes some improvements I made after this article was published originally.

I hope this article shed some light on the topic of streaming. I concentrated on video streaming because that is an area I have some experience, but streaming has many more uses besides video. For example, this technique can be used to keep a connection between the client and the server alive for a long time, allowing the server to push new information the moment it becomes available. These days the Web Socket protocol is a more efficient way to achieve this, but Web Socket is fairly new and works only in modern browsers, while streaming will work on pretty much any browser you can think of.

If you have any questions feel free to write them below. I plan to continue documenting more of the not well known Flask topics, so I hope you connect with me in some way to know when more articles are published. I hope to see you in the next one!

Miguel

Become a Patron!

Hello, and thank you for visiting my blog! If you enjoyed this article, please consider supporting my work on this blog on Patreon!

455 comments
  • #101 Miguel Grinberg said

    @Sepp: I don't think I'm doing anything with frame rates on this project. Is this my exact project or did you make changes? If this is your code, then all you need to do is change the access of the framerate as indicated in the warning.

  • #102 Sulav said

    Hi Miguel,
    Thanks for such a nice article. I have a question. How to edit the app.py such that it streams to computers other than the local host, assuming the two computers are on the same LAN connection?
    Thank you.

  • #103 Miguel Grinberg said

    @Sulav: the application is shown above listens on all the public network interfaces, so it should be reachable by any computer in the same network.

  • #104 Phill said

    Fantastic post. How would I go about setting a maximum frame rate, say 1 fps, cache it and serve that to multiple clients? (I'd be using Gevent of course)!

  • #105 Miguel Grinberg said

    @Phill: If you want to serve a specific frame rate have to modify the background thread that grabs frames from the Pi camera, here: https://github.com/miguelgrinberg/flask-video-streaming/blob/master/camera_pi.py#L40-L53.

    For example, you can add a delay inside the loop, to ensure frames are retrieved at your desired rate.

  • #106 saeed said

    hello miguel

    thanks for this nice work. i did this and i could stream the video smoothly on the network. but i have a question:
    is there any way to get the video's width and height from the client(who watch the stream) and set the camera resolution interactively?

    thank you

  • #107 Miguel Grinberg said

    @saeed: You have to consider that the application may be talking to several clients, yet you have only one camera, so it is unrealistic to set the camera to the size requested by the client.

    You can certainly have an API call that sets the video size for all clients, that should be fairly easy to implement. You can check out the API articles I've written in this blog for ideas on how the client can do this.

  • #108 Josseph said

    Hi Miguel,

    I am in awe of what you have accomplished through Flask streaming. I have been able to stream from Raspberry Pi and it is the only way I could get streaming to work on my Android phone.

    Lately I am noticing that my data usage has shot up which I believe is because the streaming is always on and chewing up a lot of bandwidth on my phone. Is there a way to make it on demand, in other words only streaming when I access it on my lan or outside my lan using my external address?

    Thanks in advance.

  • #109 Miguel Grinberg said

    @Joseph: there are many ways to do what you want. For example, on your phone you can connect to the internal LAN address of your Pi, so when you go outside of your home the address will not work. From the server side you can look at the data in the request to determine if the client is allowed to view the stream, maybe look at request.remote_addr. Hope this helps!

  • #110 Leslie Klein said

    I wrote a web server that streams text to a browser. I followed the example in this post, by returning Response(generator()). I got an error: None object has no attribute appy. Basically, the ctx context was None.
    solution: http://flask.pocoo.org/docs/0.10/api/#stream-helpers

    from flask import stream_with_context
    ...
    def f():
    ...
    return Response(stream_with_context(generator()))

  • #111 GustavoVoltanivonAtzingen said

    Thanks Miguel, great article !!!!

    I'm building a baby monitor with a raspberry pi 2 and this is by far the best way for streaming (very low latency, even with 1080p).

    I hope you don't mind, i used your github code from this article and started a baby monitor webapp from your code. I added loggin and I am current working on IF Leds for night vision and after that I will implement some audio streaming (I bought i USP audio card for raspberry pi but i did not arrived yet).

    On the subject, I loved your book (flask web development), and I am using it to make the webapp look nice !

    Thanks alot (sorry for the english - not my native language)

  • #112 Miguel Grinberg said

    @Gustavo: Great project for the Pi, glad my material is useful!

  • #113 nandha said

    File "flask-video-streaming/app.py", line 8
    from camera_pi import Camera
    ^
    IndentationError: unexpected indent

  • #114 Miguel Grinberg said

    @nandha: The error is pretty clear. You probably need to familiarize more with the Python syntax and use the correct indentation.

  • #115 Basel J. Hamadeh said

    Most of your flask applications uses function based views. I was just wondering Why not using a class based views ?

  • #116 Miguel Grinberg said

    @Basel: there's nothing wrong with class based views, but in my opinion they don't really model the stateless nature of the HTTP protocol as well as functions.

  • #117 Windfly said

    Hello Miguel:
    This is a great article, I use the same concept to make a broadcast systems containing
    SocketIO, desktop screen streaming, webcam streaming, the system is created for school PC room,
    desktop screen streaming: I use pyscreenshot for loop to grab->write->read->send to clients,
    webcam streaming: I use opencv to capture->read->imencode->tobytes->send to clients,
    My problems are :
    (1) In CPUi5-4460 environment, when client number is up to 20,CPU loading up to 90% , is there a good way to overcome?

  • #118 Miguel Grinberg said

    @Windfly: it may be interesting to run a profile on the fully loaded application to find where is the bottleneck. The report from the profile will help put the optimization effort in the correct place. I would be interested to see a profiler output from your application, if you can get one.

  • #119 Windfly said

    I test scripts as follows:
    67612 function calls (67331 primitive calls) in 34.981 seconds

    Ordered by: standard name

    ncalls tottime percall cumtime percall filename:lineno(function)
    1 0.000 0.000 0.000 0.000 <string>:1(<module>)
    1 0.000 0.000 0.000 0.000 <string>:1(DecimalTuple)
    1 0.000 0.000 0.000 0.000 <string>:1(_TagInfo)
    98 0.000 0.000 0.000 0.000 <string>:8(new)
    1 0.000 0.000 0.000 0.000 BmpImagePlugin.py:200(DibImageFile)
    1 0.001 0.001 0.002 0.002 BmpImagePlugin.py:27(<module>)
    1 0.000 0.000 0.000 0.000 BmpImagePlugin.py:61(BmpImageFile)
    1 0.002 0.002 0.002 0.002 GifImagePlugin.py:27(<module>)
    1 0.000 0.000 0.000 0.000 GifImagePlugin.py:53(GifImageFile)
    328 0.005 0.000 22.983 0.070 Image.py:1592(save)
    328 0.005 0.000 0.831 0.003 Image.py:1986(new)
    328 0.003 0.000 1.805 0.006 Image.py:2015(frombytes)
    6 0.000 0.000 0.000 0.000 Image.py:2392(register_open)
    5 0.000 0.000 0.000 0.000 Image.py:2407(register_mime)
    6 0.000 0.000 0.000 0.000 Image.py:2418(register_save)
    1 0.000 0.000 0.000 0.000 Image.py:2429(register_save_all)
    12 0.000 0.000 0.000 0.000 Image.py:2441(register_extension)
    328 0.003 0.000 0.018 0.000 Image.py:341(preinit)
    328 0.001 0.000 0.003 0.000 Image.py:401(_getdecoder)
    328 0.001 0.000 0.003 0.000 Image.py:418(_getencoder)
    656 0.004 0.000 0.004 0.000 Image.py:491(init)
    328 0.003 0.000 0.005 0.000 Image.py:511(_new)
    328 0.003 0.000 0.970 0.003 Image.py:709(frombytes)
    656 0.001 0.000 0.002 0.000 Image.py:739(load)
    1 0.000 0.000 0.000 0.000 ImageChops.py:18(<module>)
    1 0.000 0.000 0.000 0.000 ImageColor.py:20(<module>)
    1 0.000 0.000 0.000 0.000 ImageFile.py:273(StubImageFile)
    1 0.000 0.000 0.000 0.000 ImageFile.py:30(<module>)
    1 0.000 0.000 0.000 0.000 ImageFile.py:303(Parser)
    328 0.011 0.000 22.798 0.070 ImageFile.py:442(_save)
    328 0.000 0.000 0.000 0.000 ImageFile.py:69(_tilesort)
    1 0.000 0.000 0.000 0.000 ImageFile.py:78(ImageFile)
    1 0.000 0.000 0.000 0.000 ImageGrab.py:18(<module>)
    328 0.005 0.000 11.502 0.035 ImageGrab.py:38(grab)
    1 0.001 0.001 0.001 0.001 ImagePalette.py:19(<module>)
    1 0.000 0.000 0.000 0.000 ImagePalette.py:23(ImagePalette)
    1 0.000 0.000 0.000 0.000 ImageSequence.py:19(<module>)
    1 0.000 0.000 0.000 0.000 ImageSequence.py:19(Iterator)
    1 0.000 0.000 0.000 0.000 JpegImagePlugin.py:282(JpegImageFile)
    1 0.001 0.001 0.010 0.010 JpegImagePlugin.py:35(<module>)
    1 0.000 0.000 0.000 0.000 JpegPresets.py:67(<module>)
    1 0.000 0.000 0.000 0.000 PngImagePlugin.py:169(iTXt)
    1 0.000 0.000 0.000 0.000 PngImagePlugin.py:189(PngInfo)
    1 0.000 0.000 0.000 0.000 PngImagePlugin.py:266(PngStream)
    1 0.000 0.000 0.001 0.001 PngImagePlugin.py:34(<module>)
    1 0.000 0.000 0.000 0.000 PngImagePlugin.py:484(PngImageFile)
    1245 0.009 0.000 0.196 0.000 PngImagePlugin.py:615(putchunk)
    1 0.000 0.000 0.000 0.000 PngImagePlugin.py:626(_idat)
    328 0.000 0.000 0.000 0.000 PngImagePlugin.py:629(init)
    590 0.001 0.000 0.190 0.000 PngImagePlugin.py:633(write)
    328 0.009 0.000 22.823 0.070 PngImagePlugin.py:637(_save)
    1 0.000 0.000 0.000 0.000 PngImagePlugin.py:97(ChunkStream)
    1 0.000 0.000 0.001 0.001 PpmImagePlugin.py:18(<module>)
    1 0.000 0.000 0.000 0.000 PpmImagePlugin.py:59(PpmImageFile)
    1 0.000 0.000 0.000 0.000 TiffImagePlugin.py:231(ImageFileDirectory_v2)
    1 0.002 0.002 0.009 0.009 TiffImagePlugin.py:42(<module>)
    5 0.000 0.000 0.000 0.000 TiffImagePlugin.py:427(_register_loader)
    5 0.000 0.000 0.000 0.000 TiffImagePlugin.py:428(decorator)
    5 0.000 0.000 0.000 0.000 TiffImagePlugin.py:436(_register_writer)
    5 0.000 0.000 0.000 0.000 TiffImagePlugin.py:437(decorator)
    7 0.000 0.000 0.000 0.000 TiffImagePlugin.py:442(_register_basic)
    1 0.000 0.000 0.000 0.000 TiffImagePlugin.py:650(ImageFileDirectory_v1)
    1 0.000 0.000 0.000 0.000 TiffImagePlugin.py:743(TiffImageFile)
    1 0.000 0.000 0.001 0.001 TiffTags.py:20(<module>)
    1 0.000 0.000 0.000 0.000 TiffTags.py:23(TagInfo)
    98 0.000 0.000 0.000 0.000 TiffTags.py:26(new)
    1 0.000 0.000 0.000 0.000 TiffTags.py:285(_populate)
    2 0.000 0.000 0.000 0.000 init.py:1023(getLogger)
    2 0.000 0.000 0.000 0.000 init.py:1069(_fixupParents)
    2 0.000 0.000 0.000 0.000 init.py:1125(init)
    656 0.001 0.000 0.004 0.000 init.py:1143(debug)
    656 0.001 0.000 0.001 0.000 init.py:1344(getEffectiveLevel)
    656 0.003 0.000 0.003 0.000 init.py:1358(isEnabledFor)
    328 0.003 0.000 11.672 0.036 init.py:14(_grab_simple)
    2 0.000 0.000 0.000 0.000 init.py:1565(getLogger)
    2 0.000 0.000 0.000 0.000 init.py:183(_checkLevel)
    2 0.000 0.000 0.000 0.000 init.py:211(_acquireLock)
    2 0.000 0.000 0.000 0.000 init.py:220(_releaseLock)
    328 0.002 0.000 11.674 0.036 init.py:41(_grab)
    328 0.001 0.000 11.675 0.036 init.py:49(grab)
    1 0.000 0.000 0.000 0.000 init.py:49(normalize_encoding)
    2 0.000 0.000 0.000 0.000 init.py:585(init)
    1 0.000 0.000 0.000 0.000 init.py:71(search_function)
    2 0.000 0.000 0.000 0.000 init.py:974(append)
    2490 0.001 0.000 0.002 0.000 _binary.py:69(o16be)
    1901 0.001 0.000 0.004 0.000 _binary.py:73(o32be)
    328 0.001 0.000 0.002 0.000 _util.py:4(isStringType)
    328 0.000 0.000 0.001 0.000 _util.py:7(isPath)
    5 0.000 0.000 0.000 0.000 _weakrefset.py:16(init)
    5 0.000 0.000 0.000 0.000 _weakrefset.py:20(enter)
    5 0.000 0.000 0.000 0.000 _weakrefset.py:26(exit)
    14 0.000 0.000 0.000 0.000 _weakrefset.py:36(init)
    5 0.000 0.000 0.000 0.000 _weakrefset.py:52(_commit_removals)
    12 0.000 0.000 0.000 0.000 _weakrefset.py:58(iter)
    5 0.000 0.000 0.000 0.000 _weakrefset.py:70(contains)
    6 0.000 0.000 0.000 0.000 _weakrefset.py:83(add)
    1 0.000 0.000 0.000 0.000 abc.py:105(register)
    5/1 0.000 0.000 0.000 0.000 abc.py:148(subclasscheck)
    3 0.000 0.000 0.000 0.000 abc.py:86(new)
    3 0.000 0.000 0.000 0.000 abc.py:89(<genexpr>)
    1 0.000 0.000 0.000 0.000 ascii.py:13(Codec)
    1 0.000 0.000 0.000 0.000 ascii.py:20(IncrementalEncoder)
    1 0.000 0.000 0.000 0.000 ascii.py:24(IncrementalDecoder)
    1 0.000 0.000 0.000 0.000 ascii.py:28(StreamWriter)
    1 0.000 0.000 0.000 0.000 ascii.py:31(StreamReader)
    1 0.000 0.000 0.000 0.000 ascii.py:34(StreamConverter)
    1 0.000 0.000 0.000 0.000 ascii.py:41(getregentry)
    1 0.000 0.000 0.000 0.000 ascii.py:8(<module>)
    1 0.000 0.000 0.000 0.000 codecs.py:83(new)
    2 0.001 0.000 0.001 0.000 collections.py:293(namedtuple)
    71 0.000 0.000 0.000 0.000 collections.py:337(<genexpr>)
    10 0.000 0.000 0.000 0.000 collections.py:361(<genexpr>)
    10 0.000 0.000 0.000 0.000 collections.py:363(<genexpr>)
    1 0.000 0.000 0.003 0.003 decimal.py:116(<module>)
    1 0.000 0.000 0.000 0.000 decimal.py:160(DecimalException)
    1 0.000 0.000 0.000 0.000 decimal.py:183(Clamped)
    1 0.000 0.000 0.000 0.000 decimal.py:195(InvalidOperation)
    1 0.000 0.000 0.000 0.000 decimal.py:224(ConversionSyntax)
    1 0.000 0.000 0.000 0.000 decimal.py:234(DivisionByZero)
    1 0.000 0.000 0.000 0.000 decimal.py:250(DivisionImpossible)
    1 0.000 0.000 0.000 0.000 decimal.py:261(DivisionUndefined)
    1 0.000 0.000 0.000 0.000 decimal.py:272(Inexact)
    1 0.000 0.000 0.000 0.000 decimal.py:284(InvalidContext)
    1 0.000 0.000 0.000 0.000 decimal.py:298(Rounded)
    1 0.000 0.000 0.000 0.000 decimal.py:310(Subnormal)
    1 0.000 0.000 0.000 0.000 decimal.py:321(Overflow)
    1 0.000 0.000 0.000 0.000 decimal.py:359(Underflow)
    1 0.000 0.000 0.000 0.000 decimal.py:3749(_ContextManager)
    1 0.000 0.000 0.000 0.000 decimal.py:3764(Context)
    3 0.000 0.000 0.000 0.000 decimal.py:3783(init)
    27 0.000 0.000 0.000 0.000 decimal.py:3810(<genexpr>)
    27 0.000 0.000 0.000 0.000 decimal.py:3817(<genexpr>)
    1 0.000 0.000 0.000 0.000 decimal.py:505(Decimal)
    6 0.000 0.000 0.000 0.000 decimal.py:514(new)
    1 0.000 0.000 0.000 0.000 decimal.py:5419(_WorkRep)
    1 0.000 0.000 0.000 0.000 decimal.py:5673(_Log10Memoize)
    1 0.000 0.000 0.000 0.000 decimal.py:5677(init)
    5 0.000 0.000 0.000 0.000 fractions.py:280(_operator_fallbacks)
    1 0.002 0.002 0.006 0.006 fractions.py:4(<module>)
    1 0.000 0.000 0.000 0.000 fractions.py:44(Fraction)
    328 0.001 0.000 0.002 0.000 genericpath.py:93(_splitext)
    328 0.003 0.000 0.003 0.000 loader.py:13(init)
    656 0.000 0.000 0.000 0.000 loader.py:24(plugin_classes)
    328 0.001 0.000 0.005 0.000 loader.py:31(force)
    328 0.000 0.000 0.000 0.000 loader.py:36(is_forced)
    328 0.005 0.000 0.011 0.000 loader.py:44(get_valid_plugin_by_name)
    1968 0.000 0.000 0.000 0.000 loader.py:46(<lambda>)
    328 0.001 0.000 0.011 0.000 loader.py:58(get_valid_plugin_by_list)
    328 0.002 0.000 0.014 0.000 loader.py:64(selected)
    1 0.000 0.000 0.000 0.000 locale.py:363(normalize)
    1 0.000 0.000 0.000 0.000 locale.py:447(_parse_localename)
    1 0.000 0.000 0.000 0.000 locale.py:493(getdefaultlocale)
    1 0.000 0.000 0.000 0.000 locale.py:546(getlocale)
    328 0.001 0.000 0.002 0.000 ntpath.py:199(splitext)
    655 0.004 0.000 0.010 0.000 ntpath.py:63(join)
    1965 0.005 0.000 0.006 0.000 ntpath.py:96(splitdrive)
    328 0.004 0.000 0.004 0.000 pil.py:13(init)
    328 0.144 0.000 11.646 0.036 pil.py:17(grab)
    8 0.000 0.000 0.004 0.000 re.py:192(compile)
    8 0.000 0.000 0.004 0.000 re.py:230(_compile)
    8 0.000 0.000 0.000 0.000 sre_compile.py:101(fixup)
    28 0.000 0.000 0.000 0.000 sre_compile.py:228(_compile_charset)
    28 0.000 0.000 0.000 0.000 sre_compile.py:256(_optimize_charset)
    2 0.000 0.000 0.000 0.000 sre_compile.py:411(_mk_bitmap)
    36 0.000 0.000 0.000 0.000 sre_compile.py:428(_simple)
    8 0.000 0.000 0.000 0.000 sre_compile.py:433(_compile_info)
    16 0.000 0.000 0.000 0.000 sre_compile.py:546(isstring)
    8 0.000 0.000 0.001 0.000 sre_compile.py:552(_code)
    8 0.000 0.000 0.004 0.000 sre_compile.py:567(compile)
    88/8 0.000 0.000 0.001 0.000 sre_compile.py:64(_compile)
    160 0.000 0.000 0.000 0.000 sre_parse.py:137(len)
    337 0.000 0.000 0.000 0.000 sre_parse.py:141(getitem)
    36 0.000 0.000 0.000 0.000 sre_parse.py:145(setitem)
    109 0.000 0.000 0.000 0.000 sre_parse.py:149(append)
    116/44 0.000 0.000 0.000 0.000 sre_parse.py:151(getwidth)
    8 0.000 0.000 0.000 0.000 sre_parse.py:189(init)
    1480 0.001 0.000 0.001 0.000 sre_parse.py:193(next)
    274 0.000 0.000 0.000 0.000 sre_parse.py:206(match)
    1360 0.000 0.000 0.001 0.000 sre_parse.py:212(get)
    104 0.000 0.000 0.000 0.000 sre_parse.py:221(isident)
    19 0.000 0.000 0.000 0.000 sre_parse.py:227(isname)
    30 0.000 0.000 0.000 0.000 sre_parse.py:268(_escape)
    41/8 0.000 0.000 0.003 0.000 sre_parse.py:317(_parse_sub)
    47/8 0.001 0.000 0.003 0.000 sre_parse.py:395(_parse)
    8 0.000 0.000 0.000 0.000 sre_parse.py:67(__init
    )
    8 0.000 0.000 0.003 0.000 sre_parse.py:706(parse)
    23 0.000 0.000 0.000 0.000 sre_parse.py:74(opengroup)
    23 0.000 0.000 0.000 0.000 sre_parse.py:85(closegroup)
    88 0.000 0.000 0.000 0.000 sre_parse.py:92(init)
    1 0.183 0.183 34.981 34.981 test_camera.py:56(get_frame)
    2 0.000 0.000 0.000 0.000 threading.py:147(acquire)
    2 0.000 0.000 0.000 0.000 threading.py:187(release)
    4 0.000 0.000 0.000 0.000 threading.py:64(_note)
    2490 0.063 0.000 0.063 0.000 {PIL._imaging.crc32}
    328 0.818 0.002 0.818 0.002 {PIL._imaging.fill}
    328 9.692 0.030 9.692 0.030 {PIL._imaging.grabscreen}
    328 0.001 0.000 0.001 0.000 {PIL._imaging.raw_decoder}
    328 0.001 0.000 0.001 0.000 {PIL._imaging.zip_encoder}
    1 0.000 0.000 0.000 0.000 {import}
    1 0.000 0.000 0.000 0.000 {_locale._getdefaultlocale}
    1 0.000 0.000 0.000 0.000 {_locale.setlocale}
    8 0.000 0.000 0.000 0.000 {_sre.compile}
    27 0.000 0.000 0.000 0.000 {_sre.getlower}
    7 0.000 0.000 0.000 0.000 {_struct.calcsize}
    4391 0.003 0.000 0.003 0.000 {_struct.pack}
    3 0.000 0.000 0.000 0.000 {abs}
    10 0.000 0.000 0.000 0.000 {all}
    108 0.000 0.000 0.000 0.000 {built-in method new of type object at 0x000000001E2A0670}
    328 0.001 0.000 0.001 0.000 {filter}
    815 0.001 0.000 0.001 0.000 {getattr}
    984 0.001 0.000 0.001 0.000 {hasattr}
    2032 0.002 0.000 0.002 0.000 {isinstance}
    13/2 0.000 0.000 0.000 0.000 {issubclass}
    7734/7692 0.001 0.000 0.001 0.000 {len}
    3 0.000 0.000 0.000 0.000 {map}
    663 0.001 0.000 0.001 0.000 {max}
    10 0.000 0.000 0.000 0.000 {method 'contains' of 'frozenset' objects}
    5 0.000 0.000 0.000 0.000 {method 'subclasses' of 'type' objects}
    5 0.000 0.000 0.000 0.000 {method 'subclasshook' of 'object' objects}
    2 0.000 0.000 0.000 0.000 {method 'acquire' of 'thread.lock' objects}
    19 0.000 0.000 0.000 0.000 {method 'add' of 'set' objects}
    877 0.000 0.000 0.000 0.000 {method 'append' of 'list' objects}
    327 0.000 0.000 0.000 0.000 {method 'cleanup' of 'ImagingEncoder' objects}
    328 0.035 0.000 0.035 0.000 {method 'close' of 'file' objects}
    328 0.000 0.000 0.000 0.000 {method 'copy' of 'dict' objects}
    328 0.964 0.003 0.964 0.003 {method 'decode' of 'ImagingDecoder' objects}
    1 0.000 0.000 0.000 0.000 {method 'decode' of 'str' objects}
    1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
    591 22.591 0.038 22.591 0.038 {method 'encode' of 'ImagingEncoder' objects}
    1 0.000 0.000 0.000 0.000 {method 'encode' of 'unicode' objects}
    4 0.000 0.000 0.000 0.000 {method 'extend' of 'list' objects}
    56 0.000 0.000 0.000 0.000 {method 'find' of 'bytearray' objects}
    327 0.005 0.000 0.005 0.000 {method 'flush' of 'file' objects}
    18 0.000 0.000 0.000 0.000 {method 'format' of 'str' objects}
    2357 0.000 0.000 0.000 0.000 {method 'get' of 'dict' objects}
    10 0.000 0.000 0.000 0.000 {method 'group' of '_sre.SRE_Match' objects}
    61 0.000 0.000 0.000 0.000 {method 'isalnum' of 'str' objects}
    10 0.000 0.000 0.000 0.000 {method 'isdigit' of 'str' objects}
    19 0.000 0.000 0.000 0.000 {method 'items' of 'dict' objects}
    1250 0.001 0.000 0.001 0.000 {method 'join' of 'str' objects}
    340 0.000 0.000 0.000 0.000 {method 'lower' of 'str' objects}
    1 0.000 0.000 0.000 0.000 {method 'lstrip' of 'str' objects}
    3 0.000 0.000 0.000 0.000 {method 'match' of '_sre.SRE_Pattern' objects}
    656 0.000 0.000 0.000 0.000 {method 'pixel_access' of 'ImagingCore' objects}
    327 0.087 0.000 0.087 0.000 {method 'read' of 'file' objects}
    2 0.000 0.000 0.000 0.000 {method 'release' of 'thread.lock' objects}
    23 0.000 0.000 0.000 0.000 {method 'remove' of 'list' objects}
    5 0.000 0.000 0.000 0.000 {method 'remove' of 'set' objects}
    1660 0.001 0.000 0.001 0.000 {method 'replace' of 'str' objects}
    988 0.001 0.000 0.001 0.000 {method 'rfind' of 'str' objects}
    328 0.001 0.000 0.001 0.000 {method 'setimage' of 'ImagingDecoder' objects}
    328 0.001 0.000 0.001 0.000 {method 'setimage' of 'ImagingEncoder' objects}
    1 0.000 0.000 0.000 0.000 {method 'setter' of 'property' objects}
    328 0.001 0.000 0.001 0.000 {method 'sort' of 'list' objects}
    3 0.000 0.000 0.000 0.000 {method 'split' of 'str' objects}
    13 0.000 0.000 0.000 0.000 {method 'startswith' of 'str' objects}
    3 0.000 0.000 0.000 0.000 {method 'strip' of 'str' objects}
    4 0.000 0.000 0.000 0.000 {method 'translate' of 'str' objects}
    686 0.000 0.000 0.000 0.000 {method 'upper' of 'str' objects}
    4063 0.121 0.000 0.121 0.000 {method 'write' of 'file' objects}
    167 0.000 0.000 0.000 0.000 {min}
    655 0.139 0.000 0.139 0.000 {open}
    55 0.000 0.000 0.000 0.000 {ord}
    39 0.000 0.000 0.000 0.000 {range}
    2 0.000 0.000 0.000 0.000 {repr}
    24 0.000 0.000 0.000 0.000 {setattr}
    2 0.000 0.000 0.000 0.000 {sys._getframe}
    4 0.000 0.000 0.000 0.000 {thread.get_ident}
    Do you have any idea?

  • #120 Miguel Grinberg said

    @Windfly: the biggest offender in your profile report is the encode image method, and the second

    591 22.591 0.038 22.591 0.038 {method 'encode' of 'ImagingEncoder' objects}
    328 9.692 0.030 9.692 0.030 {PIL._imaging.grabscreen}

    So basically you need to figure out how to optimize the image pipeline, there is nothing on the web server side that is on that same scale in terms of performance. It's odd that there were 328 screen grabs, but 591 image encoders during your profile period. Do you have an explanation for that discrepancy?

  • #121 ron said

    hi,

    great tutorial!

    i am trying to stream a live webcam feed and i am using this camera.py:

    import cv2

    class VideoCamera(object):
    def init(self):
    # Using OpenCV to capture from device 0. If you have trouble capturing
    # from a webcam, comment the line below out and use a video file
    # instead.
    self.video = cv2.VideoCapture(0)
    # If you decide to use video.mp4, you must have this file in the folder
    # as the main.py.
    # self.video = cv2.VideoCapture('1.mp4')

    def __del__(self):
        self.video.release()
    
    def get_frame(self):
        success, image = self.video.read()
        System.out.println("Frame Obtained");
        # 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 stream.
        ret, jpeg = cv2.imencode('.jpg', image)
        return jpeg.tobytes()
    

    unfortunately i don't even see my webcam startup so no webcam video is streamed into the img tag on the html file.

    any reason why? do i need to give the webcam time to sleep? or is there another hardware issue i am not considering?

    thanks,
    ron

  • #122 Windfly said

    When I run the server project , it shows nothing and
    the servcie is occupied, so just test the process of image.
    May I send my project to you?

  • #123 kashif ali said

    sir how can i access value of textbox in python.py file while streaming is running on server actually problem is when streaming is running it cannot allow me to show value or manipulate with the value of textbox can you tell me how can i solve thin problem??

  • #124 Miguel Grinberg said

    @ron: did you confirm with a debug or print statements that the camera object is created and that the get_frame function is called?

  • #125 Miguel Grinberg said

    @kashif: my guess is that you need to use a web server that supports handling multiple tasks at a time. The Flask development server handles one client at a time by default, so when you stream video there is no way to make the server do something else.

Leave a Comment