/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package org.mobicents.media.server.impl.resource.audio;
import java.io.IOException;
import java.util.concurrent.LinkedBlockingQueue;
import org.apache.log4j.Logger;
import org.mobicents.media.Buffer;
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.AbstractSink;
import org.mobicents.media.server.impl.AbstractSource;
import org.mobicents.media.server.impl.BaseComponent;
import org.mobicents.media.server.spi.Connection;
import org.mobicents.media.server.spi.Endpoint;
import org.mobicents.media.server.spi.Timer;
import org.mobicents.media.server.spi.dsp.Codec;
/**
*
* @author kulikov
*/
public class EndpointTransmittor extends BaseComponent implements Inlet, Outlet {
private final static long SILENCE = 80;
private Format format = Codec.LINEAR_AUDIO;
private Input input;
private Output output;
private volatile LinkedBlockingQueue<Buffer> queue = new LinkedBlockingQueue();
private boolean isAcceptable = false;
private Format fmt;
private volatile long silence;
private boolean isTransmittSilence;
public static final byte[] SILENCE_DATA = new byte[]{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
private Logger logger = Logger.getLogger(EndpointTransmittor.class);
public EndpointTransmittor(String name, Timer timer) {
super(name);
input = new Input(name);
output = new Output(name, timer);
}
public MediaSink getInput() {
return input;
}
public void start() {
input.start();
output.start();
}
public void stop() {
input.stop();
output.stop();
}
@Override
public void setEndpoint(Endpoint endpoint) {
input.setEndpoint(endpoint);
output.setEndpoint(endpoint);
}
@Override
public void setConnection(Connection connection) {
input.setConnection(connection);
output.setConnection(connection);
}
public MediaSource getOutput() {
return output;
}
private Format[] combine(Format[] fmts, Format f) {
for (int i = 0; i < fmts.length; i++) {
if (fmts[i].matches(f)) {
return fmts;
}
}
Format[] formats = new Format[fmts.length + 1];
System.arraycopy(fmts, 0, formats, 0, fmts.length);
formats[fmts.length] = f;
return formats;
}
private class Input extends AbstractSink {
public Input(String name) {
super(name);
}
@Override
public void onMediaTransfer(Buffer buffer) throws IOException {
Buffer buff = new Buffer();
buff.copy(buffer);
queue.offer(buff);
}
public Format[] getFormats() {
return output.getOtherPartyFormats();
}
public boolean isAcceptable(Format format) {
// if (fmt != null && fmt.matches(format)) {
// return isAcceptable;
// }
Format[] supported = getFormats();
for (int i = 0; i < supported.length; i++) {
if (format.matches(supported[i])) {
fmt = format;
isAcceptable = true;
return true;
}
}
fmt = format;
return false;
}
private Format[] getOtherPartyFormats() {
return otherParty != null ? otherParty.getFormats() : new Format[0];
}
}
private class Output extends AbstractSource {
private boolean isSilence = false;
public Output(String name, Timer timer) {
super(name);
setSyncSource(timer);
}
private Format[] getOtherPartyFormats() {
return otherParty != null ? otherParty.getFormats() : new Format[0];
}
@Override
public void evolve(Buffer buffer, long timestamp, long sequenceNumber) {
if (!queue.isEmpty()) {
if (logger.isDebugEnabled() && silence != 0) {
logger.debug(this + " start audio data");
this.isSilence = false;
}
Buffer buff = queue.poll();
buffer.copy(buff);
buffer.setSequenceNumber(sequenceNumber);
buffer.setDiscard(false);
silence = 0;
} else if (silence > SILENCE) {
if (logger.isDebugEnabled() && !this.isSilence) {
logger.debug(this + " start audio silence");
this.isSilence = true;
}
buffer.setOffset(0);
buffer.setLength(320);
buffer.setFormat(format);
buffer.setSequenceNumber(sequenceNumber);
buffer.setTimeStamp(System.currentTimeMillis());
buffer.setDiscard(false);
System.arraycopy(SILENCE_DATA, 0, (byte[]) buffer.getData(), 0, 320);
} else {
silence += getDuration();
buffer.setDiscard(true);
}
}
public Format[] getFormats() {
return combine(input.getOtherPartyFormats(), format);
}
}
public void connect(MediaSource source) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void disconnect(MediaSource source) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void connect(MediaSink sink) {
throw new UnsupportedOperationException("Not supported yet.");
}
public void disconnect(MediaSink sink) {
throw new UnsupportedOperationException("Not supported yet.");
}
}