21

私の Web サーバーから、FFMPEG を使用して、HTML<audio>または<video>タグで使用するメディア ファイルをトランスコードしたいと考えています。簡単ですよね?

HTTP クライアントが変換されたファイルを要求したときに、変換をリアルタイムで行う必要があります。理想的には、ファイルはトランスコードされているときに HTTP クライアントにストリームバックされます (データが送り返され始めるまでに時間がかかる可能性があるため、最後ではありません)。

Rangeこれは問題ありませんが、今日のブラウザーでは、HTML5 オーディオまたはビデオ タグが、ヘッダーを含む複数の HTTP 要求でメディア ファイルを要求します。詳細については、この質問を参照してください

上にリンクされているその質問では、Safari が最後の数バイトを含むファイルの奇妙なチャンクを要求していることがわかります。Rangeこれは、リクエストに適合するようにファイルの最後のバイトを配信するために、Web サーバーが変換が完了するのを待たなければならないという問題を引き起こします。

だから私の質問は、私の思考の流れは正しいですか?変換全体が完了するのを待たずに、<audio>またはタグにトランスコーディング コンテンツを配信するより良い方法はありますか? <video>前もって感謝します!

4

5 に答える 5

11

ライブラリをブラウザに提供したいので、最近同じ問題に遭遇しました。驚くべきことに、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にあります。

でろ

于 2014-05-08T05:26:41.077 に答える
3

カミロさん、返信ありがとうございます。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/*したがって、たとえば で応答するときにブラウザーがどのように反応するかをテストするだけです。何が起こるかお知らせします。

于 2010-09-10T05:32:02.463 に答える
0

私の知る限り、ffmpeg で stdout にエンコードできます。したがって、HTTP サーバーを次のように構成できます。

  • GET を受信したときにキャッシュへのエンコードを開始します。
  • 要求された範囲のバイトをクライアントにストリームします。
  • バッファーを満たし、それを後続の範囲に使用します。

私は無知ですが、最終ストリームの長さを知らなくても逃げることができると思います.

余談ですが、これは DoS になりやすいと思います。

于 2010-09-09T05:18:02.667 に答える
-1

これはVLCを介して実行できるはずです.VLCを設定して大きなaviファイルをホストし、それをOGGにトランスコードすることで動作させることができ、html5はストリームを参照しました:

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

vlc でトランスコードでき、Chrome ブラウザと Android フォンで問題なくレンダリングできましたがメディア コレクションをホストし、要求されたファイル-私が必要/好きな方法でそれを行った無料のファイルが既にそこにあるのを探しましたが、見つけることができませんでした.

于 2011-08-11T20:21:35.993 に答える