2015-10-19 2 views
3

Я использую Python2.7, django==1.7 и uwsgi для потокового видео/mp4-файла до iPhone.Python - Django: потоковое видео/mp4-файл с использованием HttpResponse

Мой код, как показано ниже:

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 

Когда я использую некоторые небольшие видео (менее 1 Мб), то потоки в браузере, но в 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]

И когда размер видео больше 5 МБ, он не передает в обоих (означает браузер и 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.

fyi Я могу передавать файлы PDF и изображений. Эта проблема связана только с файлами mp4. А также я изменил content_type на 'video-mpeg', браузер загрузил это, в то время как я хочу предотвратить загрузку файлов.

Что вы думаете? Любое решение !!?

+0

Для того, чтобы поток вам нужен другой поток для записи данных в ответ. Потому что так, как вы это делаете, просто подождите, пока вы не прочитаете весь файл и не отправите его вместе. –

+0

@BogdanIulianBursuc Спасибо за ваш комментарий, но во втором решении (StreamHttpResponse) я читаю видеофайл в виде байта и возвращаю его в каждом фрагменте с помощью команды yield. это означает, что не нужно ждать получения всего файла. –

+0

Да. Вы правы –

ответ

1

После большого поиска я не нашел своего решения.

Итак, я попытался создать поток-сервер легко, используя nodejs из html5-video-streamer.js ссылки, как показано ниже:

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); 

Теперь у меня есть отдельный поток-сервер с nodejs, что потоками mp4 файлов ряда питона проекта, который предоставляет свои интерфейсы ,

Я знаю, это не мое решение, но это работает для меня;)

+0

На самом деле, я уже использую сервер nodejs для потоковой передачи нашего видео через websocket. Но ошибка все еще здесь :( – Charlie

+0

@Charlie Я использовал метод createReadStream для потоковой передачи файлов mp4, а не websocket. Я поставлю свой код в свой ответ. –

+0

Большое спасибо. Но я не уверен, что это может соответствовать моим потребностям Мы не используем HttpResponse: мы показываем видео в веб-браузере, используя videocanvas с помощью «ws: // serverip: port» – Charlie

4

У меня была такая же проблема, и сделал много копать, прежде чем найти приемлемое решение!

По-видимому, заголовок Accept Ranges необходим для управления видео в формате HTML5 (https://stackoverflow.com/a/24977085/4264463). Итак, нам нужно разобрать запрошенный диапазон от HTTP_RANGE и вернуть Content-Range с ответом. Генератору, который передан в StreamingHttpResponse, также необходимо вернуть контент на основе этого диапазона (по offset и length). Я нашел фрагмент кода следует, что отлично работает (от 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 
+0

Не могли бы вы показать пример о том, как это сделать? в браузере –

+0

@JosephDaudi HTML5 видео будет автоматически передавать данные из конечной точки, связанной с 'stream_video'. Таким образом, я использовал 'stream_video' как представление в файле' urls.py', а затем имел html, например: '' –

Смежные вопросы