// 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 #include #include #include #include #include #include namespace android { V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device, const std::string& threadName, scoped_refptr 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