web-dev-qa-db-ja.com

Python-Django:HttpResponseを使用したスト​​リーミングビデオ/ mp4ファイル

私はPython2.7、Django==1.7uwsgiを使用して、video/mp4ファイルをiPhoneプレーヤーにストリーミングしています。

私のコードは以下の通りです:

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プレーヤーでは次のエラーが発生します。

[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

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

しかし、同じエラーがあります:Broken pipe

私はpdfと画像ファイルをストリーミングできます。この問題は、mp4ファイルでのみ発生します。また、content_typeを 'video-mpeg'に変更しました。ブラウザーはそれをダウンロードしましたが、ファイルのダウンロードを防ぎたいのです。

あなたのアイデアは何ですか?何か解決策!!?

11
Aida.Mirabadi

私は同じ問題を抱えており、実行可能な解決策を見つける前に多くの掘り下げを行いました!

どうやらAccept Rangesヘッダーは、HTML5ビデオコントロールが機能するために必要です( https://stackoverflow.com/a/24977085/426446 )。したがって、HTTP_RANGEからリクエストされた範囲を解析し、レスポンスでContent-Rangeを返す必要があります。 StreamingHttpResponseに渡されるジェネレーターも、この範囲に基づいてコンテンツを返す必要があります(offsetおよびlengthによる)。うまくいく次のスニペットを見つけました( 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'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
15
Kevin Lee

何度も検索したところ、解決策が見つかりませんでした。

したがって、以下のように html5-video-streamer.js 参照からnodejsを使用して簡単にストリームサーバーを作成しようとしました:

var http       = require('http'),
    fs         = require('fs'),
    url        = require('url'),
    basePath   = '/var/www/my_project/media/',
    baseUrl    = 'Your Domain or IP',
    basePort   = 8081;

http.createServer(function (req, res) {

    // Get params from request.
    var params    = url.parse(req.url, true).query, 
        filePath  = basePath + params.type + '/' + params.name,
        stat      = fs.statSync(filePath),
        total     = stat.size;

      if (req.headers['range']) {
        var range         = req.headers.range,
            parts         = range.replace(/bytes=/, "").split("-"),
            partialstart  = parts[0],
            partialend    = parts[1],
            start         = parseInt(partialstart, 10),
            end           = partialend ? parseInt(partialend, 10) : total-1,
            chunksize     = (end-start)+1;

        var file = fs.createReadStream(filePath, {start: start, end: end});
        res.writeHead(206, { 'Content-Range'  : 'bytes ' + start + '-' + end + '/' + total,
                             'Accept-Ranges'  : 'bytes',
                             'Content-Length' : chunksize,
                             'Content-Type'   : 'video/mp4' });
        file.pipe(res);

        // Close file at end of stream.
        file.on('end', function(){
          file.close();
        });
      } 
      else {
        res.writeHead(206, { 'Content-Length'   : total,
                             'Content-Type'     : 'video/mp4' });

        var file = fs.createReadStream(filePath);
        file.pipe(res);

        // Close file at end of stream.
        file.on('end', function(){
          file.close();
        });
      }
 }).listen(basePort, baseUrl);

これで、APIを提供するpythonプロジェクトの横にmp4ファイルをストリーミングするnodejsを備えた別のストリームサーバーがあります。

私はそれが私の解決策ではないことを知っていますが、それは私にとってはうまくいきます;)

2
Aida.Mirabadi