RED5内部数据转发流程

0        PUBLISH方在publish时,创建BroadcastScope,ClientBroadcastStream作为Provier形式注册到BroadcastScope的pipe上。

IProviderService providerService =(IProviderService) context.getBean(IProviderService.BEAN_NAME);
            // TODO handle registration failure
            if(providerService.registerBroadcastStream(conn.getScope(), name, bs)) {
                bsScope= getBroadcastScope(conn.getScope(), name);
                bsScope.setAttribute(IBroadcastScope.STREAM_ATTRIBUTE,bs);
                if (conninstanceof BaseConnection){
                   ((BaseConnection)conn).registerBasicScope(bsScope);
                }
            }

1 PLAY方在play是,PlaylistSubscriberStream将作为一个Consumer注册到BroadcastScope的pipe上。这里的msgIn实际上就是BroadcastScope

</pre><p></p><p></p><pre code_snippet_id="650620" snippet_file_name="blog_20150422_3_8155983" name="code" class="java">                             msgIn = providerService.getLiveProviderInput(thisScope, itemName, false);

				//drop all frames up to the next keyframe
				videoFrameDropper.reset(IFrameDropper.SEND_KEYFRAMES_CHECK);

				if (msgIn instanceof IBroadcastScope) {
					IBroadcastStream stream = (IBroadcastStream) ((IBroadcastScope) msgIn).getAttribute(IBroadcastScope.STREAM_ATTRIBUTE);

					if (stream != null && stream.getCodecInfo() != null) {
						IVideoStreamCodec videoCodec = stream.getCodecInfo().getVideoCodec();
						if (videoCodec != null) {
							if (withReset) {
								sendReset();
								sendResetStatus(item);
								sendStartStatus(item);
							}
							sendNotifications = false;
						}
					}
				}
				//Subscribe to stream (ClientBroadcastStream.onPipeConnectionEvent)
				msgIn.subscribe(this, null);
				//execute the processes to get Live playback setup
				playLive();

2 RED5服务器收到PUBLISH方上来的数据后,在管道上转发。核心代码在BaseRTMPHandler:: messageReceived函数中

 

case TYPE_AUDIO_DATA:
case TYPE_VIDEO_DATA:
			message.setSourceType(Constants.SOURCE_TYPE_LIVE);
					if (stream != null) {
						((IEventDispatcher) stream).dispatchEvent(message);
					}
					break;

3 ClientBroadcastStream::dispatchEvent

l  如果是音视频数据,更新音视频数据的编解码信息。

l  如果是Invoke,那么是控制信息,不需要处理直接返回

l  如果是Notify,看是不是TYPE_STREAM_METADATA,更新metaData

public void onPipeConnectionEvent(PipeConnectionEvent event) {
		switch (event.getType()) {
			case PipeConnectionEvent.PROVIDER_CONNECT_PUSH:
				log.info("Provider connect");
				if (event.getProvider() == this && event.getSource() != connMsgOut
						&& (event.getParamMap() == null || !event.getParamMap().containsKey("record"))) {

					this.livePipe = (IPipe) event.getSource();   //livePipe is pipe of BroadcastScope
					log.debug("Provider: {}", this.livePipe.getClass().getName());
					for (IConsumer consumer : this.livePipe.getConsumers()) {
						subscriberStats.increment();
					}
				}
				break;

4 转发数据,向livePipe push数据。livePipe是在bs注册到BrocastScope时,消息通知时赋值的。

 

if (livePipe != null) {
				// create new RTMP message, initialize it and push through pipe
				RTMPMessage msg = new RTMPMessage();
				msg.setBody(rtmpEvent);
				msg.getBody().setTimestamp(eventTime);
				livePipe.pushMessage(msg);
}

  再观察一下RED5中InMemoryPushPushPipe,对pushMessage的实现。轮询所有的Consumer,调用Consumer的pushMessage方法。

public void pushMessage(IMessage message) throws IOException {
		for (IConsumer consumer : consumers) {
			try {
				IPushableConsumer pcon = (IPushableConsumer) consumer;
				pcon.pushMessage(this, message);
			} catch (Throwable t) {
				if (t instanceof IOException) {
					// Pass this along
					throw (IOException) t;
				}
				log.error("Exception when pushing message to consumer", t);
			}
		}
	}

 

而PlayEngin实现了IPushableConsumer接口,PlayEngin::pushMessage就实现了PLAY方收到数据后的处理,正常情况下它应该就是要转发到客户端连接去了。最终都会调用到msgOut.pushMessage(message);网RTMP连接中写入数据。

 

到这里RED5服务器收到PUBLISH方上来的数据后,怎么转发给PLAY方的内部机制就介绍清楚了。

 

但是不管对于PlaylistSubscriberStream还是ClientBroadcastStream,如果往自己对应的连接中发送RTMP消息呢?RED5内部还是使用了管道机制来实现这套逻辑。PlaylistSubscriberStream和ClientBroadcastStream中都有一个成员,

protected IMessageOutput connMsgOut;

connMsgOut实际是一个管道,在stream的start函数中初始化,并注册provider和consumer。

public void start() {
		log.info("Stream start");
		IConsumerService consumerManager = (IConsumerService) getScope().getContext().getBean(IConsumerService.KEY);
		checkVideoCodec = true;
		checkAudioCodec = true;
		firstPacketTime = -1;
		latestTimeStamp = -1;
		connMsgOut = consumerManager.getConsumerOutput(this);   //创建InMemoryPushPushPipe
		connMsgOut.subscribe(this, null);
		setCodecInfo(new StreamCodecInfo());
		closed = false;
		bytesReceived = 0;
		creationTime = System.currentTimeMillis();
	}


consumerManager.getConsumerOutput函数中创建了一个pipe,同时注册了一个ConnectionConsumer,这个ConnectionConsumer就是用来往RTMPConnection中写入RTMP消息的,详细的实现去看ConnectionConsumer的pushMessage的实现即可。

public class ConsumerService implements IConsumerService {

	/** {@inheritDoc} */
    public IMessageOutput getConsumerOutput(IClientStream stream) {
		IStreamCapableConnection streamConn = stream.getConnection();
		if (streamConn == null || !(streamConn instanceof RTMPConnection)) {
			return null;
		}
		RTMPConnection conn = (RTMPConnection) streamConn;
		// TODO Better manage channels.
		// now we use OutputStream as a channel wrapper.
		OutputStream o = conn.createOutputStream(stream.getStreamId());
		IPipe pipe = new InMemoryPushPushPipe();
		pipe.subscribe(new ConnectionConsumer(conn, o.getVideo().getId(), o.getAudio().getId(), o.getData().getId()), null);
		return pipe;
	}

}

也就是说一个Stream往它对应的RTMPConnction发送消息的途径是,调用connMsgOut的pushMessage方法。


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值