package org.red5.client.net.rtmp.codec;
import java.util.List;
import java.util.concurrent.Semaphore;
import org.apache.commons.codec.binary.Hex;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolCodecException;
import org.apache.mina.filter.codec.ProtocolCodecFactory;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.apache.mina.filter.codec.ProtocolEncoder;
import org.apache.mina.filter.codec.ProtocolEncoderOutput;
import org.red5.client.net.rtmp.RTMPConnManager;
import org.red5.server.api.Red5;
import org.red5.server.net.rtmp.RTMPConnection;
import org.red5.server.net.rtmp.codec.RTMPMinaProtocolDecoder;
import org.red5.server.net.rtmp.codec.RTMPMinaProtocolEncoder;
public class RTMPMinaCodecFactory implements ProtocolCodecFactory {
private RTMPMinaProtocolDecoder clientDecoder;
private RTMPMinaProtocolEncoder clientEncoder;
{
// RTMP Decoding
clientDecoder = new RTMPMinaProtocolDecoder() {
@Override
public void decode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws ProtocolCodecException {
log.trace("decode buffer position: {}", in.position());
// get the connection from the session
String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
log.trace("Session id: {}", sessionId);
RTMPConnection conn = (RTMPConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId);
Red5.setConnectionLocal(conn);
byte[] arr = new byte[in.remaining()];
in.get(arr);
// create a buffer and store it on the session
IoBuffer buf = (IoBuffer) session.getAttribute("buffer");
if (buf == null) {
buf = IoBuffer.allocate(arr.length);
buf.setAutoExpand(true);
session.setAttribute("buffer", buf);
}
// copy incoming into buffer
buf.put(arr);
// flip so we can read
buf.flip();
final Semaphore lock = conn.getDecoderLock();
try {
// acquire the decoder lock
lock.acquire();
// construct any objects from the decoded buffer
List<?> objects = getDecoder().decodeBuffer(conn, buf);
log.trace("Decoded: {}", objects);
if (objects != null) {
for (Object object : objects) {
log.trace("Writing {} to decoder output: {}", object, out);
out.write(object);
}
}
log.trace("Input buffer position: {}", in.position());
} catch (Exception e) {
log.error("Error during decode", e);
} finally {
lock.release();
Red5.setConnectionLocal(null);
}
}
};
clientDecoder.setDecoder(new RTMPClientProtocolDecoder());
// RTMP Encoding
clientEncoder = new RTMPMinaProtocolEncoder() {
@Override
public void encode(IoSession session, Object message, ProtocolEncoderOutput out) throws ProtocolCodecException {
// get the connection from the session
String sessionId = (String) session.getAttribute(RTMPConnection.RTMP_SESSION_ID);
log.trace("Session id: {}", sessionId);
RTMPConnection conn = (RTMPConnection) RTMPConnManager.getInstance().getConnectionBySessionId(sessionId);
if (conn != null) {
Red5.setConnectionLocal(conn);
final Semaphore lock = conn.getEncoderLock();
try {
// acquire the encoder lock
lock.acquire();
// get the buffer
final IoBuffer buf = message instanceof IoBuffer ? (IoBuffer) message : getEncoder().encode(message);
if (buf != null) {
if (log.isTraceEnabled()) {
log.trace("Writing output data: {}", Hex.encodeHexString(buf.array()));
}
out.write(buf);
} else {
log.trace("Response buffer was null after encoding");
}
} catch (Exception ex) {
log.error("Exception during encode", ex);
} finally {
lock.release();
Red5.setConnectionLocal(null);
}
} else {
log.debug("Connection is no longer available for encoding, may have been closed already");
}
}
};
clientEncoder.setEncoder(new RTMPClientProtocolEncoder());
}
/** {@inheritDoc} */
@Override
public ProtocolDecoder getDecoder(IoSession session) {
return clientDecoder;
}
/** {@inheritDoc} */
@Override
public ProtocolEncoder getEncoder(IoSession session) {
return clientEncoder;
}
}