/* * Mobicents, Communications Middleware * * Copyright (c) 2008, Red Hat Middleware LLC or third-party * contributors as * indicated by the @author tags or express copyright attribution * statements applied by the authors. All third-party contributions are * distributed under license by Red Hat Middleware LLC. * * This copyrighted material is made available to anyone wishing to use, modify, * copy, or redistribute it subject to the terms and conditions of the GNU * Lesser General Public License, as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License * for more details. * * * You should have received a copy of the GNU Lesser General Public License * along with this distribution; if not, write to: * Free Software Foundation, Inc. * 51 Franklin Street, Fifth Floor * * Boston, MA 02110-1301 USA */ package org.mobicents.media.server.resource; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import org.mobicents.media.Component; import org.mobicents.media.Format; import org.mobicents.media.Inlet; import org.mobicents.media.MediaSink; import org.mobicents.media.MediaSource; import org.mobicents.media.Outlet; import org.mobicents.media.server.impl.resource.Proxy; import org.mobicents.media.server.spi.Connection; import org.mobicents.media.server.spi.Endpoint; /** * Channel is used to join media source with media sink with * customized media path. * * For customization of media path inner pipes are used where each pipe has c * onfigured its inlet and outlet. * * @author kulikov */ public class Channel { //holders for components classified by its type //one component can be holded in more then one map protected HashMap<String, MediaSource> sources; protected HashMap<String, MediaSink> sinks; protected HashMap<String, Inlet> inlets; protected HashMap<String, Outlet> outlets; //The list of internal pipes private List<Pipe> pipes = new ArrayList(); private Proxy localPipe = new Proxy("Channel"); //The external sink to which this channel is attached // private MediaSink sink; //The external source to which this channel is attached // private MediaSource source; //The component connected to extenal source. private MediaSink intake; //The component connected to extenal sink. private MediaSource exhaust; private Endpoint endpoint; private Connection connection; private boolean directLink = true; private Channel txChannel; private Channel rxChannel; private HashMap<String, ? extends Component> components; /** * Constructs new channel with specified components. * * @param sources the map of components of type MediaSource * @param sinks the map of components of type MediaSink * @param inlets the map of components of type Inlet * @param outlets the map of components of type Outlet */ protected Channel( HashMap<String, MediaSource> sources, HashMap<String, MediaSink> sinks, HashMap<String, Inlet> inlets, HashMap<String, Outlet> outlets) { this.sources = sources; this.sinks = sinks; this.inlets = inlets; this.outlets = outlets; this.intake = localPipe.getInput(); this.exhaust = localPipe.getOutput(); } protected Channel(HashMap<String, ? extends Component> components) { this.components = components; } public Format[] getFormats() { return null; } public Endpoint getEndpoint() { return this.endpoint; } public Connection getConnection() { return connection; } public void setEndpoint(Endpoint endpoint) { Collection<MediaSource> list = sources.values(); for (MediaSource s : list) { s.setEndpoint(endpoint); } Collection<MediaSink> list1 = sinks.values(); for (MediaSink s : list1) { s.setEndpoint(endpoint); } Collection<Inlet> list2 = inlets.values(); for (Inlet s : list2) { s.setEndpoint(endpoint); } Collection<Outlet> list3 = outlets.values(); for (Outlet s : list3) { s.setEndpoint(endpoint); } intake.setEndpoint(endpoint); exhaust.setEndpoint(endpoint); } public void setConnection(Connection connection) { this.connection = connection; Collection<MediaSource> list = sources.values(); for (MediaSource s : list) { s.setConnection(connection); } Collection<MediaSink> list1 = sinks.values(); for (MediaSink s : list1) { s.setConnection(connection); } Collection<Inlet> list2 = inlets.values(); for (Inlet s : list2) { s.setConnection(connection); } Collection<Outlet> list3 = outlets.values(); for (Outlet s : list3) { s.setConnection(connection); } intake.setConnection(connection); exhaust.setConnection(connection); } public void start() { /* Collection <MediaSource> list = sources.values(); for (MediaSource s : list) { s.start(); } Collection <MediaSink> list2 = sinks.values(); for (MediaSink s : list2) { s.start(); } */ Collection <Inlet> list3 = inlets.values(); for (Inlet s : list3) { if (s instanceof Outlet || s instanceof MediaSource) { s.getInput().start(); s.start(); } } Collection <Outlet> list4 = outlets.values(); for (Outlet s : list4) { if (s instanceof Inlet || s instanceof MediaSink) { s.getOutput().start(); s.start(); } } intake.start(); exhaust.start(); } public void stop() { Collection <MediaSource> list = sources.values(); for (MediaSource s : list) { s.stop(); } Collection <MediaSink> list2 = sinks.values(); for (MediaSink s : list2) { s.stop(); } Collection <Inlet> list3 = inlets.values(); for (Inlet s : list3) { s.getInput().stop(); } Collection <Outlet> list4 = outlets.values(); for (Outlet s : list4) { s.getOutput().stop(); } intake.stop(); exhaust.stop(); } /** * Opens pipes between source and sink. * * @param pipe the pipe to be opened * @param inlet the name of the source * @param outlet the name of the destination. * * @throws org.mobicents.media.server.resource.UnknownComponentException * if name of the sink or source is not known. */ public void openPipe(Pipe pipe, String inlet, String outlet) throws UnknownComponentException { directLink = false; //when inlet is null pipe acts as intake for the channel //so component with name outlet will be connected to external source //we have to consider it as intake. if (inlet == null && outlet != null) { if (sinks.containsKey(outlet)) { intake = sinks.get(outlet); } else throw new UnknownComponentException(outlet); } //when outlet is null then pipe acts as exhaust for the channel //the component with name inlet will be connected to an external sink //we are assigning this component to exhaust varibale else if (inlet != null && outlet == null) { if (sources.containsKey(inlet)) { exhaust = sources.get(inlet); } else throw new UnknownComponentException(inlet); } //it is an internal pipe. just join to components and save //pipe in the list else { pipe.open(inlet, outlet); pipes.add(pipe); } } /** * Closes specified pipe. * * @param pipe the pipe to be closed. */ public void closePipe(Pipe pipe) { pipes.remove(pipe); pipe.close(); } public Format[] getInputFormats() { return intake.getFormats(); } public Format[] getOutputFormats() { return exhaust.getFormats(); } /** * Connects channel to a sink. * * @param sink the sink to connect to */ public Format[] connect(MediaSink sink) { //sink may be connected if this channel is not connected to other channel //as source of media if (rxChannel != null) { throw new IllegalStateException("Channel is connected as source for other channel"); } exhaust.connect(sink); return this.getSubset(exhaust.getFormats(), sink.getFormats()); } /** * Disconnects channel from a sink. * * @param sink the sink to connect from */ public void disconnect(MediaSink sink) { //sink may be connected if this channel is not connected to other channel //as source of media if (rxChannel != null) { throw new IllegalStateException("Channel is connected as source for other channel"); } exhaust.disconnect(sink); } /** * Connects channel to a source. * * @param source the source to connect to */ public Format[] connect(MediaSource source) { //source may be connected if this channel is not connected to other channel //as source of media if (txChannel != null) { throw new IllegalStateException("Channel is connected as sink for other channel"); } intake.connect(source); return getSubset(intake.getFormats(), source.getFormats()); } /** * Disconnects channel from a source. * * @param source the source to connect from */ public void disconnect(MediaSource source) { //source may be connected if this channel is not connected to other channel //as source of media if (txChannel != null) { throw new IllegalStateException("Channel is connected as sink for other channel"); } intake.disconnect(source); } /** * Establish a connection with other channel * * @param other */ public void connect(Channel rxChannel) { this.rxChannel = rxChannel; rxChannel.txChannel = this; exhaust.connect(rxChannel.intake); } /** * Deletes connection with other channel * * @param other */ public void disconnect(Channel channel) { if (rxChannel != null && rxChannel == channel) { exhaust.disconnect(rxChannel.intake); rxChannel.txChannel = null; rxChannel = null; } else if (txChannel != null && txChannel == channel) { intake.disconnect(txChannel.exhaust); txChannel.rxChannel = null; txChannel = null; } else { throw new IllegalArgumentException("Channels " + this + " and " + channel + " was never connected"); } } /** * Searches the component with specified name that can be explicitly added * to this channel. * * @param name the name of the component to find. * @return component that was found or null if component with specified name * absent. */ public Component getComponent(String name) { //search result depends from the order of maps observation! //inlet/outlet implicitly registers input/output in source or sink map //and as result can be returned implicit sink/source of the complex //component. To prevent such kind of search error let's start to search //from inlets/outlets and look into sink,source only if explicitly added //component was not found yet if (inlets.containsKey(name)) { return inlets.get(name); } else if (outlets.containsKey(name)){ return outlets.get(name); } else if (sources.containsKey(name)) { return sources.get(name); } else if (sinks.containsKey(name)) { return sinks.get(name); } return null; } public void close() { for (Pipe pipe : pipes) { closePipe(pipe); } pipes.clear(); sources.clear(); sinks.clear(); outlets.clear(); inlets.clear(); } private Format[] getSubset(Format[] f1, Format[] f2) { ArrayList<Format> list = new ArrayList(); for (int i = 0; i < f1.length; i++) { for (int j = 0; j < f2.length; j++) { if (f1[i].matches(f2[j])) { list.add(f1[i]); } } } Format[] f = new Format[list.size()]; list.toArray(f); return f; } public long getPacketsTransmitted() { return exhaust.getPacketsTransmitted(); } }