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 <cutils/log.h> 26 #include "EmulatedFakeCamera.h" 27 28 namespace android { 29 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 58 EmulatedFakeCameraDevice::~EmulatedFakeCameraDevice() {} 59 60 /**************************************************************************** 61 * Emulated camera device abstract interface implementation. 62 ***************************************************************************/ 63 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 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 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 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 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 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 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 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 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 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