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 //#define LOG_NDEBUG 0
7 #define LOG_TAG "V4L2DevicePoller"
8
9 #include <v4l2_codec2/v4l2/V4L2DevicePoller.h>
10
11 #include <string>
12
13 #include <base/bind.h>
14 #include <base/threading/sequenced_task_runner_handle.h>
15 #include <base/threading/thread_checker.h>
16 #include <log/log.h>
17
18 #include <v4l2_codec2/v4l2/V4L2Device.h>
19
20 namespace android {
21
V4L2DevicePoller(V4L2Device * const device,const std::string & threadName,scoped_refptr<base::SequencedTaskRunner> taskRunner)22 V4L2DevicePoller::V4L2DevicePoller(V4L2Device* const device, const std::string& threadName,
23 scoped_refptr<base::SequencedTaskRunner> taskRunner)
24 : mDevice(device),
25 mPollThread(std::move(threadName)),
26 mClientTaskTunner(std::move(taskRunner)),
27 mStopPolling(false) {}
28
~V4L2DevicePoller()29 V4L2DevicePoller::~V4L2DevicePoller() {
30 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
31
32 stopPolling();
33 }
34
startPolling(EventCallback eventCallback,base::RepeatingClosure errorCallback)35 bool V4L2DevicePoller::startPolling(EventCallback eventCallback,
36 base::RepeatingClosure errorCallback) {
37 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
38
39 if (isPolling()) return true;
40
41 ALOGV("Starting polling");
42
43 mErrorCallback = errorCallback;
44
45 if (!mPollThread.Start()) {
46 ALOGE("Failed to start device poll thread");
47 return false;
48 }
49
50 mEventCallback = std::move(eventCallback);
51
52 mPollBuffers.store(false);
53 mStopPolling.store(false);
54 mPollThread.task_runner()->PostTask(
55 FROM_HERE, base::BindOnce(&V4L2DevicePoller::devicePollTask, base::Unretained(this)));
56
57 ALOGV("Polling thread started");
58
59 schedulePoll();
60
61 return true;
62 }
63
stopPolling()64 bool V4L2DevicePoller::stopPolling() {
65 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
66
67 if (!isPolling()) return true;
68
69 ALOGV("Stopping polling");
70
71 mStopPolling.store(true);
72
73 if (!mDevice->setDevicePollInterrupt()) {
74 ALOGE("Failed to interrupt device poll.");
75 return false;
76 }
77
78 ALOGV("Stop device poll thread");
79 mPollThread.Stop();
80
81 if (!mDevice->clearDevicePollInterrupt()) {
82 ALOGE("Failed to clear interrupting device poll.");
83 return false;
84 }
85
86 ALOGV("Polling thread stopped");
87
88 return true;
89 }
90
isPolling() const91 bool V4L2DevicePoller::isPolling() const {
92 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
93
94 return mPollThread.IsRunning();
95 }
96
schedulePoll()97 void V4L2DevicePoller::schedulePoll() {
98 ALOG_ASSERT(mClientTaskTunner->RunsTasksInCurrentSequence());
99
100 // A call to DevicePollTask() will be posted when we actually start polling.
101 if (!isPolling()) return;
102
103 if (!mPollBuffers.exchange(true)) {
104 // Call mDevice->setDevicePollInterrupt only if pollBuffers is not
105 // already pending.
106 ALOGV("Scheduling poll");
107 if (!mDevice->setDevicePollInterrupt()) {
108 ALOGE("Failed to clear interrupting device poll.");
109 }
110 }
111 }
112
devicePollTask()113 void V4L2DevicePoller::devicePollTask() {
114 ALOG_ASSERT(mPollThread.task_runner()->RunsTasksInCurrentSequence());
115
116 while (true) {
117 ALOGV("Waiting for poll to be scheduled.");
118
119 if (mStopPolling) {
120 ALOGV("Poll stopped, exiting.");
121 break;
122 }
123
124 bool event_pending = false;
125 bool buffers_pending = false;
126
127 ALOGV("Polling device.");
128 bool poll_buffers = mPollBuffers.exchange(false);
129 if (!mDevice->poll(true, poll_buffers, &event_pending, &buffers_pending)) {
130 ALOGE("An error occurred while polling, calling error callback");
131 mClientTaskTunner->PostTask(FROM_HERE, mErrorCallback);
132 return;
133 }
134
135 if (poll_buffers && !buffers_pending) {
136 // If buffer polling was requested but the buffers are not pending,
137 // then set to poll buffers again in the next iteration.
138 mPollBuffers.exchange(true);
139 }
140
141 if (!mDevice->clearDevicePollInterrupt()) {
142 ALOGE("Failed to clear interrupting device poll.");
143 }
144
145 if (buffers_pending || event_pending) {
146 ALOGV("Poll returned, calling event callback. event_pending=%d buffers_pending=%d",
147 event_pending, buffers_pending);
148 mClientTaskTunner->PostTask(FROM_HERE, base::Bind(mEventCallback, event_pending));
149 }
150 }
151 }
152
153 } // namespace android
154