red5源码分析---11

本文深入分析了Red5服务器中客户端如何处理onStatus命令,并详细阐述了客户端向服务器发送数据的具体过程。从BaseRTMPClientHandler的onCommand函数入手,介绍了如何获取streamId并利用NetStreamPrivateData进行数据发送。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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发送。
下一章开始分析服务器如何处理这些数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值