Обслуживание файла .mp4 с помощью Flask и его воспроизведение в приложении Objective-C приводит к нарушению работы канала и отсутствию воспроизведения. - PullRequest
0 голосов
/ 24 апреля 2018

Я пытаюсь воспроизвести видео, которое обслуживается веб-приложением Flask в моем приложении iOS. Хотя я могу воспроизводить любое видео, подаваемое на «обычном» веб-сервере (например, Apache), я не могу воспроизвести видео, обслуживаемое Flask. Вот соответствующий код:

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: если я пытаюсь получить URL-адрес из браузера, видео загружается правильно.

Как я мог решить мою проблему?

Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 26 февраля 2019

Я столкнулся с той же проблемой и в итоге обнаружил, что реальная проблема заключается в том, что клиент видеопроигрывателя (по крайней мере, в iOS для Objective C) использует заголовок «range» в ответе (вы можете распечатать Flask request.headers для проверки ). Другими словами, потоковая передача действительно реализована с использованием поддержки «диапазона» в HTTP.

Я следовал за примерами на https://codeburst.io/the-taste-of-media-streaming-with-flask-cdce35908a50, Код сервера Flask должен создавать ответ с использованием «частичного содержимого» (код состояния HTTP 206) и обрабатывать заголовок «range» в запросе. Связанный код выглядит так:

  1. добавить «Accept-Ranges» в приложение Flask after_request, чтобы клиент знал, что «range» поддерживается:
@app.after_request
def after_request(response):
    response.headers.add('Accept-Ranges', 'bytes')
    return response
  1. в вашей функции, которая обслуживает файл mp4, предположим, что путь к файлу "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

В моем тестировании приведенный выше код Flask работает с клиентом iOS Objective-C, а также браузерами Chrome и Firefox для файлов .mp4.

0 голосов
/ 24 апреля 2018

У вас есть два варианта:

  1. Откройте файл и прочитайте его кусками вместо того, чтобы читать его как один большой двоичный объект, как в вашем коде. Следуйте примеру от: 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. Использовать прямой подход из Как мне передать файл с помощью werkzeug? : return Response(file("/root/media_assets/" + file["path"]), direct_passthrough=True)

...