/** * Returns the sequence number to use for a specific RTX packet, which is based on the packet's * original sequence number. * * <p>Because we terminate the RTX format, and with simulcast we might translate RTX packets from * multiple SSRCs into the same SSRC, we keep count of the RTX packets (and their sequence * numbers) which we sent for each SSRC. * * @param ssrc the SSRC of the RTX stream for the packet. * @param defaultSeq the default sequence number to use in case we don't (yet) have any * information about <tt>ssrc</tt>. * @return the sequence number which should be used for the next RTX packet sent using SSRC * <tt>ssrc</tt>. */ private int getNextRtxSequenceNumber(long ssrc, int defaultSeq) { Integer seq; synchronized (rtxSequenceNumbers) { seq = rtxSequenceNumbers.get(ssrc); if (seq == null) seq = defaultSeq; else seq++; rtxSequenceNumbers.put(ssrc, seq); } return seq; }
/** * Returns the sequence number to use for a specific RTX packet, which is based on the packet's * original sequence number. * * <p>Because we terminate the RTX format, and with simulcast we might translate RTX packets from * multiple SSRCs into the same SSRC, we keep count of the RTX packets (and their sequence * numbers) which we sent for each SSRC. * * @param ssrc the SSRC of the RTX stream for the packet. * @return the sequence number which should be used for the next RTX packet sent using SSRC * <tt>ssrc</tt>. */ private int getNextRtxSequenceNumber(long ssrc) { Integer seq; synchronized (rtxSequenceNumbers) { seq = rtxSequenceNumbers.get(ssrc); if (seq == null) seq = new Random().nextInt(0xffff); else seq++; rtxSequenceNumbers.put(ssrc, seq); } return seq; }
/** * Notifies this instance that the dynamic payload types of the associated {@link MediaStream} * have changed. */ public void onDynamicPayloadTypesChanged() { rtxPayloadType = -1; rtxAssociatedPayloadType = -1; MediaStream mediaStream = channel.getStream(); Map<Byte, MediaFormat> mediaFormatMap = mediaStream.getDynamicRTPPayloadTypes(); Iterator<Map.Entry<Byte, MediaFormat>> it = mediaFormatMap.entrySet().iterator(); while (it.hasNext() && rtxPayloadType == -1) { Map.Entry<Byte, MediaFormat> entry = it.next(); MediaFormat format = entry.getValue(); if (!Constants.RTX.equalsIgnoreCase(format.getEncoding())) { continue; } // XXX(gp) we freak out if multiple codecs with RTX support are // present. rtxPayloadType = entry.getKey(); rtxAssociatedPayloadType = Byte.parseByte(format.getFormatParameters().get("apt")); } }
/** * Estimate the <tt>RemoteClock</tt> of a given RTP stream (identified by its SSRC) at a given * time. * * @param ssrc the SSRC of the RTP stream whose <tt>RemoteClock</tt> we want to estimate. * @param time the local time that will be mapped to a remote time. * @return An estimation of the <tt>RemoteClock</tt> at time "time". */ public RemoteClock estimate(int ssrc, long time) { ReceivedRemoteClock receivedRemoteClock = receivedClocks.get(ssrc); if (receivedRemoteClock == null || receivedRemoteClock.getFrequencyHz() == -1) { // We can't continue if we don't have NTP and RTP timestamps // and/or the original sender frequency, so move to the next // one. return null; } long delayMillis = time - receivedRemoteClock.getReceivedTime(); // Estimate the remote wall clock. long remoteTime = receivedRemoteClock.getRemoteClock().getRemoteTime(); long estimatedRemoteTime = remoteTime + delayMillis; // Drift the RTP timestamp. int rtpTimestamp = receivedRemoteClock.getRemoteClock().getRtpTimestamp() + ((int) delayMillis) * (receivedRemoteClock.getFrequencyHz() / 1000); return new RemoteClock(estimatedRemoteTime, rtpTimestamp); }
/** * 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; } } }