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