src/jlibrtp/RtcpPktPSFB.java
changeset 823 2036ebfaccda
equal deleted inserted replaced
536:537ddd8aa407 823:2036ebfaccda
       
     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
       
    75 	 *            macroblock (MB) address of the first lost macroblock
       
    76 	 * @param sliNumber
       
    77 	 *            number of lost macroblocks
       
    78 	 * @param sliPictureId
       
    79 	 *            six least significant bits of the codec-specific identifier
       
    80 	 */
       
    81 	protected void makeSliceLossIndication(int[] sliFirst, int[] sliNumber,
       
    82 			int[] sliPictureId) {
       
    83 		super.itemCount = 2; // FMT
       
    84 		this.sliFirst = sliFirst;
       
    85 		this.sliNumber = sliNumber;
       
    86 		this.sliPictureId = sliPictureId;
       
    87 	}
       
    88 
       
    89 	/**
       
    90 	 * Make this packet a Reference Picture Selection Indication
       
    91 	 * 
       
    92 	 * @param bitPadding
       
    93 	 *            number of padded bits at end of bitString
       
    94 	 * @param payloadType
       
    95 	 *            RTP payload type for codec
       
    96 	 * @param bitString
       
    97 	 *            RPSI information as natively defined by the video codec
       
    98 	 */
       
    99 	protected void makeRefPictureSelIndic(int bitPadding, int payloadType,
       
   100 			byte[] bitString) {
       
   101 		super.itemCount = 3; // FMT
       
   102 		this.rpsiPadding = bitPadding;
       
   103 		this.rpsiPayloadType = payloadType;
       
   104 		this.rpsiBitString = bitString;
       
   105 	}
       
   106 
       
   107 	/**
       
   108 	 * Make this packet an Application specific feedback message
       
   109 	 * 
       
   110 	 * @param bitString
       
   111 	 *            the original application message
       
   112 	 */
       
   113 	protected void makeAppLayerFeedback(byte[] bitString) {
       
   114 		super.itemCount = 15; // FMT
       
   115 		this.alfBitString = bitString;
       
   116 	}
       
   117 
       
   118 	/**
       
   119 	 * Constructor that parses a raw packet to retrieve information
       
   120 	 * 
       
   121 	 * @param aRawPkt
       
   122 	 *            the raw packet to be parsed
       
   123 	 * @param start
       
   124 	 *            the start of the packet, in bytes
       
   125 	 * @param rtpSession
       
   126 	 *            the session on which the callback interface resides
       
   127 	 */
       
   128 	protected RtcpPktPSFB(byte[] aRawPkt, int start, RTPSession rtpSession) {
       
   129 		if (RTPSession.rtpDebugLevel > 8) {
       
   130 			System.out.println("  -> RtcpPktPSFB(byte[], int start)");
       
   131 		}
       
   132 		this.rtpSession = rtpSession;
       
   133 
       
   134 		rawPkt = aRawPkt;
       
   135 
       
   136 		if (!super.parseHeaders(start) || packetType != 206 || super.length < 2) {
       
   137 			if (RTPSession.rtpDebugLevel > 2) {
       
   138 				System.out
       
   139 						.println(" <-> RtcpPktRTPFB.parseHeaders() etc. problem");
       
   140 			}
       
   141 			super.problem = -206;
       
   142 		} else {
       
   143 			// FMT = super.itemCount;
       
   144 			ssrcMediaSource = StaticProcs.bytesToUIntLong(aRawPkt, 8 + start);
       
   145 
       
   146 			if (ssrcMediaSource == rtpSession.ssrc) {
       
   147 				super.ssrc = StaticProcs.bytesToUIntLong(aRawPkt, 4 + start);
       
   148 
       
   149 				switch (super.itemCount) {
       
   150 				case 1: // Picture Loss Indication
       
   151 					decPictureLossIndic();
       
   152 					break;
       
   153 				case 2: // Slice Loss Indication
       
   154 					decSliceLossIndic(aRawPkt, start + 12);
       
   155 					break;
       
   156 				case 3: // Reference Picture Selection Indication
       
   157 					decRefPictureSelIndic(aRawPkt, start + 12);
       
   158 					break;
       
   159 				case 15: // Application Layer Feedback Messages
       
   160 					decAppLayerFB(aRawPkt, start + 12);
       
   161 					break;
       
   162 				default:
       
   163 					System.out
       
   164 							.println("!!!! RtcpPktPSFB(byte[], int start) unexpected FMT "
       
   165 									+ super.itemCount);
       
   166 				}
       
   167 			} else {
       
   168 				this.notRelevant = true;
       
   169 			}
       
   170 		}
       
   171 		if (RTPSession.rtpDebugLevel > 8) {
       
   172 			System.out.println("  <- RtcpPktPSFB()");
       
   173 		}
       
   174 	}
       
   175 
       
   176 	/**
       
   177 	 * Decode Picture Loss indication
       
   178 	 * 
       
   179 	 */
       
   180 	private void decPictureLossIndic() {
       
   181 		if (this.rtpSession.rtcpAVPFIntf != null) {
       
   182 			this.rtpSession.rtcpAVPFIntf.PSFBPktPictureLossReceived(super.ssrc);
       
   183 		}
       
   184 	}
       
   185 
       
   186 	/**
       
   187 	 * Decode Slice Loss Indication
       
   188 	 * 
       
   189 	 * @param aRawPkt
       
   190 	 * @param start
       
   191 	 */
       
   192 	private void decSliceLossIndic(byte[] aRawPkt, int start) {
       
   193 		// 13 bit off-boundary numbers? That's rather cruel
       
   194 		// 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
       
   195 		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   196 		// | First | Number | PictureID |
       
   197 		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   198 
       
   199 		int count = super.length - 2;
       
   200 
       
   201 		sliFirst = new int[count];
       
   202 		sliNumber = new int[count];
       
   203 		sliPictureId = new int[count];
       
   204 
       
   205 		// Loop over the FCI lines
       
   206 		for (int i = 0; i < count; i++) {
       
   207 			sliFirst[i] = StaticProcs.bytesToUIntInt(aRawPkt, start) >> 3;
       
   208 			sliNumber[i] = (int) (StaticProcs.bytesToUIntInt(aRawPkt, start) & 0x0007FFC0) >> 6;
       
   209 			sliPictureId[i] = (StaticProcs.bytesToUIntInt(aRawPkt, start + 2) & 0x003F);
       
   210 			start += 4;
       
   211 		}
       
   212 
       
   213 		if (this.rtpSession.rtcpAVPFIntf != null) {
       
   214 			this.rtpSession.rtcpAVPFIntf.PSFBPktSliceLossIndic(super.ssrc,
       
   215 					sliFirst, sliNumber, sliPictureId);
       
   216 		}
       
   217 	}
       
   218 
       
   219 	/**
       
   220 	 * Decode Reference Picture Selection Indication
       
   221 	 * 
       
   222 	 * @param aRawPkt
       
   223 	 * @param start
       
   224 	 */
       
   225 	private void decRefPictureSelIndic(byte[] aRawPkt, int start) {
       
   226 		// 0 1 2 3
       
   227 		// 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
       
   228 		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   229 		// | PB |0| Payload Type| Native RPSI bit string |
       
   230 		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   231 		// | defined per codec ... | Padding (0) |
       
   232 		// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
       
   233 
       
   234 		rpsiPadding = aRawPkt[start];
       
   235 
       
   236 		if (rpsiPadding > 32) {
       
   237 			System.out
       
   238 					.println("!!!! RtcpPktPSFB.decRefPictureSelcIndic paddingBits: "
       
   239 							+ rpsiPadding);
       
   240 		}
       
   241 
       
   242 		rpsiPayloadType = (int) rawPkt[start];
       
   243 		if (rpsiPayloadType < 0) {
       
   244 			System.out
       
   245 					.println("!!!! RtcpPktPSFB.decRefPictureSelcIndic 8th bit not zero: "
       
   246 							+ rpsiPayloadType);
       
   247 		}
       
   248 
       
   249 		rpsiBitString = new byte[(super.length - 2) * 4 - 2];
       
   250 		System.arraycopy(aRawPkt, start + 2, rpsiBitString, 0,
       
   251 				rpsiBitString.length);
       
   252 
       
   253 		if (this.rtpSession.rtcpAVPFIntf != null) {
       
   254 			this.rtpSession.rtcpAVPFIntf.PSFBPktRefPictureSelIndic(super.ssrc,
       
   255 					rpsiPayloadType, rpsiBitString, rpsiPadding);
       
   256 		}
       
   257 
       
   258 	}
       
   259 
       
   260 	/**
       
   261 	 * Decode Application specific feedback message
       
   262 	 * 
       
   263 	 * @param aRawPkt
       
   264 	 * @param start
       
   265 	 */
       
   266 	private void decAppLayerFB(byte[] aRawPkt, int start) {
       
   267 		// Application Message (FCI): variable length
       
   268 		int stringLength = (super.length - 2) * 4;
       
   269 
       
   270 		alfBitString = new byte[stringLength];
       
   271 
       
   272 		System.arraycopy(aRawPkt, start, alfBitString, 0, stringLength);
       
   273 
       
   274 		if (this.rtpSession.rtcpAVPFIntf != null) {
       
   275 			this.rtpSession.rtcpAVPFIntf.PSFBPktAppLayerFBReceived(super.ssrc,
       
   276 					alfBitString);
       
   277 		}
       
   278 	}
       
   279 
       
   280 	/**
       
   281 	 * Encode a Slice Loss Indication
       
   282 	 */
       
   283 	private void encSliceLossIndic() {
       
   284 		byte[] firstBytes;
       
   285 		byte[] numbBytes;
       
   286 		byte[] picBytes;
       
   287 
       
   288 		int offset = 8;
       
   289 		// Loop over the FCI lines
       
   290 		for (int i = 0; i < sliFirst.length; i++) {
       
   291 			offset = 8 + 8 * i;
       
   292 			firstBytes = StaticProcs.uIntLongToByteWord(sliFirst[i] << 3);
       
   293 			numbBytes = StaticProcs.uIntLongToByteWord(sliNumber[i] << 2);
       
   294 			picBytes = StaticProcs.uIntIntToByteWord(sliPictureId[i]);
       
   295 
       
   296 			super.rawPkt[offset] = firstBytes[2];
       
   297 			super.rawPkt[offset + 1] = (byte) (firstBytes[3] | numbBytes[2]);
       
   298 			super.rawPkt[offset + 2] = numbBytes[3];
       
   299 			super.rawPkt[offset + 3] = (byte) (numbBytes[3] | picBytes[1]);
       
   300 		}
       
   301 	}
       
   302 
       
   303 	/**
       
   304 	 * Encode a Reference Picture Selection Indication
       
   305 	 * 
       
   306 	 */
       
   307 	private void encRefPictureSelIndic() {
       
   308 		byte[] someBytes;
       
   309 		someBytes = StaticProcs.uIntIntToByteWord(rpsiPadding);
       
   310 		super.rawPkt[8] = someBytes[1];
       
   311 		someBytes = StaticProcs.uIntIntToByteWord(rpsiPayloadType);
       
   312 		super.rawPkt[9] = someBytes[1];
       
   313 
       
   314 		System.arraycopy(rpsiBitString, 0, super.rawPkt, 10,
       
   315 				rpsiBitString.length);
       
   316 	}
       
   317 
       
   318 	/**
       
   319 	 * Encode Application Layer Feedback
       
   320 	 * 
       
   321 	 */
       
   322 	private void encAppLayerFB() {
       
   323 		// Application Message (FCI): variable length
       
   324 		System.arraycopy(alfBitString, 0, super.rawPkt, 8, alfBitString.length);
       
   325 	}
       
   326 
       
   327 	/**
       
   328 	 * Get the FMT (Feedback Message Type)
       
   329 	 * 
       
   330 	 * @return value stored in .itemcount, same field
       
   331 	 */
       
   332 	protected int getFMT() {
       
   333 		return this.itemCount;
       
   334 	}
       
   335 
       
   336 	/**
       
   337 	 * Encode the packet into a byte[], saved in .rawPkt
       
   338 	 * 
       
   339 	 * CompRtcpPkt will call this automatically
       
   340 	 */
       
   341 	protected void encode() {
       
   342 		switch (super.itemCount) {
       
   343 		case 1: // Picture Loss Indication
       
   344 			// Nothing to do really
       
   345 			super.rawPkt = new byte[24];
       
   346 			break;
       
   347 		case 2: // Slice Loss Indication
       
   348 			super.rawPkt = new byte[24 + 4 * this.sliFirst.length];
       
   349 			encSliceLossIndic();
       
   350 			break;
       
   351 		case 3: // Reference Picture Selection Indication
       
   352 			super.rawPkt = new byte[24 + 2 + this.rpsiBitString.length / 4];
       
   353 			encRefPictureSelIndic();
       
   354 			break;
       
   355 		case 15: // Application Layer Feedback Messages
       
   356 			super.rawPkt = new byte[24 + this.alfBitString.length / 4];
       
   357 			encAppLayerFB();
       
   358 			break;
       
   359 		}
       
   360 
       
   361 		byte[] someBytes = StaticProcs.uIntLongToByteWord(super.ssrc);
       
   362 		System.arraycopy(someBytes, 0, super.rawPkt, 4, 4);
       
   363 		someBytes = StaticProcs.uIntLongToByteWord(this.ssrcMediaSource);
       
   364 		System.arraycopy(someBytes, 0, super.rawPkt, 8, 4);
       
   365 
       
   366 		writeHeaders();
       
   367 	}
       
   368 
       
   369 	/**
       
   370 	 * Debug purposes only
       
   371 	 */
       
   372 	public void debugPrint() {
       
   373 		System.out.println("->RtcpPktPSFB.debugPrint() ");
       
   374 
       
   375 		String str;
       
   376 		switch (super.itemCount) {
       
   377 		case 1: // Picture Loss Indication
       
   378 			System.out.println("  FMT: Picture Loss Indication");
       
   379 			break;
       
   380 		case 2: // Slice Loss Indication
       
   381 			if (sliFirst != null) {
       
   382 				str = "sliFirst[].length: " + sliFirst.length;
       
   383 			} else {
       
   384 				str = "sliFirst[] is null";
       
   385 			}
       
   386 			System.out.println("  FMT: Slice Loss Indication, " + str);
       
   387 			break;
       
   388 		case 3: // Reference Picture Selection Indication
       
   389 			if (rpsiBitString != null) {
       
   390 				str = "rpsiBitString[].length: " + rpsiBitString.length;
       
   391 			} else {
       
   392 				str = "rpsiBitString[] is null";
       
   393 			}
       
   394 			System.out
       
   395 					.println("  FMT: Reference Picture Selection Indication, "
       
   396 							+ str + " payloadType: " + this.rpsiPayloadType);
       
   397 			break;
       
   398 		case 15: // Application Layer Feedback Messages
       
   399 			if (alfBitString != null) {
       
   400 				str = "alfBitString[].length: " + alfBitString.length;
       
   401 			} else {
       
   402 				str = "alfBitString[] is null";
       
   403 			}
       
   404 			System.out.println("  FMT: Application Layer Feedback Messages, "
       
   405 					+ str);
       
   406 			break;
       
   407 		}
       
   408 
       
   409 		System.out.println("<-RtcpPktPSFB.debugPrint() ");
       
   410 	}
       
   411 }