Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Detect when webcam is unplugged



  • My app has a QCameraViewfinder that shows the output from a webcam. When the webcam is unplugged, it just shows the last frame it got. In an ideal world I'd like to detect this situation and switch to showing the error I show when the camera can't be connected to on startup. Is there a good way of detecting the "camera unplugged" condition?


  • Lifetime Qt Champion

    Hi,

    Just a quick idea but the QCamera::status property might be what you are looking for.

    Hope it helps



  • Thanks for the suggestion. Unfortunately, when a webcam is simply unplugged it never changes status or state (both things that there are signals for). So my solution for now is polling. I know, I know... but I simply could not figure out any other way. So I made a QThread subclass that when constructed takes a QCameraInfo object and stores it off. When you fire off that thread its run() function loops and scans for available cameras. It looks through the cameras and if it finds the one it was constructed with it does nothing. If not, it fires off a signal. Here's the code (suggestions welcome):

    void CameraMonitor::run()
    {
        qDebug() << "Starting a new check thread";
        while (true) {
            if (QThread::currentThread()->isInterruptionRequested()) {
                return;
            }
            auto cameras = QCameraInfo::availableCameras();
            bool found (false);
            for (auto camera: cameras) {
                if (camera == _camera) {
                    found = true;
                }
            }
            if (!found) {
                emit cameraLost();
                return;
            }
    
            if (QThread::currentThread()->isInterruptionRequested()) {
                return;
            }
            sleep(1);
        }
    }
    


  • @Chris-Hennes Actually I think polling is the only option you have. Neither statusChanged, stateChanged or availabilityChanged is triggered when a camera is unplugged (at least on Windows with the camera I tested), neither in the C++ or QML API. As even the almighty OpenCV doesn't seem to have an API for "camera unplugged", I assume it's really a limitation of the underlying OS-subsystems.


  • Moderators

    @Chris-Hennes @Wieland

    I haven't tested it my self, but QCamera inherits from QMediaObject and therefore should have access to the the Signal void QMediaObject::availabilityChanged(QMultimedia::AvailabilityStatus availability)

    this should emit a Signal when the camera becomes unavailable!?



  • @J.Hilk Unfortunately, although it certainly seems like it should emit that signal, it does not (at least in Windows). Absolutely nothing that I could find emits a signal when I unplug the webcam from my Win10 PC. The good news is that the polling solution I posted above works very well, and by running it in its own thread doesn't impact the performance of my program. The call to QCameraInfo::availableCameras() takes over a half second on my machine!


  • Qt Champions 2017

    @Chris-Hennes said in Detect when webcam is unplugged:

    I know, I know... but I simply could not figure out any other way.

    I would suggest a more responsive polling method, however. Consider this:

    void CameraMonitor::interrupt()
    {
        requestInterruption();
        threadWait.release();
    }
    
    void CameraMonitor::run()
    {
        qDebug() << "Starting a new check thread";
        while (!isInterruptionRequested()) {
            auto cameras = QCameraInfo::availableCameras();
            bool found = false;
            for (auto camera : cameras)  {
                if (camera == _camera) //< Beware!! This may be a race condition (QCameraInfo *might* not be reentrant)!! You should be careful about it.
                    found = true;
            }
            if (!found) {
                emit cameraLost();
                return;
            }
    
            threadWait.tryAcquire(1, 1000); // Wait 1 second or until we are flagged that we should exit (i.e. interrupt() was called).
        }
    }
    

    Where threadWait is a default initialized QSemaphore instance. Also take note on my comment about the way you compare the camera info objects, it may be dangerous ...



  • Thanks, using the QSemaphore is a good idea. I'm not sure how to address your concern about QCameraInfo. In my implementation _camera is set before the thread is started, and then only changes in response to the cameraLost() signal (which is emitted and then ends the thread). My thinking was that this was enough to ensure I was avoiding a race condition. Is there a corner case I am missing, or is a changing _camera not what you were concerned about?


  • Qt Champions 2017

    @Chris-Hennes said in Detect when webcam is unplugged:

    In my implementation _camera is set before the thread is started, and then only changes in response to the cameraLost() signal (which is emitted and then ends the thread). My thinking was that this was enough to ensure I was avoiding a race condition.

    If CameraInfo is reentrant it is. However, it's not stated in the QtMultimedia docs, so any calls into that module (i.e. calling some function like QCameraInfo::availableCameras() from a different thread) while your polling thread is running may be a race condition. To make matter worse the last is not even a requirement as the module might queue some async operation behind the scenes, which might also induce a race condition (e.g. modifying an internal static member from the main thread, while you hold a camera info instance which depends on that resource). And finally using a non-reentrant class/function makes the user code non-reentrant too, which is the worst.

    @Chris-Hennes said in Detect when webcam is unplugged:

    Is there a corner case I am missing, or is a changing _camera not what you were concerned about?

    I'm concerned about the module pulling the rug from under your feet, so to speak. What you should do to be sure is to take a look at the module's source and make certain there are no common statics between the classes that break reentrancy or bring the question of whether the classes are reentrant to the mailing list. I strongly suspect the QMediaObject implementations are not reentrant however, so don't hold your breath ...



  • The QCameraInfo class itself is basically trivial, it's just a bit of storage and and accessor, with no static variables. It's that static member function that introduces the complication: in particular I have no idea what QMediaServiceProvider might be doing in the call to provider->devices(service); (qcamerainfo.cpp:248). I don't see a clear path forward from here. That call is slow -- for usability it's got to be in its own thread. It seems like overkill to write an entire wrapper class for the QCameraInfo functionality, but I don't see how short of doing that I can guarantee thread safety.


  • Qt Champions 2017

    @Chris-Hennes said in Detect when webcam is unplugged:

    The QCameraInfo class itself is basically trivial, it's just a bit of storage and and accessor, with no static variables.

    Then it is probably okay.

    It's that static member function that introduces the complication: in particular I have no idea what QMediaServiceProvider might be doing in the call to provider->devices(service); (qcamerainfo.cpp:248). I don't see a clear path forward from here.

    I advise to bring it to the mailing list. There you could get thoughts from the developers of the module and perhaps a better/improved solution. I currently don't have the time to dig into the module, but I suspect that provider->devices(service); is listing the devices through a common system resource, which implies it isn't thread-safe.


Log in to reply