1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "VirtualCamera.h"
18 
19 #include "Enumerator.h"
20 #include "HalCamera.h"
21 
22 #include <ui/GraphicBufferAllocator.h>
23 #include <ui/GraphicBufferMapper.h>
24 
25 #include <assert.h>
26 
27 namespace android {
28 namespace automotive {
29 namespace evs {
30 namespace V1_0 {
31 namespace implementation {
32 
VirtualCamera(sp<HalCamera> halCamera)33 VirtualCamera::VirtualCamera(sp<HalCamera> halCamera) : mHalCamera(halCamera) {}
34 
~VirtualCamera()35 VirtualCamera::~VirtualCamera() {
36     shutdown();
37 }
38 
shutdown()39 void VirtualCamera::shutdown() {
40     // In normal operation, the stream should already be stopped by the time we get here
41     if (mStreamState != STOPPED) {
42         // Note that if we hit this case, no terminating frame will be sent to the client,
43         // but they're probably already dead anyway.
44         ALOGW("Virtual camera being shutdown while stream is running");
45 
46         // Tell the frame delivery pipeline we don't want any more frames
47         mStreamState = STOPPING;
48 
49         if (mFramesHeld.size() > 0) {
50             ALOGW("VirtualCamera destructing with frames in flight.");
51 
52             // Return to the underlying hardware camera any buffers the client was holding
53             for (auto&& heldBuffer : mFramesHeld) {
54                 // Tell our parent that we're done with this buffer
55                 mHalCamera->doneWithFrame(heldBuffer);
56             }
57             mFramesHeld.clear();
58         }
59 
60         // Give the underlying hardware camera the heads up that it might be time to stop
61         mHalCamera->clientStreamEnding();
62     }
63 
64     // Drop our reference to our associated hardware camera
65     mHalCamera = nullptr;
66 }
67 
deliverFrame(const BufferDesc & buffer)68 bool VirtualCamera::deliverFrame(const BufferDesc& buffer) {
69     if (buffer.memHandle == nullptr) {
70         // Warn if we got an unexpected stream termination
71         if (mStreamState != STOPPING) {
72             // TODO:  Should we suicide in this case to trigger a restart of the stack?
73             ALOGW("Stream unexpectedly stopped");
74         }
75 
76         // This is the stream end marker, so send it along, then mark the stream as stopped
77         mStream->deliverFrame(buffer);
78         mStreamState = STOPPED;
79         return true;
80     } else {
81         if (mStreamState == STOPPED) {
82             // A stopped stream gets no frames
83             return false;
84         } else if (mFramesHeld.size() >= mFramesAllowed) {
85             // Indicate that we declined to send the frame to the client because they're at quota
86             ALOGI("Skipping new frame as we hold %zu of %u allowed.", mFramesHeld.size(),
87                   mFramesAllowed);
88             return false;
89         } else {
90             // Keep a record of this frame so we can clean up if we have to in case of client death
91             mFramesHeld.push_back(buffer);
92 
93             // Pass this buffer through to our client
94             mStream->deliverFrame(buffer);
95             return true;
96         }
97     }
98 }
99 
100 // Methods from ::android::hardware::automotive::evs::V1_0::IEvsCamera follow.
getCameraInfo(getCameraInfo_cb info_cb)101 Return<void> VirtualCamera::getCameraInfo(getCameraInfo_cb info_cb) {
102     // Straight pass through to hardware layer
103     return mHalCamera->getHwCamera()->getCameraInfo(info_cb);
104 }
105 
setMaxFramesInFlight(uint32_t bufferCount)106 Return<EvsResult> VirtualCamera::setMaxFramesInFlight(uint32_t bufferCount) {
107     // How many buffers are we trying to add (or remove if negative)
108     int bufferCountChange = bufferCount - mFramesAllowed;
109 
110     // Ask our parent for more buffers
111     bool result = mHalCamera->changeFramesInFlight(bufferCountChange);
112     if (!result) {
113         ALOGE("Failed to change buffer count by %d to %d", bufferCountChange, bufferCount);
114         return EvsResult::BUFFER_NOT_AVAILABLE;
115     }
116 
117     // Update our notion of how many frames we're allowed
118     mFramesAllowed = bufferCount;
119     return EvsResult::OK;
120 }
121 
startVideoStream(const::android::sp<IEvsCameraStream> & stream)122 Return<EvsResult> VirtualCamera::startVideoStream(const ::android::sp<IEvsCameraStream>& stream) {
123     // We only support a single stream at a time
124     if (mStreamState != STOPPED) {
125         ALOGE("ignoring startVideoStream call when a stream is already running.");
126         return EvsResult::STREAM_ALREADY_RUNNING;
127     }
128 
129     // Validate our held frame count is starting out at zero as we expect
130     assert(mFramesHeld.size() == 0);
131 
132     // Record the user's callback for use when we have a frame ready
133     mStream = stream;
134     mStreamState = RUNNING;
135 
136     // Tell the underlying camera hardware that we want to stream
137     Return<EvsResult> result = mHalCamera->clientStreamStarting();
138     if ((!result.isOk()) || (result != EvsResult::OK)) {
139         // If we failed to start the underlying stream, then we're not actually running
140         mStream = nullptr;
141         mStreamState = STOPPED;
142         return EvsResult::UNDERLYING_SERVICE_ERROR;
143     }
144 
145     // TODO:  Detect and exit if we encounter a stalled stream or unresponsive driver?
146     // Consider using a timer and watching for frame arrival?
147 
148     return EvsResult::OK;
149 }
150 
doneWithFrame(const BufferDesc & buffer)151 Return<void> VirtualCamera::doneWithFrame(const BufferDesc& buffer) {
152     if (buffer.memHandle == nullptr) {
153         ALOGE("ignoring doneWithFrame called with invalid handle");
154     } else {
155         // Find this buffer in our "held" list
156         auto it = mFramesHeld.begin();
157         while (it != mFramesHeld.end()) {
158             if (it->bufferId == buffer.bufferId) {
159                 // found it!
160                 break;
161             }
162             ++it;
163         }
164         if (it == mFramesHeld.end()) {
165             // We should always find the frame in our "held" list
166             ALOGE("Ignoring doneWithFrame called with unrecognized frameID %d", buffer.bufferId);
167         } else {
168             // Take this frame out of our "held" list
169             mFramesHeld.erase(it);
170 
171             // Tell our parent that we're done with this buffer
172             mHalCamera->doneWithFrame(buffer);
173         }
174     }
175 
176     return Void();
177 }
178 
stopVideoStream()179 Return<void> VirtualCamera::stopVideoStream() {
180     if (mStreamState == RUNNING) {
181         // Tell the frame delivery pipeline we don't want any more frames
182         mStreamState = STOPPING;
183 
184         // Deliver an empty frame to close out the frame stream
185         BufferDesc nullBuff = {};
186         auto result = mStream->deliverFrame(nullBuff);
187         if (!result.isOk()) {
188             ALOGE("Error delivering end of stream marker");
189         }
190 
191         // Since we are single threaded, no frame can be delivered while this function is running,
192         // so we can go directly to the STOPPED state here on the server.
193         // Note, however, that there still might be frames already queued that client will see
194         // after returning from the client side of this call.
195         mStreamState = STOPPED;
196 
197         // Give the underlying hardware camera the heads up that it might be time to stop
198         mHalCamera->clientStreamEnding();
199     }
200 
201     return Void();
202 }
203 
getExtendedInfo(uint32_t opaqueIdentifier)204 Return<int32_t> VirtualCamera::getExtendedInfo(uint32_t opaqueIdentifier) {
205     // Pass straight through to the hardware device
206     return mHalCamera->getHwCamera()->getExtendedInfo(opaqueIdentifier);
207 }
208 
setExtendedInfo(uint32_t opaqueIdentifier,int32_t opaqueValue)209 Return<EvsResult> VirtualCamera::setExtendedInfo(uint32_t opaqueIdentifier, int32_t opaqueValue) {
210     // Pass straight through to the hardware device
211     // TODO: Should we restrict access to this entry point somehow?
212     return mHalCamera->getHwCamera()->setExtendedInfo(opaqueIdentifier, opaqueValue);
213 }
214 
215 }  // namespace implementation
216 }  // namespace V1_0
217 }  // namespace evs
218 }  // namespace automotive
219 }  // namespace android
220