3

以下のクライアントは、localhost の red5 で実行されている oflaDemo アプリケーションで実行することを目的としています。アバター映画のプロモーションが含まれています。

問題は、クライアントが 15 秒のムービーを読み取ってハングすることです。なんで?

以下のプログラムをコンパイルするには、 http: //wiki.red5.org/wiki/1_0_RC1 から red5 をダウンロードし、インストールして実行します。ルート ページhttp://localhost:5080を開き、デモのインストール ページに移動します。サンプルをインストールしoflaDemoます。次に、oflaDemo ページに移動して、動作していることを確認します。

次に、red5 のすべての jar をライブラリとして使用して、新しい Java プロジェクトを作成します。red5 を実行して実行します。

クライアントはポート 1935 経由でサーバーと通信します。

アプリの構造は次のとおりです。

1)connect()メソッドがアプリケーションに接続する

2)connectCallback前の操作の結果、新しいストリームが作成されます。ライブラリ関数は、カスタム ストリーム クラスを挿入するために使用されません

3)createStreamCallbackストリーム作成の結果に注入しています

4) カスタムストリームはMyClientStream; 発送されたものを印刷するだけです

私のマシンでは、タイムスタンプ 15203 まで動作し、ハングします。

public class SSCCE_RTMPPlayer extends RTMPClient{

private String server = "localhost";
private int port = 1935;
private String application = "oflaDemo";
private String filename = "avatar.flv";

private static boolean finished = false;

public static void main(String[] args) throws InterruptedException {

    final SSCCE_RTMPPlayer player = new SSCCE_RTMPPlayer ();
    player.connect();

    synchronized( SSCCE_RTMPPlayer.class ) {
        if( !finished ) SSCCE_RTMPPlayer.class.wait();
    }

    System.out.println("Ended");
}

public void connect() {
    connect(server, port, application, connectCallback);

    setExceptionHandler(new ClientExceptionHandler() {

        @Override
        public void handleException(Throwable throwable) {
            throwable.printStackTrace();
        }
    });
}

private IPendingServiceCallback connectCallback = new IPendingServiceCallback() {
    @Override
    public void resultReceived(IPendingServiceCall call) {
        System.out.println("connectCallback");
        invoke("createStream", null, createStreamCallback);
    }
};

private IPendingServiceCallback createStreamCallback = new IPendingServiceCallback() {
    @Override
    public void resultReceived(IPendingServiceCall call) {
        Integer streamIdInteger = (Integer) call.getResult();
        MyClientStream myClientStream = new MyClientStream();
        myClientStream.setStreamId(streamIdInteger.intValue());
        myClientStream.setConnection(conn);
        conn.addClientStream(myClientStream);


        play(streamIdInteger.intValue(), filename, 0, -2);
    }
};

protected void onInvoke(RTMPConnection conn, Channel channel, Header header, Notify notify, RTMP rtmp) {
    super.onInvoke(conn, channel, header, notify, rtmp);
    System.out.println("onInvoke, header = " + header.toString());
    System.out.println("onInvoke, notify = " + notify.toString());
    System.out.println("onInvoke, rtmp = " + rtmp.toString());

};

public static class MyClientStream extends AbstractClientStream implements IEventDispatcher {

    @Override
    public void start() {
        // TODO Auto-generated method stub

    }

    @Override
    public void stop() {
        // TODO Auto-generated method stub

    }

    @Override
    public void close() {
        // TODO Auto-generated method stub

    }

    @Override
    public void dispatchEvent(IEvent event) {
        System.out.println("AudioListenerClientStream.dispachEvent()" + event.toString());
    }

}

}

更新 1

従来のバージョンsetStreamEventDispatcher()も同じように動作します。

