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