Java:CaptureDeviceManager#getDeviceList() 为空?

发布于 2024-12-25 12:52:36 字数 180 浏览 1 评论 0原文

我试图打印出使用 CaptureDeviceManager 类中的 #getDeviceList() 方法支持的所有捕获设备,并且返回的 Vector 的大小为 0。

这是为什么?我有一个可以使用的网络摄像头 - 所以应该至少有一个。我正在运行 Mac OS X Lion - 使用 JMF 2.1.1e。

谢谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

痕至 2025-01-01 12:52:36

CaptureDeviceManager.getDeviceList(Format format) 未检测到设备。相反,它从 JMF 注册表中读取,即 jmf.properties 文件。它在类路径中搜索jmf.properties文件。

如果您的 JMF 安装成功,那么类路径将被配置为包含所有相关的 JMF jar 和目录。 JMF 安装附带一个 jmf.properties 文件,该文件包含在 JMF 安装目录下的“lib”文件夹中。这意味着 jmf.properties 将由 JMStudio 定位,并且您通常会看到 JMStudio 应用程序正确执行。 (如果您的 JMF 安装在“C:\Program Files”下,则以管理员身份运行以绕过 UAC

当您创建自己的应用程序来检测设备时,可能会出现上述问题。我看到了一些与同一问题相关的问题。这是因为您的应用程序的类路径可能不同,并且可能不包含环境类路径。在此处查看您的 IDE 的属性。问题是 CaptureDeviceManager 无法找到 jmf.properties 文件,因为它不存在。

正如您所发现的,您可以从 JMF 安装文件夹复制 jmf.properties 文件。它将包含正确的设备列表,因为 JMF 在安装过程中检测到它(无论如何都要检查一下以确保)。

如果您想自己进行设备检测,则创建一个空的 jmf.properties 文件并将其放在类路径中的某个位置(最初在执行过程中可能会抛出 java.io.EOFException,但这由 JMF 类正确处理)。然后使用以下代码来检测网络摄像头...

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());
    }

}

VFWAuto 类的代码如下所示。这是 JMStudio 源代码的一部分。您可以很好地了解如何检测设备并将其记录在注册表中。测试时将这两个类放在同一个包中。忽略 VFWAuto 类中的 main 方法。

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);
}
}

假设您使用的是 Windows 平台并且有一个可用的网络摄像头,那么此代码应该检测设备并填充 jmf.properties 文件。在下次运行时,您还可以注释掉 VFWAuto 部分及其对象引用,并且您可以看到 CaptureDeviceManager 从 jmf.properties 文件中读取。

VFWAuto 类是 jmf.jar 的一部分。您还可以在 JMStudio 示例源代码中查看用于检测音频设备的 DirectSoundAuto 和 JavaSoundAuto 类。按照与 VFWAuto 相同的方式进行尝试。

我的配置是 Windows 7 64 位 + JMF 2.1.1e Windows 性能包 + 网络摄像头。

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.

鸠魁 2025-01-01 12:52:36

我遇到了同样的问题,我通过在 ObjectInputStream 对象上调用 flush() 来解决。

根据 ObjectInputStream 构造函数的 API 文档:

从流中读取包含幻数和版本号的流标头并进行验证。此方法将阻塞,直到相应的 ObjectOutputStream 写入并刷新标头。
当尝试通过套接字双向发送对象时,这是需要注意的非常重要的一点,因为以错误的顺序打开流将导致死锁。
例如,考虑一下如果客户端和服务器在构造相应的 ObjectOutputStream 之前都尝试从套接字的输入流构造 ObjectInputStream 会发生什么情况。客户端上的 ObjectInputStream 构造函数将阻塞,等待幻数和版本号通过连接到达,而同时服务器端的 ObjectInputStream 构造函数将阻塞也因为同样的原因而被阻止。因此,陷入僵局。

因此,您应该始终在代码中养成这样的习惯:在打开 ObjectInputStream 之前,首先打开 ObjectOutputStream 并刷新它。 ObjectOutputStream 构造函数不会阻塞,并且调用 flush() 将强制幻数和版本号通过网络传输。如果您在客户端和服务器中都遵循这种做法,则不会出现死锁问题。

感谢 Tim Rohaly 和他的解释此处

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.

一抹淡然 2025-01-01 12:52:36

在调用CaptureDeviceManager.getDeviceList()之前,必须先将可用设备加载到内存中。

您可以在安装 JMF 后通过运行 JMFRegistry 手动执行此操作。

输入图片此处的描述

或借助扩展库 FMJ(Java 中的自由媒体)以编程方式执行此操作。这是代码:

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);
        }
    }
}

这是输出:

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文