src/jlibrtp/RtcpPktPSFB.java
changeset 13 e684f11070d5
equal deleted inserted replaced
12:c9ff263c29ad 13:e684f11070d5
       
     1 /**
       
     2  * Java RTP Library (jlibrtp)
       
     3  * Copyright (C) 2006 Arne Kepp
       
     4  * 
       
     5  * This library is free software; you can redistribute it and/or
       
     6  * modify it under the terms of the GNU Lesser General Public
       
     7  * License as published by the Free Software Foundation; either
       
     8  * version 2.1 of the License, or (at your option) any later version.
       
     9  *
       
    10  * This library is distributed in the hope that it will be useful,
       
    11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    13  * Lesser General Public License for more details.
       
    14  * 
       
    15  * You should have received a copy of the GNU Lesser General Public
       
    16  * License along with this library; if not, write to the Free Software
       
    17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
       
    18  */
       
    19 package jlibrtp;
       
    20 
       
    21 /**
       
    22  * RTCP packets for Payload-Specific Feedback Messages 
       
    23  * 
       
    24  * @author Arne Kepp
       
    25  */
       
    26 public class RtcpPktPSFB extends RtcpPkt {
       
    27 	/** If this packet was for a different SSRC */
       
    28 	protected boolean notRelevant = false;
       
    29 	/** Parent RTP Session */
       
    30 	private RTPSession rtpSession;
       
    31 	/** SSRC we are sending feeback to */
       
    32 	protected long ssrcMediaSource = -1;
       
    33 	 
       
    34 	/** SLI macroblock (MB) address of the first lost macroblock number */
       
    35 	protected int[] sliFirst;
       
    36 	/** SLI number of lost macroblocks */
       
    37 	protected int[] sliNumber;
       
    38 	/** SLI six least significant bits of the codec-specific identifier */
       
    39 	protected int[] sliPictureId;
       
    40 	
       
    41 	// Picture loss indication
       
    42 	/** RPSI number of padded bits at end of bitString */
       
    43 	protected int rpsiPadding = -1;
       
    44 	/** RPSI payloadType RTP payload type */
       
    45 	protected int rpsiPayloadType = -1;
       
    46 	/** RPSI information as natively defined by the video codec */
       
    47 	protected byte[] rpsiBitString;
       
    48 	
       
    49 	/** Application Layer Feedback Message */
       
    50 	protected byte[] alfBitString;
       
    51 	
       
    52 	/**
       
    53 	 * Generic constructor, then call make<something>
       
    54 	 * 
       
    55 	 * @param ssrcPacketSender
       
    56 	 * @param ssrcMediaSource
       
    57 	 */
       
    58 	protected RtcpPktPSFB(long ssrcPacketSender, long ssrcMediaSource) {
       
    59 		super.ssrc = ssrcPacketSender;
       
    60 		this.ssrcMediaSource = ssrcMediaSource;
       
    61 		super.packetType = 206; //PSFB
       
    62 	}
       
    63 	
       
    64 	/**
       
    65 	 * Make this packet a Picture loss indication
       
    66 	 */
       
    67 	protected void makePictureLossIndication() {
       
    68 		super.itemCount = 1; //FMT
       
    69 	}
       
    70 	
       
    71 	/**
       
    72 	 * Make this packet a Slice Loss Indication
       
    73 	 * 
       
    74 	 * @param sliFirst macroblock (MB) address of the first lost macroblock
       
    75 	 * @param sliNumber number of lost macroblocks
       
    76 	 * @param sliPictureId six least significant bits of the codec-specific identifier
       
    77 	 */
       
    78 	protected void makeSliceLossIndication(int[] sliFirst, int[] sliNumber, int[] sliPictureId) {
       
    79 		super.itemCount = 2; //FMT
       
    80 		this.sliFirst = sliFirst;
       
    81 		this.sliNumber = sliNumber;
       
    82 		this.sliPictureId = sliPictureId;
       
    83 	}
       
    84 	
       
    85 	/**
       
    86 	 * Make this packet a Reference Picture Selection Indication
       
    87 	 * 
       
    88 	 * @param bitPadding number of padded bits at end of bitString
       
    89 	 * @param payloadType RTP payload type for codec
       
    90 	 * @param bitString RPSI information as natively defined by the video codec
       
    91 	 */
       
    92 	protected void makeRefPictureSelIndic(int bitPadding, int payloadType, byte[] bitString) {
       
    93 		super.itemCount = 3; //FMT
       
    94 		this.rpsiPadding = bitPadding;
       
    95 		this.rpsiPayloadType = payloadType;
       
    96 		this.rpsiBitString = bitString;
       
    97 	}
       
    98 	
       
    99 	/** 
       
   100 	 * Make this packet an Application specific feedback message
       
   101 	 * 
       
   102 	 * @param bitString the original application message
       
   103 	 */
       
   104 	protected void makeAppLayerFeedback(byte[] bitString) {
       
   105 		super.itemCount = 15; //FMT
       
   106 		this.alfBitString = bitString; 
       
   107 	}
       
   108 	
       
   109 	/**
       
   110 	 * Constructor that parses a raw packet to retrieve information
       
   111 	 * 
       
   112 	 * @param aRawPkt the raw packet to be parsed
       
   113 	 * @param start the start of the packet, in bytes
       
   114 	 * @param rtpSession the session on which the callback interface resides
       
   115 	 */
       
   116 	protected RtcpPktPSFB(byte[] aRawPkt, int start, RTPSession rtpSession) {		
       
   117 		if(RTPSession.rtpDebugLevel > 8) {
       
   118 			System.out.println("  -> RtcpPktPSFB(byte[], int start)");
       
   119 		}
       
   120 		this.rtpSession = rtpSession;
       
   121 		
       
   122 		rawPkt = aRawPkt;
       
   123 
       
   124 		if(! super.parseHeaders(start) || packetType != 206 || super.length < 2 ) {
       
   125 			if(RTPSession.rtpDebugLevel > 2) {
       
   126 				System.out.println(" <-> RtcpPktRTPFB.parseHeaders() etc. problem");
       
   127 			}
       
   128 			super.problem = -206;
       
   129 		} else {
       
   130 			//FMT = super.itemCount;
       
   131 			ssrcMediaSource = StaticProcs.bytesToUIntLong(aRawPkt,8+start);
       
   132 			
       
   133 			if(ssrcMediaSource == rtpSession.ssrc) {
       
   134 				super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt,4+start);
       
   135 				
       
   136 				switch(super.itemCount) {
       
   137 				case 1: // Picture Loss Indication 
       
   138 					decPictureLossIndic();
       
   139 					break; 
       
   140 				case 2: // Slice Loss Indication
       
   141 					decSliceLossIndic(aRawPkt, start + 12); 
       
   142 					break;
       
   143 				case 3: // Reference Picture Selection Indication 
       
   144 					decRefPictureSelIndic(aRawPkt, start + 12); 
       
   145 					break;
       
   146 				case 15: // Application Layer Feedback Messages
       
   147 					decAppLayerFB(aRawPkt, start + 12); 
       
   148 					break;
       
   149 				default: 
       
   150 					System.out.println("!!!! RtcpPktPSFB(byte[], int start) unexpected FMT " + super.itemCount);
       
   151 				}
       
   152 			} else {
       
   153 				this.notRelevant = true;
       
   154 			}
       
   155 		}
       
   156 		if(RTPSession.rtpDebugLevel > 8) {
       
   157 			System.out.println("  <- RtcpPktPSFB()");
       
   158 		}
       
   159 	}
       
   160 	
       
   161 	/**
       
   162 	 * Decode Picture Loss indication
       
   163 	 *
       
   164 	 */
       
   165 	private void decPictureLossIndic() {
       
   166 		if(this.rtpSession.rtcpAVPFIntf != null) {
       
   167 			this.rtpSession.rtcpAVPFIntf.PSFBPktPictureLossReceived(
       
   168 					super.ssrc);
       
   169 		}
       
   170 	}
       
   171 	
       
   172 	/**
       
   173 	 * Decode Slice Loss Indication
       
   174 	 * 
       
   175 	 * @param aRawPkt
       
   176 	 * @param start
       
   177 	 */
       
   178 	private void decSliceLossIndic(byte[] aRawPkt, int start) {	
       
   179 		// 13 bit off-boundary numbers? That's rather cruel
       
   180 		//    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
       
   181 		//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   182 		//   |            First        |        Number           | PictureID |
       
   183 		//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   184 				
       
   185 		int count = super.length - 2;
       
   186 		
       
   187 		sliFirst = new int[count];
       
   188 		sliNumber = new int[count];
       
   189 		sliPictureId = new int[count];
       
   190 		
       
   191 		// Loop over the FCI lines
       
   192 		for(int i=0; i < count; i++) {
       
   193 			sliFirst[i] = StaticProcs.bytesToUIntInt(aRawPkt, start) >> 3;
       
   194 			sliNumber[i] = (int) (StaticProcs.bytesToUIntInt(aRawPkt, start) & 0x0007FFC0) >> 6;
       
   195 			sliPictureId[i] = (StaticProcs.bytesToUIntInt(aRawPkt, start + 2) & 0x003F);
       
   196 			start += 4;
       
   197 		}
       
   198 		
       
   199 		if(this.rtpSession.rtcpAVPFIntf != null) {
       
   200 			this.rtpSession.rtcpAVPFIntf.PSFBPktSliceLossIndic(
       
   201 					super.ssrc,
       
   202 					sliFirst, sliNumber, sliPictureId);
       
   203 		}
       
   204 	}
       
   205 	
       
   206 	/**
       
   207 	 * Decode Reference Picture Selection Indication
       
   208 	 * 
       
   209 	 * @param aRawPkt
       
   210 	 * @param start
       
   211 	 */
       
   212 	private void decRefPictureSelIndic(byte[] aRawPkt, int start) {	
       
   213 		//  0                   1                   2                   3
       
   214 		//  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
       
   215 		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   216 		// |      PB       |0| Payload Type|    Native RPSI bit string     |
       
   217 		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   218 		// |   defined per codec          ...                | Padding (0) |
       
   219 		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   220 		
       
   221 		rpsiPadding = aRawPkt[start];
       
   222 		
       
   223 		if(rpsiPadding  > 32) {
       
   224 			System.out.println("!!!! RtcpPktPSFB.decRefPictureSelcIndic paddingBits: " 
       
   225 					+ rpsiPadding);
       
   226 		}
       
   227 		
       
   228 		rpsiPayloadType = (int) rawPkt[start];
       
   229 		if(rpsiPayloadType < 0) {
       
   230 			System.out.println("!!!! RtcpPktPSFB.decRefPictureSelcIndic 8th bit not zero: " 
       
   231 					+ rpsiPayloadType);
       
   232 		}
       
   233 		
       
   234 		rpsiBitString = new byte[(super.length - 2)*4 - 2];
       
   235 		System.arraycopy(aRawPkt, start + 2, rpsiBitString, 0, rpsiBitString.length);
       
   236 		
       
   237 		if(this.rtpSession.rtcpAVPFIntf != null) {
       
   238 			this.rtpSession.rtcpAVPFIntf.PSFBPktRefPictureSelIndic(
       
   239 					super.ssrc,
       
   240 					rpsiPayloadType, rpsiBitString, rpsiPadding);
       
   241 		}
       
   242 		
       
   243 	}
       
   244 	
       
   245 	/**
       
   246 	 * Decode Application specific feedback message
       
   247 	 * 
       
   248 	 * @param aRawPkt
       
   249 	 * @param start
       
   250 	 */
       
   251 	private void decAppLayerFB(byte[] aRawPkt, int start) {	
       
   252 		//Application Message (FCI): variable length
       
   253 		int stringLength = (super.length - 2)*4;
       
   254 		
       
   255 		alfBitString = new byte[stringLength];
       
   256 		
       
   257 		System.arraycopy(aRawPkt, start, alfBitString, 0, stringLength);
       
   258 
       
   259 		if(this.rtpSession.rtcpAVPFIntf != null) {
       
   260 			this.rtpSession.rtcpAVPFIntf.PSFBPktAppLayerFBReceived(
       
   261 					super.ssrc, alfBitString);
       
   262 		}
       
   263 	}
       
   264 	
       
   265 
       
   266 	
       
   267 	/**
       
   268 	 * Encode a Slice Loss Indication
       
   269 	 */
       
   270 	private void encSliceLossIndic() {
       
   271 		byte[] firstBytes;
       
   272 		byte[] numbBytes;
       
   273 		byte[] picBytes;
       
   274 		
       
   275 		int offset = 8;
       
   276 		// Loop over the FCI lines
       
   277 		for(int i=0; i < sliFirst.length; i++) {
       
   278 			offset = 8 + 8*i;
       
   279 			firstBytes = StaticProcs.uIntLongToByteWord(sliFirst[i] << 3);
       
   280 			numbBytes = StaticProcs.uIntLongToByteWord(sliNumber[i] << 2);
       
   281 			picBytes = StaticProcs.uIntIntToByteWord(sliPictureId[i]);
       
   282 			
       
   283 			super.rawPkt[offset] = firstBytes[2];
       
   284 			super.rawPkt[offset+1] = (byte) (firstBytes[3] | numbBytes[2]);
       
   285 			super.rawPkt[offset+2] = numbBytes[3];
       
   286 			super.rawPkt[offset+3] = (byte) (numbBytes[3] | picBytes[1]);
       
   287 		}
       
   288 	}
       
   289 	
       
   290 	/**
       
   291 	 * Encode a Reference Picture Selection Indication
       
   292 	 *
       
   293 	 */
       
   294 	private void encRefPictureSelIndic() {	
       
   295 		byte[] someBytes;
       
   296 		someBytes = StaticProcs.uIntIntToByteWord(rpsiPadding);
       
   297 		super.rawPkt[8] = someBytes[1];
       
   298 		someBytes = StaticProcs.uIntIntToByteWord(rpsiPayloadType);
       
   299 		super.rawPkt[9] = someBytes[1];
       
   300 		
       
   301 		System.arraycopy(rpsiBitString, 0, super.rawPkt, 10, rpsiBitString.length);
       
   302 	}
       
   303 	
       
   304 	
       
   305 	/**
       
   306 	 * Encode Application Layer Feedback
       
   307 	 *
       
   308 	 */
       
   309 	private void encAppLayerFB() {	
       
   310 		//Application Message (FCI): variable length
       
   311 		System.arraycopy(alfBitString, 0, super.rawPkt, 8, alfBitString.length);
       
   312 	}
       
   313 	
       
   314 	/** 
       
   315 	 * Get the FMT (Feedback Message Type)
       
   316 	 * @return value stored in .itemcount, same field
       
   317 	 */
       
   318 	protected int getFMT() {
       
   319 		return this.itemCount;
       
   320 	}
       
   321 	
       
   322 	/**
       
   323 	 * Encode the packet into a byte[], saved in .rawPkt
       
   324 	 * 
       
   325 	 * CompRtcpPkt will call this automatically
       
   326 	 */
       
   327 	protected void encode() {
       
   328 		switch(super.itemCount) {
       
   329 		case 1: // Picture Loss Indication 
       
   330 			//Nothing to do really
       
   331 			super.rawPkt = new byte[24];
       
   332 			break; 
       
   333 		case 2: // Slice Loss Indication
       
   334 			super.rawPkt = new byte[24 + 4*this.sliFirst.length];
       
   335 			encSliceLossIndic(); 
       
   336 			break;
       
   337 		case 3: // Reference Picture Selection Indication
       
   338 			super.rawPkt = new byte[24 + 2 + this.rpsiBitString.length/4];
       
   339 			encRefPictureSelIndic();
       
   340 			break;
       
   341 		case 15: // Application Layer Feedback Messages
       
   342 			super.rawPkt = new byte[24 + this.alfBitString.length/4];
       
   343 			encAppLayerFB();
       
   344 			break;
       
   345 		}
       
   346 		
       
   347 		byte[] someBytes = StaticProcs.uIntLongToByteWord(super.ssrc);
       
   348 		System.arraycopy(someBytes, 0, super.rawPkt, 4, 4);
       
   349 		someBytes = StaticProcs.uIntLongToByteWord(this.ssrcMediaSource);
       
   350 		System.arraycopy(someBytes, 0, super.rawPkt, 8, 4);
       
   351 		
       
   352 		writeHeaders();
       
   353 	}
       
   354 
       
   355 	/**
       
   356 	 * Debug purposes only
       
   357 	 */
       
   358 	public void debugPrint() {
       
   359 		System.out.println("->RtcpPktPSFB.debugPrint() ");
       
   360 		
       
   361 		String str;
       
   362 		switch(super.itemCount) {
       
   363 		case 1: // Picture Loss Indication 
       
   364 			System.out.println("  FMT: Picture Loss Indication");
       
   365 			break; 
       
   366 		case 2: // Slice Loss Indication
       
   367 			if(sliFirst != null) {
       
   368 				str = "sliFirst[].length: " + sliFirst.length;
       
   369 			} else {
       
   370 				str = "sliFirst[] is null";
       
   371 			}
       
   372 			System.out.println("  FMT: Slice Loss Indication, " + str);
       
   373 			break;
       
   374 		case 3: // Reference Picture Selection Indication
       
   375 			if(rpsiBitString != null) {
       
   376 				str = "rpsiBitString[].length: " + rpsiBitString.length;
       
   377 			} else {
       
   378 				str = "rpsiBitString[] is null";
       
   379 			}
       
   380 			System.out.println("  FMT: Reference Picture Selection Indication, " 
       
   381 					+ str + " payloadType: " + this.rpsiPayloadType);
       
   382 			break;
       
   383 		case 15: // Application Layer Feedback Messages
       
   384 			if(alfBitString != null) {
       
   385 				str = "alfBitString[].length: " + alfBitString.length;
       
   386 			} else {
       
   387 				str = "alfBitString[] is null";
       
   388 			}
       
   389 			System.out.println("  FMT: Application Layer Feedback Messages, " + str);
       
   390 			break;
       
   391 		}
       
   392 
       
   393 		System.out.println("<-RtcpPktPSFB.debugPrint() ");
       
   394 	}
       
   395 }