/** * Plays back a stream based on the supplied name, from the specified position for the given * length of time. * * @param name - The name of a recorded file, or the identifier for live data. If * @param start - The start time, in seconds. Allowed values are -2, -1, 0, or a positive number. * The default value is -2, which looks for a live stream, then a recorded stream, and if it * finds neither, opens a live stream. If -1, plays only a live stream. If 0 or a positive * number, plays a recorded stream, beginning start seconds in. * @param length - The duration of the playback, in seconds. Allowed values are -1, 0, or a * positive number. The default value is -1, which plays a live or recorded stream until it * ends. If 0, plays a single frame that is start seconds from the beginning of a recorded * stream. If a positive number, plays a live or recorded stream for length seconds. * @param reset - Whether to clear a playlist. The default value is 1 or true, which clears any * previous play calls and plays name immediately. If 0 or false, adds the stream to a * playlist. If 2, maintains the playlist and returns all stream messages at once, rather than * at intervals. If 3, clears the playlist and returns all stream messages at once. */ public void play(String name, int start, int length, Object reset) { if (reset instanceof Boolean) { play(name, start, length, ((Boolean) reset).booleanValue()); } else { if (reset instanceof Integer) { int value = (Integer) reset; switch (value) { case 0: // adds the stream to a playlist IStreamCapableConnection streamConn = (IStreamCapableConnection) Red5.getConnectionLocal(); IPlaylistSubscriberStream playlistStream = (IPlaylistSubscriberStream) streamConn.getStreamById(streamConn.getStreamId()); IPlayItem item = SimplePlayItem.build(name); playlistStream.addItem(item); play(name, start, length, false); break; case 2: // maintains the playlist and returns all stream messages at once, rather than at // intervals break; case 3: // clears the playlist and returns all stream messages at once break; default: // clears any previous play calls and plays name immediately play(name, start, length, true); } } else { play(name, start, length); } } }
/** * Dynamic streaming play method. * * <p>The following properties are supported on the play options: * * <pre> * streamName: String. The name of the stream to play or the new stream to switch to. * oldStreamName: String. The name of the initial stream that needs to be switched out. This is not needed and ignored * when play2 is used for just playing the stream and not switching to a new stream. * start: Number. The start time of the new stream to play, just as supported by the existing play API. and it has the * same defaults. This is ignored when the method is called for switching (in other words, the transition * is either NetStreamPlayTransition.SWITCH or NetStreamPlayTransitions.SWAP) * len: Number. The duration of the playback, just as supported by the existing play API and has the same defaults. * transition: String. The transition mode for the playback command. It could be one of the following: * NetStreamPlayTransitions.RESET * NetStreamPlayTransitions.APPEND * NetStreamPlayTransitions.SWITCH * NetStreamPlayTransitions.SWAP * </pre> * * NetStreamPlayTransitions: * * <pre> * APPEND : String = "append" - Adds the stream to a playlist and begins playback with the first stream. * APPEND_AND_WAIT : String = "appendAndWait" - Builds a playlist without starting to play it from the first stream. * RESET : String = "reset" - Clears any previous play calls and plays the specified stream immediately. * RESUME : String = "resume" - Requests data from the new connection starting from the point at which the previous connection ended. * STOP : String = "stop" - Stops playing the streams in a playlist. * SWAP : String = "swap" - Replaces a content stream with a different content stream and maintains the rest of the playlist. * SWITCH : String = "switch" - Switches from playing one stream to another stream, typically with streams of the same content. * </pre> * * @see <a * href="http://www.adobe.com/devnet/flashmediaserver/articles/dynstream_actionscript.html">ActionScript * guide to dynamic streaming</a> * @see <a * href="http://www.adobe.com/devnet/flashmediaserver/articles/dynstream_advanced_pt1.html">Dynamic * streaming in Flash Media Server - Part 1: Overview of the new capabilities</a> * @see <a * href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStreamPlayTransitions.html">NetStreamPlayTransitions</a> * @param playOptions play options */ public void play2(Map<String, ?> playOptions) { log.debug("play2 options: {}", playOptions.toString()); /* { streamName=streams/new.flv, oldStreamName=streams/old.flv, start=0, len=-1, offset=12.195, transition=switch } */ // get the transition type String transition = (String) playOptions.get("transition"); String streamName = (String) playOptions.get("streamName"); String oldStreamName = (String) playOptions.get("oldStreamName"); // now initiate new playback int start = (Integer) playOptions.get("start"); int length = (Integer) playOptions.get("len"); // get the clients connection IConnection conn = Red5.getConnectionLocal(); if (conn != null && conn instanceof IStreamCapableConnection) { // get the stream id int streamId = conn.getStreamId(); IStreamCapableConnection streamConn = (IStreamCapableConnection) conn; if ("stop".equals(transition)) { play(Boolean.FALSE); } else if ("reset".equals(transition)) { // just reset the currently playing stream play(streamName); } else if ("switch".equals(transition)) { try { // set the playback type simplePlayback.set(Boolean.FALSE); // send the "start" of transition sendNSStatus( conn, StatusCodes.NS_PLAY_TRANSITION, String.format("Transitioning from %s to %s.", oldStreamName, streamName), streamName, streamId); // support offset? // playOptions.get("offset") play(streamName, start, length); } finally { // clean up simplePlayback.remove(); } } else if ("append".equals(transition) || "appendAndWait".equals(transition)) { IPlaylistSubscriberStream playlistStream = (IPlaylistSubscriberStream) streamConn.getStreamById(streamId); IPlayItem item = SimplePlayItem.build(streamName); playlistStream.addItem(item); if ("append".equals(transition)) { play(streamName, start, length, false); } } else if ("swap".equals(transition)) { IPlaylistSubscriberStream playlistStream = (IPlaylistSubscriberStream) streamConn.getStreamById(streamId); IPlayItem item = SimplePlayItem.build(streamName); int itemCount = playlistStream.getItemSize(); for (int i = 0; i < itemCount; i++) { IPlayItem tmpItem = playlistStream.getItem(i); if (tmpItem.getName().equals(oldStreamName)) { if (!playlistStream.replace(tmpItem, item)) { log.warn("Playlist item replacement failed"); sendNSFailed( streamConn, StatusCodes.NS_PLAY_FAILED, "Playlist swap failed.", streamName, streamId); } break; } } } else { log.warn("Unhandled transition: {}", transition); sendNSFailed( conn, StatusCodes.NS_FAILED, "Transition type not supported", streamName, streamId); } } else { log.info("Connection was null ?"); } }
/** {@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"); } }