Java: CaptureDeviceManager#getDeviceList() is empty?

5.3k Views Asked by At

I am trying to print out all of the capture devices that are supported using the #getDeviceList() method in the CaptureDeviceManager class and the returned Vector has a size of 0.

Why is that? I have a webcam that works - so there should be at least one. I am running Mac OS X Lion - using JMF 2.1.1e.

Thanks!

3

There are 3 best solutions below

0
On

Before calling CaptureDeviceManager.getDeviceList(), the available devices must be loaded into the memory first.

You can do it manually by running JMFRegistry after installing JMF.

enter image description here

or do it programmatically with the help of the extension library FMJ (Free Media in Java). Here is the code:

import java.lang.reflect.Field;
import java.util.Vector;
import javax.media.*;
import javax.media.format.RGBFormat;
import net.sf.fmj.media.cdp.GlobalCaptureDevicePlugger;

public class FMJSandbox {
    static {
        System.setProperty("java.library.path", "D:/fmj-sf/native/win32-x86/");
        try {
            final Field sysPathsField = ClassLoader.class.getDeclaredField("sys_paths");
            sysPathsField.setAccessible(true);
            sysPathsField.set(null, null);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static void main(String args[]) {
        GlobalCaptureDevicePlugger.addCaptureDevices(); 
        Vector deviceInfo = CaptureDeviceManager.getDeviceList(new RGBFormat());
        System.out.println(deviceInfo.size());
        for (Object obj : deviceInfo ) {
            System.out.println(obj);
        }
    }
}

Here is the output:

USB2.0 Camera : civil:\\?\usb#vid_5986&pid_02d3&mi_00#7&584a19f&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global
RGB, -1-bit, Masks=-1:-1:-1, PixelStride=-1, LineStride=-1
1
On

CaptureDeviceManager.getDeviceList(Format format) does not detect devices. Instead it reads from the JMF registry which is the jmf.properties file. It searches for the jmf.properties file in the classpath.

If your JMF install has succeeded, then the classpath would have been configured to include all the relevant JMF jars and directories. The JMF install comes with a jmf.properties file included in the 'lib' folder under the JMF installation directory. This means the jmf.properties would be located by JMStudio and you would usually see the JMStudio application executing correctly. (If your JMF install is under 'C:\Program Files', then run as administrator to get around UAC)

When you create your own application to detect the devices, the problem you described above might occur. I have seen a few questions related to the same problem. This is because your application's classpath might be different and might not include the environment classpath. Check out your IDE's properties here. The problem is that CaptureDeviceManager cannot find the jmf.properties file because it is not there.

As you have found out correctly, you can copy the jmf.properties file from the JMF installation folder. It would contain the correct device list since JMF detects it during the install (Check it out just to make sure anyway).

If you want do device detection yourself, then create an empty jmf.properties file and put it somewhere in your classpath (it might throw a java.io.EOFException initially during execution but that's properly handled by the JMF classes). Then use the following code for detecting webcams...

import javax.media.*;
import java.util.*;

    public static void main(String[] args) {

    VFWAuto vfwObj = new VFWAuto();

    Vector devices = CaptureDeviceManager.getDeviceList(null);
    Enumeration deviceEnum = devices.elements();

    System.out.println("Device count : " + devices.size());

    while (deviceEnum.hasMoreElements()) {
        CaptureDeviceInfo cdi = (CaptureDeviceInfo) deviceEnum.nextElement();
        System.out.println("Device : " + cdi.getName());
    }

}

The code for the VFWAuto class is given below. This is part of the JMStudio source code. You can get a good idea on how the devices are detected and recorded in the registry. Put both classes in the same package when you test. Disregard the main method in the VFWAuto class.

import com.sun.media.protocol.vfw.VFWCapture;
import java.util.*;

import javax.media.*;

public class VFWAuto {

public VFWAuto() {
    Vector devices = (Vector) CaptureDeviceManager.getDeviceList(null).clone();
    Enumeration enum = devices.elements();

    while (enum.hasMoreElements()) {
        CaptureDeviceInfo cdi = (CaptureDeviceInfo) enum.nextElement();
        String name = cdi.getName();
        if (name.startsWith("vfw:"))
            CaptureDeviceManager.removeDevice(cdi);
    }

    int nDevices = 0;
    for (int i = 0; i < 10; i++) {
        String name = VFWCapture.capGetDriverDescriptionName(i);
        if (name != null && name.length() > 1) {
            System.err.println("Found device " + name);
            System.err.println("Querying device. Please wait...");
            com.sun.media.protocol.vfw.VFWSourceStream.autoDetect(i);
            nDevices++;
        }
    }
}

public static void main(String [] args) {
    VFWAuto a = new VFWAuto();
    System.exit(0);
}
}

Assuming you are on a Windows platform and you have a working web-cam, then this code should detect the device and populate the jmf.properties file. On the next run you can also comment out the VFWAuto section and it's object references and you can see that CaptureDeviceManager reads from the jmf.properties file.

The VFWAuto class is part of jmf.jar. You can also see the DirectSoundAuto and JavaSoundAuto classes for detecting audio devices in the JMStudio sample source code. Try it out the same way as you did for VFWAuto.

My configuration was Windows 7 64 bit + JMF 2.1.1e windows performance pack + a web-cam.

0
On

I had the same issue and I solved by invoking flush() on my ObjectInputStream object.

According to the API documentation for ObjectInputStream's constructor:

The stream header containing the magic number and version number are read from the stream and verified. This method will block until the corresponding ObjectOutputStream has written and flushed the header. This is a very important point to be aware of when trying to send objects in both directions over a socket because opening the streams in the wrong order will cause deadlock. Consider for example what would happen if both client and server tried to construct an ObjectInputStream from a socket's input stream, prior to either constructing the corresponding ObjectOutputStream. The ObjectInputStream constructor on the client would block, waiting for the magic number and version number to arrive over the connection, while at the same time the ObjectInputStream constructor on the server side would also block for the same reason. Hence, deadlock.

Because of this, you should always make it a practice in your code to open the ObjectOutputStream and flush it first, before you open the ObjectInputStream. The ObjectOutputStream constructor will not block, and invoking flush() will force the magic number and version number to travel over the wire. If you follow this practice in both your client and server, you shouldn't have a problem with deadlock.

Credit goes to Tim Rohaly and his explanation here.