In the imgstreaming CaptureDevice, fixes an inconsistency in the Format reporting and a racing which could possibly result in a failure to start the screen capturing.

cusax-fix
Lyubomir Marinov 17 years ago
parent 8b1d30aa69
commit 2a5985d7b5

@ -0,0 +1,624 @@
/*
* SIP Communicator, the OpenSource Java VoIP and Instant Messaging client.
*
* Distributable under LGPL license.
* See terms of license at gnu.org.
*/
package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol;
import java.io.*;
import java.lang.reflect.*;
import java.util.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.protocol.*;
import net.java.sip.communicator.impl.neomedia.control.*;
import net.java.sip.communicator.util.*;
/**
* Provides a base implementation of <tt>PushBufferDataSource</tt> and
* <tt>CaptureDevice</tt> in order to facilitate implementers by taking care of
* boilerplate in the most common cases.
*
* @author Lubomir Marinov
*/
public abstract class AbstractPushBufferCaptureDevice
extends PushBufferDataSource
implements CaptureDevice
{
/**
* The <tt>Logger</tt> used by the <tt>AbstractPushBufferCaptureDevice</tt>
* class and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(AbstractPushBufferCaptureDevice.class);
/**
* The value of the <tt>formatControls</tt> property of
* <tt>AbstractPushBufferCaptureDevice</tt> which represents an empty array
* of <tt>FormatControl</tt>s. Explicitly defined in order to reduce
* unnecessary allocations.
*/
protected static final FormatControl[] EMPTY_FORMAT_CONTROLS
= new FormatControl[0];
/**
* The value of the <tt>streams</tt> property of
* <tt>AbstractPushBufferCaptureDevice</tt> which represents an empty array
* of <tt>PushBufferStream</tt>s. Explicitly defined in order to reduce
* unnecessary allocations.
*/
protected static final PushBufferStream[] EMPTY_STREAMS
= new PushBufferStream[0];
/**
* The indicator which determines whether a connection to the media source
* specified by the <tt>MediaLocator</tt> of this <tt>DataSource</tt> has
* been opened.
*/
private boolean connected = false;
/**
* The array of <tt>FormatControl</tt> instances each one of which can be
* used before {@link #connect()} to get and set the capture <tt>Format</tt>
* of each one of the capture streams.
*/
private FormatControl[] formatControls;
/**
* The indicator which determines whether the transfer of media data from
* this <tt>DataSource</tt> has been started.
*/
private boolean started = false;
/**
* The <tt>PushBufferStream</tt>s through which this
* <tt>PushBufferDataSource</tt> gives access to its media data.
*/
private AbstractPushBufferStream[] streams;
/**
* Initializes a new <tt>AbstractPushBufferCaptureDevice</tt> instance.
*/
protected AbstractPushBufferCaptureDevice()
{
}
/**
* Initializes a new <tt>AbstractPushBufferCaptureDevice</tt> instance from
* a specific <tt>MediaLocator</tt>.
*
* @param locator the <tt>MediaLocator</tt> to create the new instance from
*/
protected AbstractPushBufferCaptureDevice(MediaLocator locator)
{
setLocator(locator);
}
/**
* Determines whether it is allowed to set the format of a specific
* <tt>FormatControl</tt> of the <tt>PushBufferStream</tt> of this
* <tt>PushBufferDataSource</tt> with a specific index to a specific
* <tt>Format</tt>. The <tt>AbstractPushBufferCaptureDevice</tt>
* implementation always returns <tt>false</tt>.
*
* @param format the <tt>Format</tt> which is to be set
* @param streamIndex the zero-based index of the <tt>PushBufferStream</tt>
* in the list of streams of this <tt>PushBufferDataSource</tt> which is to
* have its <tt>Format</tt> set
* @param formatControl the <tt>FormatControl</tt> of the stream through
* which the set request is received
* @return <tt>true</tt> if the specified <tt>Format</tt> is to be set as
* the format of <tt>formatControl</tt>; otherwise, <tt>false</tt>
*/
protected boolean canSetFormat(
Format format,
int streamIndex,
FormatControl formatControl)
{
return false;
}
/**
* Opens a connection to the media source specified by the
* <tt>MediaLocator</tt> of this <tt>DataSource</tt>.
*
* @throws IOException if anything goes wrong while opening the connection
* to the media source specified by the <tt>MediaLocator</tt> of this
* <tt>DataSource</tt>
*/
public synchronized void connect()
throws IOException
{
if (!connected)
{
doConnect();
connected = true;
}
}
/**
* Creates a new <tt>FormatControl</tt> instance which is to be associated
* with a <tt>PushBufferStream</tt> at a specific zero-based index in the
* list of streams of this <tt>PushBufferDataSource</tt>. As the
* <tt>FormatControl</tt>s of a <tt>PushBufferDataSource</tt> can be
* requested before {@link #connect()}, its <tt>PushBufferStream</tt>s may
* not exist at the time of the request for the creation of the
* <tt>FormatControl</tt>.
*
* @param streamIndex the zero-based index of the <tt>PushBufferStream</tt>
* in the list of streams of this <tt>PushBufferDataSource</tt> which is to
* be associated with the new <tt>FormatControl</tt> instance
* @return a new <tt>FormatControl</tt> instance which is to be associated
* with a <tt>PushBufferStream</tt> at the specified <tt>streamIndex</tt> in
* the list of streams of this <tt>PushBufferDataSource</tt>
*/
protected FormatControl createFormatControl(final int streamIndex)
{
return
new AbstractFormatControl()
{
/**
* The <tt>Format</tt> of this <tt>FormatControl</tt> and,
* respectively, of the media data of its owner.
*/
private Format format;
/**
* Gets the <tt>Format</tt> of the media data of the owner of
* this <tt>FormatControl</tt>.
*
* @return the <tt>Format</tt> of the media data of the owner of
* this <tt>FormatControl</tt>
*/
public Format getFormat()
{
if (format == null)
format
= AbstractPushBufferCaptureDevice.this
.getFormat(streamIndex, this);
return format;
}
/**
* Gets the <tt>Format</tt>s in which the owner of this
* <tt>FormatControl</tt> is capable of providing media data.
*
* @return an array of <tt>Format</tt>s in which the owner of
* this <tt>FormatControl</tt> is capable of providing media
* data
*/
public Format[] getSupportedFormats()
{
return
AbstractPushBufferCaptureDevice.this
.getSupportedFormats(streamIndex, this);
}
/**
* Implements {@link FormatControl#setFormat(Format)}. Attempts
* to set the <tt>Format</tt> in which the owner of this
* <tt>FormatControl</tt> is to provide media data.
*
* @param format the <tt>Format</tt> to be set on this instance
* @return the currently set <tt>Format</tt> after the attempt
* to set it on this instance if <tt>format</tt> is supported by
* this instance and regardless of whether it was actually set;
* <tt>null</tt> if <tt>format</tt> is not supported by this
* instance
*/
@Override
public Format setFormat(Format format)
{
format = super.setFormat(format);
if ((format != null)
&& canSetFormat(format, streamIndex, this))
this.format = format;
return getFormat();
}
};
}
/**
* Creates the <tt>FormatControl</tt>s of this <tt>CaptureDevice</tt>.
*
* @return an array of the <tt>FormatControl</tt>s of this
* <tt>CaptureDevice</tt>
*/
protected FormatControl[] createFormatControls()
{
FormatControl formatControl = createFormatControl(0);
return
(formatControl == null)
? EMPTY_FORMAT_CONTROLS
: new FormatControl[] { formatControl };
}
/**
* Create a new <tt>PushBufferStream</tt> which is to be at a specific
* zero-based index in the list of streams of this
* <tt>PushBufferDataSource</tt>. The <tt>Format</tt>-related information of
* the new instance is to be abstracted by a specific
* <tt>FormatControl</tt>.
*
* @param streamIndex the zero-based index of the <tt>PushBufferStream</tt>
* in the list of streams of this <tt>PushBufferDataSource</tt>
* @param formatControl the <tt>FormatControl</tt> which is to abstract the
* <tt>Format</tt>-related information of the new instance
* @return a new <tt>PushBufferStream</tt> which is to be at the specified
* <tt>streamIndex</tt> in the list of streams of this
* <tt>PushBufferDataSource</tt> and which has its <tt>Format</tt>-related
* information abstracted by the specified <tt>formatControl</tt>
*/
protected abstract AbstractPushBufferStream createStream(
int streamIndex,
FormatControl formatControl);
/**
* Closes the connection to the media source specified by the
* <tt>MediaLocator</tt> of this <tt>DataSource</tt>. If such a connection
* has not been opened, the call is ignored.
*/
public synchronized void disconnect()
{
try
{
stop();
}
catch (IOException ioex)
{
logger.error("Failed to stop " + getClass().getSimpleName(), ioex);
}
if (connected)
{
doDisconnect();
connected = false;
}
}
/**
* Opens a connection to the media source specified by the
* <tt>MediaLocator</tt> of this <tt>DataSource</tt>. Allows extenders to
* override and be sure that there will be no request to open a connection
* if the connection has already been opened.
*
* @throws IOException if anything goes wrong while opening the connection
* to the media source specified by the <tt>MediaLocator</tt> of this
* <tt>DataSource</tt>
*/
protected synchronized void doConnect()
throws IOException
{
}
/**
* Closes the connection to the media source specified by the
* <tt>MediaLocator</tt> of this <tt>DataSource</tt>. Allows extenders to
* override and be sure that there will be no request to close a connection
* if the connection has not been opened yet.
*/
protected synchronized void doDisconnect()
{
if (streams != null)
try
{
for (AbstractPushBufferStream stream : streams)
stream.close();
}
finally
{
streams = null;
}
}
/**
* Starts the transfer of media data from this <tt>DataSource</tt>. Allows
* extenders to override and be sure that there will be no request to start
* the transfer of media data if it has already been started.
*
* @throws IOException if anything goes wrong while starting the transfer of
* media data from this <tt>DataSource</tt>
*/
protected synchronized void doStart()
throws IOException
{
if (streams != null)
for (AbstractPushBufferStream stream : streams)
stream.start();
}
/**
* Stops the transfer of media data from this <tt>DataSource</tt>. Allows
* extenders to override and be sure that there will be no request to stop
* the transfer of media data if it has not been started yet.
*
* @throws IOException if anything goes wrong while stopping the transfer of
* media data from this <tt>DataSource</tt>
*/
protected synchronized void doStop()
throws IOException
{
if (streams != null)
for (AbstractPushBufferStream stream : streams)
stream.stop();
}
/**
* Gets the <tt>CaptureDeviceInfo</tt> of this <tt>CaptureDevice</tt> which
* describes it.
*
* @return the <tt>CaptureDeviceInfo</tt> of this <tt>CaptureDevice</tt>
* which describes it
*/
public CaptureDeviceInfo getCaptureDeviceInfo()
{
return getCaptureDeviceInfo(this);
}
/**
* Gets the <tt>CaptureDeviceInfo</tt> of a specific <tt>CaptureDevice</tt>
* by locating its registration in JMF using its <tt>MediaLocator</tt>.
*
* @param captureDevice the <tt>CaptureDevice</tt> to gets the
* <tt>CaptureDeviceInfo</tt> of
* @return the <tt>CaptureDeviceInfo</tt> of the specified
* <tt>CaptureDevice</tt> as registered in JMF
*/
public static CaptureDeviceInfo getCaptureDeviceInfo(
DataSource captureDevice)
{
/*
* TODO The implemented search for the CaptureDeviceInfo of this
* CaptureDevice by looking for its MediaLocator is inefficient.
*/
@SuppressWarnings("unchecked")
Vector<CaptureDeviceInfo> captureDeviceInfos
= (Vector<CaptureDeviceInfo>)
CaptureDeviceManager.getDeviceList(null);
MediaLocator locator = captureDevice.getLocator();
for (CaptureDeviceInfo captureDeviceInfo : captureDeviceInfos)
if (captureDeviceInfo.getLocator().equals(locator))
return captureDeviceInfo;
return null;
}
/**
* Gets the content type of the media represented by this instance. The
* <tt>AbstractPushBufferCaptureDevice</tt> implementation always returns
* {@link ContentDescriptor#RAW}.
*
* @return the content type of the media represented by this instance
*/
public String getContentType()
{
return ContentDescriptor.RAW;
}
/**
* Gets the control of the specified type available for this instance.
*
* @param controlType the type of the control available for this instance to
* be retrieved
* @return an <tt>Object</tt> which represents the control of the specified
* type available for this instance if such a control is indeed available;
* otherwise, <tt>null</tt>
*/
public Object getControl(String controlType)
{
return AbstractControls.getControl(this, controlType);
}
/**
* Implements {@link Controls#getControls()}. Gets the controls available
* for this instance.
*
* @return an array of <tt>Object</tt>s which represent the controls
* available for this instance
*/
public Object[] getControls()
{
FormatControl[] formatControls = internalGetFormatControls();
if ((formatControls == null) || (formatControls.length == 0))
return ControlsAdapter.EMPTY_CONTROLS;
else
{
Object[] controls = new Object[formatControls.length];
System.arraycopy(
formatControls,
0,
controls,
0,
formatControls.length);
return controls;
}
}
/**
* Gets the duration of the media represented by this instance. The
* <tt>AbstractPushBufferCaptureDevice</tt> always returns
* {@link #DURATION_UNBOUNDED}.
*
* @return the duration of the media represented by this instance
*/
public Time getDuration()
{
return DURATION_UNBOUNDED;
}
/**
* Gets the <tt>Format</tt> which is to be reported by a specific
* <tt>FormatControl</tt> for a <tt>PushBufferStream</tt> at a specific
* zero-based index in the list of streams of this
* <tt>PushBufferDataSource</tt>.
*
* @param streamIndex the zero-based index of the <tt>PushBufferStream</tt>
* in the list of streams of this <tt>PushBufferDataSource</tt> for which
* the specified <tt>FormatControl</tt> is to report a <tt>Format</tt>
* @param formatControl the <tt>FormatControl</tt> which si to report the
* <tt>Format</tt>
* @return the <tt>Format</tt> which is to be reported by
* <tt>formatControl</tt> as the format of the <tt>PushBufferStream</tt> at
* the specified <tt>streamIndex</tt> in the list of streams of this
* <tt>PushBufferDataSource</tt>
*/
protected Format getFormat(int streamIndex, FormatControl formatControl)
{
Format[] supportedFormats
= getSupportedFormats(streamIndex, formatControl);
return
((supportedFormats == null) || (supportedFormats.length < 1))
? null
: supportedFormats[0];
}
/**
* Gets an array of <tt>FormatControl</tt> instances each one of which can
* be used before {@link #connect()} to get and set the capture
* <tt>Format</tt> of each one of the capture streams.
*
* @return an array of <tt>FormatControl</tt> instances each one of which
* can be used before {@link #connect()} to get and set the capture
* <tt>Format</tt> of each one of the capture streams
*/
public FormatControl[] getFormatControls()
{
return AbstractFormatControl.getFormatControls(this);
}
/**
* Gets the <tt>PushBufferStream</tt>s through which this
* <tt>PushBufferDataSource</tt> gives access to its media data.
*
* @return an array of the <tt>PushBufferStream</tt>s through which this
* <tt>PushBufferDataSource</tt> gives access to its media data
*/
public synchronized PushBufferStream[] getStreams()
{
if (streams == null)
{
FormatControl[] formatControls = internalGetFormatControls();
if (formatControls != null)
{
int formatControlCount = formatControls.length;
streams = new AbstractPushBufferStream[formatControlCount];
for (int i = 0; i < formatControlCount; i++)
streams[i] = createStream(i, formatControls[i]);
/*
* Start the streams if this DataSource has already been
* started.
*/
if (started)
for (AbstractPushBufferStream stream : streams)
try
{
stream.start();
}
catch (IOException ioex)
{
throw new UndeclaredThrowableException(ioex);
}
}
}
if (streams == null)
return EMPTY_STREAMS;
else
{
PushBufferStream[] clone = new PushBufferStream[streams.length];
System.arraycopy(streams, 0, clone, 0, streams.length);
return clone;
}
}
/**
* Gets the <tt>Format</tt>s which are to be reported by a specific
* <tt>FormatControl</tt> as supported formats for a
* <tt>PushBufferStream</tt> at a specific zero-based index in the list of
* streams of this <tt>PushBufferDataSource</tt>.
*
* @param streamIndex the zero-based index of the <tt>PushBufferStream</tt>
* for which the specified <tt>FormatControl</tt> is to report the list of
* supported <tt>Format</tt>s
* @param formatControl the <tt>FormatControl</tt> which is to reports a
* list of supported <tt>Format</tt>s
* @return an array of <tt>Format</tt>s to be reported by
* <tt>formatControl</tt> as the supported formats for the
* <tt>PushBufferStream</tt> at the specified <tt>streamIndex</tt> in the
* list of streams of this <tt>PushBufferDataSource</tt>
*/
protected Format[] getSupportedFormats(
int streamIndex,
FormatControl formatControl)
{
CaptureDeviceInfo captureDeviceInfo = getCaptureDeviceInfo();
return
(captureDeviceInfo == null) ? null : captureDeviceInfo.getFormats();
}
/**
* Gets an array of <tt>FormatControl</tt> instances each one of which can
* be used before {@link #connect()} to get and set the capture
* <tt>Format</tt> of each one of the capture streams.
*
* @return an array of <tt>FormatControl</tt> instances each one of which
* can be used before {@link #connect()} to get and set the capture
* <tt>Format</tt> of each one of the capture streams
*/
private synchronized FormatControl[] internalGetFormatControls()
{
if (formatControls == null)
formatControls = createFormatControls();
return formatControls;
}
/**
* Starts the transfer of media data from this <tt>DataSource</tt>
*
* @throws IOException if anything goes wrong while starting the transfer of
* media data from this <tt>DataSource</tt>
*/
public synchronized void start()
throws IOException
{
if (!started)
{
if (!connected)
throw
new IOException(
getClass().getSimpleName() + " not connected");
doStart();
started = true;
}
}
/**
* Stops the transfer of media data from this <tt>DataSource</tt>.
*
* @throws IOException if anything goes wrong while stopping the transfer of
* media data from this <tt>DataSource</tt>
*/
public synchronized void stop()
throws IOException
{
if (started)
{
doStop();
started = false;
}
}
}

