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;
EmulatedCameraDevice(EmulatedCamera * camera_hal)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
~EmulatedCameraDevice()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
Initialize()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
startDeliveringFrames(bool one_burst)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
stopDeliveringFrames()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
setExposureCompensation(const float ev)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
initializeWhiteBalanceModes(const char * mode,const float r_scale,const float b_scale)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
setWhiteBalanceMode(const char * mode)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 */
changeWhiteBalance(uint8_t & y,uint8_t & u,uint8_t & v) const144 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
getCurrentPreviewFrame(void * buffer)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
commonStartDevice(int width,int height,uint32_t pix_fmt)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
commonStopDevice()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
startWorkerThread(bool one_burst)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
stopWorkerThread()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
inWorkerThread()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
readyToRun()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 status_t ret;
295 Mutex::Autolock lock(mCameraDevice->mObjectLock);
296 if (pipe(thread_fds) == 0) {
297 mThreadControl = thread_fds[1];
298 mControlFD = thread_fds[0];
299 ALOGV("Emulated device's worker thread has been started.");
300 ret = NO_ERROR;
301 } else {
302 ALOGE("%s: Unable to create thread control FDs: %d -> %s",
303 __FUNCTION__, errno, strerror(errno));
304 ret = errno;
305 }
306
307 mSetup.signal();
308 return ret;
309 }
310
stopThread()311 status_t EmulatedCameraDevice::WorkerThread::stopThread()
312 {
313 ALOGV("Stopping emulated camera device's worker thread...");
314
315 status_t res = EINVAL;
316
317 // Limit the scope of the Autolock
318 {
319 // If thread is running and readyToRun() has not finished running,
320 // then wait until it is done.
321 Mutex::Autolock lock(mCameraDevice->mObjectLock);
322 if (isRunning() && (mThreadControl < 0 || mControlFD < 0)) {
323 mSetup.wait(mCameraDevice->mObjectLock);
324 }
325 }
326
327 if (mThreadControl >= 0) {
328 /* Send "stop" message to the thread loop. */
329 const ControlMessage msg = THREAD_STOP;
330 const int wres =
331 TEMP_FAILURE_RETRY(write(mThreadControl, &msg, sizeof(msg)));
332 if (wres == sizeof(msg)) {
333 /* Stop the thread, and wait till it's terminated. */
334 res = requestExitAndWait();
335 if (res == NO_ERROR) {
336 /* Close control FDs. */
337 if (mThreadControl >= 0) {
338 close(mThreadControl);
339 mThreadControl = -1;
340 }
341 if (mControlFD >= 0) {
342 close(mControlFD);
343 mControlFD = -1;
344 }
345 ALOGV("Emulated camera device's worker thread has been stopped.");
346 } else {
347 ALOGE("%s: requestExitAndWait failed: %d -> %s",
348 __FUNCTION__, res, strerror(-res));
349 }
350 } else {
351 ALOGE("%s: Unable to send THREAD_STOP message: %d -> %s",
352 __FUNCTION__, errno, strerror(errno));
353 res = errno ? errno : EINVAL;
354 }
355 } else {
356 ALOGE("%s: Thread control FDs are not opened", __FUNCTION__);
357 }
358
359 return res;
360 }
361
362 EmulatedCameraDevice::WorkerThread::SelectRes
Select(int fd,int timeout)363 EmulatedCameraDevice::WorkerThread::Select(int fd, int timeout)
364 {
365 fd_set fds[1];
366 struct timeval tv, *tvp = NULL;
367
368 const int fd_num = (fd >= 0) ? max(fd, mControlFD) + 1 :
369 mControlFD + 1;
370 FD_ZERO(fds);
371 FD_SET(mControlFD, fds);
372 if (fd >= 0) {
373 FD_SET(fd, fds);
374 }
375 if (timeout) {
376 tv.tv_sec = timeout / 1000000;
377 tv.tv_usec = timeout % 1000000;
378 tvp = &tv;
379 }
380 int res = TEMP_FAILURE_RETRY(select(fd_num, fds, NULL, NULL, tvp));
381 if (res < 0) {
382 ALOGE("%s: select returned %d and failed: %d -> %s",
383 __FUNCTION__, res, errno, strerror(errno));
384 return ERROR;
385 } else if (res == 0) {
386 /* Timeout. */
387 return TIMEOUT;
388 } else if (FD_ISSET(mControlFD, fds)) {
389 /* A control event. Lets read the message. */
390 ControlMessage msg;
391 res = TEMP_FAILURE_RETRY(read(mControlFD, &msg, sizeof(msg)));
392 if (res != sizeof(msg)) {
393 ALOGE("%s: Unexpected message size %d, or an error %d -> %s",
394 __FUNCTION__, res, errno, strerror(errno));
395 return ERROR;
396 }
397 /* THREAD_STOP is the only message expected here. */
398 if (msg == THREAD_STOP) {
399 ALOGV("%s: THREAD_STOP message is received", __FUNCTION__);
400 return EXIT_THREAD;
401 } else {
402 ALOGE("Unknown worker thread message %d", msg);
403 return ERROR;
404 }
405 } else {
406 /* Must be an FD. */
407 ALOGW_IF(fd < 0 || !FD_ISSET(fd, fds), "%s: Undefined 'select' result",
408 __FUNCTION__);
409 return READY;
410 }
411 }
412
413 }; /* namespace android */
414