標籤:
red5源碼分析—用戶端處理connect命令並發送createStream命令
在《red5源碼分析—5》中提到過,當用戶端發送connect命令後,伺服器經過處理會將其connect命令返回,不同的是伺服器返回的結果包含了一些串連後需要發送給用戶端的資訊,包括伺服器版本、模式等等。當返回的資訊經過伺服器的發送過濾器RTMPMinaProtocolEncoder時,會調用其中的RTMPProtocolEncoder的encodeCommand函數,下面來看其中的一段代碼,
protected void encodeCommand(IoBuffer out, ICommand command) { Output output = new org.red5.io.amf.Output(out); final IServiceCall call = command.getCall(); final boolean isPending = (call.getStatus() == Call.STATUS_PENDING); if (!isPending) { Serializer.serialize(output, call.isSuccess() ? "_result" : "_error"); } else { final String action = (call.getServiceName() == null) ? call.getServiceMethodName() : call.getServiceName() + ‘.‘ + call.getServiceMethodName(); Serializer.serialize(output, action); } ... }
這裡可以簡單看出,如果經過處理後的call的狀態不是STATUS_PENDING,並且處理成功,則返回”_result”,否則返回”_error”。是否處理成功由Call的isSuccess方法判斷,
public boolean isSuccess() { return (status == STATUS_SUCCESS_RESULT) || (status == STATUS_SUCCESS_NULL) || (status == STATUS_SUCCESS_VOID); }
因此,只要Call的狀態是STATUS_SUCCESS_RESULT、STATUS_SUCCESS_NULL或者STATUS_SUCCESS_VOID就可以判定為處理成功了。如果call的狀態是STATUS_PENDING,則根據情況返回方法名,或者服務名加上方法名。
根據上面的分析結果回顧伺服器對connect命令的處理,如果串連成功串連上伺服器的某個scope後,便會設定Call的狀態為STATUS_SUCCESS_RESULT,也因此返回的方法名(serviceMethodName)為_result。因此,當伺服器處理完connect命令後訊息返回用戶端時,
便調用BaseRTMPClientHandler的onCommand函數,如下
protected void onCommand(RTMPConnection conn, Channel channel, Header source, ICommand command) { final IServiceCall call = command.getCall(); final String methodName = call.getServiceMethodName(); if ("_result".equals(methodName) || "_error".equals(methodName)) { final IPendingServiceCall pendingCall = conn.getPendingCall(command.getTransactionId()); if (pendingCall != null) { if ("connect".equals(methodName)) { Integer encoding = (Integer) connectionParams.get("objectEncoding"); if (encoding != null && encoding.intValue() == 3) { conn.getState().setEncoding(IConnection.Encoding.AMF3); } } } handlePendingCallResult(conn, (Invoke) command); return; } ... }
根據前面的分析,當伺服器返回的訊息到達用戶端後,最後會調用handlePendingCallResult函數進行處理,handlePendingCallResult定義在BaseRTMPHandler中,
protected void handlePendingCallResult(RTMPConnection conn, Invoke invoke) { final IServiceCall call = invoke.getCall(); final IPendingServiceCall pendingCall = conn.retrievePendingCall(invoke.getTransactionId()); if (pendingCall != null) { Object[] args = call.getArguments(); if (args != null && args.length > 0) { pendingCall.setResult(args[0]); } Set<IPendingServiceCallback> callbacks = pendingCall.getCallbacks(); if (!callbacks.isEmpty()) { HashSet<IPendingServiceCallback> tmp = new HashSet<IPendingServiceCallback>(); tmp.addAll(callbacks); for (IPendingServiceCallback callback : tmp) { try { callback.resultReceived(pendingCall); } catch (Exception e) { } } } } }
首先通過retrievePendingCall根據事務ID擷取pendingCall,
public IPendingServiceCall retrievePendingCall(int invokeId) { return pendingCalls.remove(invokeId); }
該pendingCall是在TCP建立串連後的調用BaseRTMPClientHandler的connectionOpened中根據事務ID註冊的(《red5源碼分析—3》)。獲得PendingCall後,就設定結果,這裡注意一點,也就是伺服器最後設定的結果results經過RTMP的編解碼(amf)處理後到達這裡會變為參數params(具體的可以查看轉碼器RTMPProtocolEncoder和RTMPProtocolDecoder的對應關係)。最後通過resultReceived調用之前註冊的回呼函數,根據《red5源碼分析—1》,該註冊的回呼函數如下,
public void resultReceived(IPendingServiceCall call) { Object result = call.getResult(); if (result instanceof ObjectMap) { if ("connect".equals(call.getServiceMethodName())) { createStream(this); } } else { if ("createStream".equals(call.getServiceMethodName())) { if (result instanceof Integer) { Integer streamIdInt = (Integer) result; // int streamId = streamIdInt.intValue(); // publish(streamId, "testgio2", "live", this); invoke("getRoomsInfo", this); } else { disconnect(); } } else if ("getRoomsInfo".equals(call.getServiceMethodName())) { ArrayList<String> list = (ArrayList<String>) result; for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); } } } }
因此,用戶端再收到connect命令的結果後立即調用createStream來建立一個流,
public void createStream(IPendingServiceCallback callback) { IPendingServiceCallback wrapper = new CreateStreamCallBack(callback); invoke("createStream", null, wrapper); }
CreateStreamCallBack和處理回呼函數有關係,回頭再分析。下面就是發送一個”createStream”的命令給用戶端了。下一章看看伺服器是如何處理”createStream”命令的。
red5源碼分析---7