blob: b76353dd82fdf66c6b3aebef9995c5c89923d64b
1 | /* |
2 | * Copyright (C) 2011 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 | /* |
18 | * Contains implementation of an abstract class EmulatedCameraDevice that defines |
19 | * functionality expected from an emulated physical camera device: |
20 | * - Obtaining and setting camera parameters |
21 | * - Capturing frames |
22 | * - Streaming video |
23 | * - etc. |
24 | */ |
25 | |
26 | #define LOG_NDEBUG 0 |
27 | #define LOG_TAG "EmulatedCamera_Device" |
28 | #include <cutils/log.h> |
29 | #include <sys/select.h> |
30 | #include <cmath> |
31 | #include "EmulatedCameraDevice.h" |
32 | |
33 | namespace android { |
34 | |
35 | const float GAMMA_CORRECTION = 2.2f; |
36 | EmulatedCameraDevice::EmulatedCameraDevice(EmulatedCamera* camera_hal) |
37 | : mObjectLock(), |
38 | mCurFrameTimestamp(0), |
39 | mCameraHAL(camera_hal), |
40 | mCurrentFrame(NULL), |
41 | mExposureCompensation(1.0f), |
42 | mWhiteBalanceScale(NULL), |
43 | mSupportedWhiteBalanceScale(), |
44 | mState(ECDS_CONSTRUCTED) |
45 | { |
46 | } |
47 | |
48 | EmulatedCameraDevice::~EmulatedCameraDevice() |
49 | { |
50 | ALOGV("EmulatedCameraDevice destructor"); |
51 | if (mCurrentFrame != NULL) { |
52 | delete[] mCurrentFrame; |
53 | } |
54 | for (size_t i = 0; i < mSupportedWhiteBalanceScale.size(); ++i) { |
55 | if (mSupportedWhiteBalanceScale.valueAt(i) != NULL) { |
56 | delete[] mSupportedWhiteBalanceScale.valueAt(i); |
57 | } |
58 | } |
59 | } |
60 | |
61 | /**************************************************************************** |
62 | * Emulated camera device public API |
63 | ***************************************************************************/ |
64 | |
65 | status_t EmulatedCameraDevice::Initialize() |
66 | { |
67 | if (isInitialized()) { |
68 | ALOGW("%s: Emulated camera device is already initialized: mState = %d", |
69 | __FUNCTION__, mState); |
70 | return NO_ERROR; |
71 | } |
72 | |
73 | /* Instantiate worker thread object. */ |
74 | mWorkerThread = new WorkerThread(this); |
75 | if (getWorkerThread() == NULL) { |
76 | ALOGE("%s: Unable to instantiate worker thread object", __FUNCTION__); |
77 | return ENOMEM; |
78 | } |
79 | |
80 | mState = ECDS_INITIALIZED; |
81 | |
82 | return NO_ERROR; |
83 | } |
84 | |
85 | status_t EmulatedCameraDevice::startDeliveringFrames(bool one_burst) |
86 | { |
87 | ALOGV("%s", __FUNCTION__); |
88 | |
89 | if (!isStarted()) { |
90 | ALOGE("%s: Device is not started", __FUNCTION__); |
91 | return EINVAL; |
92 | } |
93 | |
94 | /* Frames will be delivered from the thread routine. */ |
95 | const status_t res = startWorkerThread(one_burst); |
96 | ALOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__); |
97 | return res; |
98 | } |
99 | |
100 | status_t EmulatedCameraDevice::stopDeliveringFrames() |
101 | { |
102 | ALOGV("%s", __FUNCTION__); |
103 | |
104 | if (!isStarted()) { |
105 | ALOGW("%s: Device is not started", __FUNCTION__); |
106 | return NO_ERROR; |
107 | } |
108 | |
109 | const status_t res = stopWorkerThread(); |
110 | ALOGE_IF(res != NO_ERROR, "%s: startWorkerThread failed", __FUNCTION__); |
111 | return res; |
112 | } |
113 | |
114 | void EmulatedCameraDevice::setExposureCompensation(const float ev) { |
115 | ALOGV("%s", __FUNCTION__); |
116 | |
117 | if (!isStarted()) { |
118 | ALOGW("%s: Fake camera device is not started.", __FUNCTION__); |
119 | } |
120 | |
121 | mExposureCompensation = std::pow(2.0f, ev / GAMMA_CORRECTION); |
122 | ALOGV("New exposure compensation is %f", mExposureCompensation); |
123 | } |
124 | |
125 | void EmulatedCameraDevice::initializeWhiteBalanceModes(const char* mode, |
126 | const float r_scale, |
127 | const float b_scale) { |
128 | ALOGV("%s with %s, %f, %f", __FUNCTION__, mode, r_scale, b_scale); |
129 | float* value = new float[3]; |
130 | value[0] = r_scale; value[1] = 1.0f; value[2] = b_scale; |
131 | mSupportedWhiteBalanceScale.add(String8(mode), value); |
132 | } |
133 | |
134 | void EmulatedCameraDevice::setWhiteBalanceMode(const char* mode) { |
135 | ALOGV("%s with white balance %s", __FUNCTION__, mode); |
136 | mWhiteBalanceScale = |
137 | mSupportedWhiteBalanceScale.valueFor(String8(mode)); |
138 | } |
139 | |
140 | /* Computes the pixel value after adjusting the white balance to the current |
141 | * one. The input the y, u, v channel of the pixel and the adjusted value will |
142 | * be stored in place. The adjustment is done in RGB space. |
143 | */ |
144 | void EmulatedCameraDevice::changeWhiteBalance(uint8_t& y, |
145 | uint8_t& u, |
146 | uint8_t& v) const { |
147 | float r_scale = mWhiteBalanceScale[0]; |
148 | float b_scale = mWhiteBalanceScale[2]; |
149 | int r = static_cast<float>(YUV2R(y, u, v)) / r_scale; |
150 | int g = YUV2G(y, u, v); |
151 | int b = static_cast<float>(YUV2B(y, u, v)) / b_scale; |
152 | |
153 | y = RGB2Y(r, g, b); |
154 | u = RGB2U(r, g, b); |
155 | v = RGB2V(r, g, b); |
156 | } |
157 | |
158 | status_t EmulatedCameraDevice::getCurrentPreviewFrame(void* buffer) |
159 | { |
160 | if (!isStarted()) { |
161 | ALOGE("%s: Device is not started", __FUNCTION__); |
162 | return EINVAL; |
163 | } |
164 | if (mCurrentFrame == NULL || buffer == NULL) { |
165 | ALOGE("%s: No framebuffer", __FUNCTION__); |
166 | return EINVAL; |
167 | } |
168 | |
169 | /* In emulation the framebuffer is never RGB. */ |
170 | switch (mPixelFormat) { |
171 | case V4L2_PIX_FMT_YVU420: |
172 | YV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight); |
173 | return NO_ERROR; |
174 | case V4L2_PIX_FMT_YUV420: |
175 | YU12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight); |
176 | return NO_ERROR; |
177 | case V4L2_PIX_FMT_NV21: |
178 | NV21ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight); |
179 | return NO_ERROR; |
180 | case V4L2_PIX_FMT_NV12: |
181 | NV12ToRGB32(mCurrentFrame, buffer, mFrameWidth, mFrameHeight); |
182 | return NO_ERROR; |
183 | |
184 | default: |
185 | ALOGE("%s: Unknown pixel format %.4s", |
186 | __FUNCTION__, reinterpret_cast<const char*>(&mPixelFormat)); |
187 | return EINVAL; |
188 | } |
189 | } |
190 | |
191 | /**************************************************************************** |
192 | * Emulated camera device private API |
193 | ***************************************************************************/ |
194 | |
195 | status_t EmulatedCameraDevice::commonStartDevice(int width, |
196 | int height, |
197 | uint32_t pix_fmt) |
198 | { |
199 | /* Validate pixel format, and calculate framebuffer size at the same time. */ |
200 | switch (pix_fmt) { |
201 | case V4L2_PIX_FMT_YVU420: |
202 | case V4L2_PIX_FMT_YUV420: |
203 | case V4L2_PIX_FMT_NV21: |
204 | case V4L2_PIX_FMT_NV12: |
205 | mFrameBufferSize = (width * height * 12) / 8; |
206 | break; |
207 | |
208 | default: |
209 | ALOGE("%s: Unknown pixel format %.4s", |
210 | __FUNCTION__, reinterpret_cast<const char*>(&pix_fmt)); |
211 | return EINVAL; |
212 | } |
213 | |
214 | /* Cache framebuffer info. */ |
215 | mFrameWidth = width; |
216 | mFrameHeight = height; |
217 | mPixelFormat = pix_fmt; |
218 | mTotalPixels = width * height; |
219 | |
220 | /* Allocate framebuffer. */ |
221 | mCurrentFrame = new uint8_t[mFrameBufferSize]; |
222 | if (mCurrentFrame == NULL) { |
223 | ALOGE("%s: Unable to allocate framebuffer", __FUNCTION__); |
224 | return ENOMEM; |
225 | } |
226 | ALOGV("%s: Allocated %p %zu bytes for %d pixels in %.4s[%dx%d] frame", |
227 | __FUNCTION__, mCurrentFrame, mFrameBufferSize, mTotalPixels, |
228 | reinterpret_cast<const char*>(&mPixelFormat), mFrameWidth, mFrameHeight); |
229 | return NO_ERROR; |
230 | } |
231 | |
232 | void EmulatedCameraDevice::commonStopDevice() |
233 | { |
234 | mFrameWidth = mFrameHeight = mTotalPixels = 0; |
235 | mPixelFormat = 0; |
236 | |
237 | if (mCurrentFrame != NULL) { |
238 | delete[] mCurrentFrame; |
239 | mCurrentFrame = NULL; |
240 | } |
241 | } |
242 | |
243 | /**************************************************************************** |
244 | * Worker thread management. |
245 | ***************************************************************************/ |
246 | |
247 | status_t EmulatedCameraDevice::startWorkerThread(bool one_burst) |
248 | { |
249 | ALOGV("%s", __FUNCTION__); |
250 | |
251 | if (!isInitialized()) { |
252 | ALOGE("%s: Emulated camera device is not initialized", __FUNCTION__); |
253 | return EINVAL; |
254 | } |
255 | |
256 | const status_t res = getWorkerThread()->startThread(one_burst); |
257 | ALOGE_IF(res != NO_ERROR, "%s: Unable to start worker thread", __FUNCTION__); |
258 | return res; |
259 | } |
260 | |
261 | status_t EmulatedCameraDevice::stopWorkerThread() |
262 | { |
263 | ALOGV("%s", __FUNCTION__); |
264 | |
265 | if (!isInitialized()) { |
266 | ALOGE("%s: Emulated camera device is not initialized", __FUNCTION__); |
267 | return EINVAL; |
268 | } |
269 | |
270 | const status_t res = getWorkerThread()->stopThread(); |
271 | ALOGE_IF(res != NO_ERROR, "%s: Unable to stop worker thread", __FUNCTION__); |
272 | return res; |
273 | } |
274 | |
275 | bool EmulatedCameraDevice::inWorkerThread() |
276 | { |
277 | /* This will end the thread loop, and will terminate the thread. Derived |
278 | * classes must override this method. */ |
279 | return false; |
280 | } |
281 | |
282 | /**************************************************************************** |
283 | * Worker thread implementation. |
284 | ***************************************************************************/ |
285 | |
286 | status_t EmulatedCameraDevice::WorkerThread::readyToRun() |
287 | { |
288 | ALOGV("Starting emulated camera device worker thread..."); |
289 | |
290 | ALOGW_IF(mThreadControl >= 0 || mControlFD >= 0, |
291 | "%s: Thread control FDs are opened", __FUNCTION__); |
292 | /* Create a pair of FDs that would be used to control the thread. */ |
293 | int thread_fds[2]; |
294 | if (pipe(thread_fds) == 0) { |
295 | mThreadControl = thread_fds[1]; |
296 | mControlFD = thread_fds[0]; |
297 | ALOGV("Emulated device's worker thread has been started."); |
298 | return NO_ERROR; |
299 | } else { |
300 | ALOGE("%s: Unable to create thread control FDs: %d -> %s", |
301 | __FUNCTION__, errno, strerror(errno)); |
302 | return errno; |
303 | } |
304 | } |
305 | |
306 | status_t EmulatedCameraDevice::WorkerThread::stopThread() |
307 | { |
308 | ALOGV("Stopping emulated camera device's worker thread..."); |
309 | |
310 | status_t res = EINVAL; |
311 | if (mThreadControl >= 0) { |
312 | /* Send "stop" message to the thread loop. */ |
313 | const ControlMessage msg = THREAD_STOP; |
314 | const int wres = |
315 | TEMP_FAILURE_RETRY(write(mThreadControl, &msg, sizeof(msg))); |
316 | if (wres == sizeof(msg)) { |
317 | /* Stop the thread, and wait till it's terminated. */ |
318 | res = requestExitAndWait(); |
319 | if (res == NO_ERROR) { |
320 | /* Close control FDs. */ |
321 | if (mThreadControl >= 0) { |
322 | close(mThreadControl); |
323 | mThreadControl = -1; |
324 | } |
325 | if (mControlFD >= 0) { |
326 | close(mControlFD); |
327 | mControlFD = -1; |
328 | } |
329 | ALOGV("Emulated camera device's worker thread has been stopped."); |
330 | } else { |
331 | ALOGE("%s: requestExitAndWait failed: %d -> %s", |
332 | __FUNCTION__, res, strerror(-res)); |
333 | } |
334 | } else { |
335 | ALOGE("%s: Unable to send THREAD_STOP message: %d -> %s", |
336 | __FUNCTION__, errno, strerror(errno)); |
337 | res = errno ? errno : EINVAL; |
338 | } |
339 | } else { |
340 | ALOGE("%s: Thread control FDs are not opened", __FUNCTION__); |
341 | } |
342 | |
343 | return res; |
344 | } |
345 | |
346 | EmulatedCameraDevice::WorkerThread::SelectRes |
347 | EmulatedCameraDevice::WorkerThread::Select(int fd, int timeout) |
348 | { |
349 | fd_set fds[1]; |
350 | struct timeval tv, *tvp = NULL; |
351 | |
352 | const int fd_num = (fd >= 0) ? max(fd, mControlFD) + 1 : |
353 | mControlFD + 1; |
354 | FD_ZERO(fds); |
355 | FD_SET(mControlFD, fds); |
356 | if (fd >= 0) { |
357 | FD_SET(fd, fds); |
358 | } |
359 | if (timeout) { |
360 | tv.tv_sec = timeout / 1000000; |
361 | tv.tv_usec = timeout % 1000000; |
362 | tvp = &tv; |
363 | } |
364 | int res = TEMP_FAILURE_RETRY(select(fd_num, fds, NULL, NULL, tvp)); |
365 | if (res < 0) { |
366 | ALOGE("%s: select returned %d and failed: %d -> %s", |
367 | __FUNCTION__, res, errno, strerror(errno)); |
368 | return ERROR; |
369 | } else if (res == 0) { |
370 | /* Timeout. */ |
371 | return TIMEOUT; |
372 | } else if (FD_ISSET(mControlFD, fds)) { |
373 | /* A control event. Lets read the message. */ |
374 | ControlMessage msg; |
375 | res = TEMP_FAILURE_RETRY(read(mControlFD, &msg, sizeof(msg))); |
376 | if (res != sizeof(msg)) { |
377 | ALOGE("%s: Unexpected message size %d, or an error %d -> %s", |
378 | __FUNCTION__, res, errno, strerror(errno)); |
379 | return ERROR; |
380 | } |
381 | /* THREAD_STOP is the only message expected here. */ |
382 | if (msg == THREAD_STOP) { |
383 | ALOGV("%s: THREAD_STOP message is received", __FUNCTION__); |
384 | return EXIT_THREAD; |
385 | } else { |
386 | ALOGE("Unknown worker thread message %d", msg); |
387 | return ERROR; |
388 | } |
389 | } else { |
390 | /* Must be an FD. */ |
391 | ALOGW_IF(fd < 0 || !FD_ISSET(fd, fds), "%s: Undefined 'select' result", |
392 | __FUNCTION__); |
393 | return READY; |
394 | } |
395 | } |
396 | |
397 | }; /* namespace android */ |
398 |