@ -6,9 +6,14 @@
*/
package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol;
import java.io.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.protocol.*;
import net.java.sip.communicator.impl.neomedia.control.*;
import net.java.sip.communicator.util.*;
/**
* Provides a base implementation of <tt>PushBufferStream</tt> in order to
@ -18,10 +23,17 @@
* @author Lubomir Marinov
*/
public abstract class AbstractPushBufferStream
extends ControlsAdapter
extends AbstractControls
implements PushBufferStream
{
/**
* The <tt>Logger</tt> used by the <tt>AbstractPushBufferStream</tt> class
* and its instances for logging output.
*/
private static final Logger logger
= Logger.getLogger(AbstractPushBufferStream.class);
/**
* The (default) <tt>ContentDescriptor</tt> of the
* <tt>AbstractPushBufferStream</tt> instances.
@ -29,12 +41,49 @@ public abstract class AbstractPushBufferStream
private static final ContentDescriptor CONTENT_DESCRIPTOR
= new ContentDescriptor(ContentDescriptor.RAW);
/**
* The <tt>FormatControl</tt> which gives access to the <tt>Format</tt> of
* the media data provided by this <tt>SourceStream</tt> and which,
* optionally, allows setting it.
*/
private final FormatControl formatControl;
/**
* The <tt>BufferTransferHandler</tt> which is notified by this
* <tt>PushBufferStream</tt> when data is available for reading.
*/
protected BufferTransferHandler transferHandler;
/**
* Initializes a new <tt>AbstractPushBufferStream</tt> instance which is to
* have its <tt>Format</tt>-related information abstracted by a specific
* <tt>FormatControl</tt>.
*
* @param formatControl the <tt>FormatControl</tt> which is to abstract the
* <tt>Format</tt>-related information of the new instance
*/
protected AbstractPushBufferStream(FormatControl formatControl)
{
this.formatControl = formatControl;
}
/**
* Releases the resources used by this instance throughout its existence and
* makes it available for garbage collection. This instance is considered
* unusable after closing.
*/
public void close()
{
try
{
stop();
}
catch (IOException ioex)
{
logger.error("Failed to stop " + getClass().getSimpleName(), ioex);
}
}
/**
* Determines whether the end of this <tt>SourceStream</tt> has been
* reached. The <tt>AbstractPushBufferStream</tt> implementation always
@ -76,6 +125,33 @@ public long getContentLength()
return LENGTH_UNKNOWN;
}
/**
* Implements {@link Controls#getControls()}. Gets the controls available
* for this instance.
*
* @return an array of <tt>Object</tt>s which represent the controls
* available for this instance
*/
public Object[] getControls()
{
if (formatControl != null)
return new Object[] { formatControl };
else
return ControlsAdapter.EMPTY_CONTROLS;
}
/**
* Gets the <tt>Format</tt> of the media data made available by this
* <tt>PushBufferStream</tt>.
*
* @return the <tt>Format</tt> of the media data made available by this
* <tt>PushBufferStream</tt>
*/
public Format getFormat()
{
return (formatControl == null) ? null : formatControl.getFormat();
}
/**
* Sets the <tt>BufferTransferHandler</tt> which is to be notified by this
* <tt>PushBufferStream</tt> when data is available for reading.
@ -88,4 +164,22 @@ public void setTransferHandler(BufferTransferHandler transferHandler)
{
this.transferHandler = transferHandler;
}
/**
* Starts the transfer of media data from this <tt>PushBufferStream</tt>.
*
* @throws IOException if anything goes wrong while starting the transfer of
* media data from this <tt>PushBufferStream</tt>
*/
public abstract void start()
throws IOException;
/**
* Stops the transfer of media data from this <tt>PushBufferStream</tt>.
*
* @throws IOException if anything goes wrong while stopping the transfer of
* media data from this <tt>PushBufferStream</tt>
*/
public abstract void stop()
throws IOException;
}

