red5源码分析---10

来源:互联网 发布:baby同款钻戒淘宝 编辑:程序博客网 时间:2024/05/16 05:23

red5源码分析—服务器处理publish命令

和前几章的分析一样,服务器接收到客户端发来的publish命令后,最终会执行RTMPHandler的onCommand函数,再参考《red5源码分析—8》的分析,最终会调用StreamService的publish方法,代码如下

    public void publish(String name, String mode) {        Map<String, String> params = null;        if (name != null && name.contains("?")) {            params = new HashMap<String, String>();            String tmp = name;            if (name.charAt(0) != '?') {                tmp = name.split("\\?")[1];            } else if (name.charAt(0) == '?') {                tmp = name.substring(1);            }            String[] kvs = tmp.split("&");            for (String kv : kvs) {                String[] split = kv.split("=");                params.put(split[0], split[1]);            }            name = name.substring(0, name.indexOf("?"));        }        IConnection conn = Red5.getConnectionLocal();        if (conn instanceof IStreamCapableConnection) {            IScope scope = conn.getScope();            IStreamCapableConnection streamConn = (IStreamCapableConnection) conn;            Number streamId = conn.getStreamId();            if (StringUtils.isEmpty(name)) {                return;            }            IStreamSecurityService security = (IStreamSecurityService) ScopeUtils.getScopeService(scope, IStreamSecurityService.class);            if (security != null) {                Set<IStreamPublishSecurity> handlers = security.getStreamPublishSecurity();                for (IStreamPublishSecurity handler : handlers) {                    if (!handler.isPublishAllowed(scope, name, mode)) {                        return;                    }                }            }            IBroadcastScope bsScope = getBroadcastScope(scope, name);            if (bsScope != null && !bsScope.getProviders().isEmpty()) {                     return;            }            IClientStream stream = streamConn.getStreamById(streamId);            if (stream != null && !(stream instanceof IClientBroadcastStream)) {                return;            }            boolean created = false;            if (stream == null) {                stream = streamConn.newBroadcastStream(streamId);                created = true;            }            IClientBroadcastStream bs = (IClientBroadcastStream) stream;            try {                bs.setPublishedName(name);                if (params != null) {                    bs.setParameters(params);                }                IContext context = conn.getScope().getContext();                IProviderService providerService = (IProviderService) context.getBean(IProviderService.BEAN_NAME);                if (providerService.registerBroadcastStream(conn.getScope(), name, bs)) {                    bsScope = getBroadcastScope(conn.getScope(), name);                    bsScope.setClientBroadcastStream(bs);                    if (conn instanceof BaseConnection) {                        ((BaseConnection) conn).registerBasicScope(bsScope);                    }                }                if (IClientStream.MODE_RECORD.equals(mode)) {                    bs.start();                    bs.saveAs(name, false);                } else if (IClientStream.MODE_APPEND.equals(mode)) {                    bs.start();                    bs.saveAs(name, true);                } else {                    bs.start();                }                bs.startPublishing();            } catch (IOException e) {                sendNSFailed(streamConn, StatusCodes.NS_RECORD_NOACCESS, "The file could not be created/written to.", name, streamId);                bs.close();                if (created) {                    streamConn.deleteStreamById(streamId);                }            } catch (Exception e) {            }        }    }

publish函数首先对传入的参数name进行处理,从name中提取出请求参数并保存在params中,将name去除参数部分,这个name就可以唯一表示一个publish流;publish函数接下来执行ScopeUtils的getScopeService方法构造StreamSecurityService用来对安全方面进行验证,这里不管它;然后通过getBroadcastScope检查对应的name下是否已经创建过BroadcastScope,一个BoradcastScope表示某个Scope下的某种类型的Scope,代码如下

    public IBroadcastScope getBroadcastScope(IScope scope, String name) {        return scope.getBroadcastScope(name);    }    public IBroadcastScope getBroadcastScope(String name) {        return (IBroadcastScope) children.getBasicScope(ScopeType.BROADCAST, name);    }

