0

I have an application written in C++Builder 11.1.5, where I am trying to capture live video from 2 web cams.

There is an excellent tutorial which describes how to do this with 1 web cam at the following URL, and I have it working perfectly: Video Capturing.

Following that example, I use the following code on the form's OnCreate event, which stores the information of all the available video capture devices on the computer:

NumbAvailableCameras = 0;

DeviceList = TCaptureDeviceManager::Current->GetDevicesByMediaType(TMediaType::Video);
for (i = 0; i < DeviceList->Count; i++) {
    UniqueID = DeviceList->Items[i]->UniqueID;
    UniqueDescription = DeviceList->Items[i]->Description;
    UniqueName = DeviceList->Items[i]->Name;
    ComboBox1->Items->Add(DeviceList->Items[i]->Name);
    AvailableCameraIndices[NumbAvailableCameras] = i;
    AvailableCameraNames[NumbAvailableCameras] = DeviceList->Items[i]->Name;
    AvailableCameraDescriptions[NumbAvailableCameras] = UniqueDescription;
    AvailableCameraIdentifiers[NumbAvailableCameras] = UniqueID;
    CameraIndex[i] = i;
    NumbAvailableCameras++;
    }

Then, there is a button and its OnClick event allows the starting up of the capturing:

CAM1VideoCamera = dynamic_cast<TVideoCaptureDevice*>
        (TCaptureDeviceManager::Current->GetDevicesByName(ComboBox1->Selected->Text));

Now, I have 2 webcams whose names are identical, so the GetDevicesByName() routine doesn't work as it picks off the first camera it finds with the selected name.

As an alternative, I have tried the following code, but it returns NULL for the capture device:

CAM1VideoCamera = dynamic_cast<TVideoCaptureDevice*>
        (TCaptureDeviceManager::Current->Devices[CameraIndex[0]]);

Any ideas as to how to do this properly?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770

1 Answers1

0

I have 2 webcams whose names are identical so the GetDevicesByName routine doesn't work as it picks off the first camera it finds with the selected name

Actually, it returns the last device with the selected name (bug?).

I find it odd that the method is named as a plural but it only returns a single device.

I have now reported these issues to Embarcadero:

RSP-41707: TCaptureDeviceManager.GetDevicesByName() issues

I have tried the following code but it returns NULL for the CaptureDevice

That is because you are using the wrong index.

You are retrieving a TCaptureDeviceList of just video devices, but then you are using indexes relative to that TCaptureDeviceList when accessing the main TCaptureDeviceManager::Devices[] list. So, if there are any audio devices present in the Devices[] list in front of the video devices then your CameraIndex[] indexes won't match up correctly, so you can end up passing a TAudioCaptureDevice object into dynamic_cast, which is why it would return NULL.

To do what you are attempting, you will have to forget about using GetDevicesByMediaType() and instead iterate the Devices[] list directly, eg:

NumbAvailableCameras = 0;

int DeviceCount = TCaptureDeviceManager::Current->Count;

for (int DeviceIndex = 0; DeviceIndex < DeviceCount; ++DeviceIndex) {
    TCaptureDevice *Device = TCaptureDeviceManager::Current->Devices[DeviceIndex];
    if (Device->MediaType != TMediaType::Video) continue;

    UniqueName = Device->Name;
    ComboBox1->Items->Add(UniqueName);

    AvailableCameraIndices[NumbAvailableCameras] = NumbAvailableCameras; // <-- or DeviceIndex?
    // Should this be a relative index to AvailableCameraIndices
    // or an absolute index to TCaptureDeviceManager?

    AvailableCameraNames[NumbAvailableCameras] = UniqueName;
    AvailableCameraDescriptions[NumbAvailableCameras] = Device->Description;
    AvailableCameraIdentifiers[NumbAvailableCameras] = Device->UniqueID;

    CameraIndex[NumbAvailableCameras] = DeviceIndex; // <--
    // definitely use absolute index here,
    // not a relative index!

    ++NumbAvailableCameras;
}

Now, accessing TCaptureDeviceManager::Current->Devices[CameraIndex[...]] should return only TVideoCaptureDevice objects, as expected. In which case, since you know all indexes in CameraIndex[] are only for video devices, you can (and should) use static_cast instead of dynamic_cast.


That being said, it appears that AvailableCameraIndices[] and CameraIndex[] may be doing the same job, so you might consider eliminating one of them.

Even better, consider changing AvailableCameraIndices[]/CameraIndex[] to store (a pointer to) the actual TVideoCaptureDevice object itself rather than storing an index to it, since the devices and indexes never change during TCaptureDeviceManager's lifetime, eg:

NumbAvailableCameras = 0;

int DeviceCount = TCaptureDeviceManager::Current->Count;

for (int i = 0; i < DeviceCount; ++i) {
    TCaptureDevice *Device = TCaptureDeviceManager::Current->Devices[i];
    if (Device->MediaType != TMediaType::Video) continue;
    AvailableCameras[NumbAvailableCameras] = static_cast<TVideoCaptureDevice*>(Device);
    ++NumbAvailableCameras;
    ComboBox1->Items->Add(Device->Name);
}

In which case, you could just eliminate all of your separate AvailableCamera...[] arrays and just use GetDevicesByMediaType() by itself, eg:

AvailableCameras = TCaptureDeviceManager::Current->GetDevicesByMediaType(TMediaType::Video);

for (auto Device : AvailableCameras) {
    ComboBox1->Items->Add(Device->Name);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • I ran into another issue. I am setting up everything for use with the 2 web cams. When I go to start capturing, the first web cam starts ok. The second cam will only start for a second (Green Light comes on the web cam) then immediately stops . In my next comment, I have included some code that I use for starting the process (Keep in mind the SampleBufferReady Routines are not included here but do work: – Drewski7280 May 19 '23 at 12:57
  • `code` // Prepare the Camera Sample Threads CAM1VideoCamera->OnSampleBufferReady = CAM1SampleBufferReady; CAM1VideoCamera->Quality = TVideoCaptureQuality::PhotoQuality; if(NumbCameras > 1) { CAM2VideoCamera->OnSampleBufferReady = CAM2SampleBufferReady; CAM2VideoCamera->Quality = TVideoCaptureQuality::PhotoQuality; } // Now start capturing frames from the camera CAM1VideoCamera->StartCapture(); if(NumbCameras > 1) CAM2VideoCamera->StartCapture(); 'code' – Drewski7280 May 19 '23 at 12:57
  • If I set a breakpoint Before the StartCapture routines and step through it, cam1 works ok. Cam2 only comes on for a brief second. Any ideas what might be causing this? – Drewski7280 May 19 '23 at 12:59
  • @Drewski7280 that issue has nothing to do with your question in this post about just retrieving the capture devices. It warrants a separate post of its own. One question per post, please – Remy Lebeau May 19 '23 at 14:39