/******************************************************************************* * Copyright 2015 alladin-IT GmbH * * 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 at.alladin.rmbt.util.net.rtp; import java.nio.ByteOrder; import at.alladin.rmbt.util.ByteUtil; /** * contains basic RTP definitions and oprations (payload types, versions, codecs, exceptions, header generation) * @author lb * */ public class RealtimeTransportProtocol { public static class RtpException extends Exception { /** * */ private static final long serialVersionUID = 1L; public static enum RtpErrorType { PACKET_SIZE_TOO_SMALL, INVALID_HEADER } protected RtpErrorType rtpErrorType; public RtpException(RtpErrorType rtpErrorType) { this.rtpErrorType = rtpErrorType; } public RtpErrorType getRtpErrorType() { return rtpErrorType; } public void setRtpErrorType(RtpErrorType rtpErrorType) { this.rtpErrorType = rtpErrorType; } } public static enum CodecType { AUDIO, VIDEO, BOTH, UNKNOWN } /** * RTP payload types as defined in RFC 3551 * @author lb * */ public static enum PayloadType { UNKNOWN(-1, -1, -1, CodecType.UNKNOWN), PCMU(0, 8000, 1, CodecType.AUDIO), GSM(3, 8000, 1, CodecType.AUDIO), G723(4, 8000, 1, CodecType.AUDIO), DVI4_8(5, 8000, 1, CodecType.AUDIO), DVI4_16(6, 16000, 1, CodecType.AUDIO), LPC(7, 8000, 1, CodecType.AUDIO), PCMA(8, 8000, 1, CodecType.AUDIO), G722(9, 8000, 1, CodecType.AUDIO), L16_1(10, 44100, 2, CodecType.AUDIO), L16_2(11, 44100, 1, CodecType.AUDIO), QCELP(12, 8000, 1, CodecType.AUDIO), CN(13, 8000, 1, CodecType.AUDIO), MPA(14, 90000, 1, CodecType.AUDIO), G728(15, 8000, 1, CodecType.AUDIO), DVI4_11(16, 11025, 1, CodecType.AUDIO), DVI4_22(17, 22050, 1, CodecType.AUDIO), G729(18, 8000, 1, CodecType.AUDIO), G726_40(-1, 8000, 1, CodecType.AUDIO), G726_32(-1, 8000, 1, CodecType.AUDIO), G726_24(-1, 8000, 1, CodecType.AUDIO), G726_16(-1, 8000, 1, CodecType.AUDIO), G729D(-1, 8000, 1, CodecType.AUDIO), G729E(-1, 8000, 1, CodecType.AUDIO), GSM_EFR(-1, 8000, 1, CodecType.AUDIO), L8(-1, -1, -1, CodecType.AUDIO), RED(-1, -1, -1, CodecType.AUDIO), VDVI(-1, -1, 1, CodecType.AUDIO), CELB(25, 90000, -1, CodecType.VIDEO), JPEG(26, 90000, -1, CodecType.VIDEO), NV(28, 90000, -1, CodecType.VIDEO), H261(31, 90000, -1, CodecType.VIDEO), MPV(32, 90000, -1, CodecType.VIDEO), MP2T(33, 90000, -1, CodecType.BOTH), H263(34, 90000, -1, CodecType.VIDEO), H263_1998(-1, 90000, -1, CodecType.VIDEO); protected final int value; protected final int sampleRate; protected final int channels; protected final CodecType codecType; private PayloadType(int value, int sampleRate, int channels, CodecType codecType) { this.value = value; this.sampleRate = sampleRate; this.channels = channels; this.codecType = codecType; } /** * * @return payload type value or -1 if the codec is defined as dynamic (see RFC 3551) */ public int getValue() { return value; } /** * * @return sample rate or -1 if the codec's sample rate is variable or undefined (see RFC 3551) */ public int getSampleRate() { return sampleRate; } /** * * @return number of channels used for this codec or -1 for unknown/special values (see RFC 3551) */ public int getChannels() { return channels; } /** * * @return the codec type (either {@link CodecType#AUDIO} or {@link CodecType#VIDEO}) */ public CodecType getCodecType() { return codecType; } public static PayloadType getByCodecValue(int value) { for (PayloadType p : PayloadType.values()) { if (p.getValue() == value) { return p; } } return UNKNOWN; } public static PayloadType getByCodecValue(int value, PayloadType defaultType) { final PayloadType p = getByCodecValue(value); if (UNKNOWN.equals(p)) { return defaultType; } return p; } } public static enum RtpVersion { VER0(0), VER1(1), VER2(2), UNKNOWN(-1); final int version; RtpVersion(int version) { this.version = version; } public int getVersion() { return version; } public static RtpVersion getByVersion(int version) { for (RtpVersion v : RtpVersion.values()) { if (v.getVersion() == version) { return v; } } return UNKNOWN; } } /** * creates the first 4 bytes of the RTP header * @param version * @param hasPadding * @param hasExtension * @return */ public static byte[] createHeaderBytes(RtpVersion version, boolean hasPadding, boolean hasExtension, int csrcCount, boolean setMarker, PayloadType payloadType, int sequenceNumber, long timeStamp, long ssrc) { byte[] h = new byte[12]; h[0] = ByteUtil.setLeftBitsValue(h[0], 2, version.getVersion()); h[0] = ByteUtil.setBit(h[0], 5, hasPadding); h[0] = ByteUtil.setBit(h[0], 4, hasExtension); h[0] = ByteUtil.setRightBitsValue(h[0], 4, csrcCount); h[1] = ByteUtil.setBit(h[1], 7, setMarker); h[1] = ByteUtil.setRightBitsValue(h[1], 7, payloadType.getValue()); //network byte order = big endian ByteUtil.setInt(h, 2, 3, sequenceNumber, ByteOrder.BIG_ENDIAN); ByteUtil.setLong(h, 4, 7, timeStamp, ByteOrder.BIG_ENDIAN); ByteUtil.setLong(h, 8, 11, ssrc, ByteOrder.BIG_ENDIAN); return h; } /** * * @param csrcIds * @return */ public static byte[] createCsrcIdentifierBytes(long[] csrcIds) { if (csrcIds != null && csrcIds.length > 0) { byte[] h = new byte[csrcIds.length * 4]; for (int i = 0; i < csrcIds.length; i++) { ByteUtil.setLong(h, i*4, 3 + i*4, csrcIds[i], ByteOrder.BIG_ENDIAN); } return h; } return null; } }