/*
* RED5 Open Source Flash Server - http://code.google.com/p/red5/
*
* Copyright 2006-2012 by respective authors (see below). All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.red5.server.net.mrtmp.codec;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.red5.server.net.mrtmp.MRTMPPacket;
import org.red5.server.net.rtmp.message.Packet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author Steven Gong (steven.gong@gmail.com)
*/
public class MRTMPProtocolDecoder implements ProtocolDecoder {
private static final Logger log = LoggerFactory.getLogger(MRTMPProtocolDecoder.class);
public void decode(IoSession session, IoBuffer in,
ProtocolDecoderOutput out) throws Exception {
IoBuffer buffer = (IoBuffer) session.getAttribute("buffer");
if (buffer == null) {
buffer = IoBuffer.allocate(16 * 1024);
buffer.setAutoExpand(true);
session.setAttribute("buffer", buffer);
}
buffer.put(in);
buffer.flip();
while (true) {
if (buffer.remaining() < MRTMPPacket.COMMON_HEADER_LENGTH) {
break;
}
int pos = buffer.position();
MRTMPPacket.Header header = decodeHeader(buffer);
if (header == null) {
buffer.position(pos);
break;
}
if (buffer.remaining() < header.getBodyLength()) {
buffer.position(pos);
break;
}
MRTMPPacket.Body body = decodeBody(buffer, header);
MRTMPPacket packet = new MRTMPPacket();
packet.setHeader(header);
packet.setBody(body);
if (log.isDebugEnabled()) {
log.debug(packet.toString());
}
out.write(packet);
}
buffer.compact();
}
public void dispose(IoSession session) throws Exception {
// nothing to dispose for decoding
}
public void finishDecode(IoSession session, ProtocolDecoderOutput out)
throws Exception {
IoBuffer buffer = (IoBuffer) session.getAttribute("buffer");
if (buffer != null) {
buffer.free();
}
}
public MRTMPPacket.Header decodeHeader(IoBuffer buffer) {
short type = buffer.getShort();
short bodyEncoding = buffer.getShort();
int preserved = buffer.getInt();
int clientId = buffer.getInt();
int headerLength = buffer.getInt();
int bodyLength = buffer.getInt();
if (buffer.remaining() < headerLength - MRTMPPacket.COMMON_HEADER_LENGTH) {
return null;
}
MRTMPPacket.Header header = null;
if (type == MRTMPPacket.RTMP && headerLength != MRTMPPacket.RTMP_HEADER_LENGTH) {
// XXX errrh, something weird happens
log.warn("Codec error: wrong RTMP header length " + headerLength);
header = new MRTMPPacket.Header();
buffer.skip(headerLength - MRTMPPacket.COMMON_HEADER_LENGTH);
} else if (type == MRTMPPacket.RTMP) {
header = new MRTMPPacket.RTMPHeader();
MRTMPPacket.RTMPHeader rtmpHeader = (MRTMPPacket.RTMPHeader) header;
rtmpHeader.setRtmpType(buffer.getInt());
} else {
header = new MRTMPPacket.Header();
buffer.skip(headerLength - MRTMPPacket.COMMON_HEADER_LENGTH);
}
header.setType(type);
header.setBodyEncoding(bodyEncoding);
header.setDynamic((preserved & 0x8000000) != 0);
header.setClientId(clientId);
header.setHeaderLength(headerLength);
header.setBodyLength(bodyLength);
return header;
}
public MRTMPPacket.Body decodeBody(IoBuffer buffer, MRTMPPacket.Header header) {
MRTMPPacket.Body body = null;
switch (header.getType()) {
case MRTMPPacket.CONNECT:
case MRTMPPacket.CLOSE:
if (header.getBodyLength() != 0) {
// XXX something weird happens
log.warn("Codec error: wrong connect/close body length " + header.getBodyLength());
}
return new MRTMPPacket.Body();
case MRTMPPacket.RTMP:
byte[] byteArray = new byte[header.getBodyLength()];
buffer.get(byteArray);
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new ByteArrayInputStream(byteArray));
Packet packet = (Packet) ois.readObject();
body = new MRTMPPacket.RTMPBody();
MRTMPPacket.RTMPBody rtmpBody = (MRTMPPacket.RTMPBody) body;
rtmpBody.setRtmpPacket(packet);
} catch (IOException e) {
// XXX should not happen
log.error("", e);
} catch (ClassNotFoundException e) {
// XXX should not happen
log.error("", e);
}
break;
default:
byteArray = new byte[header.getBodyLength()];
buffer.get(byteArray);
body = new MRTMPPacket.Body();
body.setRawBuf(IoBuffer.wrap(byteArray));
break;
}
return body;
}
}