/** * Makes <tt>RTCPSRPacket</tt>s for all the RTP streams that we're sending. * * @return a <tt>List</tt> of <tt>RTCPSRPacket</tt> for all the RTP streams that we're sending. */ private Collection<RTCPSRPacket> makeRTCPSRPackets(long time) { Collection<RTCPSRPacket> srPackets = new ArrayList<RTCPSRPacket>(); for (RTPStatsEntry rtpStatsEntry : rtpStatsMap.values()) { int ssrc = rtpStatsEntry.getSsrc(); RemoteClock estimate = remoteClockEstimator.estimate(ssrc, time); if (estimate == null) { // We're not going to go far without an estimate.. continue; } RTCPSRPacket srPacket = new RTCPSRPacket(ssrc, MIN_RTCP_REPORTS_BLOCKS_ARRAY); // Set the NTP timestamp for this SR. long estimatedRemoteTime = estimate.getRemoteTime(); long secs = estimatedRemoteTime / 1000L; double fraction = (estimatedRemoteTime - secs * 1000L) / 1000D; srPacket.ntptimestamplsw = (int) (fraction * 4294967296D); srPacket.ntptimestampmsw = secs; // Set the RTP timestamp. srPacket.rtptimestamp = estimate.getRtpTimestamp(); // Fill-in packet and octet send count. srPacket.packetcount = rtpStatsEntry.getPacketsSent(); srPacket.octetcount = rtpStatsEntry.getBytesSent(); srPackets.add(srPacket); } return srPackets; }
/** * 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; } } }