Serving a .mp4 file with Flask and playing it on an Objective-C app causes Broken pipe and no play Serving a .mp4 file with Flask and playing it on an Objective-C app causes Broken pipe and no play flask flask

Serving a .mp4 file with Flask and playing it on an Objective-C app causes Broken pipe and no play


I encountered the same problem and eventually found that the real issue is that the video player client (in Objective-C iOS at least) uses the "range" header in the response (you can print out Flask request.headers to check). In other words, the streaming is really implemented using "range" support in HTTP.

I followed examples at https://codeburst.io/the-taste-of-media-streaming-with-flask-cdce35908a50, the Flask server code needs to build response using "partial content" (HTTP status code 206) and needs to process the "range" header in the request. The related code looks like this:

  1. add "Accept-Ranges" in Flask app after_request so that the client knows "range" is supported:
@app.after_requestdef after_request(response):    response.headers.add('Accept-Ranges', 'bytes')    return response
  1. in your function that serving the mp4 file, suppose the file path is "full_path":
    file_size = os.stat(full_path).st_size    start = 0    length = 10240  # can be any default length you want    range_header = request.headers.get('Range', None)    if range_header:        m = re.search('([0-9]+)-([0-9]*)', range_header)  # example: 0-1000 or 1250-        g = m.groups()        byte1, byte2 = 0, None        if g[0]:            byte1 = int(g[0])        if g[1]:            byte2 = int(g[1])        if byte1 < file_size:            start = byte1        if byte2:            length = byte2 + 1 - byte1        else:            length = file_size - start    with open(full_path, 'rb') as f:        f.seek(start)        chunk = f.read(length)    rv = Response(chunk, 206, mimetype='video/mp4', content_type='video/mp4', direct_passthrough=True)    rv.headers.add('Content-Range', 'bytes {0}-{1}/{2}'.format(start, start + length - 1, file_size))    return rv

In my testing, the above Flask code works with iOS objective-C client as well as Chrome, Firefox browsers for .mp4 files.


You have two options there:

  1. Open the file and read it in chunks instead of reading it as a single blob, like in your code. Follow example from: https://stackoverflow.com/a/24318158/1955346:

    from flask import stream_with_context, Response@app.route('/stream_data')def stream_data():    def generate():        with open("/root/media_assets/" + file["path"], "rb") as f:            while True:                chunk = ... # read each chunk or break if EOF                yield chunk    return Response(stream_with_context(generate()), mimetype="video/mp4")
  2. Use direct approach from How do I stream a file using werkzeug?: return Response(file("/root/media_assets/" + file["path"]), direct_passthrough=True)