HTML5 <audio>/<video>和使用FFMPEG进行实时转码

Too*_*ate 20 audio video html5 ffmpeg transcoding

因此,从我的Web服务器,我想使用FFMPEG转码媒体文件以用于HTML <audio><video>标记.够容易吧?

当HTTP客户端请求转换后的文件时,转换需要实时进行.理想情况下,文件将被转码回HTTP客户端,因为它正在进行转码(而不是之后的结尾,因为在任何数据开始被发回之前可能需要一段时间).

这很好,除了在今天的浏览器中,HTML5音频或视频标签使用Range标头在多个HTTP请求中请求媒体文件.有关详情,请参阅此问题.

在上面链接的那个问题中,你可以看到Safari请求文件的奇怪块,包括结尾的几个字节.这带来了一个问题,即Web服务器必须等待转换完成,以便传送文件的最后字节以符合Range请求.

所以我的问题是,我的思路是否合适?是否有更好的方法可以将转码内容提供给<audio>或者<video>标记,而不必等待整个转换完成?提前致谢!

use*_*643 10

我最近遇到了同样的问题,因为我想将我的库提供给浏览器.令人惊讶的是,通过ffmpeg发送流并在运行中交付的想法非常有效.主要问题是支持寻求......

接下来,您将使用Flask在Python中找到代码片段来解决问题:

我们需要一个函数来传输内容:

@app.route('/media/<path:path>.ogv')
def media_content_ogv(path):
    d= os.path.abspath( os.path.join( config.media_folder, path ) )
    if not os.path.isfile( d ): abort(404)
    start= request.args.get("start") or 0
    def generate():
        cmdline= list()
        cmdline.append( config.ffmpeg )
        cmdline.append( "-i" )
        cmdline.append( d );
        cmdline.append( "-ss" )
        cmdline.append( str(start) );
        cmdline.extend( config.ffmpeg_args )
        print cmdline
        FNULL = open(os.devnull, 'w')
        proc= subprocess.Popen( cmdline, stdout=subprocess.PIPE, stderr=FNULL )
        try:
            f= proc.stdout
            byte = f.read(512)
            while byte:
                yield byte
                byte = f.read(512)
        finally:
            proc.kill()

    return Response(response=generate(),status=200,mimetype='video/ogg',headers={'Access-Control-Allow-Origin': '*', "Content-Type":"video/ogg","Content-Disposition":"inline","Content-Transfer-Enconding":"binary"})
Run Code Online (Sandbox Code Playgroud)

然后我们需要一个函数来返回持续时间:

@app.route('/media/<path:path>.js')
def media_content_js(path):
    d= os.path.abspath( os.path.join( config.media_folder, path ) )
    if not os.path.isfile( d ): abort(404)
    cmdline= list()
    cmdline.append( config.ffmpeg )
    cmdline.append( "-i" )
    cmdline.append( d );
    duration= -1
    FNULL = open(os.devnull, 'w')
    proc= subprocess.Popen( cmdline, stderr=subprocess.PIPE, stdout=FNULL )
    try:
        for line in iter(proc.stderr.readline,''):
            line= line.rstrip()
            #Duration: 00:00:45.13, start: 0.000000, bitrate: 302 kb/s
            m = re.search('Duration: (..):(..):(..)\...', line)
            if m is not None: duration= int(m.group(1)) * 3600 + int(m.group(2)) * 60 + int(m.group(3)) + 1
    finally:
        proc.kill()

    return jsonify(duration=duration)
Run Code Online (Sandbox Code Playgroud)

最后,我们使用videojs将其破解为HTML5:

<!DOCTYPE html>
<html>
<head>
    <link href="//vjs.zencdn.net/4.5/video-js.css" rel="stylesheet">
    <script src="//vjs.zencdn.net/4.5/video.js"></script>
    <script src="http://code.jquery.com/jquery-1.9.1.min.js"></script>
</head>
<body>
    <video id="video" class="video-js vjs-default-skin" controls preload="auto" width="640" height="264">
    </video>
    <script>
        var video= videojs('video');
        video.src("media/testavi.avi.ogv");

        // hack duration
        video.duration= function() { return video.theDuration; };
        video.start= 0;
        video.oldCurrentTime= video.currentTime;
        video.currentTime= function(time) 
        { 
            if( time == undefined )
            {
                return video.oldCurrentTime() + video.start;
            }
            console.log(time)
            video.start= time;
            video.oldCurrentTime(0);
            video.src("media/testavi.avi.ogv?start=" + time);
            video.play();
            return this;
        };

        $.getJSON( "media/testavi.avi.js", function( data ) 
        {
            video.theDuration= data.duration;
        });
    </script>
</body>
Run Code Online (Sandbox Code Playgroud)

可以在https://github.com/derolf/transcoder找到一个工作示例.

德罗


Cam*_*tin 0

AFAIK 你可以在 ffmpeg 中编码到标准输出。因此,您可以将 HTTP 服务器配置为:

  • 当收到 GET 时开始编码到缓存。
  • 将请求的字节范围流传输到客户端。
  • 填充缓冲区并将其用于后续范围。

我一无所知,但我认为你可以在不知道最终流长度的情况下逃脱。

顺便说一句,我认为这很容易受到 DoS 攻击。