red5源码分析—客户端处理onStatus命令并向服务器发送数据
在《red5源码分析—10》中提到过,red5服务器在处理publish命令后,会在创建的ClientBroadcastStream中调用startPublishing函数,进而通过sendStatus将onStatus命令发送给客户端。
和前面几章的分析类似,客户端在收到onStatus命令后,最终会调用到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)) {
...
}
boolean onStatus = "onStatus".equals(methodName);
if (onStatus) {
Number streamId = source.getStreamId();
if (streamId != null) {
NetStreamPrivateData streamData = streamDataMap.get(streamId);
if (streamData == null) {
if (!streamDataMap.isEmpty()) {
streamData = streamDataMap.values().iterator().next();
}
}
if (streamData != null && streamData.handler != null) {
streamData.handler.onStreamEvent((Notify) command);
}
}
}
if (serviceProvider == null) {
call.setStatus(Call.STATUS_METHOD_NOT_FOUND);
call.setException(new MethodNotFoundException(methodName));
} else {
serviceInvoker.invoke(call, serviceProvider);
}
if (call instanceof IPendingServiceCall) {
IPendingServiceCall psc = (IPendingServiceCall) call;
Object result = psc.getResult();
if (result instanceof DeferredResult) {
DeferredResult dr = (DeferredResult) result;
dr.setTransactionId(command.getTransactionId());
dr.setServiceCall(psc);
dr.setChannel(channel);
conn.registerDeferredResult(dr);
} else if (!onStatus) {
...
}
}
}
onCommand函数首先获取streamId,根据该streamId从streamDataMap中获取NetStreamPrivateData,NetStreamPrivateData是在客户端的CreateStream的回调函数中设置的,而NetStreamPrivateData的handler是在BaseRTMPClientHandler的publish中设置的,根据《red5源码分析—1》的客户端代码,该onStreamEvent函数如下,
public void onStreamEvent(Notify notify) {
ObjectMap<?, ?> map = (ObjectMap<?, ?>) notify.getCall().getArguments()[0];
String code = (String) map.get("code");
if (StatusCodes.NS_PUBLISH_START.equals(code)) {
IMessage message = null;
while ((message = frameBuffer.poll()) != null) {
rtmpClient.publishStreamData(streamId, message);
}
} else if (StatusCodes.NS_UNPUBLISHED_SUCCESS.equals(code)) {
}
}
根据《red5源码分析—10》的分析,这里的code就是StatusCodes.NS_PUBLISH_START,onStreamEvent不停地轮询frameBuffer,只要有数据就通过publishStreamData将其发送出去。publishStreamData定义在BaseRTMPClientHandler中,代码如下,
public void publishStreamData(Number streamId, IMessage message) {
NetStreamPrivateData streamData = streamDataMap.get(streamId);
if (streamData != null) {
if (streamData.connConsumer != null) {
streamData.connConsumer.pushMessage(null, message);
} else {
}
} else {
}
}
首先根据streamId获取NetStreamPrivateData,然后调用connConsumer的pushMessage函数发送消息,connConsumer定义为ConnectionConsumer,因此看ConnectionConsumer的pushMessage函数,
public void pushMessage(IPipe pipe, IMessage message) {
if (message instanceof ResetMessage) {
} else if (message instanceof StatusMessage) {
StatusMessage statusMsg = (StatusMessage) message;
data.sendStatus(statusMsg.getBody());
} else if (message instanceof RTMPMessage) {
if (!chunkSizeSent) {
sendChunkSize();
}
RTMPMessage rtmpMsg = (RTMPMessage) message;
IRTMPEvent msg = rtmpMsg.getBody();
int eventTime = msg.getTimestamp();
if (eventTime < 0) {
return;
}
byte dataType = msg.getDataType();
final Header header = new Header();
header.setTimerBase(eventTime);
IoBuffer buf = null;
switch (dataType) {
case Constants.TYPE_AGGREGATE:
data.write(msg);
break;
case Constants.TYPE_AUDIO_DATA:
buf = ((AudioData) msg).getData();
if (buf != null) {
AudioData audioData = new AudioData(buf.asReadOnlyBuffer());
audioData.setHeader(header);
audioData.setTimestamp(header.getTimer());
audioData.setSourceType(((AudioData) msg).getSourceType());
audio.write(audioData);
} else {
}
break;
case Constants.TYPE_VIDEO_DATA:
buf = ((VideoData) msg).getData();
if (buf != null) {
VideoData videoData = new VideoData(buf.asReadOnlyBuffer());
videoData.setHeader(header);
videoData.setTimestamp(header.getTimer());
videoData.setSourceType(((VideoData) msg).getSourceType());
video.write(videoData);
} else {
}
break;
case Constants.TYPE_PING:
Ping ping = new Ping((Ping) msg);
ping.setHeader(header);
conn.ping(ping);
break;
case Constants.TYPE_STREAM_METADATA:
Notify notify = new Notify(((Notify) msg).getData().asReadOnlyBuffer());
notify.setHeader(header);
notify.setTimestamp(header.getTimer());
data.write(notify);
break;
case Constants.TYPE_FLEX_STREAM_SEND:
FlexStreamSend send = new FlexStreamSend(((Notify) msg).getData().asReadOnlyBuffer());
send.setHeader(header);
send.setTimestamp(header.getTimer());
data.write(send);
break;
case Constants.TYPE_BYTES_READ:
BytesRead bytesRead = new BytesRead(((BytesRead) msg).getBytesRead());
bytesRead.setHeader(header);
bytesRead.setTimestamp(header.getTimer());
conn.getChannel((byte) 2).write(bytesRead);
break;
default:
data.write(msg);
}
} else {
}
}
本章暂时不关心数据是怎么来的,ConnectionConsumer会根据各个数据类型调用不同的Channel发送给服务器,例如音频数据就通过音频Channel发送,视频数据通过视频Channel发送,普通的的数据通过data Channel发送。
下一章开始分析服务器如何处理这些数据。