How to capture video using JMF, but without installing JMF
Asked Answered
G

1

14

A videoconferencing project I was working on used JMF to capture video and audio, and transmit it to another endpoint. An issue was that my team didn't want the user of the product to have to install JMF.

I thought it might be worthwhile to share our solution to this problem. It works. It works well. My question to you is: does anyone have a better way to do it?

Environment: Windows, XP and above

  1. Download JMF for Windows
  2. Install it on your machine

  3. Locate the following dlls in the system32 folder after jmf installs:

    jmacm.dll
    jmam.dll
    jmcvid.dll
    jmdaud.dll
    jmdaudc.dll
    jmddraw.dll
    jmfjawt.dll
    jmg723.dll
    jmgdi.dll
    jmgsm.dll
    jmh261.dll
    jmh263enc.dll
    jmjpeg.dll
    jmmci.dll
    jmmpa.dll
    jmmpegv.dll
    jmutil.dll
    jmvcm.dll
    jmvfw.dll
    jmvh263.dll
    jsound.dll

  4. Copy the dlls into a temporary folder

  5. Locate the jmf.properties file (Do a search on your computer for it)
  6. Download the JMF source code
    In the source code, find the following files:

JMFinit.java
JMRPropertiesGen.java
Registry.java
RegistryGen.java

  1. Create a package; I'll call it JMFNoInstall
  2. Add the files listed in step 6
  3. Add a class called Main to this package as such:

 

package JMFNoInstall;
// add your imports and whatnot here
public class Main()
{
    public Main()
    {
        JMFinit.main(null);
        JMFPropertiesGen.main(null);
        Registry.main(null);
        RegistryGen.main(new String[] {
            new File(".").getAbsolutePath(),
            "registrylib"
        });
    }
}

The jmf.properties file needs to go in the same folder as the class that has your main method or the same folder as the JAR archive that contains the main method.
The dlls need to go into the win32 folder. You can have your program check to see if they are in the win32 folder. If they are not, you can have it copy them over from some location. The jmf.properties file gets updated whenever the the Main class listed above runs. You only need to run this once, the first time the program is ever run, or if the user would like to add new capture devices. Lastly, just make sure the jmf.jar file and jmfcom.jar that comes along with the Windows JMF download is included in the classpath. You're good to go at this point. All the functionality of JMF without actually having to install it.

There really isn't a lot of work involved with this, and you can incorporate it into your custom installer quite easily.

Has anyone found a better way to do this though? There are a few pitfalls of doing it this way.

EDIT: I thought it might be worthwhile to share some of the code that I created. Of course youll need to modify it to handle what you. It prob wont compile, but the stuff that is missing should be easy enough to recreate. But thought it might be a good starting point to help people. The detectCaptureDevices function is probably what will help most people. Ill update this class as I go.


import GUI.Window;
import GlobalUtilities.OS;
import GlobalUtilities.ProgressBar;
import GlobalUtilities.FileUtilities;
import java.io.File;
import java.util.ArrayList;
import java.util.Vector;
import javax.swing.text.Utilities;


/**
 * This class providex easy access to the most needed info about JMF. You can test
 * a JMF install (Windows only currently) and also get info about the captrue
 * devices hooked up to JMF.
 * @author dvargo
 */
public class JMFRunner
{
    /**
     * Show the status of operations
     */
    final ProgressBar theBar = new ProgressBar();
    /**
     * Location where the dll's JMF relies on need to be placed
     */
    final String windowsDllFolder = "C:\\WINDOWS\\system32\\";

    final String linuxDllFolder = "/usr/lib/";

    /**
     * Dll's that JMF uses
     */
    final String[] windowsDllList = new String[]{
        "jmacm.dll",
        "jmam.dll",
        "jmcvid.dll",
        "jmdaud.dll",
        "jmdaudc.dll",
        "jmddraw.dll",
        "jmfjawt.dll",
        "jmg723.dll",
        "jmgdi.dll",
        "jmgsm.dll",
        "jmh261.dll",
        "jmh263enc.dll",
        "jmjpeg.dll",
        "jmmci.dll",
        "jmmpa.dll",
        "jmmpegv.dll",
        "jmutil.dll",
        "jmvcm.dll",
        "jmvfw.dll",
        "jmvh263.dll",
        "jsound.dll"};

    String[] linuxDllList = new String[]{
        "libjmcvid.so",
        "libjmdaud.so",
        "libjmfjawt.so",
        "libjmg723.so",
        "libjmgsm.so",
        "libjmh261.so",
        "libjmh263enc.so",
        "libjmjpeg.so",
        "libjmmpa.so",
        "libjmmpegv.so",
        "libjmmpx.so",
        "libjmutil.so",
        "libjmv4l.so",
        "libjmxlib.so"
    };

    String [] dlls= null;
    String dir = null;

