blob: 5f2d0a5568d39a38b81b511133f0d379d22e8f6c [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
#include <v4l2_codec2/common/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/common/V4L2Device.h>
namespace android {
V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device, const std::string& threadName)
: mDevice(device),
mPollThread(std::move(threadName)),
mTriggerPoll(base::WaitableEvent::ResetPolicy::AUTOMATIC,
base::WaitableEvent::InitialState::NOT_SIGNALED),
mStopPolling(false) {}
V4L2DevicePoller::~V4L2DevicePoller() {
ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
stopPolling();
}
bool V4L2DevicePoller::startPolling(EventCallback eventCallback,
base::RepeatingClosure errorCallback) {
if (isPolling()) return true;
ALOGV("Starting polling");
mClientTaskTunner = base::SequencedTaskRunnerHandle::Get();
mErrorCallback = errorCallback;
if (!mPollThread.Start()) {
ALOGE("Failed to start device poll thread");
return false;
}
mEventCallback = std::move(eventCallback);
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);
mTriggerPoll.Signal();
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;
ALOGV("Scheduling poll");
mTriggerPoll.Signal();
}
void V4L2DevicePoller::devicePollTask() {
ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
while (true) {
ALOGV("Waiting for poll to be scheduled.");
mTriggerPoll.Wait();
if (mStopPolling) {
ALOGV("Poll stopped, exiting.");
break;
}
bool event_pending = false;
ALOGV("Polling device.");
if (!mDevice->poll(true, &event_pending)) {
ALOGE("An error occurred while polling, calling error callback");
mClientTaskTunner->PostTask(FROM_HERE, mErrorCallback);
return;
}
ALOGV("Poll returned, calling event callback.");
mClientTaskTunner->PostTask(FROM_HERE, base::Bind(mEventCallback, event_pending));
}
}
} // namespace android