@ -7,15 +7,12 @@
package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol.imgstreaming;
import java.awt.*;
import java.io.*;
import javax.media.*;
import javax.media.format.*;
import javax.media.protocol.*;
import javax.media.control.*;
import javax.media.format.*;
import net.java.sip.communicator.impl.neomedia.control.*;
import net.java.sip.communicator.util.*;
import net.java.sip.communicator.impl.neomedia.jmfext.media.protocol.*;
/**
* DataSource for our image streaming (which is used for
@ -26,61 +23,27 @@
* @author Damian Minkov
*/
public class DataSource
extends PushBufferDataSource
implements CaptureDevice
extends AbstractPushBufferCaptureDevice
{
/**
* The <tt>Logger</tt>.
*/
private static final Logger logger = Logger.getLogger(DataSource.class);
/**
* <tt>DataSource</tt> connection state.
*/
private boolean connected = false;
/**
* <tt>DataSource</tt> start state.
*/
private boolean started = false;
/**
* The JMF controls (which are likely of type <tt>Control</tt>) available
* for this <tt>DataSource</tt>.
*/
private final Object[] controls = { new FormatControlImpl() };
/**
* Image stream.
*/
private ImageStream stream = null;
/**
* The value of the <tt>streams</tt> property of <tt>DataSource</tt> which
* represents an empty array of <tt>PushBufferStream</tt>s i.e. no
* <tt>ImageStream</tt>s in <tt>DataSource</tt>. Explicitly defined in
* order to reduce unnecessary allocations.
* Array of supported formats.
*/
private static final PushBufferStream[] EMPTY_STREAMS =
new PushBufferStream[0];
private static final Format formats[];
/**
* Resolution supported for image.
* Resolutions supported for image. Listed in decreasing order of
* preference.
*/
private static final Dimension res[] = new Dimension[] {
new Dimension(128,96),
new Dimension(176, 144),
new Dimension(320, 240),
new Dimension(352, 288),
new Dimension(704, 576),
new Dimension(720, 480),
new Dimension(704, 576),
new Dimension(352, 288),
new Dimension(320, 240),
new Dimension(176, 144),
new Dimension(128,96),
};
/**
* Array of supported formats.
*/
private static final Format formats[];
static
{
/* initialize supported format array */
@ -103,23 +66,6 @@ public class DataSource
}
}
/**
* Constructor.
*/
public DataSource()
{
}
/**
* Constructor.
*
* @param locator associated <tt>MediaLocator</tt>
*/
public DataSource(MediaLocator locator)
{
setLocator(locator);
}
/**
* Get supported formats.
*
@ -131,202 +77,43 @@ public static Format[] getFormats()
}
/**
* Get the JMF streams.
*
* @return streams (one element in image streaming case)
*/
public PushBufferStream[] getStreams()
{
if(stream == null)
{
stream = new ImageStream(getLocator());
/* XXX allow to select other format */
stream.setFormat(getFormats()[5]);
}
return (stream == null) ? EMPTY_STREAMS :
new PushBufferStream[] {stream};
}
/**
* Initialize <tt>DataSource</tt>.
*
* @throws IOException if initialization problem occurred
*/
public void connect() throws IOException
{
if(connected)
{
return;
}
connected = true;
}
/**
* Disconnect datasource.
*/
public void disconnect()
{
connected = false;
}
/**
* Get content type.
*
* @return RAW content type
*/
public String getContentType()
{
return ContentDescriptor.RAW;
}
/**
* Get duration for this source which is unknown.
*
* @return DURATION_UNKNOWN
*/
public Time getDuration()
{
return DURATION_UNKNOWN;
}
/**
* Gives control information to the caller.
*
* @return the collection of object controls.
*/
public Object[] getControls()
{
/*
* The field controls is private so we cannot directly return it.
* Otherwise, the caller will be able to modify it.
*/
return controls.clone();
}
/**
* Return required control from the Control[] array
* if exists.
*
* @param controlType the control we are interested in.
* @return the object that implements the control, or null if not found
*/
public Object getControl(String controlType)
{
return AbstractControls.getControl(this, controlType);
}
/**
* Get the <tt>CaptureDeviceInfo</tt> associated
* with this datasource.
*
* @return <tt>CaptureDeviceInfo</tt> associated
*/
public CaptureDeviceInfo getCaptureDeviceInfo()
{
MediaLocator locator = getLocator();
return
new CaptureDeviceInfo(
locator.getRemainder(),
locator,
getFormatControls()[0].getSupportedFormats());
}
/**
* Get supported <tt>FormatControl</tt>.
*
* @return array of supported <tt>FormatControl</tt>
* Constructor.
*/
public FormatControl[] getFormatControls()
public DataSource()
{
return AbstractFormatControl.getFormatControls(this);
}
/**
* Start capture.
* Constructor.
*
* @throws IOException
*/
public void start() throws IOException
{
/* DataSource already started, do not care */
if(started)
{
return;
}
if(!connected)
{
throw new IOException("DataSource must be connected!");
}
stream.start();
started = true;
}
/**
* Stop capture.
* @param locator associated <tt>MediaLocator</tt>
*/
public void stop()
public DataSource(MediaLocator locator)
{
if(started)
{
started = false;
stream.stop();
}
super(locator);
}
/**
* Implementation of <tt>FormatControl</tt> for this <tt>DataSource</tt> instance.
* Create a new <tt>PushBufferStream</tt> which is to be at a specific
* zero-based index in the list of streams of this
* <tt>PushBufferDataSource</tt>. The <tt>Format</tt>-related information of
* the new instance is to be abstracted by a specific
* <tt>FormatControl</tt>.
*
* @author Sebastien Vincent
*/
private class FormatControlImpl
extends AbstractFormatControl
{
/**
* Current format used.
*/
private Format format = formats[0];
/**
* Set the format used.
*
* @param format format to use
* @return format used or null if format is not supported
*/
@Override
public Format setFormat(Format format)
{
Format f = AbstractFormatControl.setFormat(this, format);
if(f != null)
this.format = f;
return f;
}
/**
* Get current format used.
*
* @return the <tt>Format</tt> of this <tt>DataSource</tt>
*/
public Format getFormat()
{
return format;
}
/**
* Get supported formats.
*
* @return an array of <tt>Format</tt> element type which lists the JMF
* formats supported by this <tt>DataSource</tt> i.e. the ones in which
* it is able to output
*/
public Format[] getSupportedFormats()
{
return formats.clone();
}
* @param streamIndex the zero-based index of the <tt>PushBufferStream</tt>
* in the list of streams of this <tt>PushBufferDataSource</tt>
* @param formatControl the <tt>FormatControl</tt> which is to abstract the
* <tt>Format</tt>-related information of the new instance
* @return a new <tt>PushBufferStream</tt> which is to be at the specified
* <tt>streamIndex</tt> in the list of streams of this
* <tt>PushBufferDataSource</tt> and which has its <tt>Format</tt>-related
* information abstracted by the specified <tt>formatControl</tt>
* @see AbstractPushBufferCaptureDevice#createStream(int, FormatControl)
*/
protected AbstractPushBufferStream createStream(
int streamIndex,
FormatControl formatControl)
{
return new ImageStream(formatControl);
}
}

