从 Tornado 提供视频文件

Serving video files from Tornado

我的服务器上有几百个视频文件。 由于我不是一家大公司,我发现很难在我可用的存储中转码和维护副本。我的设置涉及 nginx 和 Tornado。

为了解决这个问题,我决定尝试动态转码并提供这些 临时文件 on-demand.

在我尝试创建 subprocess 来尝试为我转换文件之前,我决定尝试通过 Tornado 提供静态视频文件。

class MediaHandler(tornado.web.RequestHandler):
    def serve(self, media, max_bufsize=1*1024*1024):

        bytes_read = 0
        file_length = None
        remaining = None

        self.set_header('Content-Type', 'video/mp4')
        with open(media.name, 'rb') as f:
            while 1:
                # While transcoding, file size will be changing
                # So compute every time
                file_length = os.fstat(f.fileno()).st_size  # Get current length
                remaining = file_length - bytes_read    # Compute remaining bytes

                # Hopefully we're producing faster than we're consuming...
                if remaining == 0:
                    break

                buffer_size = max_bufsize if max_bufsize < remaining else remaining
                bytes = f.read(buffer_size)
                assert len(bytes) == buffer_size
                bytes_read += buffer_size

                self.write(bytes)
                self.flush()


    def get(self):
        log('Media Request: %s' % (self.request.uri))
        #XXX: Currently this is hard-coded in some sense..fix it
        media_path = os.path.abspath(os.path.join(dir_path, self.request.uri[1:]))  #Strip leading '/'
        log('media_path: %s' % (media_path))
        try:
            media = avprobe(media_path)
        except SubprocessException, e:
            raise e

        if media.video_codec not in valid_video_codecs or \
                media.audio_codec not in valid_audio_codecs:
            # Transcode and serve
            pass
        else:
            self.serve(media)
        self.finish()

我注意到当我尝试从 Tornado 提供文件时,Firefox 根本不会加载视频。 经查,我的回复headers如下:

HTTP/1.1 200 OK
Date: Thu, 02 Jul 2015 00:04:50 GMT
Content-Type: video/mp4
Transfer-Encoding: chunked
Connection: keep-alive
Server: TornadoServer/4.2

Nginx 似乎生成以下 headers 当我要求它提供相同的文件时:

HTTP/1.1 206 Partial Content
Server: nginx/1.8.0
Date: Wed, 01 Jul 2015 23:57:06 GMT
Content-Type: video/mp4
Content-Length: 65796881
Last-Modified: Thu, 09 Apr 2015 21:47:56 GMT
Connection: keep-alive
Etag: "5526f38c-3ebfb11"
Content-Range: bytes 0-65796880/65796881

Firefox 似乎不喜欢我发送的数据。 它在控制台上输出以下内容:

Media resource http://xxxx:8000/video_test/media/test.mp4 could not be decoded.
All candidate resources failed to load. Media load paused.

有人可以指出我做错了什么以及如何从 Tornado 提供静态媒体(不使用 StaticFileHandler..因为我认为我不能将其用于我的最终目的)。

另外,有没有更好的方法实现on-the-fly转码+推流?

浏览器不喜欢下载嵌入视频的整个文件;他们希望能够发出 HTTP Range 请求来下载部​​分块(以支持搜索等)。一些浏览器对此非常严格,如果视频没有正确的 Content-Range header,则根本拒绝加载视频。不幸的是,这使得在事先不知道文件大小的情况下很难提供视频。

如果文件足够小,您可以在提供之前将整个文件转码为临时文件,那么您可以这样做,然后以与 StaticFileHandler 相同的方式提供 Content-Range做。如果没有,那么我认为您可能需要使用更复杂的协议,例如 HLS or MPEG-DASH 才能使其适用于所有浏览器。