/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under The MIT license. */ package com.ttProject.rtmp.netty; import java.util.List; import org.apache.log4j.Logger; import com.ttProject.rtmp.decode.Message; import com.ttProject.rtmp.decode.MessageFactory; import com.ttProject.rtmp.header.IRtmpHeader; import com.ttProject.rtmp.message.IRtmpMessage; import com.ttProject.rtmp.message.type.AggregateMessage; import com.ttProject.rtmp.message.type.Amf0Command; import com.ttProject.rtmp.message.type.Amf0DataMessage; import com.ttProject.rtmp.message.type.AudioMessage; import com.ttProject.rtmp.message.type.SetChunkSize; import com.ttProject.rtmp.message.type.SetPeerBandwidth; import com.ttProject.rtmp.message.type.UserControlMessage; import com.ttProject.rtmp.message.type.VideoMessage; import com.ttProject.rtmp.message.type.WindowAcknowledgementSize; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; /** * rtmpDecoder * @author taktod * decode rtmp message into IRtmpMessage object. */ public class RtmpDecoder extends ReplayingDecoder<RtmpDecoder.DecoderStatus> { public static enum DecoderStatus { Header, Body }; /** * constructor */ public RtmpDecoder() { super(DecoderStatus.Header); } private Logger logger = Logger.getLogger(RtmpDecoder.class); private final MessageFactory factory = new MessageFactory(); private Message message; /** * {@inheritDoc} */ @Override protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception { switch(state()) { case Header: message = factory.getMessage(buf); checkpoint(DecoderStatus.Body); break; case Body: int size = factory.getBufSize(message); if(size > buf.readableBytes()) { // need more data for rtmp packet. return; } byte[] bytes = new byte[size]; buf.readBytes(bytes); message.getBody().writeBytes(bytes); checkpoint(DecoderStatus.Header); if(!message.isComplete() || size == 0) { // need more rtmp packet. return; } // get header. IRtmpHeader header = message.getHeader(); // make type message. IRtmpMessage message = null; switch(header.getMessageType()) { case WINDOW_ACKNOWLEDGEMENT_SIZE: message = new WindowAcknowledgementSize(header, this.message.getBody()); break; case SET_PEER_BANDWIDTH: message = new SetPeerBandwidth(header, this.message.getBody()); break; case SET_CHUNK_SIZE: SetChunkSize chunkSize = new SetChunkSize(header, this.message.getBody()); factory.setChunkSize(chunkSize.getChunkSize()); message = chunkSize; break; case AMF0_COMMAND: message = new Amf0Command(header, this.message.getBody()); break; case AMF0_DATA_MESSAGE: message = new Amf0DataMessage(header, this.message.getBody()); break; case USER_CONTROL_MESSAGE: message = new UserControlMessage(header, this.message.getBody()); break; case VIDEO_MESSAGE: message = new VideoMessage(header, this.message.getBody()); break; case AUDIO_MESSAGE: message = new AudioMessage(header, this.message.getBody()); break; case AGGREGATE_MESSAGE: message = new AggregateMessage(header, this.message.getBody()); break; case ABORT_MESSAGE: case ACKNOWLEDGEMENT: case AMF0_SHARED_OBJECT_MESSAGE: case AMF3_COMMAND: case AMF3_DATA_MESSAGE: case AMF3_SHARED_OBJECT_MESSAGE: logger.info("need to make.:" + header.getMessageType()); break; default: throw new RuntimeException("undefined:" + header.getMessageType()); } // if message = null, throws NullPointerException. ctx.fireChannelRead(message); break; } } }