// 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 #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