v4l2: v4l2_camera: Don't use libcamera::Semaphore for available buffers

In V4L2, a blocked dqbuf should not not also block a streamoff. This
means that on streamoff, the blocked dqbuf must return (with error). We
cannot do this with the libcamera semaphore, so pull out the necessary
components of a semaphore, and put them into V4L2Camera, so that dqbuf
from V4L2CameraProxy can wait on a disjunct condition of the
availability of the semaphore or the stopping of the stream.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
This commit is contained in:
Paul Elder 2020-06-16 16:40:34 +09:00
parent f155e63816
commit 566ccd75ca
3 changed files with 47 additions and 7 deletions

View file

@ -18,7 +18,7 @@ LOG_DECLARE_CATEGORY(V4L2Compat);
V4L2Camera::V4L2Camera(std::shared_ptr<Camera> camera)
: camera_(camera), isRunning_(false), bufferAllocator_(nullptr),
efd_(-1)
efd_(-1), bufferAvailableCount_(0)
{
camera_->requestCompleted.connect(this, &V4L2Camera::requestComplete);
}
@ -100,7 +100,11 @@ void V4L2Camera::requestComplete(Request *request)
if (ret != sizeof(data))
LOG(V4L2Compat, Error) << "Failed to signal eventfd POLLIN";
bufferSema_.release();
{
MutexLocker locker(bufferMutex_);
bufferAvailableCount_++;
}
bufferCV_.notify_all();
}
int V4L2Camera::configure(StreamConfiguration *streamConfigOut,
@ -192,7 +196,11 @@ int V4L2Camera::streamOff()
if (ret < 0)
return ret == -EACCES ? -EBUSY : ret;
{
MutexLocker locker(bufferMutex_);
isRunning_ = false;
}
bufferCV_.notify_all();
return 0;
}
@ -228,6 +236,26 @@ int V4L2Camera::qbuf(unsigned int index)
return 0;
}
void V4L2Camera::waitForBufferAvailable()
{
MutexLocker locker(bufferMutex_);
bufferCV_.wait(locker, [&] {
return bufferAvailableCount_ >= 1 || !isRunning_;
});
if (isRunning_)
bufferAvailableCount_--;
}
bool V4L2Camera::isBufferAvailable()
{
MutexLocker locker(bufferMutex_);
if (bufferAvailableCount_ < 1)
return false;
bufferAvailableCount_--;
return true;
}
bool V4L2Camera::isRunning()
{
return isRunning_;

View file

@ -57,9 +57,10 @@ public:
int qbuf(unsigned int index);
bool isRunning();
void waitForBufferAvailable();
bool isBufferAvailable();
Semaphore bufferSema_;
bool isRunning();
private:
void requestComplete(Request *request);
@ -76,6 +77,10 @@ private:
std::deque<std::unique_ptr<Buffer>> completedBuffers_;
int efd_;
Mutex bufferMutex_;
std::condition_variable bufferCV_;
unsigned int bufferAvailableCount_;
};
#endif /* __V4L2_CAMERA_H__ */

View file

@ -588,10 +588,17 @@ int V4L2CameraProxy::vidioc_dqbuf(V4L2CameraFile *file, struct v4l2_buffer *arg)
return -EINVAL;
if (!file->nonBlocking())
vcam_->bufferSema_.acquire();
else if (!vcam_->bufferSema_.tryAcquire())
vcam_->waitForBufferAvailable();
else if (!vcam_->isBufferAvailable())
return -EAGAIN;
/*
* We need to check here again in case stream was turned off while we
* were blocked on waitForBufferAvailable().
*/
if (!vcam_->isRunning())
return -EINVAL;
updateBuffers();
struct v4l2_buffer &buf = buffers_[currentBuf_];