Python - Django:使用 HttpResponse 流式传输视频/mp4 文件 [英] Python - Django: Streaming video/mp4 file using HttpResponse

查看:139
本文介绍了Python - Django:使用 HttpResponse 流式传输视频/mp4 文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用 Python2.7、django==1.7uwsgi 将视频/mp4 文件流式传输到 iPhone 播放器.>

我的代码如下:

def 流(请求):使用 open('/path/video.mp4', 'r') 作为 video_file:响应 = HttpResponse(video_file.read(), content_type='video/mp4')response['Content-Disposition'] = 'inline;文件名=%s'%'video.mp4'返回响应video_file.close

当我使用一些小视频(小于 1MB)时,它会在浏览器中流式传输,但在 iPhone 播放器中出现此错误:

<块引用>

[uwsgi-http 密钥:127.0.0.1:8008 客户端地址:192.168.0.172client_port: 14563] hr_write(): 管道损坏 [plugins/http/http.c 行564]

当视频大小超过 5MB 时,它不会在两个(意味着浏览器和 iPhone 播放器)中以相同的错误流式传输.

我尝试通过使用 StreamHttpRespose 返回块块来做到这一点,如下所示:

def read(chunksize=8192):使用 open('/path/video.mp4', 'rb') 作为 video_file:字节 = video_file.read(chunksize)而字节:产生字节返回 StreamingHttpResponse(read(), content_type='video/mp4')

但是出现了同样的错误:Broken pipe.

仅供参考,我可以流式传输 pdf 和图像文件.此问题仅适用于 mp4 文件.而且我将 content_type 更改为video-mpeg",浏览器下载了它,而我想阻止文件下载.

你有什么想法?有什么解决办法!!?

解决方案

我遇到了同样的问题,在找到可行的解决方案之前我做了很多挖掘!

