private MediaFormat[] getEncodings() { if (encodings != null) return encodings; MediaFormat[] availableEncodings = encodingConfiguration.getAllEncodings(type); int encodingCount = availableEncodings.length; if (encodingCount < 1) encodings = MediaUtils.EMPTY_MEDIA_FORMATS; else { /* * The MediaFormats will be displayed by encoding (name) and clock * rate and EncodingConfiguration will store them that way so this * TableModel should better display unique encoding-clock rate * pairs. */ HashMap<String, MediaFormat> availableEncodingSet = new HashMap<String, MediaFormat>(); for (MediaFormat availableEncoding : availableEncodings) { availableEncodingSet.put( availableEncoding.getEncoding() + "/" + availableEncoding.getClockRateString(), availableEncoding); } availableEncodings = availableEncodingSet.values().toArray(MediaUtils.EMPTY_MEDIA_FORMATS); encodingCount = availableEncodings.length; encodings = new MediaFormat[encodingCount]; System.arraycopy(availableEncodings, 0, encodings, 0, encodingCount); // Display the encodings in decreasing priority. Arrays.sort( encodings, 0, encodingCount, new Comparator<MediaFormat>() { public int compare(MediaFormat format0, MediaFormat format1) { int ret = encodingConfiguration.getPriority(format1) - encodingConfiguration.getPriority(format0); if (ret == 0) { /* * In the cases of equal priorities, display them * sorted by encoding name in increasing order. */ ret = format0.getEncoding().compareToIgnoreCase(format1.getEncoding()); if (ret == 0) { /* * In the cases of equal priorities and equal * encoding names, display them sorted by clock * rate in decreasing order. */ ret = Double.compare(format1.getClockRate(), format0.getClockRate()); } } return ret; } }); } return encodings; }
public int write(byte[] buffer, int offset, int length, boolean transform) { RawPacket pkt = rawPacketArray[0]; if (pkt == null) pkt = new RawPacket(); rawPacketArray[0] = pkt; byte[] pktBuf = pkt.getBuffer(); if (pktBuf == null || pktBuf.length < length) { pktBuf = new byte[length]; pkt.setBuffer(pktBuf); } System.arraycopy(buffer, offset, pktBuf, 0, length); pkt.setOffset(0); pkt.setLength(length); if (transform) { PacketTransformer packetTransformer = isControlStream ? rtcpPacketTransformer : rtpPacketTransformer; if (packetTransformer != null) rawPacketArray = packetTransformer.reverseTransform(rawPacketArray); } SourceTransferHandler transferHandler; PushSourceStream pushSourceStream; try { if (isControlStream) { transferHandler = controlTransferHandler; pushSourceStream = getControlInputStream(); } else { transferHandler = dataTransferHandler; pushSourceStream = getDataInputStream(); } } catch (IOException ioe) { throw new UndeclaredThrowableException(ioe); } for (int i = 0; i < rawPacketArray.length; i++) { RawPacket packet = rawPacketArray[i]; // keep the first element for reuse if (i != 0) rawPacketArray[i] = null; if (packet != null) { if (isControlStream) pendingControlPacket = packet; else pendingDataPacket = packet; if (transferHandler != null) { transferHandler.transferData(pushSourceStream); } } } return length; }
/** * Adds a specific (RTP) SSRC to the list of SSRCs seen/received on this <tt>Channel</tt>. * Invoked by the Jitsi Videobridge server, not its clients. * * @param ssrc the (RTP) SSRC to be added to the list of SSRCs seen/received on this * <tt>Channel</tt> * @return <tt>true</tt> if the list of SSRCs seen/received on this <tt>Channel</tt> has been * modified as part of the method call; otherwise, <tt>false</tt> */ public synchronized boolean addSSRC(int ssrc) { // contains for (int i = 0; i < ssrcs.length; i++) if (ssrcs[i] == ssrc) return false; // add int[] newSSRCs = new int[ssrcs.length + 1]; System.arraycopy(ssrcs, 0, newSSRCs, 0, ssrcs.length); newSSRCs[ssrcs.length] = ssrc; ssrcs = newSSRCs; return true; }
/** * Encapsulates {@code pkt} in the RTX format, using {@code rtxSsrc} as its SSRC, and transmits it * to {@link #channel} by injecting it in the {@code MediaStream}. * * @param pkt the packet to transmit. * @param rtxSsrc the SSRC for the RTX stream. * @return {@code true} if the packet was successfully retransmitted, {@code false} otherwise. */ private boolean encapsulateInRtxAndTransmit(RawPacket pkt, long rtxSsrc) { byte[] buf = pkt.getBuffer(); int len = pkt.getLength(); int off = pkt.getOffset(); byte[] newBuf = buf; if (buf.length < len + 2) { // FIXME The byte array newly allocated and assigned to newBuf must // be made known to pkt eventually. newBuf = new byte[len + 2]; } int osn = pkt.getSequenceNumber(); int headerLength = pkt.getHeaderLength(); int payloadLength = len - headerLength; System.arraycopy(buf, off, newBuf, 0, headerLength); // FIXME If newBuf is actually buf, then we will override the first two // bytes of the payload bellow. newBuf[headerLength] = (byte) ((osn >> 8) & 0xff); newBuf[headerLength + 1] = (byte) (osn & 0xff); System.arraycopy(buf, off + headerLength, newBuf, headerLength + 2, payloadLength); // FIXME We tried to extend the payload of pkt by two bytes above but // we never told pkt that its length has increased by these two bytes. MediaStream mediaStream = channel.getStream(); if (mediaStream != null) { pkt.setSSRC((int) rtxSsrc); // Only call getNextRtxSequenceNumber() when we're sure we're going // to transmit a packet, because it consumes a sequence number. pkt.setSequenceNumber(getNextRtxSequenceNumber(rtxSsrc)); try { mediaStream.injectPacket(pkt, /* data */ true, /* after */ null); } catch (TransmissionFailedException tfe) { logger.warn("Failed to transmit an RTX packet."); return false; } } return true; }
/** * Encapsulates {@code pkt} in the RTX format, using {@code rtxSsrc} as its SSRC, and transmits it * to {@link #channel} by injecting it in the {@code MediaStream}. * * @param pkt the packet to transmit. * @param rtxSsrc the SSRC for the RTX stream. * @param after the {@code TransformEngine} in the chain of {@code TransformEngine}s of the * associated {@code MediaStream} after which the injection of {@code pkt} is to begin * @return {@code true} if the packet was successfully retransmitted, {@code false} otherwise. */ private boolean encapsulateInRtxAndTransmit(RawPacket pkt, long rtxSsrc, TransformEngine after) { byte[] buf = pkt.getBuffer(); int len = pkt.getLength(); int off = pkt.getOffset(); byte[] newBuf = new byte[len + 2]; RawPacket rtxPkt = new RawPacket(newBuf, 0, len + 2); int osn = pkt.getSequenceNumber(); int headerLength = pkt.getHeaderLength(); int payloadLength = pkt.getPayloadLength(); // Copy the header. System.arraycopy(buf, off, newBuf, 0, headerLength); // Set the OSN field. newBuf[headerLength] = (byte) ((osn >> 8) & 0xff); newBuf[headerLength + 1] = (byte) (osn & 0xff); // Copy the payload. System.arraycopy(buf, off + headerLength, newBuf, headerLength + 2, payloadLength); MediaStream mediaStream = channel.getStream(); if (mediaStream != null) { rtxPkt.setSSRC((int) rtxSsrc); rtxPkt.setPayloadType(rtxPayloadType); // Only call getNextRtxSequenceNumber() when we're sure we're going // to transmit a packet, because it consumes a sequence number. rtxPkt.setSequenceNumber(getNextRtxSequenceNumber(rtxSsrc)); try { mediaStream.injectPacket(rtxPkt, /* data */ true, after); } catch (TransmissionFailedException tfe) { logger.warn("Failed to transmit an RTX packet."); return false; } } return true; }
/** * Removes a specific (RTP) SSRC from the list of SSRCs seen/received on this <tt>Channel</tt>. * Invoked by the Jitsi Videobridge server, not its clients. * * @param ssrc the (RTP) SSRC to be removed from the list of SSRCs seen/received on this * <tt>Channel</tt> * @return <tt>true</tt> if the list of SSRCs seen/received on this <tt>Channel</tt> has been * modified as part of the method call; otherwise, <tt>false</tt> */ public synchronized boolean removeSSRC(int ssrc) { if (ssrcs.length == 1) { if (ssrcs[0] == ssrc) { ssrcs = NO_SSRCS; return true; } else return false; } else { for (int i = 0; i < ssrcs.length; i++) { if (ssrcs[i] == ssrc) { int[] newSSRCs = new int[ssrcs.length - 1]; if (i != 0) System.arraycopy(ssrcs, 0, newSSRCs, 0, i); if (i != newSSRCs.length) { System.arraycopy(ssrcs, i + 1, newSSRCs, i, newSSRCs.length - i); } ssrcs = newSSRCs; return true; } } return false; } }
@Override public int read(byte[] buffer, int offset, int length) throws IOException { RawPacket pendingPacket; if (isControlStream) { pendingPacket = pendingControlPacket; } else { pendingPacket = pendingDataPacket; } int bytesToRead = 0; byte[] pendingPacketBuffer = pendingPacket.getBuffer(); if (pendingPacketBuffer != null) { int pendingPacketLength = pendingPacket.getLength(); bytesToRead = length > pendingPacketLength ? pendingPacketLength : length; System.arraycopy( pendingPacketBuffer, pendingPacket.getOffset(), buffer, offset, bytesToRead); } return bytesToRead; }
/** * Removes the RTX encapsulation from a packet. * * @param pkt the packet to remove the RTX encapsulation from. * @return the original media packet represented by {@code pkt}, or null if we couldn't * reconstruct the original packet. */ private RawPacket deRtx(RawPacket pkt) { boolean success = false; if (pkt.getPayloadLength() - pkt.getPaddingSize() < 2) { // We need at least 2 bytes to read the OSN field. if (logger.isDebugEnabled()) { logger.debug("Dropping an incoming RTX packet with padding only: " + pkt); } return null; } long mediaSsrc = getPrimarySsrc(pkt); if (mediaSsrc != -1) { if (rtxAssociatedPayloadType != -1) { int osn = pkt.getOriginalSequenceNumber(); // Remove the RTX header by moving the RTP header two bytes // right. byte[] buf = pkt.getBuffer(); int off = pkt.getOffset(); System.arraycopy(buf, off, buf, off + 2, pkt.getHeaderLength()); pkt.setOffset(off + 2); pkt.setLength(pkt.getLength() - 2); pkt.setSSRC((int) mediaSsrc); pkt.setSequenceNumber(osn); pkt.setPayloadType(rtxAssociatedPayloadType); success = true; } else { logger.warn( "RTX packet received, but no APT is defined. Packet " + "SSRC " + pkt.getSSRCAsLong() + ", associated media" + " SSRC " + mediaSsrc); } } // If we failed to handle the RTX packet, drop it. return success ? pkt : null; }
/** * Creates a {@code RawPacket} which represents the original packet encapsulated in {@code pkt} * using the RTX format. * * @param pkt the packet from which to extract a media packet. * @return the extracted media packet. */ private RawPacket createMediaPacket(RawPacket pkt) { RawPacket mediaPacket = null; long rtxSsrc = pkt.getSSRC() & 0xffffffffL; // We need to know the SSRC paired with rtxSsrc *as seen by the // receiver (i.e. this.channel)*. However, we only store SSRCs // that endpoints *send* with. // We therefore assume that SSRC re-writing has not introduced any // new SSRCs and therefor the FID mappings known to the senders // also apply to receivers. RtpChannel sourceChannel = channel.getContent().findChannelByFidSsrc(rtxSsrc); if (sourceChannel != null) { long mediaSsrc = sourceChannel.getFidPairedSsrc(rtxSsrc); if (mediaSsrc != -1) { byte apt = sourceChannel.getRtxAssociatedPayloadType(); if (apt != -1) { mediaPacket = new RawPacket(pkt.getBuffer().clone(), pkt.getOffset(), pkt.getLength()); // Remove the RTX header by moving the RTP header two bytes // right. byte[] buf = mediaPacket.getBuffer(); int off = mediaPacket.getOffset(); System.arraycopy(buf, off, buf, off + 2, mediaPacket.getHeaderLength()); mediaPacket.setOffset(off + 2); mediaPacket.setLength(pkt.getLength() - 2); mediaPacket.setSSRC((int) mediaSsrc); mediaPacket.setSequenceNumber(pkt.getOriginalSequenceNumber()); mediaPacket.setPayloadType(apt); } } } return mediaPacket; }
/** * Makes <tt>RTCPRRPacket</tt>s using information in FMJ. * * @param time * @return A <tt>Collection</tt> of <tt>RTCPRRPacket</tt>s to inject to the <tt>MediaStream</tt>. */ private Collection<RTCPRRPacket> makeRTCPRRPackets(long time) { RTCPReportBlock[] reportBlocks = makeRTCPReportBlocks(time); if (reportBlocks == null || reportBlocks.length == 0) { return null; } Collection<RTCPRRPacket> rrPackets = new ArrayList<RTCPRRPacket>(); // We use the stream's local source ID (SSRC) as the SSRC of packet // sender. long streamSSRC = getLocalSSRC(); // Since a maximum of 31 reception report blocks will fit in an SR // or RR packet, additional RR packets SHOULD be stacked after the // initial SR or RR packet as needed to contain the reception // reports for all sources heard during the interval since the last // report. if (reportBlocks.length > MAX_RTCP_REPORT_BLOCKS) { for (int offset = 0; offset < reportBlocks.length; offset += MAX_RTCP_REPORT_BLOCKS) { RTCPReportBlock[] blocks = (reportBlocks.length - offset < MAX_RTCP_REPORT_BLOCKS) ? new RTCPReportBlock[reportBlocks.length - offset] : MAX_RTCP_REPORT_BLOCKS_ARRAY; System.arraycopy(reportBlocks, offset, blocks, 0, blocks.length); RTCPRRPacket rr = new RTCPRRPacket((int) streamSSRC, blocks); rrPackets.add(rr); } } else { RTCPRRPacket rr = new RTCPRRPacket((int) streamSSRC, reportBlocks); rrPackets.add(rr); } return rrPackets; }
/** * Creates the video advanced settings. * * @return video advanced settings panel. */ private static Component createVideoAdvancedSettings() { ResourceManagementService resources = NeomediaActivator.getResources(); final DeviceConfiguration deviceConfig = mediaService.getDeviceConfiguration(); TransparentPanel centerPanel = new TransparentPanel(new GridBagLayout()); centerPanel.setMaximumSize(new Dimension(WIDTH, 150)); JButton resetDefaultsButton = new JButton(resources.getI18NString("impl.media.configform.VIDEO_RESET")); JPanel resetButtonPanel = new TransparentPanel(new FlowLayout(FlowLayout.RIGHT)); resetButtonPanel.add(resetDefaultsButton); final JPanel centerAdvancedPanel = new TransparentPanel(new BorderLayout()); centerAdvancedPanel.add(centerPanel, BorderLayout.NORTH); centerAdvancedPanel.add(resetButtonPanel, BorderLayout.SOUTH); GridBagConstraints constraints = new GridBagConstraints(); constraints.fill = GridBagConstraints.HORIZONTAL; constraints.anchor = GridBagConstraints.NORTHWEST; constraints.insets = new Insets(5, 5, 0, 0); constraints.gridx = 0; constraints.weightx = 0; constraints.weighty = 0; constraints.gridy = 0; centerPanel.add( new JLabel(resources.getI18NString("impl.media.configform.VIDEO_RESOLUTION")), constraints); constraints.gridy = 1; constraints.insets = new Insets(0, 0, 0, 0); final JCheckBox frameRateCheck = new SIPCommCheckBox(resources.getI18NString("impl.media.configform.VIDEO_FRAME_RATE")); centerPanel.add(frameRateCheck, constraints); constraints.gridy = 2; constraints.insets = new Insets(5, 5, 0, 0); centerPanel.add( new JLabel(resources.getI18NString("impl.media.configform.VIDEO_PACKETS_POLICY")), constraints); constraints.weightx = 1; constraints.gridx = 1; constraints.gridy = 0; constraints.insets = new Insets(5, 0, 0, 5); Object[] resolutionValues = new Object[DeviceConfiguration.SUPPORTED_RESOLUTIONS.length + 1]; System.arraycopy( DeviceConfiguration.SUPPORTED_RESOLUTIONS, 0, resolutionValues, 1, DeviceConfiguration.SUPPORTED_RESOLUTIONS.length); final JComboBox sizeCombo = new JComboBox(resolutionValues); sizeCombo.setRenderer(new ResolutionCellRenderer()); sizeCombo.setEditable(false); centerPanel.add(sizeCombo, constraints); // default value is 20 final JSpinner frameRate = new JSpinner(new SpinnerNumberModel(20, 5, 30, 1)); frameRate.addChangeListener( new ChangeListener() { public void stateChanged(ChangeEvent e) { deviceConfig.setFrameRate( ((SpinnerNumberModel) frameRate.getModel()).getNumber().intValue()); } }); constraints.gridy = 1; constraints.insets = new Insets(0, 0, 0, 5); centerPanel.add(frameRate, constraints); frameRateCheck.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { if (frameRateCheck.isSelected()) { deviceConfig.setFrameRate( ((SpinnerNumberModel) frameRate.getModel()).getNumber().intValue()); } else // unlimited framerate deviceConfig.setFrameRate(-1); frameRate.setEnabled(frameRateCheck.isSelected()); } }); final JSpinner videoMaxBandwidth = new JSpinner( new SpinnerNumberModel(deviceConfig.getVideoMaxBandwidth(), 1, Integer.MAX_VALUE, 1)); videoMaxBandwidth.addChangeListener( new ChangeListener() { public void stateChanged(ChangeEvent e) { deviceConfig.setVideoMaxBandwidth( ((SpinnerNumberModel) videoMaxBandwidth.getModel()).getNumber().intValue()); } }); constraints.gridx = 1; constraints.gridy = 2; constraints.insets = new Insets(0, 0, 5, 5); centerPanel.add(videoMaxBandwidth, constraints); resetDefaultsButton.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { // reset to defaults sizeCombo.setSelectedIndex(0); frameRateCheck.setSelected(false); frameRate.setEnabled(false); frameRate.setValue(20); // unlimited framerate deviceConfig.setFrameRate(-1); videoMaxBandwidth.setValue(DeviceConfiguration.DEFAULT_VIDEO_MAX_BANDWIDTH); } }); // load selected value or auto Dimension videoSize = deviceConfig.getVideoSize(); if ((videoSize.getHeight() != DeviceConfiguration.DEFAULT_VIDEO_HEIGHT) && (videoSize.getWidth() != DeviceConfiguration.DEFAULT_VIDEO_WIDTH)) sizeCombo.setSelectedItem(deviceConfig.getVideoSize()); else sizeCombo.setSelectedIndex(0); sizeCombo.addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { Dimension selectedVideoSize = (Dimension) sizeCombo.getSelectedItem(); if (selectedVideoSize == null) { // the auto value, default one selectedVideoSize = new Dimension( DeviceConfiguration.DEFAULT_VIDEO_WIDTH, DeviceConfiguration.DEFAULT_VIDEO_HEIGHT); } deviceConfig.setVideoSize(selectedVideoSize); } }); frameRateCheck.setSelected( deviceConfig.getFrameRate() != DeviceConfiguration.DEFAULT_VIDEO_FRAMERATE); frameRate.setEnabled(frameRateCheck.isSelected()); if (frameRate.isEnabled()) frameRate.setValue(deviceConfig.getFrameRate()); return centerAdvancedPanel; }
/** * Initializes a new <tt>SRTPTransformer</tt> instance with a specific (negotiated) * <tt>SRTPProtectionProfile</tt> and the keying material specified by a specific * <tt>TlsContext</tt>. * * @param srtpProtectionProfile the (negotiated) <tt>SRTPProtectionProfile</tt> to initialize the * new instance with * @param tlsContext the <tt>TlsContext</tt> which represents the keying material * @return a new <tt>SRTPTransformer</tt> instance initialized with <tt>srtpProtectionProfile</tt> * and <tt>tlsContext</tt> */ private SinglePacketTransformer initializeSRTPTransformer( int srtpProtectionProfile, TlsContext tlsContext) { boolean rtcp; switch (componentID) { case Component.RTCP: rtcp = true; break; case Component.RTP: rtcp = false; break; default: throw new IllegalStateException("componentID"); } int cipher_key_length; int cipher_salt_length; int cipher; int auth_function; int auth_key_length; int RTCP_auth_tag_length, RTP_auth_tag_length; switch (srtpProtectionProfile) { case SRTPProtectionProfile.SRTP_AES128_CM_HMAC_SHA1_32: cipher_key_length = 128 / 8; cipher_salt_length = 112 / 8; cipher = SRTPPolicy.AESCM_ENCRYPTION; auth_function = SRTPPolicy.HMACSHA1_AUTHENTICATION; auth_key_length = 160 / 8; RTCP_auth_tag_length = 80 / 8; RTP_auth_tag_length = 32 / 8; break; case SRTPProtectionProfile.SRTP_AES128_CM_HMAC_SHA1_80: cipher_key_length = 128 / 8; cipher_salt_length = 112 / 8; cipher = SRTPPolicy.AESCM_ENCRYPTION; auth_function = SRTPPolicy.HMACSHA1_AUTHENTICATION; auth_key_length = 160 / 8; RTCP_auth_tag_length = RTP_auth_tag_length = 80 / 8; break; case SRTPProtectionProfile.SRTP_NULL_HMAC_SHA1_32: cipher_key_length = 0; cipher_salt_length = 0; cipher = SRTPPolicy.NULL_ENCRYPTION; auth_function = SRTPPolicy.HMACSHA1_AUTHENTICATION; auth_key_length = 160 / 8; RTCP_auth_tag_length = 80 / 8; RTP_auth_tag_length = 32 / 8; break; case SRTPProtectionProfile.SRTP_NULL_HMAC_SHA1_80: cipher_key_length = 0; cipher_salt_length = 0; cipher = SRTPPolicy.NULL_ENCRYPTION; auth_function = SRTPPolicy.HMACSHA1_AUTHENTICATION; auth_key_length = 160 / 8; RTCP_auth_tag_length = RTP_auth_tag_length = 80 / 8; break; default: throw new IllegalArgumentException("srtpProtectionProfile"); } byte[] keyingMaterial = tlsContext.exportKeyingMaterial( ExporterLabel.dtls_srtp, null, 2 * (cipher_key_length + cipher_salt_length)); byte[] client_write_SRTP_master_key = new byte[cipher_key_length]; byte[] server_write_SRTP_master_key = new byte[cipher_key_length]; byte[] client_write_SRTP_master_salt = new byte[cipher_salt_length]; byte[] server_write_SRTP_master_salt = new byte[cipher_salt_length]; byte[][] keyingMaterialValues = { client_write_SRTP_master_key, server_write_SRTP_master_key, client_write_SRTP_master_salt, server_write_SRTP_master_salt }; for (int i = 0, keyingMaterialOffset = 0; i < keyingMaterialValues.length; i++) { byte[] keyingMaterialValue = keyingMaterialValues[i]; System.arraycopy( keyingMaterial, keyingMaterialOffset, keyingMaterialValue, 0, keyingMaterialValue.length); keyingMaterialOffset += keyingMaterialValue.length; } SRTPPolicy srtcpPolicy = new SRTPPolicy( cipher, cipher_key_length, auth_function, auth_key_length, RTCP_auth_tag_length, cipher_salt_length); SRTPPolicy srtpPolicy = new SRTPPolicy( cipher, cipher_key_length, auth_function, auth_key_length, RTP_auth_tag_length, cipher_salt_length); SRTPContextFactory clientSRTPContextFactory = new SRTPContextFactory( /* sender */ tlsContext instanceof TlsClientContext, client_write_SRTP_master_key, client_write_SRTP_master_salt, srtpPolicy, srtcpPolicy); SRTPContextFactory serverSRTPContextFactory = new SRTPContextFactory( /* sender */ tlsContext instanceof TlsServerContext, server_write_SRTP_master_key, server_write_SRTP_master_salt, srtpPolicy, srtcpPolicy); SRTPContextFactory forwardSRTPContextFactory; SRTPContextFactory reverseSRTPContextFactory; if (tlsContext instanceof TlsClientContext) { forwardSRTPContextFactory = clientSRTPContextFactory; reverseSRTPContextFactory = serverSRTPContextFactory; } else if (tlsContext instanceof TlsServerContext) { forwardSRTPContextFactory = serverSRTPContextFactory; reverseSRTPContextFactory = clientSRTPContextFactory; } else { throw new IllegalArgumentException("tlsContext"); } SinglePacketTransformer srtpTransformer; if (rtcp) { srtpTransformer = new SRTCPTransformer(forwardSRTPContextFactory, reverseSRTPContextFactory); } else { srtpTransformer = new SRTPTransformer(forwardSRTPContextFactory, reverseSRTPContextFactory); } return srtpTransformer; }