问题
I'm trying to play a video that is served by a Flask web application on my iOS application. While I can play any video served with a "conventional" web server (like Apache), I can't play the video served by Flask. Here is the relevant code:
Objective-C
NSURL *videoURL = [NSURL URLWithString:[NSString stringWithFormat:@"%@",videourltemp]];
AVPlayer *player = [AVPlayer playerWithURL:videoURL];
playerViewController.player = player;
[self.view addSubview:playerViewController.view];
[self.navigationController pushViewController:playerViewController animated:YES];
Python
from flask import Response, ...
def get_img(imgid):
# private code hidden - file["path"] contains the path relative to /root/media_assets directory
return Response(open("/root/media_assets/" + file["path"], "rb"), mimetype="video/mp4")
Sidenote: if I try to reach my URL from a browser, the video is correctly loaded.
How could I solve my problem?
Thank you in advance!
回答1:
You have two options there:
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")
Use direct approach from How do I stream a file using werkzeug?:
return Response(file("/root/media_assets/" + file["path"]), direct_passthrough=True)
回答2:
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:
- add "Accept-Ranges" in Flask app after_request so that the client knows "range" is supported:
@app.after_request
def after_request(response):
response.headers.add('Accept-Ranges', 'bytes')
return response
- 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.
来源:https://stackoverflow.com/questions/50001356/serving-a-mp4-file-with-flask-and-playing-it-on-an-objective-c-app-causes-broke