    /**
     * List of the video capture devices found by JMF
     */
    Vector videoDevices = null;
    /**
     * List of the audio capture devices found by JMF
     */
    Vector audioDevices = null;

    public JMFRunner()
    {
        if(OS.isWindows())
        {
            dlls = windowsDllList;
            dir = windowsDllFolder;
        }
        else if(OS.isLinux())
        {
            dlls = linuxDllList;
            dir = linuxDllFolder;
        }
        else
        {
            Window.getLogger().severe("Operating system does not support JMF");
        }

    }

    /**
     * Adds new capture devices
     */
    public void detectCaptureDecives()
    {


        Thread theTread = new Thread(theBar);
        theTread.start();
        theBar.repaint();

        JMFInit.main(new String[] {""});
        JMFPropertiesGen.main(new String[] {""});
        Registry.main(new String[] {""});
        RegistryGen.main(new String[] {"-d",
            new File(".").getAbsolutePath(),
            "registrylib"
        });

        theBar.setMessage("");
        theBar.stop();
    }

    /**
     * Verifies that all the dll's that JMF needs are in their correct spot
     * @return True if all dlls are in their correct spot, false otherwise
     */
    public boolean detectDlls()
    {
        boolean retVal = true;
        String currFile;
        for(String currDll : dlls)
        {
            currFile = dir + currDll;
            if(! new File(currFile).exists())
            {
                Window.getLogger().severe("Can not find dll " + currFile + " for JMF");
                retVal = false;
            }
        }
        return retVal;
    }

    //Doesnt work quite yet
    public boolean installLibraryFiles()
    {
        boolean retVal = true;
        String currFile;
        for(String currDll : dlls)
        {
            currFile = dir + currDll;
            File newDll = new File(currFile);
            //see if this dll is already there
            if(!newDll.exists())
            {
                //its not there so lets copy it
                try
                {
                    FileUtilities.copy(newDll,FileUtilities.getResourceFile("/JMFManager/Resources/"+currDll,currDll));
                }
                catch(Exception e)
                {
                    retVal =  false;
                }
            }
        }
        return retVal;
    }

    /**
     * Returns the location of the jmf.properties file that STix is using
     * @return THe locaiton of the JMF properties
     */
    public String getJMFPropertiesFileLocation()
    {
        return Registry.getJMFPropertiesFileLocation();
    }

    /**
     * Returns a list of the audio devices found by JMF
     * @return Returns an Arraylist containing info about the audio capture devices
     */
    public ArrayList getAudioDevices()
    {
        DeviceFinder df = new DeviceFinder();
        audioDevices = df.getSoundCaptureDevices();
        return new ArrayList(audioDevices);
    }

    /**
     * Returns a list of the video decives deteced by JMF
     * @return returns an arraylist with info of the video capture devices
     */
    public ArrayList getVideoDevices()
    {
        DeviceFinder df = new DeviceFinder();
        videoDevices = df.getVideoCaptureDevices();
        return new ArrayList(videoDevices);
    }


    public static void main(String [] args)
    {
        JMFRunner x = new JMFRunner();
        //x.detectCaptureDecives();
        x.installLibraryFiles();
        System.out.println(x.detectDlls());
        System.out.println(x.getJMFPropertiesFileLocation());
        System.out.println(x.getAudioDevices());
        System.out.println(x.getVideoDevices());
    }
}

DeviceFinder.java


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

/**
 * this class gets information about capture devices (mics and cameras)
 */
public class DeviceFinder {

    Vector videoDevices = new Vector();
    Vector audioDevices = new Vector();

   /**
   * Constructor
   * Creates a new DeviceFinder
   */
   public DeviceFinder()
   {
      /*retrieve ALL video and audio devices*/
      videoDevices = CaptureDeviceManager.getDeviceList(new VideoFormat(null));
      audioDevices = CaptureDeviceManager.getDeviceList(new AudioFormat(null));
   }

