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