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