public class SSCCE_RTMPPlayer2 extends RTMPClient {

private String server = "localhost";
private int port = 1935;
private String application = "oflaDemo";
private String filename = "avatar.flv";

private static boolean finished = false;

public static void main(String[] args) throws InterruptedException {

    final SSCCE_RTMPPlayer2 player = new SSCCE_RTMPPlayer2();
    player.connect();

    synchronized( SSCCE_RTMPPlayer.class ) {
        if( !finished ) SSCCE_RTMPPlayer.class.wait();
    }

    System.out.println("Ended");
}

public void connect() {

    setExceptionHandler(new ClientExceptionHandler() {

        @Override
        public void handleException(Throwable throwable) {
            throwable.printStackTrace();
        }
    });

    setStreamEventDispatcher(streamEventDispatcher);

    connect(server, port, application, connectCallback);


}

private IEventDispatcher streamEventDispatcher = new IEventDispatcher() {

    @Override
    public void dispatchEvent(IEvent event) {
        System.out.println("AudioListenerClientStream.dispachEvent()" + event.toString());
    }
};

private IPendingServiceCallback connectCallback = new IPendingServiceCallback() {
    @Override
    public void resultReceived(IPendingServiceCall call) {
        System.out.println("connectCallback");
        createStream(createStreamCallback);
    }
};

private IPendingServiceCallback createStreamCallback = new IPendingServiceCallback() {
    @Override
    public void resultReceived(IPendingServiceCall call) {
        Integer streamIdInteger = (Integer) call.getResult();
        play(streamIdInteger.intValue(), filename, 0, -2);
    }
};

protected void onInvoke(RTMPConnection conn, Channel channel, Header header, Notify notify, RTMP rtmp) {
    super.onInvoke(conn, channel, header, notify, rtmp);

    System.out.println("onInvoke, header = " + header.toString());
    System.out.println("onInvoke, notify = " + notify.toString());
    System.out.println("onInvoke, rtmp = " + rtmp.toString());

    /*
    ObjectMap<String, String> map = (ObjectMap) notify.getCall().getArguments()[0];
    String code = map.get("code");
    if (StatusCodes.NS_PLAY_STOP.equals(code)) {

        synchronized( SSCCE_RTMPPlayer.class ) {
            finished = true;
            SSCCE_RTMPPlayer.class.notifyAll();
        }

        disconnect();
        System.out.println("Disconnected");
    }
    */
};

}

更新 2

ハングが発生した後、パケットがドロップし始めることがわかりました。ドロップ方法はRTMPProtocolEncoder#dropMessage()

更新 3

「遅刻」がリアルタイムで増えているのがわかります。値が 8000 を超えると、ドロップが開始されます。

更新 4

より正確には、サーバー側では、プロセスは約 8 秒が経過した後にパケットをドロップし始めています。これはおそらく許容時間である 8000 の値です。同時に、パケットのタイムスタンプが約 15 ~ 16 秒に達しています。クライアントはこの時間まで再生し、その後停止します。

したがって、サーバーがクライアントを 2 回追い越し、制限に達すると、待機せずにパケットをドロップし始めるという図です。

クライアントがタイムスタンプに到達して続行するまで、正しい動作が待機するように見えます....

更新 5

おそらく、クライアント ストリーム クラスはサーバーからのストリームをリッスンすることを意図していないため、適切な同期ロジックが含まれていませんか?

魔法の解決策

oflaDemo クライアントと私のアプリケーションでストリームを再生しているときにログの違いを観察しているときに、標準のクライアントは 5000 ミリ秒のバッファー サイズを報告し、私のクライアントはそうではないことがわかりました。これがどのように機能するかはわかりませんが、アプリケーションに RTMP ping を追加すると、機能し始めます。魔法のラインが続きます

conn.ping(new Ping(Ping.CLIENT_BUFFER, streamId, 5000));

役割は遅刻をその値でシフトすることなので、全体的な問題は red5 のバグによるもので、遅刻を正しく計算できないと思われます。

4

1 に答える 1

0

一部のファイルのビデオ ストリーミングで問題が発生し、再生が停止しました。しかし、それ以来サーバーで修正しました。リビジョン 4329 以降を使用してください。参照用の問題はこちら: http://code.google.com/p/red5/issues/detail?id=200

于 2012-05-01T17:18:43.630 に答える