如果对应的name下已经创建过BroadcastScope并且已被占用,getBroadcastScope就直接返回。
回到publish中,再往下就根据streamId检查RTMPMinaConnection连接里对应的IClientStream是否也被占用,如果被占用也直接返回。这里假设BroadcastScope和IClientStream都没有创建或被占用,因此接下来通过newBroadcastStream创建stream,newBroadcastStream定义在RTMPConnection中,

    public IClientBroadcastStream newBroadcastStream(Number streamId) {        if (isValidStreamId(streamId)) {            ClientBroadcastStream cbs = (ClientBroadcastStream) scope.getContext().getBean("clientBroadcastStream");            customizeStream(streamId, cbs);            if (!registerStream(cbs)) {                cbs = null;            }            return cbs;        }        return null;    }

newBroadcastStream函数首先通过isValidStreamId检查streamId的合法性,以及是否在该Id下已经创建了流。如果合法,就通过Spring创建ClientBroadcastStream,然后通过customizeStream设置ClientBroadcastStream对应的连接、Scope、name以及streamId,

    private void customizeStream(Number streamId, AbstractClientStream stream) {        Integer buffer = streamBuffers.get(streamId.doubleValue());        if (buffer != null) {            stream.setClientBufferDuration(buffer);        }        stream.setName(createStreamName());        stream.setConnection(this);        stream.setScope(this.getScope());        stream.setStreamId(streamId);    }

newBroadcastStream接下来执行registerStream向RTMPMinaConnection注册刚刚创建的流ClientBroadcastStream,

    private boolean registerStream(IClientStream stream) {        if (streams.putIfAbsent(stream.getStreamId().doubleValue(), stream) == null) {            usedStreams.incrementAndGet();            return true;        }        return false;    }

回到publish中,再往下对刚刚创建的ClientBroadcastStream进行基本的设置。然后就通过Spring获得ProviderService,并调用其registerBroadcastStream注册刚刚创建的ClientBroadcastStream,代码如下

    public boolean registerBroadcastStream(IScope scope, String name, IBroadcastStream bs) {        IBroadcastScope broadcastScope = scope.getBroadcastScope(name);        if (broadcastScope == null) {            broadcastScope = new BroadcastScope(scope, name);            if (scope.addChildScope(broadcastScope)) {            } else {            }        }        if (broadcastScope != null && bs instanceof IClientBroadcastStream) {            broadcastScope.setClientBroadcastStream((IClientBroadcastStream) bs);        }        return broadcastScope.subscribe(bs.getProvider(), null);    }

registerBroadcastStream函数首先会创建一个BroadcastScope,其构造函数如下,

    public BroadcastScope(IScope parent, String name) {        super(parent, ScopeType.BROADCAST, name, false);        pipe = new InMemoryPushPushPipe(this);        keepOnDisconnect = true;    }

注意这里会创建一个管道InMemoryPushPushPipe,后面的章节会分析到这个管道。
registerBroadcastStream函数接下来会调用addChildScope将刚刚创建的BroadcastScope注册到其父Scope上,该函数之前的章节分析过了;接着会将对应的stream流设置进BroadcastScope里;最后通过subscribe注册自身成为Provider,BroadcastScope里的Provider表示一个流的提供者,getProvider返回的是ClientBroadcastStream自身,subscribe定义如下,

    public boolean subscribe(IProvider provider, Map<String, Object> paramMap) {        return !removed && pipe.subscribe(provider, paramMap);    }

pipe就是刚刚在BroadcastScope构造函数中创建的InMemoryPushPushPipe,其subscribe函数如下,

    public boolean subscribe(IProvider provider, Map<String, Object> paramMap) {        boolean success = super.subscribe(provider, paramMap);        if (success) {            fireProviderConnectionEvent(provider, PipeConnectionEvent.PROVIDER_CONNECT_PUSH, paramMap);        }        return success;    }

InMemoryPushPushPipe的父类为AbstractPipe,其subscribe如下,

    public boolean subscribe(IProvider provider, Map<String, Object> paramMap) {        boolean success = providers.addIfAbsent(provider);        if (success && provider instanceof IPipeConnectionListener) {            listeners.addIfAbsent((IPipeConnectionListener) provider);        }        return success;    }

