/* * myLib - https://github.com/taktod/myLib * Copyright (c) 2014 ttProject. All rights reserved. * * Licensed under GNU LESSER GENERAL PUBLIC LICENSE Version 3. */ package com.ttProject.flazr.rtmp; import org.jboss.netty.buffer.ChannelBuffer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.flazr.rtmp.RtmpHeader; import com.flazr.rtmp.message.MessageType; import com.flazr.util.Utils; /** * override rtmpHeader * @author taktod */ public class RtmpHeaderEx extends RtmpHeader { @SuppressWarnings("unused") private Logger logger = LoggerFactory.getLogger(RtmpHeaderEx.class); private Type headerType; private int channelId; private int deltaTime; private int time; private int size; private MessageType messageType; private int streamId; public RtmpHeaderEx(ChannelBuffer in, RtmpHeader[] incompleteHeaders) { this(MessageType.CONTROL); final int firstByteInt = in.readByte(); final int typeAndChannel; final int headerTypeInt; if((firstByteInt & 0x3F) == 0) { typeAndChannel = (firstByteInt & 0xFF) << 8 | (in.readByte() & 0xFF); channelId = 64 + (typeAndChannel & 0xFF); headerTypeInt = typeAndChannel >> 14; } else if((firstByteInt & 0x3F) == 1) { typeAndChannel = (firstByteInt & 0xFF) << 16 | (in.readByte() & 0xFF) << 8 | (in.readByte() & 0xFF); channelId = 64 + ((typeAndChannel >> 8) & 0xFF) + ((typeAndChannel & 0xFF) << 8); headerTypeInt = typeAndChannel >> 22; } else { typeAndChannel = firstByteInt & 0xFF; channelId = (typeAndChannel & 0x3F); headerTypeInt = typeAndChannel >> 6; } headerType = Type.valueToEnum(headerTypeInt); final RtmpHeaderEx prevHeader = (RtmpHeaderEx)incompleteHeaders[channelId]; switch(headerType) { case LARGE: time = in.readMedium(); size = in.readMedium(); messageType = MessageType.valueToEnum(in.readByte()); streamId = Utils.readInt32Reverse(in); if(time == MAX_NORMAL_HEADER_TIME) { time = in.readInt(); } break; case MEDIUM: deltaTime = in.readMedium(); size = in.readMedium(); messageType = MessageType.valueToEnum(in.readByte()); streamId = prevHeader.streamId; if(deltaTime == MAX_NORMAL_HEADER_TIME) { deltaTime = in.readInt(); } break; case SMALL: deltaTime = in.readMedium(); size = prevHeader.size; messageType = prevHeader.messageType; streamId = prevHeader.streamId; if(deltaTime == MAX_NORMAL_HEADER_TIME) { deltaTime = in.readInt(); } break; case TINY: headerType = prevHeader.headerType; // preserve original time = prevHeader.time; deltaTime = prevHeader.deltaTime; size = prevHeader.size; messageType = prevHeader.messageType; streamId = prevHeader.streamId; break; } } public RtmpHeaderEx(MessageType messageType, int time, int size) { this(messageType); this.time = time; this.size = size; } public RtmpHeaderEx(MessageType messageType) { super(messageType); this.messageType = messageType; headerType = Type.LARGE; channelId = messageType.getDefaultChannelId(); } @Override public boolean isMedia() { switch(messageType) { case AUDIO: case VIDEO: case AGGREGATE: return true; default: return false; } } @Override public boolean isMetadata() { return messageType == MessageType.METADATA_AMF0 || messageType == MessageType.METADATA_AMF3; } @Override public boolean isAggregate() { return messageType == MessageType.AGGREGATE; } @Override public boolean isAudio() { return messageType == MessageType.AUDIO; } @Override public boolean isVideo() { return messageType == MessageType.VIDEO; } @Override public boolean isLarge() { return headerType == Type.LARGE; } @Override public boolean isControl() { return messageType == MessageType.CONTROL; } @Override public boolean isChunkSize() { return messageType == MessageType.CHUNK_SIZE; } @Override public Type getHeaderType() { return headerType; } @Override public void setHeaderType(Type headerType) { this.headerType = headerType; } @Override public int getChannelId() { return channelId; } @Override public void setChannelId(int channelId) { this.channelId = channelId; } @Override public int getTime() { return time; } @Override public void setTime(int time) { this.time = time; } @Override public int getDeltaTime() { return deltaTime; } @Override public void setDeltaTime(int deltaTime) { this.deltaTime = deltaTime; } @Override public void setSize(int size) { this.size = size; } @Override public int getSize() { return size; } @Override public MessageType getMessageType() { return messageType; } @Override public void setMessageType(MessageType messageType) { this.messageType = messageType; } @Override public int getStreamId() { return streamId; } @Override public void setStreamId(int streamId) { this.streamId = streamId; } public void encode(ChannelBuffer out) { int val = 0; switch(headerType) { default: case LARGE: val = 0; break; case MEDIUM: val = 1; break; case SMALL: val = 2; break; case TINY: val = 3; break; } out.writeBytes(encodeHeaderTypeAndChannel(val, channelId)); if(headerType == Type.TINY) { return; } final boolean extendedTime; if(headerType == Type.LARGE) { extendedTime = time >= MAX_NORMAL_HEADER_TIME; } else { extendedTime = deltaTime >= MAX_NORMAL_HEADER_TIME; } if(extendedTime) { out.writeMedium(MAX_NORMAL_HEADER_TIME); } else { // LARGE / MEDIUM / SMALL out.writeMedium(headerType == Type.LARGE ? time : deltaTime); } if(headerType != Type.SMALL) { out.writeMedium(size); // LARGE / MEDIUM out.writeByte((byte) messageType.intValue()); // LARGE / MEDIUM if(headerType == Type.LARGE) { Utils.writeInt32Reverse(out, streamId); // LARGE } } if(extendedTime) { out.writeInt(headerType == Type.LARGE ? time : deltaTime); } } public byte[] getTinyHeader() { return encodeHeaderTypeAndChannel(Type.TINY.intValue(), channelId); } private static byte[] encodeHeaderTypeAndChannel(final int headerType, final int channelId) { if (channelId <= 63) { return new byte[] {(byte) ((headerType << 6) + channelId)}; } else if (channelId <= 320) { return new byte[] {(byte) (headerType << 6), (byte) (channelId - 64)}; } else { return new byte[] {(byte) ((headerType << 6) | 1), (byte) ((channelId - 64) & 0xff), (byte) ((channelId - 64) >> 8)}; } } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append('[').append(headerType.ordinal()); sb.append(' ').append(messageType); sb.append(" c").append(channelId); sb.append(" #").append(streamId); sb.append(" t").append(time); sb.append(" (").append(deltaTime); sb.append(") s").append(size); sb.append(']'); return sb.toString(); } }