/*
* myLib - https://github.com/taktod/myLib
* Copyright (c) 2014 ttProject. All rights reserved.
*
* Licensed under The MIT license.
*/
package com.ttProject.rtmp;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.log4j.Logger;
import com.ttProject.container.flv.amf.Amf0Object;
import com.ttProject.rtmp.message.IRtmpMessage;
import com.ttProject.rtmp.message.type.Amf0Command;
import com.ttProject.rtmp.netty.ClientHandler;
import com.ttProject.rtmp.netty.HandshakeHandler;
import com.ttProject.rtmp.netty.NetConnectionHandler;
import com.ttProject.rtmp.netty.RtmpDecoder;
import com.ttProject.rtmp.netty.RtmpEncoder;
/**
* NetConnection
* @author taktod
*/
public class NetConnection {
@SuppressWarnings("unused")
private Logger logger = Logger.getLogger(NetConnection.class);
private String host;
private int port;
private String app;
private NetConnectionHandler handler = null;
private INetStatusEventListener listener = null;
private ChannelFuture future = null;
private EventLoopGroup workerGroup;
private int transactionId = 2;
private int bufferLength = 0;
public void connect(String address) {
// parse rtmp address information.
Pattern pattern = Pattern.compile("rtmp://([^/:]+)(:[0-9]+)?/(.+)");
Matcher matcher = pattern.matcher(address);
if(matcher.matches()) {
if(matcher.groupCount() != 3) {
throw new RuntimeException("matcher count is invalid.");
}
host = matcher.group(1);
if(matcher.group(2) == null) {
port = 1935;
}
else {
port = Integer.parseInt(matcher.group(2).substring(1));
}
app = matcher.group(3);
}
else {
throw new RuntimeException("failed to analyze rtmp address.");
}
String tcUrl = "rtmp://" + host + ":" + port + "/" + app;
handler = new NetConnectionHandler(tcUrl, this);
// try to connect.
workerGroup = new NioEventLoopGroup();
Bootstrap bootstrap = new Bootstrap()
.group(workerGroup)
.channel(NioSocketChannel.class)
.option(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.TCP_NODELAY, true)
.handler(new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast(new HandshakeHandler())
.addLast(new RtmpDecoder())
.addLast(new RtmpEncoder())
.addLast(new ClientHandler())
.addLast(handler);
}
});
future = bootstrap.connect(host, port);
}
public void setObjectEncoding(int objectEncoding) {
if(objectEncoding != 0) {
throw new RuntimeException("now amf0 is only supported.");
}
}
public void setListener(INetStatusEventListener listener) {
this.listener = listener;
}
public int getBufferLength() {
return bufferLength;
}
public void addLast(ChannelHandler handler) {
future.channel().pipeline().addLast(handler);
}
public int writeAndFlush(IRtmpMessage message) {
int resId = 0;
if(message instanceof Amf0Command) {
Amf0Command command = (Amf0Command)message;
command.setTransactionId(transactionId);
resId = transactionId;
transactionId ++;
}
future.channel().writeAndFlush(message);
return resId;
}
public void close() {
future.channel().close();
workerGroup.shutdownGracefully();
}
public void closeForWait() throws Exception {
try {
future.channel().closeFuture().sync();
}
finally {
workerGroup.shutdownGracefully();
}
}
public void onStatusEvent(Amf0Object<String, Object> obj) {
if(listener != null) {
listener.onStatusEvent(obj);
}
}
}