AbstractPipe的subscribe函数就是向InMemoryPushPushPipe的providers注册Provider,即流的提供者,并且添加监听器,因为ClientBroadcastStream自身实现了IPipeConnectionListener接口。
回到InMemoryPushPushPipe的subscribe函数中,接下来通过fireProviderConnectionEvent触发监听回调函数,fireProviderConnectionEvent的代码如下,

    protected void fireProviderConnectionEvent(IProvider provider, int type, Map<String, Object> paramMap) {        PipeConnectionEvent event = new PipeConnectionEvent(this);        event.setProvider(provider);        event.setType(type);        event.setParamMap(paramMap);        firePipeConnectionEvent(event);    }

这里构造了PipeConnectionEvent,即代表管道连接的事件,接着通过firePipeConnectionEvent触发监听器,

    protected void firePipeConnectionEvent(PipeConnectionEvent event) {        for (IPipeConnectionListener element : listeners) {            try {                element.onPipeConnectionEvent(event);            } catch (Throwable t) {            }        }        if (taskExecutor == null) {            taskExecutor = Executors.newCachedThreadPool(new CustomizableThreadFactory("Pipe-"));        }        for (Runnable task : event.getTaskList()) {            try {                taskExecutor.execute(task);            } catch (Throwable t) {            }        }        event.getTaskList().clear();    }

首先通过onPipeConnectionEvent触发监听器事件,接着启动线程处理各个task。因为前面的subscribe函数中注册了监听器,即ClientBroadcastStream自身,这里的onPipeConnectionEvent定义在ClientBroadcastStream中,

    public void onPipeConnectionEvent(PipeConnectionEvent event) {        switch (event.getType()) {            case PipeConnectionEvent.PROVIDER_CONNECT_PUSH:                if (event.getProvider() == this && event.getSource() != connMsgOut && (event.getParamMap() == null || !event.getParamMap().containsKey("record"))) {                    this.livePipe = (IPipe) event.getSource();                    for (IConsumer consumer : this.livePipe.getConsumers()) {                        subscriberStats.increment();                    }                }                break;            case PipeConnectionEvent.PROVIDER_DISCONNECT:                ...                break;            case PipeConnectionEvent.CONSUMER_CONNECT_PUSH:                ...                break;            case PipeConnectionEvent.CONSUMER_DISCONNECT:                ...                break;            default:        }    }

因为前面设置的PipeConnectionEvent的事件类型为PROVIDER_CONNECT_PUSH,因此只看这一部分代码,onPipeConnectionEvent函数其实只是设置了成员变量livePipe,其实也是InMemoryPushPushPipe自身。
回到publish中,假设registerBroadcastStream注册成功,接下来通过getBroadcastScope获得前面创建的BroadcastScope并设置对应的ClientBroadcastStream;接着调用registerBasicScope设置刚刚创建的BroadcastScope,registerBasicScope定义在BaseConnection中,

    public void registerBasicScope(IBroadcastScope basicScope) {        basicScopes.add(basicScope);        basicScope.addEventListener(this);    }

再回到publish中,假设前面客户端的请求中参数mode是MODE_LIVE,因此调用ClientBroadcastStream的start函数和startPublishing函数,下面分别分析。

start函数

start定义在ClientBroadcastStream中,

    public void start() {        checkVideoCodec = true;        checkAudioCodec = true;        firstPacketTime = -1;        latestTimeStamp = -1;        bytesReceived = 0;        IConsumerService consumerManager = (IConsumerService) getScope().getContext().getBean(IConsumerService.KEY);        connMsgOut = consumerManager.getConsumerOutput(this);        if (connMsgOut != null && connMsgOut.subscribe(this, null)) {            setCodecInfo(new StreamCodecInfo());            creationTime = System.currentTimeMillis();            closed = false;        } else {        }    }

在进行一些成员变量的初始化后,start函数通过Spring获得ConsumerService,并调用其getConsumerOutput函数创建另一个管道并作相应的设置,

    public IMessageOutput getConsumerOutput(IClientStream stream) {        IStreamCapableConnection streamConn = stream.getConnection();        if (streamConn != null && streamConn instanceof RTMPConnection) {            RTMPConnection conn = (RTMPConnection) streamConn;            OutputStream o = conn.createOutputStream(stream.getStreamId());            IPipe pipe = new InMemoryPushPushPipe();            pipe.subscribe(new ConnectionConsumer(conn, o.getVideo(), o.getAudio(), o.getData()), null);            return pipe;        }        return null;    }

getConsumerOutput首先通过ClientBroadcastStream获得RTMPMinaConnection连接,并执行其createOutputStream创建输出流,该函数前面的章节已经分析过了,这里再看一次,

    public OutputStream createOutputStream(Number streamId) {        int channelId = getChannelIdForStreamId(streamId);        final Channel data = getChannel(channelId++);        final Channel video = getChannel(channelId++);        final Channel audio = getChannel(channelId++);        return new OutputStream(video, audio, data);    }

createOutputStream通过获得数据、音频、视频的Channel构造了一个OutputStream并返回。
回到getConsumerOutput函数中,接下来构造了一个ConnectionConsumer,然后创建了另一个管道InMemoryPushPushPipe并执行该管道的subscribe函数,

    public boolean subscribe(IConsumer consumer, Map<String, Object> paramMap) {        if (consumer instanceof IPushableConsumer) {            boolean success = super.subscribe(consumer, paramMap);            if (success) {                fireConsumerConnectionEvent(consumer, PipeConnectionEvent.CONSUMER_CONNECT_PUSH, paramMap);            }            return success;        } else {        }    }

前面已经分析了一遍subscribe函数,但是这里传入的参数不是IProvider,而是IConsumer,继续看其父类的subscribe函数,

    public boolean subscribe(IConsumer consumer, Map<String, Object> paramMap) {        boolean success = consumers.addIfAbsent(consumer);        if (success && consumer instanceof IPipeConnectionListener) {            listeners.addIfAbsent((IPipeConnectionListener) consumer);        }        return success;    }

该函数只是添加了Consumer以及监听器。
继续回到子类InMemoryPushPushPipe的subscribe函数中,假设执行成功,就调用fireConsumerConnectionEvent触发监听事件了,

    protected void fireConsumerConnectionEvent(IConsumer consumer, int type, Map<String, Object> paramMap) {        PipeConnectionEvent event = new PipeConnectionEvent(this);        event.setConsumer(consumer);        event.setType(type);        event.setParamMap(paramMap);        firePipeConnectionEvent(event);    }

这里依然构造了PipeConnectionEvent事件,并调用firePipeConnectionEvent开始处理,

    protected void firePipeConnectionEvent(PipeConnectionEvent event) {        for (IPipeConnectionListener element : listeners) {            try {                element.onPipeConnectionEvent(event);            } catch (Throwable t) {            }        }        if (taskExecutor == null) {            taskExecutor = Executors.newCachedThreadPool(new CustomizableThreadFactory("Pipe-"));        }        for (Runnable task : event.getTaskList()) {            try {                taskExecutor.execute(task);            } catch (Throwable t) {            }        }        event.getTaskList().clear();    }

首先通过onPipeConnectionEvent触发监听器事件,接着启动线程处理各个task。这里的onPipeConnectionEvent定义在ClientBroadcastStream中,

    public void onPipeConnectionEvent(PipeConnectionEvent event) {        switch (event.getType()) {            case PipeConnectionEvent.PROVIDER_CONNECT_PUSH:                ...                break;            case PipeConnectionEvent.PROVIDER_DISCONNECT:                ...                break;            case PipeConnectionEvent.CONSUMER_CONNECT_PUSH:                IPipe pipe = (IPipe) event.getSource();                if (this.livePipe == pipe) {                    notifyChunkSize();                }                subscriberStats.increment();                break;            case PipeConnectionEvent.CONSUMER_DISCONNECT:                ...                break;            default:        }    }

和前面不同的是,这里触发的是PipeConnectionEvent.CONSUMER_CONNECT_PUSH事件,但其实什么也没做。
值得注意的subscribe函数会回调刚刚创建的ConnectionConsumer的onPipeConnectionEvent函数,但其实什么都没做,代码如下,

    public void onPipeConnectionEvent(PipeConnectionEvent event) {        switch (event.getType()) {            case PipeConnectionEvent.PROVIDER_DISCONNECT:                closeChannels();                break;            default:        }    }

回到ClientBroadcastStream的start函数中,返回的InMemoryPushPushPipe保存在connMsgOut中,接下来又执行了一遍subscribe,继续往InMemoryPushPushPipe注册Consumer,这里的回调函数就定义在ClientBroadcastStream中,刚刚也已经分析过了。假设注册成功,接下来就创建一个StreamCodecInfo并进行相应的设置,后面的章节会分析到。

startPublishing函数

startPublishing定义在ClientBroadcastStream中,代码如下,

    public void startPublishing() {        sendStartNotifications(Red5.getConnectionLocal());        if (automaticRecording) {            try {                saveAs(publishedName, false);            } catch (Exception e) {            }        }    }

首先通过sendStartNotifications发送流即将开始的通知,代码如下,

    private void sendStartNotifications(IEventListener source) {        if (sendStartNotification) {            sendStartNotification = false;            if (source instanceof IConnection) {                IScope scope = ((IConnection) source).getScope();                if (scope.hasHandler()) {                    final Object handler = scope.getHandler();                    if (handler instanceof IStreamAwareScopeHandler) {                        if (recordingListener != null && recordingListener.get().isRecording()) {                            ((IStreamAwareScopeHandler) handler).streamRecordStart(this);                        } else {                            try {                                File file = getRecordFile(scope, publishedName);                                if (file != null && file.exists()) {                                    if (!file.delete()) {                                    }                                }                            } catch (Exception e) {                            }                            ((IStreamAwareScopeHandler) handler).streamPublishStart(this);                        }                    }                }            }            sendPublishStartNotify();            if (recordingListener != null && recordingListener.get().isRecording()) {                sendRecordStartNotify();            }            notifyBroadcastStart();        }    }

sendStartNotification在ClientBroadcastStream创建时就默认为true,因为sendStartNotifications只执行一次,因此进入后首先将sendStartNotification设置为false。
传入的参数source实际上就是RTMPMinaConnection,通过getScope获得其对应的Scope后,再通过getHandler获得对应的handler,实际上就是CoreHandler,并没有实现IStreamAwareScopeHandler接口。
再往下通过sendPublishStartNotify向管道推送消息,

    private void sendPublishStartNotify() {        Status publishStatus = new Status(StatusCodes.NS_PUBLISH_START);        publishStatus.setClientid(getStreamId());        publishStatus.setDetails(getPublishedName());        StatusMessage startMsg = new StatusMessage();        startMsg.setBody(publishStatus);        pushMessage(startMsg);    }

这里主要创建了状态为StatusCodes.NS_PUBLISH_START的Status,包装为消息后,然后通过pushMessage发送该消息,

    protected void pushMessage(StatusMessage msg) {        if (connMsgOut != null) {            try {                connMsgOut.pushMessage(msg);            } catch (IOException err) {            }        } else {        }    }

connMsgOut就是前面在ClientBroadcastStream中创建的InMemoryPushPushPipe,来看它的pushMessage函数,

    public void pushMessage(IMessage message) throws IOException {        for (IConsumer consumer : consumers) {            try {                IPushableConsumer pcon = (IPushableConsumer) consumer;                if (message instanceof RTMPMessage) {                    RTMPMessage rtmpMessage = (RTMPMessage) message;                    IRTMPEvent body = rtmpMessage.getBody();                    int time = body.getTimestamp();                    pcon.pushMessage(this, message);                    body.setTimestamp(time);                } else {                    pcon.pushMessage(this, message);                }            } catch (Throwable t) {            }        }    }

刚函数获取该管道注册过的IConsumer,跟着前面的分析,这里传入的message并没有继承或实现RTMPMessage,因此直接调用每个IConsumer的pushMessage函数。
根据本章前面的分析结果,之前一共注册了两个IConsumer,一个是在ClientBroadcastStream的getConsumerOutput函数中创建的ConnectionConsumer,另一个也是在ClientBroadcastStream的start函数中注册的自身,因为ClientBroadcastStream实现的IPushableConsumer接口继承自IConsumer,下面分别来看它们的pushMessage函数,
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) {            ...        } else {        }    }

