/** * Close stream. This method can close both IClientBroadcastStream (coming from Flash Player to * Red5) and ISubscriberStream (from Red5 to Flash Player). Corresponding application handlers * (streamSubscriberClose, etc.) are called as if close was initiated by Flash Player. * * <p>It is recommended to remember stream id in application handlers, ex.: * * <pre> * public void streamBroadcastStart(IBroadcastStream stream) { * super.streamBroadcastStart(stream); * if (stream instanceof IClientBroadcastStream) { * int publishedStreamId = ((ClientBroadcastStream)stream).getStreamId(); * Red5.getConnectionLocal().setAttribute(PUBLISHED_STREAM_ID_ATTRIBUTE, publishedStreamId); * } * } * </pre> * * <pre> * public void streamPlaylistItemPlay(IPlaylistSubscriberStream stream, IPlayItem item, boolean isLive) { * super.streamPlaylistItemPlay(stream, item, isLive); * Red5.getConnectionLocal().setAttribute(WATCHED_STREAM_ID_ATTRIBUTE, stream.getStreamId()); * } * </pre> * * When stream is closed, corresponding NetStream status will be sent to stream provider / * consumers. Implementation is based on Red5's StreamService.close() * * @param conn client connection * @param streamId stream ID (number: 1,2,...) */ public void closeStream(IConnection conn, int streamId) { log.info("closeStream: streamId={}, connection={}", streamId, conn); if (conn instanceof IStreamCapableConnection) { IStreamCapableConnection scConn = (IStreamCapableConnection) conn; IClientStream stream = scConn.getStreamById(streamId); if (stream != null) { if (stream instanceof IClientBroadcastStream) { // this is a broadcasting stream (from Flash Player to Red5) IClientBroadcastStream bs = (IClientBroadcastStream) stream; IBroadcastScope bsScope = getBroadcastScope(conn.getScope(), bs.getPublishedName()); if (bsScope != null && conn instanceof BaseConnection) { ((BaseConnection) conn).unregisterBasicScope(bsScope); } } stream.close(); scConn.deleteStreamById(streamId); // in case of broadcasting stream, status is sent automatically by Red5 if (!(stream instanceof IClientBroadcastStream)) { StreamService.sendNetStreamStatus( conn, StatusCodes.NS_PLAY_STOP, "Stream closed by server", stream.getName(), Status.STATUS, streamId); } } else { log.info("Stream not found: streamId={}, connection={}", streamId, conn); } } else { log.warn("Connection is not instance of IStreamCapableConnection: {}", conn); } }
/** {@inheritDoc} */ @Override protected void onPing(RTMPConnection conn, Channel channel, Header source, Ping ping) { log.trace("onPing"); switch (ping.getEventType()) { case Ping.PING_CLIENT: case Ping.STREAM_BEGIN: case Ping.RECORDED_STREAM: case Ping.STREAM_PLAYBUFFER_CLEAR: // the server wants to measure the RTT Ping pong = new Ping(); pong.setEventType(Ping.PONG_SERVER); pong.setValue2((int) (System.currentTimeMillis() & 0xffffffff)); conn.ping(pong); break; case Ping.STREAM_DRY: log.debug("Stream indicates there is no data available"); break; case Ping.CLIENT_BUFFER: // set the client buffer IClientStream stream = null; // get the stream id int streamId = ping.getValue2(); // get requested buffer size in milliseconds int buffer = ping.getValue3(); log.debug("Client sent a buffer size: {} ms for stream id: {}", buffer, streamId); if (streamId != 0) { // the client wants to set the buffer time stream = conn.getStreamById(streamId); if (stream != null) { stream.setClientBufferDuration(buffer); log.info("Setting client buffer on stream: {}", buffer); } } // catch-all to make sure buffer size is set if (stream == null) { // remember buffer time until stream is created conn.rememberStreamBufferDuration(streamId, buffer); log.info("Remembering client buffer on stream: {}", buffer); } break; case Ping.PING_SWF_VERIFY: log.debug("SWF verification ping"); // TODO get the swf verification bytes from the handshake SWFResponse swfPong = new SWFResponse(new byte[42]); conn.ping(swfPong); break; case Ping.BUFFER_EMPTY: log.debug("Buffer empty ping"); break; case Ping.BUFFER_FULL: log.debug("Buffer full ping"); break; default: log.warn("Unhandled ping: {}", ping); } }
/** {@inheritDoc} */ public void play(Boolean dontStop) { log.debug("Play called. Dont stop param: {}", dontStop); if (!dontStop) { IConnection conn = Red5.getConnectionLocal(); if (conn instanceof IStreamCapableConnection) { IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; int streamId = conn.getStreamId(); IClientStream stream = streamConn.getStreamById(streamId); if (stream != null) { stream.stop(); } } } }
/** {@inheritDoc} */ public void deleteStream(IStreamCapableConnection conn, int streamId) { IClientStream stream = conn.getStreamById(streamId); if (stream != null) { if (stream instanceof IClientBroadcastStream) { IClientBroadcastStream bs = (IClientBroadcastStream) stream; IBroadcastScope bsScope = getBroadcastScope(conn.getScope(), bs.getPublishedName()); if (bsScope != null && conn instanceof BaseConnection) { ((BaseConnection) conn).unregisterBasicScope(bsScope); } } stream.close(); } conn.unreserveStreamId(streamId); }
/** {@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; }
/** {@inheritDoc} */ public void initStream(int streamId) { IConnection conn = Red5.getConnectionLocal(); log.info("initStream: id={} current id: {} connection={}", streamId, conn.getStreamId(), conn); if (conn instanceof IStreamCapableConnection) { ((IStreamCapableConnection) conn).reserveStreamId(streamId); IClientStream stream = ((IStreamCapableConnection) conn).getStreamById(streamId); if (stream != null) { if (stream instanceof IClientBroadcastStream) { IClientBroadcastStream bs = (IClientBroadcastStream) stream; IBroadcastScope bsScope = getBroadcastScope(conn.getScope(), bs.getPublishedName()); if (bsScope != null && conn instanceof BaseConnection) { ((BaseConnection) conn).unregisterBasicScope(bsScope); } } stream.close(); } ((IStreamCapableConnection) conn).deleteStreamById(streamId); } else { log.warn("ERROR in intiStream, connection is not stream capable"); } }
/** {@inheritDoc} */ @Override public void close() { getWriteLock().lock(); try { if (keepAliveJobName != null) { schedulingService.removeScheduledJob(keepAliveJobName); keepAliveJobName = null; } } finally { getWriteLock().unlock(); } Red5.setConnectionLocal(this); IStreamService streamService = (IStreamService) getScopeService(scope, IStreamService.class, StreamService.class); if (streamService != null) { for (Map.Entry<Integer, IClientStream> entry : streams.entrySet()) { IClientStream stream = entry.getValue(); if (stream != null) { log.debug("Closing stream: {}", stream.getStreamId()); streamService.deleteStream(this, stream.getStreamId()); usedStreams.decrementAndGet(); } } streams.clear(); } channels.clear(); getWriteLock().lock(); try { if (bwContext != null && getScope() != null && getScope().getContext() != null) { IBWControlService bwController = (IBWControlService) getScope().getContext().getBean(IBWControlService.KEY); bwController.unregisterBWControllable(bwContext); bwContext = null; } } finally { getWriteLock().unlock(); } super.close(); }
/** {@inheritDoc} */ public void seek(int position) { log.trace("seek - position:{}", position); IConnection conn = Red5.getConnectionLocal(); if (conn instanceof IStreamCapableConnection) { IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; int streamId = conn.getStreamId(); IClientStream stream = streamConn.getStreamById(streamId); if (stream != null && stream instanceof ISubscriberStream) { ISubscriberStream subscriberStream = (ISubscriberStream) stream; try { subscriberStream.seek(position); } catch (OperationNotSupportedException err) { sendNSFailed( streamConn, StatusCodes.NS_SEEK_FAILED, "The stream doesn't support seeking.", stream.getName(), streamId); } } } }
public void addClientStream(IClientStream stream) { int streamId = stream.getStreamId(); getWriteLock().lock(); try { if (reservedStreams.get(streamId - 1)) { return; } reservedStreams.set(streamId - 1); } finally { getWriteLock().unlock(); } streams.put(streamId - 1, stream); usedStreams.incrementAndGet(); }
/** {@inheritDoc} */ @Override public void close() { getWriteLock().lock(); try { if (keepAliveJobName != null) { schedulingService.removeScheduledJob(keepAliveJobName); keepAliveJobName = null; } } finally { getWriteLock().unlock(); } Red5.setConnectionLocal(this); IStreamService streamService = (IStreamService) getScopeService(scope, IStreamService.class, StreamService.class); if (streamService != null) { for (Map.Entry<Integer, IClientStream> entry : streams.entrySet()) { IClientStream stream = entry.getValue(); if (stream != null) { log.debug("Closing stream: {}", stream.getStreamId()); streamService.deleteStream(this, stream.getStreamId()); usedStreams.decrementAndGet(); } } } // close the base connection - disconnect scopes and unregister client super.close(); // kill all the collections etc if (channels != null) { channels.clear(); channels = null; } else { log.trace("Channels collection was null"); } if (streams != null) { streams.clear(); streams = null; } else { log.trace("Streams collection was null"); } if (pendingCalls != null) { pendingCalls.clear(); pendingCalls = null; } else { log.trace("PendingCalls collection was null"); } if (deferredResults != null) { deferredResults.clear(); deferredResults = null; } else { log.trace("DeferredResults collection was null"); } if (pendingVideos != null) { pendingVideos.clear(); pendingVideos = null; } else { log.trace("PendingVideos collection was null"); } if (streamBuffers != null) { streamBuffers.clear(); streamBuffers = null; } else { log.trace("StreamBuffers collection was null"); } }
/** * Remove a stream from the connection. * * @param stream */ @SuppressWarnings("unused") private void unregisterStream(IClientStream stream) { streams.remove(stream.getStreamId()); }
/** * Store a stream in the connection. * * @param stream */ private void registerStream(IClientStream stream) { streams.put(stream.getStreamId() - 1, stream); }
/** {@inheritDoc} */ public void play(String name, int start, int length, boolean flushPlaylist) { log.debug( "Play called - name: {} start: {} length: {} flush playlist: {}", new Object[] {name, start, length, flushPlaylist}); IConnection conn = Red5.getConnectionLocal(); if (conn instanceof IStreamCapableConnection) { IScope scope = conn.getScope(); IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; int streamId = conn.getStreamId(); if (StringUtils.isEmpty(name)) { sendNSFailed( streamConn, StatusCodes.NS_FAILED, "The stream name may not be empty.", name, streamId); return; } IStreamSecurityService security = (IStreamSecurityService) ScopeUtils.getScopeService(scope, IStreamSecurityService.class); if (security != null) { Set<IStreamPlaybackSecurity> handlers = security.getStreamPlaybackSecurity(); for (IStreamPlaybackSecurity handler : handlers) { if (!handler.isPlaybackAllowed(scope, name, start, length, flushPlaylist)) { sendNSFailed( streamConn, StatusCodes.NS_FAILED, "You are not allowed to play the stream.", name, streamId); return; } } } boolean created = false; IClientStream stream = streamConn.getStreamById(streamId); if (stream == null) { if (streamId <= 0) { streamId = streamConn.reserveStreamId(); } stream = streamConn.newPlaylistSubscriberStream(streamId); stream.setBroadcastStreamPublishName(name); stream.start(); created = true; } if (stream != null && stream instanceof ISubscriberStream) { ISubscriberStream subscriberStream = (ISubscriberStream) stream; IPlayItem item = simplePlayback.get() ? SimplePlayItem.build(name, start, length) : DynamicPlayItem.build(name, start, length); if (subscriberStream instanceof IPlaylistSubscriberStream) { IPlaylistSubscriberStream playlistStream = (IPlaylistSubscriberStream) subscriberStream; if (flushPlaylist) { playlistStream.removeAllItems(); } playlistStream.addItem(item); } else if (subscriberStream instanceof ISingleItemSubscriberStream) { ISingleItemSubscriberStream singleStream = (ISingleItemSubscriberStream) subscriberStream; singleStream.setPlayItem(item); } else { // not supported by this stream service log.warn( "Stream instance type: {} is not supported", subscriberStream.getClass().getName()); return; } try { subscriberStream.play(); } catch (IOException err) { if (created) { stream.close(); streamConn.deleteStreamById(streamId); } sendNSFailed(streamConn, StatusCodes.NS_FAILED, err.getMessage(), name, streamId); } } } else { log.debug("Connection was not stream capable"); } }