1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-31 00:03:03 +04:00

Merge pull request #15100 from Volskig:cam_multiplexing_function_v

Implement Camera Multiplexing API

* IdideoCapture + two wrong function

function waitAny

Add errors catcher

Stub for Python added.

Sifting warnings

One test added

Two tests for camera and Perf tests added

* Perf sync and async tests for waitAny() added, waitAnyInterior() deleted, getDeviceHandle() deleted

* Variable OPENCV_TEST_CAMERA_LIST added

* Without fps set

* ASSERT_FAILED for environment variable

* Perf tests is DISABLED_

* --Trailing whitespace

* Return false from cap.cpp deleted

* Two functions deleted from interface, +range for, +environment variable in test_camera

* Space deleted

* printf deleted, perror added

* CV_WRAP deleted, cv2 cleared from stubs

* -- space

* default timeout added

* @param changed

* place of waitAny changed

* --whitespace

* ++function description

* function description changed

* revert unused changes

* videoio: rework API for VideoCapture::waitAny()
This commit is contained in:
Maxim Pashchenkov
2019-11-15 19:42:12 +03:00
committed by Alexander Alekhin
parent ac2dc29525
commit 1acadd363b
7 changed files with 339 additions and 2 deletions
+23
View File
@@ -319,6 +319,29 @@ double VideoCapture::get(int propId) const
}
bool VideoCapture::waitAny(const std::vector<VideoCapture>& streams, CV_OUT std::vector<int>& readyIndex, int64 timeoutNs)
{
CV_Assert(!streams.empty());
VideoCaptureAPIs backend = (VideoCaptureAPIs)streams[0].icap->getCaptureDomain();
for (size_t i = 1; i < streams.size(); ++i)
{
VideoCaptureAPIs backend_i = (VideoCaptureAPIs)streams[i].icap->getCaptureDomain();
CV_CheckEQ((int)backend, (int)backend_i, "All captures must have the same backend");
}
#if (defined HAVE_CAMV4L2 || defined HAVE_VIDEOIO) // see cap_v4l.cpp guard
if (backend == CAP_V4L2)
return VideoCapture_V4L_waitAny(streams, readyIndex, timeoutNs);
#else
CV_UNUSED(readyIndex); CV_UNUSED(timeoutNs);
#endif
CV_Error(Error::StsNotImplemented, "VideoCapture::waitAny() is supported by V4L backend only");
}
//=================================================================================================
+16
View File
@@ -61,6 +61,15 @@ public:
virtual int getCaptureDomain() const { return cv::CAP_ANY; } // Return the type of the capture object: CAP_FFMPEG, etc...
};
namespace internal {
class VideoCapturePrivateAccessor
{
public:
static
IVideoCapture* getIVideoCapture(const VideoCapture& cap) { return cap.icap.get(); }
};
} // namespace
//===================================================
// Wrapper
@@ -116,6 +125,8 @@ public:
{
return cap ? cap->getCaptureDomain() : 0;
}
CvCapture* getCvCapture() const { return cap; }
};
class LegacyWriter : public IVideoWriter
@@ -208,6 +219,11 @@ Ptr<IVideoCapture> createXINECapture(const std::string &filename);
Ptr<IVideoCapture> createAndroidCapture_file(const std::string &filename);
bool VideoCapture_V4L_waitAny(
const std::vector<VideoCapture>& streams,
CV_OUT std::vector<int>& ready,
int64 timeoutNs);
} // cv::
#endif // CAP_INTERFACE_HPP
+119 -2
View File
@@ -228,6 +228,8 @@ make & enjoy!
#include <sys/ioctl.h>
#include <limits>
#include <poll.h>
#ifdef HAVE_CAMV4L2
#include <asm/types.h> /* for videodev2.h */
#include <linux/videodev2.h>
@@ -372,6 +374,8 @@ struct CvCaptureCAM_V4L CV_FINAL : public CvCapture
bool convertableToRgb() const;
void convertToRgb(const Buffer &currentBuffer);
void releaseFrame();
bool havePendingFrame; // true if next .grab() should be noop, .retrive() resets this flag
};
/*********************** Implementations ***************************************/
@@ -384,7 +388,8 @@ CvCaptureCAM_V4L::CvCaptureCAM_V4L() :
bufferSize(DEFAULT_V4L_BUFFERS),
fps(0), convert_rgb(0), frame_allocated(false), returnFrame(false),
channelNumber(-1), normalizePropRange(false),
type(V4L2_BUF_TYPE_VIDEO_CAPTURE)
type(V4L2_BUF_TYPE_VIDEO_CAPTURE),
havePendingFrame(false)
{
frame = cvIplImage();
memset(&timestamp, 0, sizeof(timestamp));
@@ -863,6 +868,7 @@ bool CvCaptureCAM_V4L::read_frame_v4l2()
bool CvCaptureCAM_V4L::tryIoctl(unsigned long ioctlCode, void *parameter) const
{
CV_LOG_DEBUG(NULL, "tryIoctl(handle=" << deviceHandle << ", ioctl=0x" << std::hex << ioctlCode << ", ...)")
while (-1 == ioctl(deviceHandle, ioctlCode, parameter)) {
if (!(errno == EBUSY || errno == EAGAIN))
return false;
@@ -889,7 +895,13 @@ bool CvCaptureCAM_V4L::tryIoctl(unsigned long ioctlCode, void *parameter) const
bool CvCaptureCAM_V4L::grabFrame()
{
if (FirstCapture) {
if (havePendingFrame) // frame has been already grabbed during preroll
{
return true;
}
if (FirstCapture)
{
/* Some general initialization must take place the first time through */
/* This is just a technicality, but all buffers must be filled up before any
@@ -1939,6 +1951,8 @@ bool CvCaptureCAM_V4L::streaming(bool startStream)
IplImage *CvCaptureCAM_V4L::retrieveFrame(int)
{
havePendingFrame = false; // unlock .grab()
if (bufferIndex < 0)
return &frame;
@@ -1989,6 +2003,109 @@ Ptr<IVideoCapture> create_V4L_capture_file(const std::string &filename)
return NULL;
}
static
bool VideoCapture_V4L_deviceHandlePoll(const std::vector<int>& deviceHandles, std::vector<int>& ready, int64 timeoutNs)
{
CV_Assert(!deviceHandles.empty());
const size_t N = deviceHandles.size();
ready.clear(); ready.reserve(N);
const auto poll_flags = POLLIN | POLLRDNORM | POLLERR;
std::vector<pollfd> fds; fds.reserve(N);
for (size_t i = 0; i < N; ++i)
{
int handle = deviceHandles[i];
CV_LOG_DEBUG(NULL, "camera" << i << ": handle = " << handle);
CV_Assert(handle != 0);
fds.push_back(pollfd{handle, poll_flags, 0});
}
int timeoutMs = -1;
if (timeoutNs > 0)
{
timeoutMs = saturate_cast<int>((timeoutNs + 999999) / 1000000);
}
int ret = poll(fds.data(), N, timeoutMs);
if (ret == -1)
{
perror("poll error");
return false;
}
if (ret == 0)
return 0; // just timeout
for (size_t i = 0; i < N; ++i)
{
const auto& fd = fds[i];
CV_LOG_DEBUG(NULL, "camera" << i << ": fd.revents = 0x" << std::hex << fd.revents);
if ((fd.revents & (POLLIN | POLLRDNORM)) != 0)
{
ready.push_back(i);
}
else if ((fd.revents & POLLERR) != 0)
{
CV_Error_(Error::StsError, ("Error is reported for camera stream: %d (handle = %d)", (int)i, deviceHandles[i]));
}
else
{
// not ready
}
}
return true;
}
bool VideoCapture_V4L_waitAny(const std::vector<VideoCapture>& streams, CV_OUT std::vector<int>& ready, int64 timeoutNs)
{
CV_Assert(!streams.empty());
const size_t N = streams.size();
// unwrap internal API
std::vector<CvCaptureCAM_V4L*> capPtr(N, NULL);
for (size_t i = 0; i < N; ++i)
{
IVideoCapture* iCap = internal::VideoCapturePrivateAccessor::getIVideoCapture(streams[i]);
LegacyCapture* legacyCapture = dynamic_cast<LegacyCapture*>(iCap);
CV_Assert(legacyCapture);
CvCapture* cvCap = legacyCapture->getCvCapture();
CV_Assert(cvCap);
CvCaptureCAM_V4L *ptr_CvCaptureCAM_V4L = dynamic_cast<CvCaptureCAM_V4L*>(cvCap);
CV_Assert(ptr_CvCaptureCAM_V4L);
capPtr[i] = ptr_CvCaptureCAM_V4L;
}
// initialize cameras streams and get handles
std::vector<int> deviceHandles; deviceHandles.reserve(N);
for (size_t i = 0; i < N; ++i)
{
CvCaptureCAM_V4L *ptr = capPtr[i];
if (ptr->FirstCapture)
{
ptr->havePendingFrame = ptr->grabFrame();
CV_Assert(ptr->havePendingFrame);
// TODO: Need to filter these cameras, because frame is available
}
CV_Assert(ptr->deviceHandle);
deviceHandles.push_back(ptr->deviceHandle);
}
bool res = VideoCapture_V4L_deviceHandlePoll(deviceHandles, ready, timeoutNs);
for (size_t i = 0; i < ready.size(); ++i)
{
int idx = ready[i];
CvCaptureCAM_V4L *ptr = capPtr[idx];
ptr->havePendingFrame = ptr->grabFrame();
CV_Assert(ptr->havePendingFrame);
}
return res;
}
} // cv::
#endif