因为传入的message就是StatusMessage,因此这里通过ConnectionConsumer的数据Channel(data)发送出去了,sendStatus定义在Channel中,代码如下,

    public void sendStatus(Status status) {        if (connection != null) {            final boolean andReturn = !status.getCode().equals(StatusCodes.NS_DATA_START);            final Invoke event = new Invoke();            if (andReturn) {                final PendingCall call = new PendingCall(null, CALL_ON_STATUS, new Object[] { status });                if (status.getCode().equals(StatusCodes.NS_PLAY_START)) {                    ...                }                event.setCall(call);            } else {                ...            }            write(event, connection.getStreamIdForChannelId(id));        }    }

根据前面的分析,传入的statusCode是NS_PUBLISH_START,因此andReturn为true,sendStatus设置PendingCall后,就直接调用write将数据发送给客户端了。

ClientBroadcastStream的pushMessage函数为空。

回到sendStartNotifications函数中,接下来通过notifyBroadcastStart触发别的监听器,但其实什么也没做。

再回到startPublishing函数中,接下来调用saveAs继续处理,

    public void saveAs(String name, boolean isAppend) throws IOException {        IStreamCapableConnection conn = getConnection();        if (recordingListener == null) {            IRecordingListener listener = new RecordingListener();            if (listener.init(conn, name, isAppend)) {                IStreamCodecInfo codecInfo = getCodecInfo();                if (codecInfo instanceof StreamCodecInfo) {                    StreamCodecInfo info = (StreamCodecInfo) codecInfo;                    IVideoStreamCodec videoCodec = info.getVideoCodec();                    if (videoCodec != null) {                        IoBuffer config = videoCodec.getDecoderConfiguration();                        if (config != null) {                            VideoData videoConf = new VideoData(config.asReadOnlyBuffer());                            try {                       listener.getFileConsumer().setVideoDecoderConfiguration(videoConf);                            } finally {                                videoConf.release();                            }                        }                    } else {                    }                    IAudioStreamCodec audioCodec = info.getAudioCodec();                    if (audioCodec != null) {                        IoBuffer config = audioCodec.getDecoderConfiguration();                        if (config != null) {                            AudioData audioConf = new AudioData(config.asReadOnlyBuffer());                            try {                         listener.getFileConsumer().setAudioDecoderConfiguration(audioConf);                            } finally {                                audioConf.release();                            }                        }                    } else {                    }                }                recordingListener = new WeakReference<IRecordingListener>(listener);                addStreamListener(listener);                listener.start();            } else {            }        } else {        }    }

saveAs函数首先创建了一个RecordingListener,并调用其init函数进行初始化,init函数的代码如下,

    public boolean init(IConnection conn, String name, boolean isAppend) {        return init(conn.getScope(), name, isAppend);    }    public boolean init(IScope scope, String name, boolean isAppend) {        File file = getRecordFile(scope, name);        if (file != null) {            if (!isAppend) {                if (file.exists()) {                    if (!file.delete()) {                        return false;                    }                }            } else {                if (file.exists()) {                    appending = true;                } else {                    isAppend = false;                }            }            if (!file.exists()) {                String path = file.getAbsolutePath();                int slashPos = path.lastIndexOf(File.separator);                if (slashPos != -1) {                    path = path.substring(0, slashPos);                }                File tmp = new File(path);                if (!tmp.isDirectory()) {                    tmp.mkdirs();                }                try {                    file.createNewFile();                } catch (IOException e) {                    return false;                }            }            if (scope.getContext().hasBean("keyframe.cache")) {                IKeyFrameMetaCache keyFrameCache = (IKeyFrameMetaCache) scope.getContext().getBean("keyframe.cache");                keyFrameCache.removeKeyFrameMeta(file);            }            if (scope.getContext().hasBean("fileConsumer")) {                recordingConsumer = (FileConsumer) scope.getContext().getBean("fileConsumer");                recordingConsumer.setScope(scope);                recordingConsumer.setFile(file);            } else {                recordingConsumer = new FileConsumer(scope, file);            }            if (isAppend) {                recordingConsumer.setMode("append");            } else {                recordingConsumer.setMode("record");            }            setFileName(file.getName());            scheduler = (QuartzSchedulingService) scope.getParent().getContext().getBean(QuartzSchedulingService.BEAN_NAME);            recording.set(true);        } else {        }        return recording.get();    }

