繁体   English   中英

HTML5<audio> /<video> 并使用 FFMPEG 实时转码

[英]HTML5 <audio>/<video> and live transcoding with FFMPEG

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

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

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

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

所以我的问题是,我的思路对吗? 是否有更好的方法将转码内容传送到<audio><video>标签,而无需等待整个转换完成? 提前致谢!

我最近遇到了同样的问题,因为我想将我的库提供给浏览器。 令人惊讶的是,通过 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"})

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

@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找到一个工作示例。

德罗

谢卡米洛的回复。 我仔细查看了有关 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/*时如何反应的问题,例如。 我会让你知道会发生什么。

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

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

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

附带说明一下,我认为这很容易发生 DoS。

我知道这是一个旧线程,但如果有人发现它并需要帮助,我还是会发布它。

'user3612643' 答案是正确的,解决了搜索问题。 然而,这引入了一个新问题。 当前时间不再正确。 为了解决这个问题,我们必须复制原始的currentTime函数。

现在,每次 video.js 调用currentTime (不带参数)时,它都会调用oldCurrentTime ,这是原始的currentTime函数。 其余的与'user3612643's answer相同(谢谢!)。 这适用于最新的 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;
     });

这应该可以通过VLC 实现,我能够通过将 VLC 设置为托管大型 avi 文件并将其转码为 OGG 来使其工作,然后我的 html5 引用了流:

<source src="http://localhost:8081/stream.ogg">

它能够在 vlc 中转码,并在我的 chrome 浏览器和我的 android 手机上呈现得很好,但我最终采用了不同的解决方案,而不是通过创建我自己的 web 应用程序来托管我的媒体收藏并创建流请求的文件 - 我看了看,找不到一个已经在那里以我需要/喜欢的方式完成的免费文件。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM