/** * Retransmits a packet to {@link #channel}. If the destination supports the RTX format, the * packet will be encapsulated in RTX, otherwise, the packet will be retransmitted as-is. * * @param pkt the packet to retransmit. * @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. */ public boolean retransmit(RawPacket pkt, TransformEngine after) { boolean destinationSupportsRtx = channel.getRtxPayloadType() != -1; boolean retransmitPlain; if (destinationSupportsRtx) { long rtxSsrc = getPairedSsrc(pkt.getSSRC()); if (rtxSsrc == -1) { logger.warn("Cannot find SSRC for RTX, retransmitting plain."); retransmitPlain = true; } else { retransmitPlain = !encapsulateInRtxAndTransmit(pkt, rtxSsrc); } } else { retransmitPlain = true; } if (retransmitPlain) { MediaStream mediaStream = channel.getStream(); if (mediaStream != null) { try { mediaStream.injectPacket(pkt, /* data */ true, after); } catch (TransmissionFailedException tfe) { logger.warn("Failed to retransmit a packet."); return false; } } } return true; }
/** {@inheritDoc} */ @Override public RawPacket transform(RawPacket pkt) { if (pkt == null) { return pkt; } RTCPCompoundPacket inPacket; try { inPacket = (RTCPCompoundPacket) parser.parse(pkt.getBuffer(), pkt.getOffset(), pkt.getLength()); } catch (BadFormatException e) { logger.warn("Failed to terminate an RTCP packet. " + "Dropping packet."); return null; } // Update our RTCP stats map (timestamps). This operation is // read-only. remoteClockEstimator.apply(inPacket); cnameRegistry.update(inPacket); // Remove SRs and RRs from the RTCP packet. pkt = feedbackGateway.gateway(inPacket); return pkt; }
/** * Iterate through all the <tt>ReceiveStream</tt>s that this <tt>MediaStream</tt> has and make * <tt>RTCPReportBlock</tt>s for all of them. * * @param time * @return */ private RTCPReportBlock[] makeRTCPReportBlocks(long time) { MediaStream stream = getStream(); // State validation. if (stream == null) { logger.warn("stream is null."); return MIN_RTCP_REPORTS_BLOCKS_ARRAY; } StreamRTPManager streamRTPManager = stream.getStreamRTPManager(); if (streamRTPManager == null) { logger.warn("streamRTPManager is null."); return MIN_RTCP_REPORTS_BLOCKS_ARRAY; } Collection<ReceiveStream> receiveStreams = streamRTPManager.getReceiveStreams(); if (receiveStreams == null || receiveStreams.size() == 0) { logger.info("There are no receive streams to build report " + "blocks for."); return MIN_RTCP_REPORTS_BLOCKS_ARRAY; } SSRCCache cache = streamRTPManager.getSSRCCache(); if (cache == null) { logger.info("cache is null."); return MIN_RTCP_REPORTS_BLOCKS_ARRAY; } // Create the return object. Collection<RTCPReportBlock> rtcpReportBlocks = new ArrayList<RTCPReportBlock>(); // Populate the return object. for (ReceiveStream receiveStream : receiveStreams) { // Dig into the guts of FMJ and get the stats for the current // receiveStream. SSRCInfo info = cache.cache.get((int) receiveStream.getSSRC()); if (!info.ours && info.sender) { RTCPReportBlock rtcpReportBlock = info.makeReceiverReport(time); rtcpReportBlocks.add(rtcpReportBlock); } } return rtcpReportBlocks.toArray(new RTCPReportBlock[rtcpReportBlocks.size()]); }
/** * 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; }