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