下面的客戶端旨在運行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的錯誤,這使得它不能正確計算遲到。
如果您或任何人有任何想法來改善計算,我很樂意聽到他們。 – 2013-03-02 20:24:08