@ -11,6 +11,7 @@
import java.io.*;
import javax.media.*;
import javax.media.control.*;
import javax.media.format.*;
import javax.media.protocol.*;
@ -36,11 +37,6 @@ public class ImageStream
*/
private static final Logger logger = Logger.getLogger(ImageStream.class);
/**
* Current format used.
*/
private Format currentFormat = null;
/**
* Sequence number.
*/
@ -67,39 +63,15 @@ public class ImageStream
private DesktopInteract desktopInteract = null;
/**
* Constructor.
*/
public ImageStream()
{
}
/**
* Constructor.
* Initializes a new <tt>ImageStream</tt> instance which is to have a
* specific <tt>FormatControl</tt>
*
* @param locator <tt>MediaLocator</tt> to use
* @param formatControl the <tt>FormatControl</tt> of the new instance which
* is to specify the format in which it is to provide its media data
*/
public ImageStream(MediaLocator locator)
ImageStream(FormatControl formatControl)
{
}
/**
* Set format to use.
*
* @param format new format to use
*/
public void setFormat(Format format)
{
currentFormat = format;
}
/**
* Returns the supported format by this stream.
*
* @return supported formats
*/
public Format getFormat()
{
return currentFormat;
super(formatControl);
}
/**
@ -142,6 +114,8 @@ public void read(Buffer buffer)
/**
* Start desktop capture stream.
*
* @see AbstractPushBufferStream#start()
*/
public void start()
{
@ -149,13 +123,21 @@ public void start()
{
logger.info("Start stream");
captureThread = new Thread(this);
captureThread.start();
/*
* Set the started indicator before calling Thread#start() because
* the Thread may exist upon start if Thread#run() starts executing
* before setting the started indicator.
*/
started = true;
captureThread.start();
}
}
/**
* Stop desktop capture stream.
*
* @see AbstractPushBufferStream#stop()
*/
public void stop()
{
@ -169,7 +151,7 @@ public void stop()
*/
public void run()
{
final RGBFormat format = (RGBFormat)currentFormat;
final RGBFormat format = (RGBFormat) getFormat();
Dimension formatSize = format.getSize();
final int width = (int)formatSize.getWidth();
final int height = (int)formatSize.getHeight();
@ -208,7 +190,7 @@ public void run()
buf.setData(data);
buf.setOffset(0);
buf.setLength(data.length);
buf.setFormat(currentFormat);
buf.setFormat(format);
buf.setHeader(null);
buf.setTimeStamp(System.nanoTime());
buf.setSequenceNumber(seqNo++);

@ -7,7 +7,6 @@
package net.java.sip.communicator.impl.neomedia.jmfext.media.protocol.portaudio;
import java.io.*;
import java.util.*;
import javax.media.*;
import javax.media.control.*;
@ -16,6 +15,7 @@
import net.java.sip.communicator.impl.neomedia.*;
import net.java.sip.communicator.impl.neomedia.control.*;
import net.java.sip.communicator.impl.neomedia.jmfext.media.protocol.*;
import net.java.sip.communicator.impl.neomedia.portaudio.*;
import net.java.sip.communicator.util.*;
@ -144,20 +144,7 @@ public synchronized void disconnect()
*/
public CaptureDeviceInfo getCaptureDeviceInfo()
{
/*
* TODO The implemented search for the CaptureDeviceInfo of this
* CaptureDevice by looking for its MediaLocator is inefficient.
*/
@SuppressWarnings("unchecked")
Vector<CaptureDeviceInfo> captureDeviceInfos
= (Vector<CaptureDeviceInfo>)
CaptureDeviceManager.getDeviceList(null);
MediaLocator locator = getLocator();
for (CaptureDeviceInfo captureDeviceInfo : captureDeviceInfos)
if (captureDeviceInfo.getLocator().equals(locator))
return captureDeviceInfo;
return null;
return AbstractPushBufferCaptureDevice.getCaptureDeviceInfo(this);
}
/**

Loading…
Cancel
Save