blob: 6715a2ed88f75605b359fd0d3046e00088801b2d [file] [log] [blame]
Scott Randolph5c99d852016-11-15 17:01:23 -08001/*
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#define LOG_TAG "android.hardware.evs@1.0-service"
18
19#include "EvsCamera.h"
20
21#include <ui/GraphicBufferAllocator.h>
22#include <ui/GraphicBufferMapper.h>
23
24
25namespace android {
26namespace hardware {
27namespace evs {
28namespace V1_0 {
29namespace implementation {
30
31
32// These are the special camera names for which we'll initialize custom test data
33const char EvsCamera::kCameraName_Backup[] = "backup";
34const char EvsCamera::kCameraName_RightTurn[] = "Right Turn";
35
36
37// TODO(b/31632518): Need to get notification when our client dies so we can close the camera.
38// As it stands, if the client dies suddently, the buffer may be stranded.
39// As possible work around would be to give the client a HIDL object to exclusively hold
40// and use it's destructor to perform some work in the server side.
41
42EvsCamera::EvsCamera(const char *id) {
43 ALOGD("EvsCamera instantiated");
44
45 mDescription.cameraId = id;
46 mFrameBusy = false;
47 mStreamState = STOPPED;
48
49 // Set up dummy data for testing
50 if (mDescription.cameraId == kCameraName_Backup) {
Steven Morelanddb972332017-01-05 14:11:41 -080051 mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_REVERSE);
Scott Randolph5c99d852016-11-15 17:01:23 -080052 mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary value
53 mDescription.defaultHorResolution = 320; // 1/2 NTSC/VGA
54 mDescription.defaultVerResolution = 240; // 1/2 NTSC/VGA
55 }
56 else if (mDescription.cameraId == kCameraName_RightTurn) {
57 // Nothing but the name and the usage hint
Steven Morelanddb972332017-01-05 14:11:41 -080058 mDescription.hints = static_cast<uint32_t>(UsageHint::USAGE_HINT_RIGHT_TURN);
Scott Randolph5c99d852016-11-15 17:01:23 -080059 }
60 else {
61 // Leave empty for a minimalist camera description without even a hint
62 }
63}
64
65EvsCamera::~EvsCamera() {
66 ALOGD("EvsCamera being destroyed");
67 std::lock_guard<std::mutex> lock(mAccessLock);
68
69 // Make sure our output stream is cleaned up
70 // (It really should be already)
71 stopVideoStream();
72
73 // Drop the graphics buffer we've been using
74 if (mBuffer) {
75 // Drop the graphics buffer we've been using
76 GraphicBufferAllocator& alloc(GraphicBufferAllocator::get());
77 alloc.free(mBuffer);
78 }
79
80 ALOGD("EvsCamera destroyed");
81}
82
83
84// Methods from ::android::hardware::evs::V1_0::IEvsCamera follow.
85Return<void> EvsCamera::getId(getId_cb id_cb) {
86 ALOGD("getId");
87
88 id_cb(mDescription.cameraId);
89
90 return Void();
91}
92
93
94Return<EvsResult> EvsCamera::setMaxFramesInFlight(uint32_t bufferCount) {
95 ALOGD("setMaxFramesInFlight");
96 std::lock_guard<std::mutex> lock(mAccessLock);
97
98 // TODO: Update our stored value
99
100 // TODO: Adjust our buffer count right now if we can. Otherwise, it'll adjust in doneWithFrame
101
102 // For now we support only one!
103 if (bufferCount != 1) {
104 return EvsResult::BUFFER_NOT_AVAILABLE;
105 }
106
107 return EvsResult::OK;
108}
109
110Return<EvsResult> EvsCamera::startVideoStream(const ::android::sp<IEvsCameraStream>& stream) {
111 ALOGD("startVideoStream");
112 std::lock_guard<std::mutex> lock(mAccessLock);
113
114 // We only support a single stream at a time
115 if (mStreamState != STOPPED) {
116 ALOGE("ignoring startVideoStream call when a stream is already running.");
117 return EvsResult::STREAM_ALREADY_RUNNING;
118 }
119
120 // Record the user's callback for use when we have a frame ready
121 mStream = stream;
122
123 // Allocate a graphics buffer into which we'll put our test images
124 if (!mBuffer) {
125 mWidth = (mDescription.defaultHorResolution) ? mDescription.defaultHorResolution : 640;
126 mHeight = (mDescription.defaultVerResolution) ? mDescription.defaultVerResolution : 480;
127 // TODO: What about stride? Assume no padding for now...
128 mStride = 4* mWidth; // Special cased to assume 4 byte pixels with no padding for now
129
130 ALOGD("Allocating buffer for camera frame");
131 GraphicBufferAllocator &alloc(GraphicBufferAllocator::get());
132 status_t result = alloc.allocate(mWidth, mHeight,
133 HAL_PIXEL_FORMAT_RGBA_8888, 1, GRALLOC_USAGE_HW_TEXTURE,
134 &mBuffer, &mStride, 0, "EvsCamera");
135 if (result != NO_ERROR) {
136 ALOGE("Error %d allocating %d x %d graphics buffer", result, mWidth, mHeight);
137 return EvsResult::BUFFER_NOT_AVAILABLE;
138 }
139 if (!mBuffer) {
140 ALOGE("We didn't get a buffer handle back from the allocator");
141 return EvsResult::BUFFER_NOT_AVAILABLE;
142 }
143 }
144
145 // Start the frame generation thread
146 mStreamState = RUNNING;
147 mCaptureThread = std::thread([this](){GenerateFrames();});
148
149 return EvsResult::OK;
150}
151
Steven Morelanddb972332017-01-05 14:11:41 -0800152Return<EvsResult> EvsCamera::doneWithFrame(uint32_t /* frameId */, const hidl_handle& bufferHandle) {
Scott Randolph5c99d852016-11-15 17:01:23 -0800153 ALOGD("doneWithFrame");
154 std::lock_guard<std::mutex> lock(mAccessLock);
155
156 if (!bufferHandle)
157 {
158 ALOGE("ignoring doneWithFrame called with invalid handle");
159 return EvsResult::INVALID_ARG;
160 }
161
162 // TODO: Track which frames we've delivered and validate this is one of them
163
164 // Mark the frame buffer as available for a new frame
165 mFrameBusy = false;
166
167 // TODO: If we currently have too many buffers, drop this one
168
169 return EvsResult::OK;
170}
171
172Return<void> EvsCamera::stopVideoStream() {
173 ALOGD("stopVideoStream");
174
175 bool waitForJoin = false;
176 // Lock scope
177 {
178 std::lock_guard <std::mutex> lock(mAccessLock);
179
180 if (mStreamState == RUNNING) {
181 // Tell the GenerateFrames loop we want it to stop
182 mStreamState = STOPPING;
183
184 // Note that we asked the thread to stop and should wait for it do so
185 waitForJoin = true;
186 }
187 }
188
189 if (waitForJoin) {
190 // Block outside the mutex until the "stop" flag has been acknowledged
191 // NOTE: We won't send any more frames, but the client might still get one already in flight
192 ALOGD("Waiting for stream thread to end...");
193 mCaptureThread.join();
194
195 // Lock scope
196 {
197 std::lock_guard <std::mutex> lock(mAccessLock);
198 mStreamState = STOPPED;
199 }
200 }
201
202 return Void();
203}
204
205Return<int32_t> EvsCamera::getExtendedInfo(uint32_t opaqueIdentifier) {
206 ALOGD("getExtendedInfo");
207 std::lock_guard<std::mutex> lock(mAccessLock);
208
209 // For any single digit value, return the index itself as a test value
210 if (opaqueIdentifier <= 9) {
211 return opaqueIdentifier;
212 }
213
214 // Return zero by default as required by the spec
215 return 0;
216}
217
218Return<EvsResult> EvsCamera::setExtendedInfo(uint32_t /*opaqueIdentifier*/, int32_t /*opaqueValue*/) {
219 ALOGD("setExtendedInfo");
220 std::lock_guard<std::mutex> lock(mAccessLock);
221
222 // We don't store any device specific information in this implementation
223 return EvsResult::INVALID_ARG;
224}
225
226
227void EvsCamera::GenerateFrames() {
228 ALOGD("Frame generate loop started");
229
230 uint32_t frameNumber;
231
232 while (true) {
233 bool timeForFrame = false;
234 // Lock scope
235 {
236 std::lock_guard<std::mutex> lock(mAccessLock);
237
238 // Tick the frame counter -- rollover is tolerated
239 frameNumber = mFrameId++;
240
241 if (mStreamState != RUNNING) {
242 // Break out of our main thread loop
243 break;
244 }
245
246 if (mFrameBusy) {
247 // Can't do anything right now -- skip this frame
248 ALOGW("Skipped a frame because client hasn't returned a buffer\n");
249 }
250 else {
251 // We're going to make the frame busy
252 mFrameBusy = true;
253 timeForFrame = true;
254 }
255 }
256
257 if (timeForFrame) {
258 // Lock our output buffer for writing
259 uint32_t *pixels = nullptr;
260 GraphicBufferMapper &mapper = GraphicBufferMapper::get();
261 mapper.lock(mBuffer,
262 GRALLOC_USAGE_SW_WRITE_OFTEN,
263 android::Rect(mWidth, mHeight),
264 (void **) &pixels);
265
266 // If we failed to lock the pixel buffer, we're about to crash, but log it first
267 if (!pixels) {
268 ALOGE("Camera failed to gain access to image buffer for writing");
269 }
270
271 // Fill in the test pixels
272 for (unsigned row = 0; row < mHeight; row++) {
273 for (unsigned col = 0; col < mWidth; col++) {
274 // Index into the row to set the pixel at this column
275 // (We're making vertical gradient in the green channel, and
276 // horitzontal gradient in the blue channel)
277 pixels[col] = 0xFF0000FF | ((row & 0xFF) << 16) | ((col & 0xFF) << 8);
278 }
279 // Point to the next row
280 pixels = pixels + (mStride / sizeof(*pixels));
281 }
282
283 // Release our output buffer
284 mapper.unlock(mBuffer);
285
286 // Issue the (asynchronous) callback to the client
287 mStream->deliverFrame(frameNumber, mBuffer);
288 ALOGD("Delivered %p as frame %d", mBuffer, frameNumber);
289 }
290
291 // We arbitrarily choose to generate frames at 10 fps (1/10 * uSecPerSec)
292 usleep(100000);
293 }
294
295 // If we've been asked to stop, send one last NULL frame to signal the actual end of stream
296 mStream->deliverFrame(frameNumber, nullptr);
297
298 return;
299}
300
301} // namespace implementation
302} // namespace V1_0
303} // namespace evs
304} // namespace hardware
305} // namespace android