/** * Allocates new focus for given MUC room. * * @param room the name of MUC room for which new conference has to be allocated. * @param properties configuration properties map included in the request. * @return <tt>true</tt> if conference focus is in the room and ready to handle session * participants. * @throws Exception if for any reason we have failed to create the conference */ public synchronized boolean conferenceRequest(String room, Map<String, String> properties) throws Exception { if (StringUtils.isNullOrEmpty(room)) return false; if (shutdownInProgress && !conferences.containsKey(room)) return false; if (!conferences.containsKey(room)) { createConference(room, properties); } JitsiMeetConference conference = conferences.get(room); return conference.isInTheRoom(); }
/** * Removes the RTP-NTP mapping for a given SSRC. * * @param ssrc the SSRC for which to remove the RTP-NTP mapping */ void removeMapping(long ssrc) { if (ssrcs.containsKey(ssrc)) { synchronized (ssrcs) { SSRCDesc ssrcDesc = ssrcs.get(ssrc); if (ssrcDesc != null) { synchronized (ssrcDesc) { ssrcDesc.ntpTime = -1.0; ssrcDesc.rtpTime = -1; } } } } }
/** * Opens new WebRTC data channel using specified parameters. * * @param type channel type as defined in control protocol description. Use 0 for "reliable". * @param prio channel priority. The higher the number, the lower the priority. * @param reliab Reliability Parameter<br> * This field is ignored if a reliable channel is used. If a partial reliable channel with * limited number of retransmissions is used, this field specifies the number of * retransmissions. If a partial reliable channel with limited lifetime is used, this field * specifies the maximum lifetime in milliseconds. The following table summarizes this:<br> * </br> * <p>+------------------------------------------------+------------------+ | Channel Type | * Reliability | | | Parameter | * +------------------------------------------------+------------------+ | * DATA_CHANNEL_RELIABLE | Ignored | | DATA_CHANNEL_RELIABLE_UNORDERED | Ignored | | * DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT | Number of RTX | | * DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED | Number of RTX | | * DATA_CHANNEL_PARTIAL_RELIABLE_TIMED | Lifetime in ms | | * DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED | Lifetime in ms | * +------------------------------------------------+------------------+ * @param sid SCTP stream id that will be used by new channel (it must not be already used). * @param label text label for the channel. * @return new instance of <tt>WebRtcDataStream</tt> that represents opened WebRTC data channel. * @throws IOException if IO error occurs. */ public synchronized WebRtcDataStream openChannel( int type, int prio, long reliab, int sid, String label) throws IOException { if (channels.containsKey(sid)) { throw new IOException("Channel on sid: " + sid + " already exists"); } // Label Length & Label byte[] labelBytes; int labelByteLength; if (label == null) { labelBytes = null; labelByteLength = 0; } else { labelBytes = label.getBytes("UTF-8"); labelByteLength = labelBytes.length; if (labelByteLength > 0xFFFF) labelByteLength = 0xFFFF; } // Protocol Length & Protocol String protocol = WEBRTC_DATA_CHANNEL_PROTOCOL; byte[] protocolBytes; int protocolByteLength; if (protocol == null) { protocolBytes = null; protocolByteLength = 0; } else { protocolBytes = protocol.getBytes("UTF-8"); protocolByteLength = protocolBytes.length; if (protocolByteLength > 0xFFFF) protocolByteLength = 0xFFFF; } ByteBuffer packet = ByteBuffer.allocate(12 + labelByteLength + protocolByteLength); // Message open new channel on current sid // Message Type packet.put((byte) MSG_OPEN_CHANNEL); // Channel Type packet.put((byte) type); // Priority packet.putShort((short) prio); // Reliability Parameter packet.putInt((int) reliab); // Label Length packet.putShort((short) labelByteLength); // Protocol Length packet.putShort((short) protocolByteLength); // Label if (labelByteLength != 0) { packet.put(labelBytes, 0, labelByteLength); } // Protocol if (protocolByteLength != 0) { packet.put(protocolBytes, 0, protocolByteLength); } int sentCount = sctpSocket.send(packet.array(), true, sid, WEB_RTC_PPID_CTRL); if (sentCount != packet.capacity()) { throw new IOException("Failed to open new chanel on sid: " + sid); } WebRtcDataStream channel = new WebRtcDataStream(sctpSocket, sid, label, false); channels.put(sid, channel); return channel; }
/** * Handles control packet. * * @param data raw packet data that arrived on control PPID. * @param sid SCTP stream id on which the data has arrived. */ private synchronized void onCtrlPacket(byte[] data, int sid) throws IOException { ByteBuffer buffer = ByteBuffer.wrap(data); int messageType = /* 1 byte unsigned integer */ 0xFF & buffer.get(); if (messageType == MSG_CHANNEL_ACK) { if (logger.isDebugEnabled()) { logger.debug(getEndpoint().getID() + " ACK received SID: " + sid); } // Open channel ACK WebRtcDataStream channel = channels.get(sid); if (channel != null) { // Ack check prevents from firing multiple notifications // if we get more than one ACKs (by mistake/bug). if (!channel.isAcknowledged()) { channel.ackReceived(); notifyChannelOpened(channel); } else { logger.warn("Redundant ACK received for SID: " + sid); } } else { logger.error("No channel exists on sid: " + sid); } } else if (messageType == MSG_OPEN_CHANNEL) { int channelType = /* 1 byte unsigned integer */ 0xFF & buffer.get(); int priority = /* 2 bytes unsigned integer */ 0xFFFF & buffer.getShort(); long reliability = /* 4 bytes unsigned integer */ 0xFFFFFFFFL & buffer.getInt(); int labelLength = /* 2 bytes unsigned integer */ 0xFFFF & buffer.getShort(); int protocolLength = /* 2 bytes unsigned integer */ 0xFFFF & buffer.getShort(); String label; String protocol; if (labelLength == 0) { label = ""; } else { byte[] labelBytes = new byte[labelLength]; buffer.get(labelBytes); label = new String(labelBytes, "UTF-8"); } if (protocolLength == 0) { protocol = ""; } else { byte[] protocolBytes = new byte[protocolLength]; buffer.get(protocolBytes); protocol = new String(protocolBytes, "UTF-8"); } if (logger.isDebugEnabled()) { logger.debug( "!!! " + getEndpoint().getID() + " data channel open request on SID: " + sid + " type: " + channelType + " prio: " + priority + " reliab: " + reliability + " label: " + label + " proto: " + protocol); } if (channels.containsKey(sid)) { logger.error("Channel on sid: " + sid + " already exists"); } WebRtcDataStream newChannel = new WebRtcDataStream(sctpSocket, sid, label, true); channels.put(sid, newChannel); sendOpenChannelAck(sid); notifyChannelOpened(newChannel); } else { logger.error("Unexpected ctrl msg type: " + messageType); } }
/** * Inspect an <tt>RTCPCompoundPacket</tt> and build-up the state for future estimations. * * @param pkt */ public void apply(RTCPCompoundPacket pkt) { if (pkt == null || pkt.packets == null || pkt.packets.length == 0) { return; } for (RTCPPacket rtcpPacket : pkt.packets) { switch (rtcpPacket.type) { case RTCPPacket.SR: RTCPSRPacket srPacket = (RTCPSRPacket) rtcpPacket; // The media sender SSRC. int ssrc = srPacket.ssrc; // Convert 64-bit NTP timestamp to Java standard time. // Note that java time (milliseconds) by definition has // less precision then NTP time (picoseconds) so // converting NTP timestamp to java time and back to NTP // timestamp loses precision. For example, Tue, Dec 17 // 2002 09:07:24.810 EST is represented by a single // Java-based time value of f22cd1fc8a, but its NTP // equivalent are all values ranging from // c1a9ae1c.cf5c28f5 to c1a9ae1c.cf9db22c. // Use round-off on fractional part to preserve going to // lower precision long fraction = Math.round(1000D * srPacket.ntptimestamplsw / 0x100000000L); /* * If the most significant bit (MSB) on the seconds * field is set we use a different time base. The * following text is a quote from RFC-2030 (SNTP v4): * * If bit 0 is set, the UTC time is in the range * 1968-2036 and UTC time is reckoned from 0h 0m 0s UTC * on 1 January 1900. If bit 0 is not set, the time is * in the range 2036-2104 and UTC time is reckoned from * 6h 28m 16s UTC on 7 February 2036. */ long msb = srPacket.ntptimestampmsw & 0x80000000L; long remoteTime = (msb == 0) // use base: 7-Feb-2036 @ 06:28:16 UTC ? msb0baseTime + (srPacket.ntptimestampmsw * 1000) + fraction // use base: 1-Jan-1900 @ 01:00:00 UTC : msb1baseTime + (srPacket.ntptimestampmsw * 1000) + fraction; // Estimate the clock rate of the sender. int frequencyHz = -1; if (receivedClocks.containsKey(ssrc)) { // Calculate the clock rate. ReceivedRemoteClock oldStats = receivedClocks.get(ssrc); RemoteClock oldRemoteClock = oldStats.getRemoteClock(); frequencyHz = Math.round( (float) (((int) srPacket.rtptimestamp - oldRemoteClock.getRtpTimestamp()) & 0xffffffffl) / (remoteTime - oldRemoteClock.getRemoteTime())); } // Replace whatever was in there before. receivedClocks.put( ssrc, new ReceivedRemoteClock( ssrc, remoteTime, (int) srPacket.rtptimestamp, frequencyHz)); break; case RTCPPacket.SDES: break; } } }