/** * Copyright 2012 Voxbone SA/NV * * 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.ifsoft.sip; /** * Utility functions for Parsing information from RTP packets * */ public class RtpUtil { /* The RTP header has the following format: 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|X| CC |M| PT | sequence number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | timestamp | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | synchronization source (SSRC) identifier | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ | contributing source (CSRC) identifiers | | .... | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ public static void buildRtpHeader(byte [] buffer, int payload, short seq, long timestamp, byte [] ssrc) { buffer[0] = (byte) 0x80; buffer[1] = (byte) payload; setSequenceNumber(buffer, seq); setTimeStamp(buffer, timestamp); setSSRC(buffer, ssrc); } /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P| FMT | PT | length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SSRC of packet sender | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SSRC of media source | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : Feedback Control Information (FCI) : : : Name | Value | Brief Description ----------+-------+------------------------------------ RTPFB | 205 | Transport layer FB message PSFB | 206 | Payload-specific FB message 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SSRC | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Seq nr. | Reserved | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ public static void buildFIR(byte [] buffer, int seq, byte [] senderSsrc, byte [] destSsrc) { byte [] sr ={(byte)0x80,(byte)0xc9,(byte)0x00,(byte)0x01,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x81,(byte)0xca,(byte)0x00,(byte)0x02,(byte)0x00,(byte)0x00,(byte)0x00,(byte)0x01,(byte)0x01,(byte)0x01,(byte)0x31,(byte)0x00}; System.arraycopy(sr, 0, buffer, 0, sr.length); buffer[sr.length+0] = (byte) 0x84; buffer[sr.length+1] = (byte) 206; buffer[sr.length+2] = 0; buffer[sr.length+3] = 4; // packet sender 0001 //System.arraycopy(senderSsrc, 0, buffer, sr.length+4, 4); buffer[sr.length +4+3] = (byte)0x01; // media source keeps 0 //System.arraycopy(senderSsrc, 0, buffer, sr.length+8, 4); System.arraycopy(destSsrc, 0, buffer, sr.length+12, 4); buffer[sr.length+16] = (byte) seq; } public static int getFIRSequence(byte [] buffer, int start, int end) { if(start+1 >= end) { // no FIR return -1; } if(buffer[start+1] == (byte)206 && (buffer[start]&0x0F) == (byte)0x04) { // fir found! return (int)buffer[start+16]&0xFF; } else { // not fir, check next rtcp int length = getShort(buffer, start+2)*4 + 4; return getFIRSequence(buffer, start+length, end); } } public static short getSequenceNumber(byte [] buffer) { return getShort(buffer, 2); } public static void setSequenceNumber(byte [] buffer, short seq) { buffer[3] = (byte) (seq & 0xFF); buffer[2] = (byte) ((seq >> 8) & 0xFF); } public static byte [] getSSRC(byte [] buffer) { byte ssrc[] = new byte[4]; System.arraycopy(buffer, 8, ssrc, 0, 4); return ssrc; } public static void setSSRC(byte [] buffer, byte [] ssrc) { System.arraycopy(ssrc, 0, buffer, 8, 4); } public static long getTimeStamp(byte [] buffer) { long timestamp = 0; timestamp = 0xFF & buffer[4]; timestamp = timestamp << 8; timestamp |= 0xFF & buffer[5]; timestamp = timestamp << 8; timestamp |= 0xFF & buffer[6]; timestamp = timestamp << 8; timestamp |= 0xFF & buffer[7]; return timestamp; } public static void setTimeStamp(byte [] buffer, long timestamp) { buffer[7] = (byte) (timestamp & 0xFF); buffer[6] = (byte) ((timestamp >> 8) & 0xFF); buffer[5] = (byte) ((timestamp >> 16) & 0xFF); buffer[4] = (byte) ((timestamp >> 24) & 0xFF); } public static void setMarker(byte [] buffer, boolean set) { if (set) { buffer[1] |= (0x1 << 7); } else { buffer[1] &= (0x7F); } } public static int getNALType(byte [] input, int start) { return (int) (0x1F & input[start]); } public static short getShort(byte [] buffer, int offset) { int seq = 0; seq = 0xFF & buffer[offset]; seq = seq << 8; seq |= 0xFF & buffer[offset + 1]; return (short) seq; } public static int filterSVC(byte [] input, byte [] output, int input_size) { int output_offset = 12; int input_offset = 12 + 2 + 2 + 8; System.arraycopy(input, 0, output, 0, 12); output[0] &= 0xEF; int nalType = getNALType(input, input_offset); if (nalType == 24) { output[output_offset] = input[input_offset]; input_offset += 1; output_offset += 1; while (input_offset < input_size) { short length = getShort(input, input_offset); nalType = getNALType(input, input_offset + 2); if (nalType != 30 && nalType != 14 && nalType != 20) { System.arraycopy(input, input_offset, output, output_offset, length + 2); input_offset += length + 2; output_offset += length + 2; } else { input_offset += length + 2; } } } else if (nalType != 30 && nalType != 14 && nalType != 20) { System.arraycopy(input, input_offset, output, output_offset, input_size - input_offset); output_offset += input_size - input_offset; } return output_offset; } }