1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 // Note: ported from Chromium commit head: 22d34680c8ac
5
6 #include "v4l2_device_poller.h"
7
8 #include <string>
9
10 #include "base/bind.h"
11 #include "base/threading/sequenced_task_runner_handle.h"
12 #include "base/threading/thread_checker.h"
13
14 #include "macros.h"
15 #include "v4l2_device.h"
16
17 namespace media {
18
V4L2DevicePoller(V4L2Device * const device,const std::string & thread_name)19 V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device,
20 const std::string& thread_name)
21 : device_(device),
22 poll_thread_(std::move(thread_name)),
23 trigger_poll_(base::WaitableEvent::ResetPolicy::AUTOMATIC,
24 base::WaitableEvent::InitialState::NOT_SIGNALED),
25 stop_polling_(false) {
26 DETACH_FROM_SEQUENCE(client_sequence_checker_);
27 }
28
~V4L2DevicePoller()29 V4L2DevicePoller::~V4L2DevicePoller() {
30 DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
31
32 StopPolling();
33 }
34
StartPolling(EventCallback event_callback,base::RepeatingClosure error_callback)35 bool V4L2DevicePoller::StartPolling(EventCallback event_callback,
36 base::RepeatingClosure error_callback) {
37 DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
38
39 if (IsPolling())
40 return true;
41
42 DVLOGF(4) << "Starting polling";
43
44 client_task_runner_ = base::SequencedTaskRunnerHandle::Get();
45 error_callback_ = error_callback;
46
47 if (!poll_thread_.Start()) {
48 VLOGF(1) << "Failed to start device poll thread";
49 return false;
50 }
51
52 event_callback_ = std::move(event_callback);
53
54 stop_polling_.store(false);
55 poll_thread_.task_runner()->PostTask(
56 FROM_HERE, base::BindOnce(&V4L2DevicePoller::DevicePollTask,
57 base::Unretained(this)));
58
59 DVLOGF(3) << "Polling thread started";
60
61 SchedulePoll();
62
63 return true;
64 }
65
StopPolling()66 bool V4L2DevicePoller::StopPolling() {
67 DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
68
69 if (!IsPolling())
70 return true;
71
72 DVLOGF(4) << "Stopping polling";
73
74 stop_polling_.store(true);
75
76 trigger_poll_.Signal();
77
78 if (!device_->SetDevicePollInterrupt()) {
79 VLOGF(1) << "Failed to interrupt device poll.";
80 return false;
81 }
82
83 DVLOGF(3) << "Stop device poll thread";
84 poll_thread_.Stop();
85
86 if (!device_->ClearDevicePollInterrupt()) {
87 VLOGF(1) << "Failed to clear interrupting device poll.";
88 return false;
89 }
90
91 DVLOGF(4) << "Polling thread stopped";
92
93 return true;
94 }
95
IsPolling() const96 bool V4L2DevicePoller::IsPolling() const {
97 DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
98
99 return poll_thread_.IsRunning();
100 }
101
SchedulePoll()102 void V4L2DevicePoller::SchedulePoll() {
103 DCHECK_CALLED_ON_VALID_SEQUENCE(client_sequence_checker_);
104
105 // A call to DevicePollTask() will be posted when we actually start polling.
106 if (!IsPolling())
107 return;
108
109 DVLOGF(4) << "Scheduling poll";
110
111 trigger_poll_.Signal();
112 }
113
DevicePollTask()114 void V4L2DevicePoller::DevicePollTask() {
115 DCHECK(poll_thread_.task_runner()->RunsTasksInCurrentSequence());
116
117 while (true) {
118 DVLOGF(4) << "Waiting for poll to be scheduled.";
119 trigger_poll_.Wait();
120
121 if (stop_polling_) {
122 DVLOGF(4) << "Poll stopped, exiting.";
123 break;
124 }
125
126 bool event_pending = false;
127 DVLOGF(4) << "Polling device.";
128 if (!device_->Poll(true, &event_pending)) {
129 VLOGF(1) << "An error occurred while polling, calling error callback";
130 client_task_runner_->PostTask(FROM_HERE, error_callback_);
131 return;
132 }
133
134 DVLOGF(4) << "Poll returned, calling event callback.";
135 client_task_runner_->PostTask(FROM_HERE,
136 base::Bind(event_callback_, event_pending));
137 }
138 }
139
140 } // namespace media
141