简体   繁体   English

使用 Flask 流式传输视频文件

[英]Streaming video files using Flask

Please help me to understand one moment.请帮助我理解片刻。

I am trying to make Flask to stream .mp4 video.我正在尝试让 Flask 流式传输 .mp4 视频。 I know that i can use Response(generator_function())我知道我可以使用Response(generator_function())

But it does not allow to jump to specific minute while watching a video in browser.但它不允许在浏览器中观看视频时跳转到特定分钟​​。

So i am trying to use Range header.所以我正在尝试使用Range标头。 Here is how i try it:这是我尝试的方法:

app = Flask(__name__)


def get_chunk(byte1=None, byte2=None):
    filesize = os.path.getsize('try2.mp4')
    yielded = 0
    yield_size = 1024 * 1024

    if byte1 is not None:
        if not byte2:
            byte2 = filesize
        yielded = byte1
        filesize = byte2

    with open('try2.mp4', 'rb') as f:
        content = f.read()

    while True:
        remaining = filesize - yielded
        if yielded == filesize:
            break
        if remaining >= yield_size:
            yield content[yielded:yielded+yield_size]
            yielded += yield_size
        else:
            yield content[yielded:yielded+remaining]
            yielded += remaining


@app.route('/')
def get_file():
    filesize = os.path.getsize('try2.mp4')
    range_header = flask_request.headers.get('Range', None)

    if range_header:
        byte1, byte2 = None, None
        match = re.search(r'(\d+)-(\d*)', range_header)
        groups = match.groups()

        if groups[0]:
            byte1 = int(groups[0])
        if groups[1]:
            byte2 = int(groups[1])

        if not byte2:
            byte2 = byte1 + 1024 * 1024
            if byte2 > filesize:
                byte2 = filesize

        length = byte2 + 1 - byte1

        resp = Response(
            get_chunk(byte1, byte2),
            status=206, mimetype='video/mp4',
            content_type='video/mp4',
            direct_passthrough=True
        )

        resp.headers.add('Content-Range',
                         'bytes {0}-{1}/{2}'
                         .format(byte1,
                                 length,
                                 filesize))
        return resp

    return Response(
        get_chunk(),
        status=200, mimetype='video/mp4'
    )


@app.after_request
def after_request(response):
    response.headers.add('Accept-Ranges', 'bytes')
    return response

get_chunk yields chunks from byte1 to byte2 if this bytes are specified, and from 0 to filesize otherwise (chunk size = 1MB).如果指定了这个字节, get_chunk产生从 byte1 到 byte2 的块,否则从 0 到文件大小(块大小 = 1MB)。

But it does not work.但它不起作用。 I see that firstly browser sends request with <200> status.我看到首先浏览器发送具有 <200> 状态的请求。 And then with <206>.然后使用 <206>。 Please advice me how to make it working.请告诉我如何使它工作。

On development server you need to enable threaded=True for video stream to work correctly.在开发服务器上,您需要启用threaded=True才能使视频流正常工作。

Updated:更新:

@app.after_request
def after_request(response):
    response.headers.add('Accept-Ranges', 'bytes')
    return response


def get_chunk(byte1=None, byte2=None):
    full_path = "try2.mp4"
    file_size = os.stat(full_path).st_size
    start = 0
    
    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)
    return chunk, start, length, file_size


@app.route('/video')
def get_file():
    range_header = request.headers.get('Range', None)
    byte1, byte2 = 0, None
    if range_header:
        match = re.search(r'(\d+)-(\d*)', range_header)
        groups = match.groups()

        if groups[0]:
            byte1 = int(groups[0])
        if groups[1]:
            byte2 = int(groups[1])
       
    chunk, start, length, file_size = get_chunk(byte1, byte2)
    resp = Response(chunk, 206, mimetype='video/mp4',
                      content_type='video/mp4', direct_passthrough=True)
    resp.headers.add('Content-Range', 'bytes {0}-{1}/{2}'.format(start, start + length - 1, file_size))
    return resp

if __name__ == '__main__':
    app.run(threaded=True)

okay i this might be coming late but this is a simplified code i wrote.好的,我可能会迟到,但这是我编写的简化代码。 still same concept as above but better and simpler i think.仍然与上述相同的概念,但我认为更好更简单。

import os
import re
from flask import render_template, request, Blueprint, current_app, send_file

core = Blueprint("core", __name__)

# your request handles here with @core.route()


@core.route("/")
def home():
    return render_template("index.html")


@core.route("/video", methods=["GET"])
def video():
    headers = request.headers
    if not "range" in headers:
        return current_app.response_class(status=400)

    video_path = os.path.abspath(os.path.join("media", "test.mp4"))
    size = os.stat(video_path)
    size = size.st_size

    chunk_size = 10**3
    start = int(re.sub("\D", "", headers["range"]))
    end = min(start + chunk_size, size - 1)

    content_lenght = end - start + 1

    def get_chunk(video_path, start, end):
        with open(video_path, "rb") as f:
            f.seek(start)
            chunk = f.read(end)
        return chunk

    headers = {
        "Content-Range": f"bytes {start}-{end}/{size}",
        "Accept-Ranges": "bytes",
        "Content-Length": content_lenght,
        "Content-Type": "video/mp4",
    }

    return current_app.response_class(get_chunk(video_path, start, end), 206, headers)

How does it work?它是如何工作的? I am just geting ERROR 400我只是收到错误 400

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM