/** * Returns, the <tt>TransportManager</tt> instance for the channel-bundle with ID * <tt>channelBundleId</tt>. If no instance exists and <tt>create</tt> is <tt>true</tt>, one will * be created. * * @param channelBundleId the ID of the channel-bundle for which to return the * <tt>TransportManager</tt>. * @param create whether to create a new instance, if one doesn't exist. * @return the <tt>TransportManager</tt> instance for the channel-bundle with ID * <tt>channelBundleId</tt>. */ IceUdpTransportManager getTransportManager(String channelBundleId, boolean create) { IceUdpTransportManager transportManager; synchronized (transportManagers) { transportManager = transportManagers.get(channelBundleId); if (transportManager == null && create && !isExpired()) { try { // FIXME: the initiator is hard-coded // We assume rtcp-mux when bundle is used, so we make only // one component. transportManager = new IceUdpTransportManager(this, true, 1); } catch (IOException ioe) { throw new UndeclaredThrowableException(ioe); } transportManagers.put(channelBundleId, transportManager); } } return transportManager; }
/** * Maybe send a data channel command to the associated <tt>Endpoint</tt> to make it start * streaming its hq stream, if it's being watched by some receiver. */ public void maybeSendStartHighQualityStreamCommand() { if (nativeSimulcast || !hasLayers()) { // In native simulcast the client adjusts its layers autonomously so // we don't need (nor we can) to control it with data channel // messages. return; } Endpoint newEndpoint = getSimulcastEngine().getVideoChannel().getEndpoint(); SimulcastLayer[] newSimulcastLayers = getSimulcastLayers(); SctpConnection sctpConnection; if (newSimulcastLayers == null || newSimulcastLayers.length <= 1 /* newEndpoint != null is implied */ || (sctpConnection = newEndpoint.getSctpConnection()) == null || !sctpConnection.isReady() || sctpConnection.isExpired()) { return; } // we have a new endpoint and it has an SCTP connection that is // ready and not expired. if somebody else is watching the new // endpoint, start its hq stream. boolean startHighQualityStream = false; for (Endpoint e : getSimulcastEngine().getVideoChannel().getContent().getConference().getEndpoints()) { // TODO(gp) need some synchronization here. What if the // selected endpoint changes while we're in the loop? if (e == newEndpoint) continue; Endpoint eSelectedEndpoint = e.getEffectivelySelectedEndpoint(); if (newEndpoint == eSelectedEndpoint) { // somebody is watching the new endpoint or somebody has not // yet signaled its selected endpoint to the bridge, start // the hq stream. if (logger.isDebugEnabled()) { Map<String, Object> map = new HashMap<String, Object>(3); map.put("e", e); map.put("newEndpoint", newEndpoint); map.put("maybe", eSelectedEndpoint == null ? "(maybe) " : ""); StringCompiler sc = new StringCompiler(map).c("{e.id} is {maybe} watching {newEndpoint.id}."); logDebug(sc.toString().replaceAll("\\s+", " ")); } startHighQualityStream = true; break; } } if (startHighQualityStream) { // TODO(gp) this assumes only a single hq stream. logDebug( getSimulcastEngine().getVideoChannel().getEndpoint().getID() + " notifies " + newEndpoint.getID() + " to start its HQ stream."); SimulcastLayer hqLayer = newSimulcastLayers[newSimulcastLayers.length - 1]; ; StartSimulcastLayerCommand command = new StartSimulcastLayerCommand(hqLayer); String json = mapper.toJson(command); try { newEndpoint.sendMessageOnDataChannel(json); } catch (IOException e) { logError(newEndpoint.getID() + " failed to send message on data channel.", e); } } }