init函数首先根据Scope和name通过getRecordFile函数获取文件,代码如下,

    public static File getRecordFile(IScope scope, String name) {        IStreamFilenameGenerator generator = (IStreamFilenameGenerator) ScopeUtils.getScopeService(scope, IStreamFilenameGenerator.class, DefaultStreamFilenameGenerator.class);        String fileName = generator.generateFilename(scope, name, ".flv", GenerationType.RECORD);        File file = null;        if (generator.resolvesToAbsolutePath()) {            file = new File(fileName);        } else {            Resource resource = scope.getContext().getResource(fileName);            if (resource.exists()) {                try {                    file = resource.getFile();                } catch (IOException ioe) {                }            } else {                String appScopeName = ScopeUtils.findApplication(scope).getName();                file = new File(String.format("%s/webapps/%s/%s", System.getProperty("red5.root"), appScopeName, fileName));            }        }        return file;    }

getRecordFile函数首先通过ScopeUtils的getScopeService函数构造一个DefaultStreamFilenameGenerator,并通过其generateFilename函数生成文件名,

    public String generateFilename(IScope scope, String name, GenerationType type) {        return generateFilename(scope, name, null, type);    }    public String generateFilename(IScope scope, String name, String extension, GenerationType type) {        String result = getStreamDirectory(scope) + name;        if (extension != null && !extension.equals("")) {            result += extension;        }        return result;    }

getStreamDirectory获取Scope对应的路径名,因此最后的文件名为”Scope路径名+name+extension”。
回到getRecordFile函数中,resolvesToAbsolutePath函数默认返回false,因此直接看接下来的else部分,因为是第一次创建,因此getResource返回的Resource不存在,因此最后会创建一个File,用来存储流的数据。
回到RecordingListener的init函数中,传入的参数isAppend为false,因此这里会删除同名File,紧接下来就会通过createNewFile创建该File。
再往下,init函数会根据red5-common.xml配置文件创建CachingFileKeyFrameMetaCache,并

    public void removeKeyFrameMeta(File file) {        rwLock.writeLock().lock();        try {            String canonicalPath = file.getCanonicalPath();            inMemoryMetaCache.remove(canonicalPath);        } catch (IOException e) {        } finally {            rwLock.writeLock().unlock();        }        super.removeKeyFrameMeta(file);    }    public void removeKeyFrameMeta(File file) {        String filename = String.format("%s.meta", file.getAbsolutePath());        File metadataFile = new File(filename);        if (metadataFile.exists()) {            if (metadataFile.delete()) {            } else {                metadataFile.deleteOnExit();            }        } else {        }    }

这里其实就是删除流文件对应的信息文件。

回到init函数中,再往下创建了一个FileConsumer并设置其模式为record,最后返回true。

回到saveAs函数中,RecordingListener初始化完成后,首先通过getCodecInfo获得前面在ClientBroadcastStream的start函数中创建的StreamCodecInfo,但是接下来的getVideoCodec和getAudioCodec返回空,所以直接跳到最后看,首先通过addStreamListener将刚刚创建的RecordingListener函数设置进ClientBroadcastStream的listeners中,

    public void addStreamListener(IStreamListener listener) {        listeners.add(listener);    }

接下来就调用RecordingListener的start函数了,

    public void start() {        eqj = new EventQueueJob();        eventQueueJobName = scheduler.addScheduledJob(3000, eqj);    }

start函数很简单,就是启用一个线程执行EventQueueJob,用来处理接收到的事件。

下一章开始分析客户端如何发送流给服务器。

0 0
原创粉丝点击