xref: /aosp_15_r20/external/v4l2_codec2/v4l2/V4L2DevicePoller.cpp (revision 0ec5a0ec62797f775085659156625e7f1bdb369f)
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