   /**
   * purpose:  Get information on all Video capture devices on the system
   * @return java.util.Vector 
a vector of attributes */ public Vector getVideoCaptureDevices() { return videoDevices; } /** * purpose: Get information on all audio capture devices on the system * @return java.util.Vector
a vector of attributes */ public Vector getSoundCaptureDevices() { return audioDevices; } /** * retrieve the first video capture device */ public CaptureDeviceInfo getPrimaryVideoCaptureDevice() { return (CaptureDeviceInfo)videoDevices.get(0); } /*retrieve the first audio capture device*/ public CaptureDeviceInfo getPrimaryAudioCaptureDevice() { return (CaptureDeviceInfo)audioDevices.get(0); } /** * get the first video device name * @return String
the name of the video device */ public String getVideoCaptureDeviceName() { return ((CaptureDeviceInfo)videoDevices.get(0)).getName(); } /** * get the first audio device name * @return String
the name of the audio device */ public String getAudioCaptureDeviceName() { return ((CaptureDeviceInfo)audioDevices.get(0)).getName(); } /** * get the first video device media locator * @return MediaLocator */ public MediaLocator getVideoMediaLocator() { return ((CaptureDeviceInfo)videoDevices.get(0)).getLocator(); } /** * get the first audio device media locator * @return MediaLocator */ public MediaLocator getAudioMediaLocator() { return ((CaptureDeviceInfo)audioDevices.get(0)).getLocator(); } /** * get the video device media locator at index idx * @param idx index of the media locator (0 is the first/default, * as ordered by *
the JMFRegistry) * @return MediaLocator */ public MediaLocator getVideoMediaLocator(int idx) { if(idx >= videoDevices.size()) { return null; } return ((CaptureDeviceInfo)videoDevices.get(idx)).getLocator(); } /** * get the audio device media locator at index idx * @param idx index of the audio device (as ordered by the JMFRegistry) * @return MediaLocator */ public MediaLocator getAudioMediaLocator(int idx) { return ((CaptureDeviceInfo)audioDevices.get(idx)).getLocator(); } /** * * @param args */ public static void main(String[] args) { DeviceFinder df = new DeviceFinder(); //DEBUG: System.out.println(df.getVideoMediaLocator()); System.out.println(df.getAudioMediaLocator()); } }
Greatgranduncle answered 28/10, 2010 at 19:48 Comment(16)
It would be a better idea to share some informations more on this example/sample codes and few steps, i was also trying similar. And was thinking to move to JMF or Native access ?Stoke
@Stackfan I have added some of my code if that helps anyoneGreatgranduncle
@Greatgranduncle You say "The dlls need to go into the win32 folder" you mean system32?Deliciadelicious
@Deliciadelicious Yes, sorry. I meant the system32 folder. I really need to update this post. I have simplified this process greatly.Greatgranduncle
@Greatgranduncle Update it! That'd be great. I followed along but I'm getting errors linking to the DLLs after they are extracted. I get this on the computers I run it on. Do you have any insight here? pastie.org/private/ug8gdu8ohj3fxpasgumpw -- I was able to put this project on hold, but 8 weeks later I'm back on it.Deliciadelicious
@Deliciadelicious Sure, Ill update in a bit here. I have a nice little class that takes care of it all now. For your errors, what OS are you running?Greatgranduncle
@Greatgranduncle actually, it looks like I am running into the same issues you were -- #5354942 -- whats the solution?Deliciadelicious
@Greatgranduncle Developing on Windows7. I get the exceptions in XP virtual machine, and on an xp laptop.Deliciadelicious
Might be a dumb question, but are the dll's definitely in the System32 folderGreatgranduncle
They are in c:\windows\system32. Is that what you found when you got "Can't load this .dll (machine code=0x0) on a IA 32-bit platform" -- that they weren't in place?Deliciadelicious
I am using final String windowsDllFolder = new File(System.getenv("WINDIR") + File.separator + "system32").getAbsolutePath();Deliciadelicious
After deleting the dlls from previous runs, you can see my console output displays the dlls being written to system32 pastie.org/1966273Deliciadelicious
"Add the files listed in step 5" Sure that it is step 5 and not 6?Tyrelltyrian
@H311Ghose Yes, you are correct. I updated itGreatgranduncle
@Greatgranduncle we are stuck with same problem. Would you please consider helping us? It could be paid consultancy as well. This is for a good social cause. I can be reached at jeff [at] mentorstudents [dot] orgPeppers
@jeffmusk Just emailed youGreatgranduncle
R
4

I don't think there is a better way. Unless the DLLs are explicitly loaded by path name, you would just need to make sure they are in the system path, so if they lived right next to the JVM executables it should also work. Windows does implicitly include the directory the program was started from in the system path, so that is another potential location.

Installers are a double edged sword, they make it easy to add new functionality and remove it later, but they also make it harder to deploy solutions that use the product.

One of the nice things about Java in general is that you don't have to install it for it to work. Essentially, once you perform the install of the JRE on one system, you can bundle it up and use it on another system as a zip file. Java doesn't need to explicitly register the DLLs because it loads them dynamically as needed.

Romanesque answered 10/12, 2010 at 17:19 Comment(1)
That's all very true. JMF on the other hand does require an installation. It is exactly the reason that you stated, "One of the nice things about Java in general is that you don't have to install it for it to work." We did not want the end user to have to install anything else for our application to work. This is also very true if the application were running as an applet. We wanted to just be able to copy files behind the scenes and have everything work. It was not easy to mock out the installation of JMF. There are a lot of properties that get set behind the scenes.Greatgranduncle

© 2022 - 2024 — McMap. All rights reserved.