Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Receiving EOFException for no reason #131

Closed
6thSense opened this issue Aug 3, 2013 · 14 comments
Closed

Receiving EOFException for no reason #131

6thSense opened this issue Aug 3, 2013 · 14 comments
Labels

Comments

@6thSense
Copy link

6thSense commented Aug 3, 2013

Hello,

First off, thank you for this great library. Secondly, before I present to you the problem I'm having, I would just like to say that, if you could help me solve this problem, I promise that I will donate at least $25 through paypal.

I'm pasting my code for a Webcam class I made for my project using your library here. In case the formatting gets messed up, I'm also attaching a notepad file with the class code.

The issue I'm having is this: I'm calling the readVariable(channel) method from a different class. The application is getting tripped up at the parts where the webcam has to read. Specifically, it's throwing an EOFException at the "if (webcam.getLock().isLocked()) " part. I took that part out to see if that code is the problem and then it threw the EOFException at the next piece of code where the webcam has to read, which happens to be the "image = webcam.getImage();" line. The catch is that, the same code was working perfectly up until some arbitrarily random point. The code that is below, I believe is the same code which was working fine. And suddenly, it started throwing these EOFExceptions. Any idea how I could solve this problem? I would immensely appreciate it. Everything was going extremely smooth this happened and the webcam now doesn't seem to be able to read anything at all.

Please feel free to contact me back if you need more details any way you want:

e-mail: parchaashwin@gmail.com
phone: 732-207-6408
or any other way

Here's the error stacktrace in full:

java.lang.RuntimeException: java.io.EOFException
    at com.github.sarxos.webcam.WebcamLock.read(WebcamLock.java:202)
    at com.github.sarxos.webcam.WebcamLock.isLocked(WebcamLock.java:279)
    at org.Messed.MessedUp.WebCamera.readVariable(WebCamera.java:56)
    at org.Messed.MessedUp.Variable.get(Variable.java:107)
    at org.Messed.MessedUp.Windowsform$23.actionPerformed(Windowsform.java:1171)
    at javax.swing.AbstractButton.fireActionPerformed(Unknown Source)
    at javax.swing.AbstractButton$Handler.actionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source)
    at javax.swing.DefaultButtonModel.setPressed(Unknown Source)
    at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source)
    at java.awt.Component.processMouseEvent(Unknown Source)
    at javax.swing.JComponent.processMouseEvent(Unknown Source)
    at java.awt.Component.processEvent(Unknown Source)
    at java.awt.Container.processEvent(Unknown Source)
    at java.awt.Component.dispatchEventImpl(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source)
    at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source)
    at java.awt.Container.dispatchEventImpl(Unknown Source)
    at java.awt.Window.dispatchEventImpl(Unknown Source)
    at java.awt.Component.dispatchEvent(Unknown Source)
    at java.awt.EventQueue.dispatchEventImpl(Unknown Source)
    at java.awt.EventQueue.access$200(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.awt.EventQueue$3.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.awt.EventQueue$4.run(Unknown Source)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.security.ProtectionDomain$1.doIntersectionPrivilege(Unknown Source)
    at java.awt.EventQueue.dispatchEvent(Unknown Source)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
    at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.io.EOFException
    at java.io.DataInputStream.readFully(Unknown Source)
    at java.io.DataInputStream.readLong(Unknown Source)
    at com.github.sarxos.webcam.WebcamLock.read(WebcamLock.java:200)
    ... 40 more

---------------------------------------CODE---------------------------------------------

package org.Messed.MessedUp;

import java.awt.Dimension;
import java.awt.image.BufferedImage;
import java.io.EOFException;
import java.io.File;

import javax.imageio.ImageIO;

import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamLock;
import com.github.sarxos.webcam.WebcamPanel;
import com.github.sarxos.webcam.WebcamResolution;

public class WebCamera implements Device {

    // Variables

    private String type = "WebCam";
    private String name;

    private Webcam webcam;
    private Dimension displaySize = WebcamResolution.QQVGA.getSize();

    // Constructor

    public WebCamera(String name) {
        this.name = name;
    }

    // Public Methods

    public boolean checkDeviceStatus() throws Exception {
        try {
            webcam = Webcam.getDefault();
            if ((webcam != null)) {
                return true;
            } else {
                return false;
            }
        } catch (Exception e) {
            throw new Exception(" // WebCam -> checkDeviceStatus: "
                    + e.getMessage());
        }
    }

    public ValueObject readVariable(String channel) throws Exception {
        BufferedImage image = null;
        try {
            // throw an exception if webcam is not connected
            if (checkDeviceStatus() == false) {
                throw new Exception(
                        "Device Is Not Connected; Make Sure You Connect It And Start It");
            }
            // unlock webcam if it's locked
            if (webcam.getLock().isLocked()) {
                webcam.getLock().unlock();
            }
            // throw exception if panel equals null
            try {
                WebcamPanel panel = new WebcamPanel(webcam, displaySize, false);
            } catch (Exception e1) {
                throw new Exception(
                        "Device Is Not Connected; Make Sure You Connect It And Start It");
            }
            // get image
            image = webcam.getImage();
            // throw exception if image equals null
            if (image == null) {
                throw new Exception(
                        "Device Is Not Connected; Make Sure You Connect It And Start It");
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new Exception(" // WebCam -> readVariable: " + e.getMessage());
        }
        // return image
        return new ValueObject(image);
    }

    public void writeVariable(String channel, ValueObject value)
            throws Exception {
        String guaranteedException = "yes";
        try {
            if (guaranteedException.equals("yes")) {
                throw new Exception("Cannot Write To WebCam; Can Only Read");
            }
        } catch (Exception e) {
            throw new Exception(
                    " // WebCam -> writeVariable: Cannot Write To WebCam; Can Only Read");
        }
    }

    public void start() throws Exception {
        try {
            // throw an exception if webcam is not connected
            if (checkDeviceStatus() == false) {
                throw new Exception("Device Is Not Connected");
            }
            webcam = Webcam.getDefault();
            // automatically open if webcam is closed
            Webcam.setAutoOpenMode(true);
            // set webcam resolution to 320x240
            webcam.setViewSize(new Dimension(320, 240));
        } catch (Exception e) {
            throw new Exception(" // WebCam -> start: " + e.getMessage());
        }
    }

    public void stop() throws Exception {
        try {
            // throw an exception if webcam is not connected
            if (checkDeviceStatus() == false) {
                throw new Exception("Device Was Never Started To Begin With");
            }
            webcam.close();
        } catch (Exception e) {
            throw new Exception(" // WebCam -> stop: " + e.getMessage());
        }
    }

    // Private Methods

    // Getters

    public String getType() throws Exception {
        try {
            return type;
        } catch (Exception e) {
            throw new Exception(" // WebCam -> getType: " + e.getMessage());
        }
    }

    public String getName() throws Exception {
        try {
            return name;
        } catch (Exception e) {
            throw new Exception(" // WebCam -> getName: " + e.getMessage());
        }
    }

}
@sarxos
Copy link
Owner

sarxos commented Aug 3, 2013

Hi,

Thank you for the report and your support :)

At the first glance I suppose this can be a race between read() and write() methods of WebcamLock, especially since read() which gives you this exception is not synchronized. I made appropriate code change and will push them to the repository.

Regarding your code sample I have some suggestions / warnings:

Auto-Open Mode

Try avoiding auto-open mode whenever this is possible, and especially when you are working in multi-threaded environment (e.g. GUI application, etc). Therefore, I would strongly suggest you to change this:

webcam = Webcam.getDefault();
// automatically open if webcam is closed
Webcam.setAutoOpenMode(true);
// set webcam resolution to 320x240
webcam.setViewSize(new Dimension(320, 240));

Into this:

webcam = Webcam.getDefault();
webcam.setViewSize(new Dimension(320, 240));
webcam.open();

Unlocking Webcam

The WebcamLock has been designed to prevent using camera device from multiple JVM processes using Webcam Capture API, and sometimes, in rare cases, when you terminate old process and start the new one faster than in 2 seconds, it's OK to unlock the webcam, but this shall be done only when you are opening it - it should never be unlocked when reading image. So the really best approach, is to not invoke unlock() method at all, or the second one, to wait a little while, and check if lock has been released. The refined start() method would look like:

public void start() throws Exception {

  if (webcam != null) {
    // webcam has already been set, please connect and start it
    return;
  }

  webcam = Webcam.getDefault();

  if (webcam.isOpen()) {
    // webcam has already been started, ignore call
    return;
  }

  WebcamLock lock = webcam.getLock();

  int i = 0;
  do {
    if (!lock.isLocked()) {
      break;
    }
    Thread.sleep(1000);
  } while (i++ < 4); // wait max 4 seconds for lock to be released

  if (lock.isLocked()) {
    throw new RuntimeException("There is other JVM process using this camera via Webcam Capture API, make sure to close it first");
  }

  webcam.setViewSize(new Dimension(320, 240));
  webcam.open();
}

Creating Webcam Panel

I see that you are creating new WebcamPanel just to check if it will throw an exception - this is completely unnecessary and you should avoid doing this. The WebcamPanel is heavyweight component - there are additional executors and threads used beneath, so please make sure to create it only when you are using it to display image from camera in your application's GUI.

Device Name

Instead of setting artificial name, you can use name from camera device (with or without additional modifications):

public String getName() throws Exception {
  return webcam.getName();
}

sarxos added a commit that referenced this issue Aug 3, 2013
@sarxos
Copy link
Owner

sarxos commented Aug 3, 2013

I refined your code a little bit to perform various simplifications. Hope you don't mind.

import java.awt.Dimension;
import java.util.concurrent.atomic.AtomicBoolean;

import com.github.sarxos.webcam.Webcam;
import com.github.sarxos.webcam.WebcamResolution;


public class WebCamera implements Device {

    private String type = "WebCam";

    private Webcam webcam;
    private Dimension displaySize = WebcamResolution.QQVGA.getSize();
    private AtomicBoolean started = new AtomicBoolean(false);

    public ValueObject readVariable(String channel) throws Exception {
        if (!started.get()) {
            throw new RuntimeException("WebCamera has not been started");
        }
        return new ValueObject(webcam.getImage());
    }

    public void writeVariable(String channel, ValueObject value) throws Exception {
        throw new Exception("Cannot Write To WebCam; Can Only Read");
    }

    public void start() throws Exception {
        if (started.compareAndSet(false, true)) {
            if ((webcam = Webcam.getDefault()) == null) {
                throw new RuntimeException("No webcam connected to the PC, make sure to connect it first");
            }
            webcam.setViewSize(displaySize);
            webcam.open();
            if (!webcam.isOpen()) {
                started.set(false);
                throw new RuntimeException("Cannot open the webcam, check debug log for details");
            }
        }
    }

    public void stop() throws Exception {
        if (started.compareAndSet(true, false)) {
            webcam.close();
        }
    }

    public String getType() throws Exception {
        return type;
    }

    public String getName() throws Exception {
        return webcam.getName();
    }
}

@sarxos
Copy link
Owner

sarxos commented Aug 3, 2013

Webcam Capture JAR file with the above fix can be found here:

https://oss.sonatype.org/content/repositories/snapshots/com/github/sarxos/webcam-capture/0.3.10-SNAPSHOT/webcam-capture-0.3.10-20130803.083800-13.jar

Please replace it with the one you are using currently and let me know if problem still persist.

@6thSense
Copy link
Author

6thSense commented Aug 3, 2013

Thank for your suggestions regarding my code sarxos. I will try to modify mine according to that soon as I get the webcam working again.

For now, do I need to make any code changes for that new jar file to work? Or should it work with the code that I have? It's still not working unfortunately. Same error. So, the problem still persists. Please let me know what else can be done.

Thank you,

  • 6th

@6thSense
Copy link
Author

6thSense commented Aug 3, 2013

"At the first glance I suppose this can be a race between read() and write() methods of WebcamLock, especially since read() which gives you this exception is not synchronized. I made appropriate code change and will push them to the repository."

Actually, I don't think that's what's causing the problem. I just tried removing EVERYTHING from the readVariable(channel) method other than the "image = webcam.getImage();" line and "return new ValueObject(image);" line and the problem still persists.

As you can see, I only let the read() part of Webcam stay in there and nothing else and the problem is still there.

So I think the problem is something else. And again, I have to mention that the same exact code was working perfectly. This problem cam up all of a sudden for no reason. That's what is most confusing to me. Thanks for trying to help out.

@6thSense
Copy link
Author

6thSense commented Aug 4, 2013

Here's more information:

I ran the "TakePictureExample.java" file as a completely new project by itself and even that gave me the EOF exception at the "BufferedImage image = Webcam.getDefault().getImage();" line. This file was working fine earlier too. So this suggests that the problem is not with the code but something very fundamental.

@sarxos
Copy link
Owner

sarxos commented Aug 4, 2013

Hi,

Yes, after you mentioned about this problem appeared suddenly, and everything was fine before than happen, I realised one other possible cause. The webcam lock is created every time when specific webcam is open, but since it resides in system temporary directory, and always have the same name, I thought it's not necessary to remove it. Lock file should contain 8 bytes, but if the actual number of bytes in this file is less, the EOFException would be thrown, which will make specific (only one) webcam unusable.

To confirm above I performed a small experiment. I damaged webcam lock file and tried to start program again. The effect was exactly the same as in your case:

Exception in thread "main" java.lang.RuntimeException: java.io.EOFException
    at com.github.sarxos.webcam.WebcamLock.read(WebcamLock.java:216)
    at com.github.sarxos.webcam.WebcamLock.isLocked(WebcamLock.java:294)
    at com.github.sarxos.webcam.WebcamLock.lock(WebcamLock.java:238)
    at com.github.sarxos.webcam.Webcam.open(Webcam.java:183)
    at com.github.sarxos.webcam.Webcam.open(Webcam.java:149)
    at com.github.sarxos.webcam.WebcamPanel.start(WebcamPanel.java:566)
    at com.github.sarxos.webcam.WebcamPanel.<init>(WebcamPanel.java:515)
    at com.github.sarxos.webcam.WebcamPanel.<init>(WebcamPanel.java:476)
    at com.github.sarxos.webcam.WebcamPanel.<init>(WebcamPanel.java:465)
    at WebcamPanelExample.main(WebcamPanelExample.java:13)
Caused by: java.io.EOFException
    at java.io.DataInputStream.readFully(DataInputStream.java:180)
    at java.io.DataInputStream.readLong(DataInputStream.java:399)
    at com.github.sarxos.webcam.WebcamLock.read(WebcamLock.java:214)
    ... 9 more

The below code fragment will remove all lock files and fix the problem. Just make sure you have webcam connected to the PC. I tested it on my environment and it fixed the issue, but I'm on Ubuntu Linux, so please let me know if it's working in your case.

public static void main(String[] args) throws IOException {
    List<Webcam> webcams = Webcam.getWebcams();
    File tmp = File.createTempFile("abc", "xyz");
    for (Webcam webcam : webcams) {
        File lock = new File(tmp.getParentFile(), String.format(".webcam-lock-%d", Math.abs(webcam.getName().hashCode())));
        if (lock.exists() && !lock.delete()) {
            lock.deleteOnExit();
        }
    }
    if (!tmp.delete()) {
        tmp.deleteOnExit();
    }
}

You can also remove them manually, but this can be harder, so above suggestion is a better approach:

  • On Linux / Unix:
    • cd /tmp
    • rm -f .webcam-lock-*
  • On Windows ([username] refers to your system's user name):
    • XP, go to C:\Documents And Settings\[username]\Local Settings\Temp
    • Vista, 7, 8, go to ```C:\Users[username]\AppData\Local\Temp
    • Delete all files starting with ".webcam-lock-" (there should be one or two, depending on the number of webcams available in the system)
  • On Mac OSX:
    • cd $TMPDIR
    • rm -f .webcam-lock-*

I will fix this in the source code ASAP, so problem with lock file will simply remove it and then re-create, instead of crashing whole application.

Thank you for your support and time you spent on this issue!

BR
Bartosz

@6thSense
Copy link
Author

6thSense commented Aug 4, 2013

It's working now. Excellent. Thank you very much.

But I would also like to resolve a minor problem.

  1. I read the image within my GUI application
  2. Disconnect the webcam
  3. Connect it
  4. Wait for more than 2 seconds (like you said)
  5. And try to read it again

Then I get this: Webcam Logitech HD Webcam C270 0 has already been locked

Clearly the webcam is not being used by any other application. And I even wait for more than 2 seconds but it still keeps giving the same message. When I freshly open up my GUI application and read, it works fine. But when I disconnect..connect and then read it..that's when it keeps giving that message.

This is the reason why I used the following code within my readVariable(channel) method:

// unlock webcam if it's locked
if (webcam.getLock().isLocked()) {
webcam.getLock().unlock();
}

This used to actually unlock the webcam but suddenly, it's not doing the job anymore.

So I want to ask you: Could you change anything in the source code (at least just for me) so that the webcam doesn't get locked at all? It's very troubling to my application that I'm working on. It would be very helpful.

Thanks again for everything sarxos.

@sarxos
Copy link
Owner

sarxos commented Aug 5, 2013

Sure! I never expected that locking mechanism will introduce such kind of issues, so it's good to have some turn-off switch which will disable it completely. With the newest code from HEAD you can do that for specific webcam by calling:

Webcam w = Webcam.getDefault();
w.getLock().disable();
// do other great things

Due to my regular job work load I do not have much free time now, but I will try to investigate this locking issues much deeper ASAP. Especially the ones you mentioned:

  • Call to unlock() does not unclok webcam,
  • Webcam is still locked after it has been disconnected.

The newest SNAPSHOT Webcam Capture JAR, which contains fix for you, can be found here:

https://oss.sonatype.org/content/repositories/snapshots/com/github/sarxos/webcam-capture/0.3.10-SNAPSHOT/webcam-capture-0.3.10-20130805.213713-14.jar

Please let me know if it's working fine.

@sarxos sarxos closed this as completed in a2ee2f3 Aug 5, 2013
@6thSense
Copy link
Author

6thSense commented Aug 7, 2013

It's working fine. But what's happening is: everytime I click the button which runs the readVariable(channel) method, it displays the image that was read from my last click. It's basically an image behind. This seems to be like a side effect of this update (which has successfully helped me disable the lock). Please look into why that's happening.

Thank you very much.

@6thSense
Copy link
Author

6thSense commented Aug 7, 2013

The other jar also did that initially. And when I added "WebcamPanel panel = new WebcamPanel(webcam, displaySize, false);" in between my readVariable(channel) method, it was returning the right image upon the button click. But with this jar, even with that webcampanel line in the middle, it's not returning the right image upon the button click. May be that gives you a hint about why it's happening now.

@sarxos
Copy link
Owner

sarxos commented Aug 7, 2013

I suppose that's because the webcam or native API has some kind of ring buffer which holds 4 last frames. The default driver is very simple and fetch new frame on-demand, when user calls getImage() method. The other drivers, e.g. GStreamerDriver or V4l4jDriver (and some other ones) are much more sophisticated - frames are not fetched on-demand, but in the parallel loop working in the native API background - because of that every image is "fresh".

To be honest I was not aware of this issue with default driver till now (I thought that every image is "fresh"). I suppose, but would have to check to be 100% sure, that this specific case is not observable on Windows since DirectShow API is updating buffer in background.

Ok, that's should be enough for the explanation - the w/a for this issue is very simple - use non-blocking mode:

webcam.open(true);

Instead of:

webcam.open();

It's much better than instantiating new WebcamPanel each time :)

I will try to find some long term solution for this, but it can be pretty rough when thread-safety must be considered - for Linux / Mac OS only there would be no problem, but the simplest solution (e.g. simple parallel thread) will crash JVM on Windows due to SIGSEGV in native, or it will not be able to fetch image at all.

@sarxos
Copy link
Owner

sarxos commented Aug 7, 2013

I've just pushed small fix to update buffer in background, but will need to verify it on Windows to make sure that natives will not crash JVM.

@sarxos
Copy link
Owner

sarxos commented Aug 7, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants