blob: b4add7d4b8760d41ef51cfdd0d40e11341f53f7b [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Note: ported from Chromium commit head: 22d34680c8ac
//#define LOG_NDEBUG 0
#define LOG_TAG "V4L2DevicePoller"
#include <v4l2_codec2/v4l2/V4L2DevicePoller.h>
#include <string>
#include <base/bind.h>
#include <base/threading/sequenced_task_runner_handle.h>
#include <base/threading/thread_checker.h>
#include <log/log.h>
#include <v4l2_codec2/v4l2/V4L2Device.h>
namespace android {
V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device, const std::string& threadName,
scoped_refptr<base::SequencedTaskRunner> taskRunner)
: mDevice(device),
mPollThread(std::move(threadName)),
mClientTaskTunner(std::move(taskRunner)),
mStopPolling(false) {}
V4L2DevicePoller::~V4L2DevicePoller() {
ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
stopPolling();
}
bool V4L2DevicePoller::startPolling(EventCallback eventCallback,
base::RepeatingClosure errorCallback) {
ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
if (isPolling()) return true;
ALOGV("Starting polling");
mErrorCallback = errorCallback;
if (!mPollThread.Start()) {
ALOGE("Failed to start device poll thread");
return false;
}
mEventCallback = std::move(eventCallback);
mPollBuffers.store(false);
mStopPolling.store(false);
mPollThread.task_runner()->PostTask(
FROM_HERE, base::BindOnce(&V4L2DevicePoller::devicePollTask, base::Unretained(this)));
ALOGV("Polling thread started");
schedulePoll();
return true;
}
bool V4L2DevicePoller::stopPolling() {
ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
if (!isPolling()) return true;
ALOGV("Stopping polling");
mStopPolling.store(true);
if (!mDevice->setDevicePollInterrupt()) {
ALOGE("Failed to interrupt device poll.");
return false;
}
ALOGV("Stop device poll thread");
mPollThread.Stop();
if (!mDevice->clearDevicePollInterrupt()) {
ALOGE("Failed to clear interrupting device poll.");
return false;
}
ALOGV("Polling thread stopped");
return true;
}
bool V4L2DevicePoller::isPolling() const {
ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
return mPollThread.IsRunning();
}
void V4L2DevicePoller::schedulePoll() {
ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
// A call to DevicePollTask() will be posted when we actually start polling.
if (!isPolling()) return;
if (!mPollBuffers.exchange(true)) {
// Call mDevice->setDevicePollInterrupt only if pollBuffers is not
// already pending.
ALOGV("Scheduling poll");
if (!mDevice->setDevicePollInterrupt()) {
ALOGE("Failed to clear interrupting device poll.");
}
}
}
void V4L2DevicePoller::devicePollTask() {
ALOG_ASSERT(mPollThread.task_runner()->RunsTasksInCurrentSequence());
while (true) {
ALOGV("Waiting for poll to be scheduled.");
if (mStopPolling) {
ALOGV("Poll stopped, exiting.");
break;
}
bool event_pending = false;
bool buffers_pending = false;
ALOGV("Polling device.");
bool poll_buffers = mPollBuffers.exchange(false);
if (!mDevice->poll(true, poll_buffers, &event_pending, &buffers_pending)) {
ALOGE("An error occurred while polling, calling error callback");
mClientTaskTunner->PostTask(FROM_HERE, mErrorCallback);
return;
}
if (poll_buffers && !buffers_pending) {
// If buffer polling was requested but the buffers are not pending,
// then set to poll buffers again in the next iteration.
mPollBuffers.exchange(true);
}
if (!mDevice->clearDevicePollInterrupt()) {
ALOGE("Failed to clear interrupting device poll.");
}
if (buffers_pending || event_pending) {
ALOGV("Poll returned, calling event callback. event_pending=%d buffers_pending=%d",
event_pending, buffers_pending);
mClientTaskTunner->PostTask(FROM_HERE, base::Bind(mEventCallback, event_pending));
}
}
}
} // namespace android