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

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

问题描述

我使用Python2.7, django == 1.7 uwsgi 为流式视频/ mp4文件



我的代码如下:

  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

当我使用一些小的视频(小于1MB),它在浏览器中流式传输,但在iPhone palyer中,我有这个错误:


[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]


当视频大小更多为5MB时,它不会同时传输(意味着浏览器和iPhone播放器)同样的错误。 >

我试图通过使用StreamHttpRespose返回的块块来做到这一点:

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

返回StreamingHttpResponse(read(),content_type ='video / mp4')

但是也有同样的错误: Broken pipe



fyi我可以流式传输pdf和图像文件。这个问题只适用于mp4文件。而且我更改content_type为'video-mpeg',浏览器下载,而我想防止文件下载。



你的想法是什么?任何解决方案!!?

解决方案

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

显然,HTML5视频控件需要使用接受范围标头( https://stackoverflow.com/a/24977085/4264463 )。因此,我们需要从 HTTP_RANGE 中解析所请求的范围,并返回 Content-Range 与响应。传递给 StreamingHttpResponse 的生成器还需要根据此范围返回内容(由 offset 长度)。我发现以下代码片段的效果非常好(来自 https://codegists.com/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'bytes\s * = \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。剩余=长度
self.blksize = blksize

def close(self):
如果hasattr(self.filelike,'close'):
self.filelike。 close()

def __iter __(self):
retur n self

def __next __(self):
如果self.remaining是None:
#如果剩余的是None,我们正在阅读整个文件。
data = self.filelike.read(self.blksize)
如果数据:
返回数据
raise StopIteration()
else:
如果自己。剩余< = 0:
raise StopIteration()
data = self.filelike.read(min(self.remaining,self.blksize))
如果不是数据:
raise StopIteration()
self.remaining - = len(data)
返回数据


def stream_video(请求,路径):
range_header =请求。 META.get('HTTP_RANGE','').strip()
range_match = range_rematch(range_header)
size = 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)如果first_byte else 0
last_byt e = 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


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 https://codegists.com/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'bytes\s*=\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天全站免登陆