2012-04-06 76 views
3

下面的客戶端旨在運行onlaDemo應用程序,該應用程序在localhost上的red5上運行。它包含阿凡達電影宣傳片。Red5 RTMPClient播放示例在播放完整流時開始丟棄消息

問題是客戶端讀取15秒的電影並掛起。爲什麼?

要編譯下面的程序,只需從這裏下載red,安裝並運行它。打開根頁面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()); 
    } 

} 

} 

UPDATE 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

我看到「tardiness`以實時的速度增加。當它超過8000的值時,開始下降。

UPDATE 4

更精確地,在服務器側的處理開始下降大約8秒之後通過的報文。這可能是8000的容忍時間的值。同時分組時間戳達到約15-16秒。客戶正在玩這個時間,然後才停止。

因此,它服務器摧毀客戶端2次,當它達到一定的限制,它不會等待,但開始丟棄數據包。

看起來,正確的行爲會等到客戶端達到時間戳並繼續......

UPDATE 5

也許客戶端流類不打算聽從服務器流,因此不包含適當的同步邏輯?

神奇的解決方案

在觀察日誌中的差異而有oflaDemo客戶端和我的應用程序播放的流,我發現,標準的客戶端報告的5000毫秒緩衝區大小,而我的客戶不。我不明白這是怎麼玩的,但是當我將RTMP ping添加到我的應用程序時,它開始工作。魔術線如下

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

由於它的作用只是轉移遲到由它的價值,我想這整個問題是由於RED5的錯誤,這使得它不能正確計算遲到。

+0

如果您或任何人有任何想法來改善計算,我很樂意聽到他們。 – 2013-03-02 20:24:08

回答