import java.io.File;
import org.apache.mina.core.buffer.IoBuffer;
import org.red5.io.IStreamableFile;
import org.red5.io.ITag;
import org.red5.io.ITagWriter;
import org.red5.io.flv.impl.FLVService;
import org.red5.io.flv.impl.Tag;
import org.red5.io.utils.ObjectMap;
import org.red5.server.api.event.IEvent;
import org.red5.server.api.event.IEventDispatcher;
import org.red5.server.api.service.IPendingServiceCall;
import org.red5.server.api.service.IPendingServiceCallback;
import org.red5.server.net.rtmp.Channel;
import org.red5.server.net.rtmp.RTMPClient;
import org.red5.server.net.rtmp.RTMPConnection;
import org.red5.server.net.rtmp.codec.RTMP;
import org.red5.server.net.rtmp.event.AudioData;
import org.red5.server.net.rtmp.event.IRTMPEvent;
import org.red5.server.net.rtmp.event.Notify;
import org.red5.server.net.rtmp.event.VideoData;
import org.red5.server.net.rtmp.message.Header;
import org.red5.server.net.rtmp.status.StatusCodes;
import org.red5.server.stream.AbstractClientStream;
import org.red5.server.stream.IStreamData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class MyRtmpClient extends RTMPClient {private static final Logger logger = LoggerFactory.getLogger(MyRtmpClient.class); private String saveAsFileName = "test.flv"; public static void main(String[] args) { String host = "localhost"; String app = "oflaDemo"; final String name = "IronMan.flv"; int port = 1935; final int duration = 10000; // milliseconds, -2 means until end of stream
final MyRtmpClient client = new MyRtmpClient(); logger.debug("connecting, host: " + host + ", app: " + app + ", port: " + port);
IPendingServiceCallback callback = new IPendingServiceCallback() { public void resultReceived(IPendingServiceCall call) { logger.debug("service call result: " + call); if ("connect".equals(call.getServiceMethodName())) { client.createStream(this); } else if ("createStream".equals(call.getServiceMethodName())) { Integer streamId = (Integer) call.getResult(); logger.debug("createStream result stream id: " + streamId); logger.debug("playing video by name: " + name); client.play(streamId, name, 0, duration); } } };
client.connect(host, port, app, callback);
}
private RTMPConnection conn; private ITagWriter writer;
private int videoTs; private int audioTs;
@Override public void connectionOpened(RTMPConnection conn, RTMP state) { logger.debug("connection opened"); super.connectionOpened(conn, state); this.conn = conn; init(); }
@Override public void connectionClosed(RTMPConnection conn, RTMP state) { logger.debug("connection closed"); super.connectionClosed(conn, state); if (writer != null) { writer.close(); writer = null; } System.exit(0); }
@Override public void createStream(IPendingServiceCallback callback) { logger.debug("create stream"); IPendingServiceCallback wrapper = new CreateStreamCallBack(callback); invoke("createStream", null, wrapper); }
@Override protected void onInvoke(RTMPConnection conn, Channel channel, Header header, Notify notify, RTMP rtmp) { super.onInvoke(conn, channel, header, notify, rtmp); ObjectMap<String, String> map = (ObjectMap) notify.getCall().getArguments()[0]; String code = map.get("code"); if (StatusCodes.NS_PLAY_STOP.equals(code)) { logger.debug("onInvoke, code == NetStream.Play.Stop, disconnecting"); disconnect(); } }
private void init() { File file = new File(saveAsFileName); FLVService flvService = new FLVService(); flvService.setGenerateMetadata(true); try { IStreamableFile flv = flvService.getStreamableFile(file); writer = flv.getWriter(); } catch (Exception e) { throw new RuntimeException(e); } }
private class CreateStreamCallBack implements IPendingServiceCallback {
private IPendingServiceCallback wrapped;
public CreateStreamCallBack(IPendingServiceCallback wrapped) { this.wrapped = wrapped; }
public void resultReceived(IPendingServiceCall call) { Integer streamIdInt = (Integer) call.getResult(); if (conn != null && streamIdInt != null) { MyNetStream stream = new MyNetStream(); stream.setConnection(conn); stream.setStreamId(streamIdInt.intValue()); conn.addClientStream(stream); } wrapped.resultReceived(call); }
}
private class MyNetStream extends AbstractClientStream implements IEventDispatcher {
public void close() { }
public void start() { }
public void stop() { }
public void dispatchEvent(IEvent event) { if (!(event instanceof IRTMPEvent)) { logger.debug("skipping non rtmp event: " + event); return; } IRTMPEvent rtmpEvent = (IRTMPEvent) event; if (logger.isDebugEnabled()) { logger.debug("rtmp event: " + rtmpEvent.getHeader() + ", " + rtmpEvent.getClass().getSimpleName()); } if (!(rtmpEvent instanceof IStreamData)) { logger.debug("skipping non stream data"); return; } if (rtmpEvent.getHeader().getSize() == 0) { logger.debug("skipping event where size == 0"); return; } ITag tag = new Tag(); tag.setDataType(rtmpEvent.getDataType()); if (rtmpEvent instanceof VideoData) { videoTs += rtmpEvent.getTimestamp(); tag.setTimestamp(videoTs); } else if (rtmpEvent instanceof AudioData) { audioTs += rtmpEvent.getTimestamp(); tag.setTimestamp(audioTs); } IoBuffer data = ((IStreamData) rtmpEvent).getData().asReadOnlyBuffer(); tag.setBodySize(data.limit()); tag.setBody(data); try { writer.writeTag(tag); } catch (Exception e) { throw new RuntimeException(e); } } }
}