Repository: opencv
Issue: Proposal: camera multiplexing API
Labels: feature,category: videoio(camera)

# OpenCV camera multiplexing API

## Motivation
Current `cv::VideoCapture` provide only blocking-style API for access cameras, 
call to `VideoCapture::read` or `VideoCapture::grab` will block calling thread until
frame data is available.
When working with multiple cameras this require to create dedicated thread for each
camera.

We propose a way to wait for multiple cameras in single thread and a method to check
for camera frame availability without blocking calling thread.

## New OpenCV APIs

"videoio.hpp":
```C++

enum GrabAnyResult {
  GRAB_ANY_TIMEOUT,
  GRAB_ANY_EVENT,
  GRAB_ANY_READY
}

class GrabAnyEvent {
public:
  GrabAnyEvent();
  GrabAnyEvent(const GrabAnyEvent&);
  GrabAnyEvent& operator=(const GrabAnyEvent&);

  void signal();
  void reset();
};

struct GrabAnyItem {
  VideoCapture* capture;
  bool ready;
};

GrabAnyResult grabAny(/*some_array_type<GrabAnyItem>*/ items, GrabAnyEvent* event, int timeout_milliseconds);
```

### `cv::grabAny`

#### Parameters
* items - array of GrabAnyItem structures (can be implicitly created from `std::vector<GrabAnyItem>`, `std::array<GrabAnyItem>`, or `GrabAnyItem[]`),
used both as input and output.
User must set `capture` pointer to valid, opened `VideoCapture` instance.
After return `ready` field will be set to `true` if new call to `VideoCapture::grab` or `VideoCapture::read` won't block or some error happened (e.g. camera was disconneted).
File based `VideoCapture` instances are considered always ready.
This functions doesn't change state or dequeue any frames from `VideoCapture`, if ready `VideoCapture` passed to this function it will return immidiately.
* event - optional event which can be used to asynchronously abort waiting operation, can be null.
This function doens't change status of event, if signaled event is passed to this function it will
return immidiately.
Event must be manually reset by user.
* timeout_milliseconds - timeout in milliseconds after which this function will return. If 0 it will return immediately, which can be used for checking current `VideoCapture` status. Will wait indefinitely if negative.

#### Return value
* GRAB_ANY_TIMEOUT - timeout has expired
* GRAB_ANY_EVENT - asynchronous event was signaled, current `VideoCapture` status still updated in `items`.
* GRAB_ANY_READY - one or more `VideoCapture` instances is ready for reading.


#### Exceptions
* cv::Exception(CV_StsBadArg) - `items` is empty, `capture` field in any item is null, multiple `VideoCapture` instances have different backends.
* cv::Exception(CV_StsNotImplemented) - camera multiplexing is not supported by backend.

### `cv::GrabAnyEvent`
Events is implicitly shared
```C++
GrabAnyEvent event1;
GrabAnyEvent event2 = event1; // event2 refers to same event internally
```
`GrabAnyEvent::signal()` call is thread-safe relative to other asynchronous `GrabAnyEvent::signal()` calls.
Event state is undefined if both `GrabAnyEvent::signal()` and `GrabAnyEvent::reset()` called asynchronously.

## TODO
Check backend support for camera multiplexing without creating instances of `VideoCapturea` or `GrabAnyEvent`.

## Example

```C++
cv::VideoCapture cap0(0);
cv::VideoCapture cap1(1);

cv::GrabAnyEvent event;

...

std::vector<cv::GrabAnyItem> items(2);
items[0].capture = &cap0;
items[1].capture = &cap1;

cv::GrabAnyResult result = cv::grabAny(items, &event, 1000/*1s timeout*/);
if (result == GRAB_ANY_TIMEOUT)
{
  // Handle timeout
}
else if (result == GRAB_ANY_EVENT)
{
  // Event was signaled asynchronously
  event.reset();
}
else if (result == GRAB_ANY_READY)
{
  if (items[0].ready)
  {
    ... = cap0.read();
  }
  if (items[1].ready)
  {
    ... = cap1.read();
  }
}
```