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 a class EmulatedFakeCameraDevice that encapsulates
19 * fake camera device.
20 */
21
22 #define LOG_NDEBUG 0
23 #define LOG_TAG "EmulatedCamera_FakeDevice"
24 #include "EmulatedFakeCameraDevice.h"
25 #include <log/log.h>
26 #include "EmulatedFakeCamera.h"
27
28 namespace android {
29
EmulatedFakeCameraDevice(EmulatedFakeCamera * camera_hal)30 EmulatedFakeCameraDevice::EmulatedFakeCameraDevice(
31 EmulatedFakeCamera* camera_hal)
32 : EmulatedCameraDevice(camera_hal),
33 mBlackYUV(kBlack32),
34 mWhiteYUV(kWhite32),
35 mRedYUV(kRed8),
36 mGreenYUV(kGreen8),
37 mBlueYUV(kBlue8),
38 mLastRedrawn(0),
39 mCheckX(0),
40 mCheckY(0),
41 mCcounter(0)
42 #if EFCD_ROTATE_FRAME
43 ,
44 mLastRotatedAt(0),
45 mCurrentFrameType(0),
46 mCurrentColor(&mWhiteYUV)
47 #endif // EFCD_ROTATE_FRAME
48 {
49 // Makes the image with the original exposure compensation darker.
50 // So the effects of changing the exposure compensation can be seen.
51 mBlackYUV.Y = mBlackYUV.Y / 2;
52 mWhiteYUV.Y = mWhiteYUV.Y / 2;
53 mRedYUV.Y = mRedYUV.Y / 2;
54 mGreenYUV.Y = mGreenYUV.Y / 2;
55 mBlueYUV.Y = mBlueYUV.Y / 2;
56 }
57
~EmulatedFakeCameraDevice()58 EmulatedFakeCameraDevice::~EmulatedFakeCameraDevice() {}
59
60 /****************************************************************************
61 * Emulated camera device abstract interface implementation.
62 ***************************************************************************/
63
connectDevice()64 status_t EmulatedFakeCameraDevice::connectDevice() {
65 ALOGV("%s", __FUNCTION__);
66
67 Mutex::Autolock locker(&mObjectLock);
68 if (!isInitialized()) {
69 ALOGE("%s: Fake camera device is not initialized.", __FUNCTION__);
70 return EINVAL;
71 }
72 if (isConnected()) {
73 ALOGW("%s: Fake camera device is already connected.", __FUNCTION__);
74 return NO_ERROR;
75 }
76
77 /* There is no device to connect to. */
78 mState = ECDS_CONNECTED;
79
80 return NO_ERROR;
81 }
82
disconnectDevice()83 status_t EmulatedFakeCameraDevice::disconnectDevice() {
84 ALOGV("%s", __FUNCTION__);
85
86 Mutex::Autolock locker(&mObjectLock);
87 if (!isConnected()) {
88 ALOGW("%s: Fake camera device is already disconnected.", __FUNCTION__);
89 return NO_ERROR;
90 }
91 if (isStarted()) {
92 ALOGE("%s: Cannot disconnect from the started device.", __FUNCTION__);
93 return EINVAL;
94 }
95
96 /* There is no device to disconnect from. */
97 mState = ECDS_INITIALIZED;
98
99 return NO_ERROR;
100 }
101
startDevice(int width,int height,uint32_t pix_fmt,int fps)102 status_t EmulatedFakeCameraDevice::startDevice(int width, int height,
103 uint32_t pix_fmt, int fps) {
104 ALOGV("%s", __FUNCTION__);
105
106 Mutex::Autolock locker(&mObjectLock);
107 if (!isConnected()) {
108 ALOGE("%s: Fake camera device is not connected.", __FUNCTION__);
109 return EINVAL;
110 }
111 if (isStarted()) {
112 ALOGE("%s: Fake camera device is already started.", __FUNCTION__);
113 return EINVAL;
114 }
115
116 /* Initialize the base class. */
117 const status_t res =
118 EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt, fps);
119 if (res == NO_ERROR) {
120 /* Calculate U/V panes inside the framebuffer. */
121 switch (mPixelFormat) {
122 case V4L2_PIX_FMT_YVU420:
123 mFrameV = mCurrentFrame + mTotalPixels;
124 mFrameU = mFrameU + mTotalPixels / 4;
125 mUVStep = 1;
126 mUVTotalNum = mTotalPixels / 4;
127 break;
128
129 case V4L2_PIX_FMT_YUV420:
130 mFrameU = mCurrentFrame + mTotalPixels;
131 mFrameV = mFrameU + mTotalPixels / 4;
132 mUVStep = 1;
133 mUVTotalNum = mTotalPixels / 4;
134 break;
135
136 case V4L2_PIX_FMT_NV21:
137 /* Interleaved UV pane, V first. */
138 mFrameV = mCurrentFrame + mTotalPixels;
139 mFrameU = mFrameV + 1;
140 mUVStep = 2;
141 mUVTotalNum = mTotalPixels / 4;
142 break;
143
144 case V4L2_PIX_FMT_NV12:
145 /* Interleaved UV pane, U first. */
146 mFrameU = mCurrentFrame + mTotalPixels;
147 mFrameV = mFrameU + 1;
148 mUVStep = 2;
149 mUVTotalNum = mTotalPixels / 4;
150 break;
151
152 default:
153 ALOGE("%s: Unknown pixel format %.4s", __FUNCTION__,
154 reinterpret_cast<const char*>(&mPixelFormat));
155 return EINVAL;
156 }
157 /* Number of items in a single row inside U/V panes. */
158 mUVInRow = (width / 2) * mUVStep;
159 mState = ECDS_STARTED;
160 mCurFrameTimestamp = 0;
161 } else {
162 ALOGE("%s: commonStartDevice failed", __FUNCTION__);
163 }
164
165 return res;
166 }
167
stopDevice()168 status_t EmulatedFakeCameraDevice::stopDevice() {
169 ALOGV("%s", __FUNCTION__);
170
171 Mutex::Autolock locker(&mObjectLock);
172 if (!isStarted()) {
173 ALOGW("%s: Fake camera device is not started.", __FUNCTION__);
174 return NO_ERROR;
175 }
176
177 mFrameU = mFrameV = NULL;
178 EmulatedCameraDevice::commonStopDevice();
179 mState = ECDS_CONNECTED;
180
181 return NO_ERROR;
182 }
183
184 /****************************************************************************
185 * Worker thread management overrides.
186 ***************************************************************************/
187
inWorkerThread()188 bool EmulatedFakeCameraDevice::inWorkerThread() {
189 /* Wait till FPS timeout expires, or thread exit message is received. */
190 WorkerThread::SelectRes res =
191 getWorkerThread()->Select(-1, 1000000 / (mTargetFps / 1000));
192 if (res == WorkerThread::EXIT_THREAD) {
193 ALOGV("%s: Worker thread has been terminated.", __FUNCTION__);
194 return false;
195 }
196
197 /* Lets see if we need to generate a new frame. */
198 if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRedrawn) >= mRedrawAfter) {
199 /*
200 * Time to generate a new frame.
201 */
202
203 #if EFCD_ROTATE_FRAME
204 const int frame_type = rotateFrame();
205 switch (frame_type) {
206 case 0:
207 drawCheckerboard();
208 break;
209 case 1:
210 drawStripes();
211 break;
212 case 2:
213 drawSolid(mCurrentColor);
214 break;
215 }
216 #else
217 /* Draw the checker board. */
218 drawCheckerboard();
219
220 #endif // EFCD_ROTATE_FRAME
221
222 // mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
223 mCurFrameTimestamp += (1000000000 / (mTargetFps / 1000));
224 /* Timestamp the current frame, and notify the camera HAL about new frame.
225 */
226 mLastRedrawn = systemTime(SYSTEM_TIME_MONOTONIC);
227 mCameraHAL->onNextFrameAvailable(mCurrentFrame, mCurFrameTimestamp, this);
228 }
229
230 return true;
231 }
232
233 /****************************************************************************
234 * Fake camera device private API
235 ***************************************************************************/
236
drawCheckerboard()237 void EmulatedFakeCameraDevice::drawCheckerboard() {
238 const int size = mFrameWidth / 10;
239 bool black = true;
240
241 if (size == 0) {
242 // When this happens, it happens at a very high rate,
243 // so don't log any messages and just return.
244 return;
245 }
246
247 if ((mCheckX / size) & 1) black = false;
248 if ((mCheckY / size) & 1) black = !black;
249
250 int county = mCheckY % size;
251 int checkxremainder = mCheckX % size;
252 uint8_t* Y = mCurrentFrame;
253 uint8_t* U_pos = mFrameU;
254 uint8_t* V_pos = mFrameV;
255 uint8_t* U = U_pos;
256 uint8_t* V = V_pos;
257
258 YUVPixel adjustedWhite = YUVPixel(mWhiteYUV);
259 changeWhiteBalance(adjustedWhite.Y, adjustedWhite.U, adjustedWhite.V);
260
261 for (int y = 0; y < mFrameHeight; y++) {
262 int countx = checkxremainder;
263 bool current = black;
264 for (int x = 0; x < mFrameWidth; x += 2) {
265 if (current) {
266 mBlackYUV.get(Y, U, V);
267 } else {
268 adjustedWhite.get(Y, U, V);
269 }
270 *Y = changeExposure(*Y);
271 Y[1] = *Y;
272 Y += 2;
273 U += mUVStep;
274 V += mUVStep;
275 countx += 2;
276 if (countx >= size) {
277 countx = 0;
278 current = !current;
279 }
280 }
281 if (y & 0x1) {
282 U_pos = U;
283 V_pos = V;
284 } else {
285 U = U_pos;
286 V = V_pos;
287 }
288 if (county++ >= size) {
289 county = 0;
290 black = !black;
291 }
292 }
293 mCheckX += 3;
294 mCheckY++;
295
296 /* Run the square. */
297 int sqx = ((mCcounter * 3) & 255);
298 if (sqx > 128) sqx = 255 - sqx;
299 int sqy = ((mCcounter * 5) & 255);
300 if (sqy > 128) sqy = 255 - sqy;
301 const int sqsize = mFrameWidth / 10;
302 drawSquare(sqx * sqsize / 32, sqy * sqsize / 32, (sqsize * 5) >> 1,
303 (mCcounter & 0x100) ? &mRedYUV : &mGreenYUV);
304 mCcounter++;
305 }
306
drawSquare(int x,int y,int size,const YUVPixel * color)307 void EmulatedFakeCameraDevice::drawSquare(int x, int y, int size,
308 const YUVPixel* color) {
309 const int square_xstop = std::min(mFrameWidth, x + size);
310 const int square_ystop = std::min(mFrameHeight, y + size);
311 uint8_t* Y_pos = mCurrentFrame + y * mFrameWidth + x;
312
313 YUVPixel adjustedColor = *color;
314 changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V);
315
316 // Draw the square.
317 for (; y < square_ystop; y++) {
318 const int iUV = (y / 2) * mUVInRow + (x / 2) * mUVStep;
319 uint8_t* sqU = mFrameU + iUV;
320 uint8_t* sqV = mFrameV + iUV;
321 uint8_t* sqY = Y_pos;
322 for (int i = x; i < square_xstop; i += 2) {
323 adjustedColor.get(sqY, sqU, sqV);
324 *sqY = changeExposure(*sqY);
325 sqY[1] = *sqY;
326 sqY += 2;
327 sqU += mUVStep;
328 sqV += mUVStep;
329 }
330 Y_pos += mFrameWidth;
331 }
332 }
333
334 #if EFCD_ROTATE_FRAME
335
drawSolid(YUVPixel * color)336 void EmulatedFakeCameraDevice::drawSolid(YUVPixel* color) {
337 YUVPixel adjustedColor = *color;
338 changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V);
339
340 /* All Ys are the same. */
341 memset(mCurrentFrame, changeExposure(adjustedColor.Y), mTotalPixels);
342
343 /* Fill U, and V panes. */
344 uint8_t* U = mFrameU;
345 uint8_t* V = mFrameV;
346 for (int k = 0; k < mUVTotalNum; k++, U += mUVStep, V += mUVStep) {
347 *U = color->U;
348 *V = color->V;
349 }
350 }
351
drawStripes()352 void EmulatedFakeCameraDevice::drawStripes() {
353 /* Divide frame into 4 stripes. */
354 const int change_color_at = mFrameHeight / 4;
355 const int each_in_row = mUVInRow / mUVStep;
356 uint8_t* pY = mCurrentFrame;
357 for (int y = 0; y < mFrameHeight; y++, pY += mFrameWidth) {
358 /* Select the color. */
359 YUVPixel* color;
360 const int color_index = y / change_color_at;
361 if (color_index == 0) {
362 /* White stripe on top. */
363 color = &mWhiteYUV;
364 } else if (color_index == 1) {
365 /* Then the red stripe. */
366 color = &mRedYUV;
367 } else if (color_index == 2) {
368 /* Then the green stripe. */
369 color = &mGreenYUV;
370 } else {
371 /* And the blue stripe at the bottom. */
372 color = &mBlueYUV;
373 }
374 changeWhiteBalance(color->Y, color->U, color->V);
375
376 /* All Ys at the row are the same. */
377 memset(pY, changeExposure(color->Y), mFrameWidth);
378
379 /* Offset of the current row inside U/V panes. */
380 const int uv_off = (y / 2) * mUVInRow;
381 /* Fill U, and V panes. */
382 uint8_t* U = mFrameU + uv_off;
383 uint8_t* V = mFrameV + uv_off;
384 for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) {
385 *U = color->U;
386 *V = color->V;
387 }
388 }
389 }
390
rotateFrame()391 int EmulatedFakeCameraDevice::rotateFrame() {
392 if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRotatedAt) >= mRotateFreq) {
393 mLastRotatedAt = systemTime(SYSTEM_TIME_MONOTONIC);
394 mCurrentFrameType++;
395 if (mCurrentFrameType > 2) {
396 mCurrentFrameType = 0;
397 }
398 if (mCurrentFrameType == 2) {
399 ALOGD("********** Rotated to the SOLID COLOR frame **********");
400 /* Solid color: lets rotate color too. */
401 if (mCurrentColor == &mWhiteYUV) {
402 ALOGD("----- Painting a solid RED frame -----");
403 mCurrentColor = &mRedYUV;
404 } else if (mCurrentColor == &mRedYUV) {
405 ALOGD("----- Painting a solid GREEN frame -----");
406 mCurrentColor = &mGreenYUV;
407 } else if (mCurrentColor == &mGreenYUV) {
408 ALOGD("----- Painting a solid BLUE frame -----");
409 mCurrentColor = &mBlueYUV;
410 } else {
411 /* Back to white. */
412 ALOGD("----- Painting a solid WHITE frame -----");
413 mCurrentColor = &mWhiteYUV;
414 }
415 } else if (mCurrentFrameType == 0) {
416 ALOGD("********** Rotated to the CHECKERBOARD frame **********");
417 } else if (mCurrentFrameType == 1) {
418 ALOGD("********** Rotated to the STRIPED frame **********");
419 }
420 }
421
422 return mCurrentFrameType;
423 }
424
425 #endif // EFCD_ROTATE_FRAME
426
427 }; /* namespace android */
428