MyRtmpClient

JavaをつかってつくったプログラムでFlashMediaServerやWowzaMediaServer、Red5等に接続させる便利なクラスがあります。
Red5のRTMPClientというクラスですが・・・

参考URL
のようにサンプルコードがあるんですが、ちと情報が古くてコンパイルできないので、とりあえずメモ代わりにコンパイルできる形に変更したものとコンパイルのコマンドを・・・

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);
		}
	}
}

}

apache.minaのバージョンが2になったときにByteBufferが別のクラスになったんで、変更対応しただけです。

コンパイルコマンドは
javac -classpath /home/red5/red5.jar:/home/red5/lib/mina-core-2.0.0-RC1.jar:/home/red5/lib/slf4j-api-1.5.8.jar:/home/red5/lib/log4j-over-slf4j-1.5.8.jar:/home/red5/lib/spring-context-2.5.6.jar:/home/red5/lib/spring-beans-2.5.6.jar:/home/red5/lib/spring-core-2.5.6.jar -d ./classes ./src/*.java
こんな感じです。

dispatchEventが面白くて、これをXuggle-Xuggler-Red5のBroadcastStreamにぶちこんでやると
取得したデータを別のストリームとして流すことができます。

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2010年03月27日 21:57
ツールボックス

下から選んでください:

新しいページを作成する
ヘルプ / FAQ もご覧ください。