diff options
Diffstat (limited to 'accel/v4l2_device_poller.cc')
-rw-r--r-- | accel/v4l2_device_poller.cc | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/accel/v4l2_device_poller.cc b/accel/v4l2_device_poller.cc new file mode 100644 index 0000000..c5eb820 --- /dev/null +++ b/accel/v4l2_device_poller.cc @@ -0,0 +1,140 @@ +// 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_device_poller.h" + +#include <string> + +#include "base/bind.h" +#include "base/threading/sequenced_task_runner_handle.h" +#include "base/threading/thread_checker.h" + +#include "macros.h" +#include "v4l2_device.h" + +namespace media { + +V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device, + const std::string& thread_name) + : device_(device), + poll_thread_(std::move(thread_name)), + trigger_poll_(base::WaitableEvent::ResetPolicy::AUTOMATIC, + base::WaitableEvent::InitialState::NOT_SIGNALED), + stop_polling_(false) { + DETACH_FROM_SEQUENCE(client_sequence_checker_); +} + +V4L2DevicePoller::~V4L2DevicePoller() { + DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); + + StopPolling(); +} + +bool V4L2DevicePoller::StartPolling(EventCallback event_callback, + base::RepeatingClosure error_callback) { + DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); + + if (IsPolling()) + return true; + + DVLOGF(4) << "Starting polling"; + + client_task_runner_ = base::SequencedTaskRunnerHandle::Get(); + error_callback_ = error_callback; + + if (!poll_thread_.Start()) { + VLOGF(1) << "Failed to start device poll thread"; + return false; + } + + event_callback_ = std::move(event_callback); + + stop_polling_.store(false); + poll_thread_.task_runner()->PostTask( + FROM_HERE, base::BindOnce(&V4L2DevicePoller::DevicePollTask, + base::Unretained(this))); + + DVLOGF(3) << "Polling thread started"; + + SchedulePoll(); + + return true; +} + +bool V4L2DevicePoller::StopPolling() { + DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); + + if (!IsPolling()) + return true; + + DVLOGF(4) << "Stopping polling"; + + stop_polling_.store(true); + + trigger_poll_.Signal(); + + if (!device_->SetDevicePollInterrupt()) { + VLOGF(1) << "Failed to interrupt device poll."; + return false; + } + + DVLOGF(3) << "Stop device poll thread"; + poll_thread_.Stop(); + + if (!device_->ClearDevicePollInterrupt()) { + VLOGF(1) << "Failed to clear interrupting device poll."; + return false; + } + + DVLOGF(4) << "Polling thread stopped"; + + return true; +} + +bool V4L2DevicePoller::IsPolling() const { + DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); + + return poll_thread_.IsRunning(); +} + +void V4L2DevicePoller::SchedulePoll() { + DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_); + + // A call to DevicePollTask() will be posted when we actually start polling. + if (!IsPolling()) + return; + + DVLOGF(4) << "Scheduling poll"; + + trigger_poll_.Signal(); +} + +void V4L2DevicePoller::DevicePollTask() { + DCHECK(poll_thread_.task_runner()->RunsTasksInCurrentSequence()); + + while (true) { + DVLOGF(4) << "Waiting for poll to be scheduled."; + trigger_poll_.Wait(); + + if (stop_polling_) { + DVLOGF(4) << "Poll stopped, exiting."; + break; + } + + bool event_pending = false; + DVLOGF(4) << "Polling device."; + if (!device_->Poll(true, &event_pending)) { + VLOGF(1) << "An error occurred while polling, calling error callback"; + client_task_runner_->PostTask(FROM_HERE, error_callback_); + return; + } + + DVLOGF(4) << "Poll returned, calling event callback."; + client_task_runner_->PostTask(FROM_HERE, + base::Bind(event_callback_, event_pending)); + } +} + +} // namespace media |