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 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 56 EmulatedFakeCameraDevice::~EmulatedFakeCameraDevice() 57 { 58 } 59 60 /**************************************************************************** 61 * Emulated camera device abstract interface implementation. 62 ***************************************************************************/ 63 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 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 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 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 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 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 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 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 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 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