/** * Determines whether {@code effect} has been paused/stopped by the remote peer. The determination * is based on counting frames and is triggered by the receipt of (a piece of) a new (video) frame * by {@code cause}. * * @param cause the {@code SimulcastLayer} which has received (a piece of) a new (video) frame and * has thus triggered a check on {@code effect} * @param pkt the {@code RawPacket} which was received by {@code cause} and possibly influenced * the decision to trigger a check on {@code effect} * @param effect the {@code SimulcastLayer} which is to be checked whether it looks like it has * been paused/stopped by the remote peer * @param endIndexInSimulcastLayerFrameHistory */ private void maybeTimeout( SimulcastLayer cause, RawPacket pkt, SimulcastLayer effect, int endIndexInSimulcastLayerFrameHistory) { Iterator<SimulcastLayer> it = simulcastLayerFrameHistory.iterator(); boolean timeout = true; for (int ix = 0; it.hasNext() && ix < endIndexInSimulcastLayerFrameHistory; ++ix) { if (it.next() == effect) { timeout = false; break; } } if (timeout) { effect.maybeTimeout(pkt); if (!effect.isStreaming()) { // Since effect has been determined to have been paused/stopped // by the remote peer, its possible presence in // simulcastLayerFrameHistory is irrelevant now. In other words, // remove effect from simulcastLayerFrameHistory. while (it.hasNext()) { if (it.next() == effect) it.remove(); } } } }
@Override public void run() { synchronized (RESTReservations.this) { Iterator<Conference> conferenceIterator = conferenceMap.values().iterator(); while (conferenceIterator.hasNext()) { Conference conference = conferenceIterator.next(); Date startTimeDate = conference.getStartTime(); if (startTimeDate == null) { logger.error("No 'start_time' for conference: " + conference.getName()); continue; } long startTime = startTimeDate.getTime(); long duration = conference.getDuration(); // Convert duration to millis duration = duration * 1000L; long now = System.currentTimeMillis(); if (now - startTime > duration - EXPIRE_INTERVAL) { // Destroy the conference String mucRoomName = conference.getMucRoomName(); deleteConference(conference.getId()); conferenceIterator.remove(); focusManager.destroyConference(mucRoomName, "Scheduled conference duration exceeded."); } } } }
/** * Notifies this {@code SimulcastReceiver} that a specific {@code SimulcastReceiver} has detected * the start of a new video frame in the RTP stream that it represents. Determines whether any of * {@link #simulcastLayers} other than {@code source} have been paused/stopped by the remote peer. * The determination is based on counting (video) frames. * * @param source the {@code SimulcastLayer} which is the source of the event i.e. which has * detected the start of a new video frame in the RTP stream that it represents * @param pkt the {@code RawPacket} which was received by {@code source} and possibly influenced * the decision that a new view frame was started in the RTP stream represented by {@code * source} * @param layers the set of {@code SimulcastLayer}s managed by this {@code SimulcastReceiver}. * Explicitly provided to the method in order to avoid invocations of {@link * #getSimulcastLayers()} because the latter makes a copy at the time of this writing. */ private void simulcastLayerFrameStarted( SimulcastLayer source, RawPacket pkt, SimulcastLayer[] layers) { // Allow the value of the constant TIMEOUT_ON_FRAME_COUNT to disable (at // compile time) the frame-based approach to the detection of layer // drops. if (TIMEOUT_ON_FRAME_COUNT <= 1) return; // Timeouts in layers caused by source may occur only based on the span // (of time or received frames) during which source has received // TIMEOUT_ON_FRAME_COUNT number of frames. The current method // invocation signals the receipt of 1 frame by source. int indexOfLastSourceOccurrenceInHistory = -1; int sourceFrameCount = 0; int ix = 0; for (Iterator<SimulcastLayer> it = simulcastLayerFrameHistory.iterator(); it.hasNext(); ++ix) { if (it.next() == source) { if (indexOfLastSourceOccurrenceInHistory != -1) { // Prune simulcastLayerFrameHistory so that it does not // become unnecessarily long. it.remove(); } else if (++sourceFrameCount >= TIMEOUT_ON_FRAME_COUNT - 1) { // The span of TIMEOUT_ON_FRAME_COUNT number of frames // received by source only is to be examined for the // purposes of timeouts. The current method invocations // signals the receipt of 1 frame by source so // TIMEOUT_ON_FRAME_COUNT - 1 occurrences of source in // simulcastLayerFrameHistory is enough. indexOfLastSourceOccurrenceInHistory = ix; } } } if (indexOfLastSourceOccurrenceInHistory != -1) { // Presumably, if a SimulcastLayer is active, all SimulcastLayers // before it (according to SimulcastLayer's order) are active as // well. Consequently, timeouts may occur in SimulcastLayers which // are after source. boolean maybeTimeout = false; for (SimulcastLayer layer : layers) { if (maybeTimeout) { // There's no point in timing layer out if it's timed out // already. if (layer.isStreaming()) { maybeTimeout(source, pkt, layer, indexOfLastSourceOccurrenceInHistory); } } else if (layer == source) { maybeTimeout = true; } } } // As previously stated, the current method invocation signals the // receipt of 1 frame by source. simulcastLayerFrameHistory.add(0, source); // TODO Prune simulcastLayerFrameHistory by forgetting so that it does // not become too long. }
private void nudge() { for (Iterator<RecorderEvent> iter = pendingEvents.iterator(); iter.hasNext(); ) { RecorderEvent ev = iter.next(); long instant = getSynchronizer().getLocalTime(ev.getSsrc(), ev.getRtpTimestamp()); if (instant != -1) { iter.remove(); ev.setInstant(instant); handler.handleEvent(ev); } } }