显然,HTML5 视频控件需要 Accept Ranges 标头才能工作 (https://stackoverflow.com/a/24977085/4264463).因此,我们需要从 HTTP_RANGE 解析请求的范围并返回 Content-Range 与响应.传递给 StreamingHttpResponse 的生成器也需要根据这个范围返回内容(通过 offsetlength).我发现以下代码段效果很好(来自 http://codegist.net/snippet/python/range_streamingpy_dcwatson_python):

导入操作系统进口重新导入 mimetypes从 wsgiref.util 导入 FileWrapper从 django.http.response 导入 StreamingHttpResponserange_re = re.compile(r'bytess*=s*(d+)s*-s*(d*)', re.I)类 RangeFileWrapper(对象):def __init__(self, filelike, blksize=8192, offset=0, length=None):self.filelike = filelikeself.filelike.seek(偏移量,os.SEEK_SET)self.remaining = 长度self.blksize = blksize定义关闭(自我):如果 hasattr(self.filelike, 'close'):self.filelike.close()def __iter__(self):回归自我def __next__(self):如果 self.remaining 是 None:# 如果剩余为无,我们正在读取整个文件.数据 = self.filelike.read(self.blksize)如果数据:返回数据引发停止迭代()别的:如果 self.remaining <= 0:引发停止迭代()数据 = self.filelike.read(min(self.remaining, self.blksize))如果不是数据:引发停止迭代()self.remaining -= len(data)返回数据def stream_video(请求,路径):range_header = request.META.get('HTTP_RANGE', '').strip()range_match = range_re.match(range_header)大小 = os.path.getsize(path)content_type, encoding = mimetypes.guess_type(path)content_type = content_type 或 'application/octet-stream'如果 range_match:first_byte, last_byte = range_match.groups()first_byte = int(first_byte) if first_byte else 0last_byte = int(last_byte) if last_byte else size - 1如果 last_byte >= 大小:last_byte = 大小 - 1长度 = last_byte - first_byte + 1resp = StreamingHttpResponse(RangeFileWrapper(open(path, 'rb'), offset=first_byte, length=length), status=206, content_type=content_type)resp['Content-Length'] = str(length)resp['Content-Range'] = 'bytes %s-%s/%s' % (first_byte, last_byte, size)别的:resp = StreamingHttpResponse(FileWrapper(open(path, 'rb')), content_type=content_type)resp['Content-Length'] = str(size)resp['接受范围'] = '字节'返回响应

I'm using Python2.7, django==1.7 and uwsgi for streaming video/mp4 file to iPhone player.

My code is as below:

def stream(request):
     with open('/path/video.mp4', 'r') as video_file:
        response = HttpResponse(video_file.read(), content_type='video/mp4')
        response['Content-Disposition'] = 'inline; filename=%s' % 'video.mp4'
        return response
     video_file.close

When i use some small video (less than 1MB), it streams in browser, but in iPhone palyer i have this error:

[uwsgi-http key: 127.0.0.1:8008 client_addr: 192.168.0.172 client_port: 14563] hr_write(): Broken pipe [plugins/http/http.c line 564]

And when the video size is more that 5MB, it doesn't stream in both (means browser and iPhone player) with same error.

I tried to do that by chunk chunk returning using StreamHttpRespose as below:

def read(chunksize=8192):
    with open('/path/video.mp4', 'rb') as video_file:
        byte = video_file.read(chunksize)
        while byte:
            yield byte

return StreamingHttpResponse(read(), content_type='video/mp4')

But there is the same error: Broken pipe.

fyi I can stream pdf and image files. This problem is only with mp4 files. And also i changed the content_type to 'video-mpeg', the browser downloaded that, while i want to prevent file downloading.

What's your idea? Any solution!!?

解决方案

I had the same problem and did a lot of digging before finding a workable solution!

Apparently the Accept Ranges header is needed for HTML5 video controls to work (https://stackoverflow.com/a/24977085/4264463). So, we need to both parse the requested range from HTTP_RANGE and return Content-Range with the response. The generator that is passed to StreamingHttpResponse also needs to return content based on this range as well (by offset and length). I've found the follow snippet that works great (from http://codegist.net/snippet/python/range_streamingpy_dcwatson_python):

import os
import re
import mimetypes
from wsgiref.util import FileWrapper

from django.http.response import StreamingHttpResponse


range_re = re.compile(r'bytess*=s*(d+)s*-s*(d*)', re.I)


class RangeFileWrapper(object):
    def __init__(self, filelike, blksize=8192, offset=0, length=None):
        self.filelike = filelike
        self.filelike.seek(offset, os.SEEK_SET)
        self.remaining = length
        self.blksize = blksize

    def close(self):
        if hasattr(self.filelike, 'close'):
            self.filelike.close()

    def __iter__(self):
        return self

    def __next__(self):
        if self.remaining is None:
            # If remaining is None, we're reading the entire file.
            data = self.filelike.read(self.blksize)
            if data:
                return data
            raise StopIteration()
        else:
            if self.remaining <= 0:
                raise StopIteration()
            data = self.filelike.read(min(self.remaining, self.blksize))
            if not data:
                raise StopIteration()
            self.remaining -= len(data)
            return data


def stream_video(request, path):
    range_header = request.META.get('HTTP_RANGE', '').strip()
    range_match = range_re.match(range_header)
    size = os.path.getsize(path)
    content_type, encoding = mimetypes.guess_type(path)
    content_type = content_type or 'application/octet-stream'
    if range_match:
        first_byte, last_byte = range_match.groups()
        first_byte = int(first_byte) if first_byte else 0
        last_byte = int(last_byte) if last_byte else size - 1
        if last_byte >= size:
            last_byte = size - 1
        length = last_byte - first_byte + 1
        resp = StreamingHttpResponse(RangeFileWrapper(open(path, 'rb'), offset=first_byte, length=length), status=206, content_type=content_type)
        resp['Content-Length'] = str(length)
        resp['Content-Range'] = 'bytes %s-%s/%s' % (first_byte, last_byte, size)
    else:
        resp = StreamingHttpResponse(FileWrapper(open(path, 'rb')), content_type=content_type)
        resp['Content-Length'] = str(size)
    resp['Accept-Ranges'] = 'bytes'
    return resp

这篇关于Python - Django:使用 HttpResponse 流式传输视频/mp4 文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