HTML5 / 以及使用FFMPEG进行实时转码

问题描述 投票:21回答:5

因此,从我的Web服务器上,我想使用FFMPEG对媒体文件进行转码,以与HTML <audio><video>标签一起使用。很容易吧?

当HTTP客户端请求转换后的文件时,转换必须实时进行。理想情况下,文件在进行代码转换时将被流回HTTP客户端(而不是在最后,因为这可能需要一段时间才能开始将数据发送回)。

这很好,只是在当今的浏览器中,HTML5音频或视频标签使用Range标头在多个HTTP请求中请求媒体文件。 See this question for details

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

所以我的问题是,我的思路正确吗?是否有更好的方式将转码内容传递到<audio><video>标签,而无需等待整个转换完成?预先感谢!

video audio html ffmpeg transcoding
5个回答
11
投票
接着,您可以使用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"})

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

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

最后,我们使用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>

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

dero    

3
投票
Camilo。我仔细查看了有关Range请求的HTTP规范,发现:

The header SHOULD indicate the total length of the full entity-body, unless this length is unknown or difficult to determine. The asterisk "*" character means that the instance-length is unknown at the time when the response was generated. 例如,实际上只是测试浏览器在回复Content-Range: bytes 0-1/*时的反应方式。我会让你知道会发生什么。


0
投票

收到GET时开始编码以缓存。

    向客户端流请求的字节范围。
  • 填充缓冲区并将其用于后续范围。
  • 我一无所知,但我认为您可以在不了解最终流的长度的情况下逃脱。
  • 附带说明,我认为这很容易发生DoS。

  • 0
    投票
    'user3612643'的答案是正确的,它解决了查找问题。然而,这引入了新的问题。当前时间不再正确。要解决此问题,我们必须复制原始的currentTime函数。

    现在video.js每次调用currentTime(不带参数)时,都会调用oldCurrentTime,这是原始的currentTime函数。其余与“ user3612643”的答案相同(谢谢!)。这适用于最新的video.js(7.7.6)

    video = videojs("video"); video.src({ src: 'http://localhost:4000/api/video/sdf', type: 'video/webm' }); // hack duration video.duration= function() {return video.theDuration; }; video.start= 0; // The original code for "currentTime" video.oldCurrentTime = function currentTime(seconds) { if (typeof seconds !== 'undefined') { if (seconds < 0) { seconds = 0; } this.techCall_('setCurrentTime', seconds); return; } this.cache_.currentTime = this.techGet_('currentTime') || 0; return this.cache_.currentTime; } // Our modified currentTime video.currentTime= function(time) { if( time == undefined ) { return video.oldCurrentTime() + video.start; } video.start= time; video.oldCurrentTime(0); video.src({ src: "http://localhost:4000/api/video/sdf?start=" + time, type: 'video/webm' }); video.play(); return this; }; // Get the dureation of the movie $.getJSON( "http://localhost:4000/api/video/sdf/getDuration", function( data ) { video.theDuration= data.duration; });


    -1
    投票
    <source src="http://localhost:8081/stream.ogg">

    它能够在vlc中进行转码,并且可以在chrome浏览器和Android手机上很好地呈现,但是我最终还是选择了different solution,而不是通过创建自己的网络应用来托管媒体集和为请求的文件创建流-我查看了却找不到一个已经按照我需要/喜欢的方式进行操作的免费文件。

    © www.soinside.com 2019 - 2024. All rights reserved.