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 #undef min 29 #undef max 30 #include <algorithm> 31 32 namespace android { 33 34 static const double kCheckXSpeed = 0.00000000096; 35 static const double kCheckYSpeed = 0.00000000032; 36 37 static const double kSquareXSpeed = 0.000000000096; 38 static const double kSquareYSpeed = 0.000000000160; 39 40 static const nsecs_t kSquareColorChangeIntervalNs = seconds(5); 41 42 EmulatedFakeCameraDevice::EmulatedFakeCameraDevice(EmulatedFakeCamera* camera_hal) 43 : EmulatedCameraDevice(camera_hal), 44 mBlackYUV(kBlack32), 45 mWhiteYUV(kWhite32), 46 mRedYUV(kRed8), 47 mGreenYUV(kGreen8), 48 mBlueYUV(kBlue8), 49 mSquareColor(&mRedYUV), 50 mLastRedrawn(0), 51 mLastColorChange(0), 52 mCheckX(0), 53 mCheckY(0), 54 mSquareX(0), 55 mSquareY(0), 56 mSquareXSpeed(kSquareXSpeed), 57 mSquareYSpeed(kSquareYSpeed) 58 #if EFCD_ROTATE_FRAME 59 , mLastRotatedAt(0), 60 mCurrentFrameType(0), 61 mCurrentColor(&mWhiteYUV) 62 #endif // EFCD_ROTATE_FRAME 63 { 64 // Makes the image with the original exposure compensation darker. 65 // So the effects of changing the exposure compensation can be seen. 66 mBlackYUV.Y = mBlackYUV.Y / 2; 67 mWhiteYUV.Y = mWhiteYUV.Y / 2; 68 mRedYUV.Y = mRedYUV.Y / 2; 69 mGreenYUV.Y = mGreenYUV.Y / 2; 70 mBlueYUV.Y = mBlueYUV.Y / 2; 71 } 72 73 EmulatedFakeCameraDevice::~EmulatedFakeCameraDevice() 74 { 75 } 76 77 /**************************************************************************** 78 * Emulated camera device abstract interface implementation. 79 ***************************************************************************/ 80 81 status_t EmulatedFakeCameraDevice::connectDevice() 82 { 83 ALOGV("%s", __FUNCTION__); 84 85 Mutex::Autolock locker(&mObjectLock); 86 if (!isInitialized()) { 87 ALOGE("%s: Fake camera device is not initialized.", __FUNCTION__); 88 return EINVAL; 89 } 90 if (isConnected()) { 91 ALOGW("%s: Fake camera device is already connected.", __FUNCTION__); 92 return NO_ERROR; 93 } 94 95 /* There is no device to connect to. */ 96 mState = ECDS_CONNECTED; 97 98 return NO_ERROR; 99 } 100 101 status_t EmulatedFakeCameraDevice::disconnectDevice() 102 { 103 ALOGV("%s", __FUNCTION__); 104 105 Mutex::Autolock locker(&mObjectLock); 106 if (!isConnected()) { 107 ALOGW("%s: Fake camera device is already disconnected.", __FUNCTION__); 108 return NO_ERROR; 109 } 110 if (isStarted()) { 111 ALOGE("%s: Cannot disconnect from the started device.", __FUNCTION__); 112 return EINVAL; 113 } 114 115 /* There is no device to disconnect from. */ 116 mState = ECDS_INITIALIZED; 117 118 return NO_ERROR; 119 } 120 121 status_t EmulatedFakeCameraDevice::startDevice(int width, 122 int height, 123 uint32_t pix_fmt) 124 { 125 ALOGV("%s", __FUNCTION__); 126 127 Mutex::Autolock locker(&mObjectLock); 128 if (!isConnected()) { 129 ALOGE("%s: Fake camera device is not connected.", __FUNCTION__); 130 return EINVAL; 131 } 132 if (isStarted()) { 133 ALOGE("%s: Fake camera device is already started.", __FUNCTION__); 134 return EINVAL; 135 } 136 137 /* Initialize the base class. */ 138 const status_t res = 139 EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt); 140 if (res == NO_ERROR) { 141 /* Calculate U/V panes inside the framebuffer. */ 142 switch (mPixelFormat) { 143 case V4L2_PIX_FMT_YVU420: 144 mFrameVOffset = mYStride * mFrameHeight; 145 mFrameUOffset = mFrameVOffset + mUVStride * (mFrameHeight / 2); 146 mUVStep = 1; 147 break; 148 149 case V4L2_PIX_FMT_YUV420: 150 mFrameUOffset = mYStride * mFrameHeight; 151 mFrameVOffset = mFrameUOffset + mUVStride * (mFrameHeight / 2); 152 mUVStep = 1; 153 break; 154 155 case V4L2_PIX_FMT_NV21: 156 /* Interleaved UV pane, V first. */ 157 mFrameVOffset = mYStride * mFrameHeight; 158 mFrameUOffset = mFrameVOffset + 1; 159 mUVStep = 2; 160 break; 161 162 case V4L2_PIX_FMT_NV12: 163 /* Interleaved UV pane, U first. */ 164 mFrameUOffset = mYStride * mFrameHeight; 165 mFrameVOffset = mFrameUOffset + 1; 166 mUVStep = 2; 167 break; 168 169 default: 170 ALOGE("%s: Unknown pixel format %.4s", __FUNCTION__, 171 reinterpret_cast<const char*>(&mPixelFormat)); 172 return EINVAL; 173 } 174 mLastRedrawn = systemTime(SYSTEM_TIME_MONOTONIC); 175 mLastColorChange = mLastRedrawn; 176 /* Number of items in a single row inside U/V panes. */ 177 mUVInRow = (width / 2) * mUVStep; 178 mState = ECDS_STARTED; 179 } else { 180 ALOGE("%s: commonStartDevice failed", __FUNCTION__); 181 } 182 183 return res; 184 } 185 186 status_t EmulatedFakeCameraDevice::stopDevice() 187 { 188 ALOGV("%s", __FUNCTION__); 189 190 Mutex::Autolock locker(&mObjectLock); 191 if (!isStarted()) { 192 ALOGW("%s: Fake camera device is not started.", __FUNCTION__); 193 return NO_ERROR; 194 } 195 196 EmulatedCameraDevice::commonStopDevice(); 197 mState = ECDS_CONNECTED; 198 199 return NO_ERROR; 200 } 201 202 /**************************************************************************** 203 * Worker thread management overrides. 204 ***************************************************************************/ 205 206 bool EmulatedFakeCameraDevice::produceFrame(void* buffer) 207 { 208 #if EFCD_ROTATE_FRAME 209 const int frame_type = rotateFrame(); 210 switch (frame_type) { 211 case 0: 212 drawCheckerboard(buffer); 213 break; 214 case 1: 215 drawStripes(buffer); 216 break; 217 case 2: 218 drawSolid(buffer, mCurrentColor); 219 break; 220 } 221 #else 222 drawCheckerboard(buffer); 223 #endif // EFCD_ROTATE_FRAME 224 return true; 225 } 226 227 /**************************************************************************** 228 * Fake camera device private API 229 ***************************************************************************/ 230 231 void EmulatedFakeCameraDevice::drawCheckerboard(void* buffer) 232 { 233 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); 234 nsecs_t elapsed = now - mLastRedrawn; 235 uint8_t* currentFrame = reinterpret_cast<uint8_t*>(buffer); 236 uint8_t* frameU = currentFrame + mFrameUOffset; 237 uint8_t* frameV = currentFrame + mFrameVOffset; 238 239 const int size = std::min(mFrameWidth, mFrameHeight) / 10; 240 bool black = true; 241 242 if (size == 0) { 243 // When this happens, it happens at a very high rate, 244 // so don't log any messages and just return. 245 return; 246 } 247 248 mCheckX += kCheckXSpeed * elapsed; 249 mCheckY += kCheckYSpeed * elapsed; 250 251 // Allow the X and Y values to transition across two checkerboard boxes 252 // before resetting it back. This allows for the gray to black transition. 253 // Note that this is in screen size independent coordinates so that frames 254 // will look similar regardless of resolution 255 if (mCheckX > 2.0) { 256 mCheckX -= 2.0; 257 } 258 if (mCheckY > 2.0) { 259 mCheckY -= 2.0; 260 } 261 262 // Are we in the gray or black zone? 263 if (mCheckX >= 1.0) 264 black = false; 265 if (mCheckY >= 1.0) 266 black = !black; 267 268 int county = static_cast<int>(mCheckY * size) % size; 269 int checkxremainder = static_cast<int>(mCheckX * size) % size; 270 271 YUVPixel adjustedWhite = YUVPixel(mWhiteYUV); 272 changeWhiteBalance(adjustedWhite.Y, adjustedWhite.U, adjustedWhite.V); 273 adjustedWhite.Y = changeExposure(adjustedWhite.Y); 274 YUVPixel adjustedBlack = YUVPixel(mBlackYUV); 275 adjustedBlack.Y = changeExposure(adjustedBlack.Y); 276 277 for(int y = 0; y < mFrameHeight; y++) { 278 int countx = checkxremainder; 279 bool current = black; 280 uint8_t* Y = currentFrame + mYStride * y; 281 uint8_t* U = frameU + mUVStride * (y / 2); 282 uint8_t* V = frameV + mUVStride * (y / 2); 283 for(int x = 0; x < mFrameWidth; x += 2) { 284 if (current) { 285 adjustedBlack.get(Y, U, V); 286 } else { 287 adjustedWhite.get(Y, U, V); 288 } 289 Y[1] = *Y; 290 Y += 2; U += mUVStep; V += mUVStep; 291 countx += 2; 292 if(countx >= size) { 293 countx = 0; 294 current = !current; 295 } 296 } 297 if(county++ >= size) { 298 county = 0; 299 black = !black; 300 } 301 } 302 303 /* Run the square. */ 304 const int squareSize = std::min(mFrameWidth, mFrameHeight) / 4; 305 mSquareX += mSquareXSpeed * elapsed; 306 mSquareY += mSquareYSpeed * elapsed; 307 int squareX = mSquareX * mFrameWidth; 308 int squareY = mSquareY * mFrameHeight; 309 if (squareX + squareSize > mFrameWidth) { 310 mSquareXSpeed = -mSquareXSpeed; 311 double relativeWidth = static_cast<double>(squareSize) / mFrameWidth; 312 mSquareX -= 2.0 * (mSquareX + relativeWidth - 1.0); 313 squareX = mSquareX * mFrameWidth; 314 } else if (squareX < 0) { 315 mSquareXSpeed = -mSquareXSpeed; 316 mSquareX = -mSquareX; 317 squareX = mSquareX * mFrameWidth; 318 } 319 if (squareY + squareSize > mFrameHeight) { 320 mSquareYSpeed = -mSquareYSpeed; 321 double relativeHeight = static_cast<double>(squareSize) / mFrameHeight; 322 mSquareY -= 2.0 * (mSquareY + relativeHeight - 1.0); 323 squareY = mSquareY * mFrameHeight; 324 } else if (squareY < 0) { 325 mSquareYSpeed = -mSquareYSpeed; 326 mSquareY = -mSquareY; 327 squareY = mSquareY * mFrameHeight; 328 } 329 330 if (now - mLastColorChange > kSquareColorChangeIntervalNs) { 331 mLastColorChange = now; 332 mSquareColor = mSquareColor == &mRedYUV ? &mGreenYUV : &mRedYUV; 333 } 334 335 drawSquare(buffer, squareX, squareY, squareSize, mSquareColor); 336 mLastRedrawn = now; 337 } 338 339 void EmulatedFakeCameraDevice::drawSquare(void* buffer, 340 int x, 341 int y, 342 int size, 343 const YUVPixel* color) 344 { 345 uint8_t* currentFrame = reinterpret_cast<uint8_t*>(buffer); 346 uint8_t* frameU = currentFrame + mFrameUOffset; 347 uint8_t* frameV = currentFrame + mFrameVOffset; 348 349 const int square_xstop = std::min(mFrameWidth, x + size); 350 const int square_ystop = std::min(mFrameHeight, y + size); 351 uint8_t* Y_pos = currentFrame + y * mYStride + x; 352 353 YUVPixel adjustedColor = *color; 354 changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V); 355 356 // Draw the square. 357 for (; y < square_ystop; y++) { 358 const int iUV = (y / 2) * mUVStride + (x / 2) * mUVStep; 359 uint8_t* sqU = frameU + iUV; 360 uint8_t* sqV = frameV + iUV; 361 uint8_t* sqY = Y_pos; 362 for (int i = x; i < square_xstop; i += 2) { 363 adjustedColor.get(sqY, sqU, sqV); 364 *sqY = changeExposure(*sqY); 365 sqY[1] = *sqY; 366 sqY += 2; sqU += mUVStep; sqV += mUVStep; 367 } 368 Y_pos += mYStride; 369 } 370 } 371 372 #if EFCD_ROTATE_FRAME 373 374 void EmulatedFakeCameraDevice::drawSolid(void* buffer, YUVPixel* color) 375 { 376 YUVPixel adjustedColor = *color; 377 changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V); 378 379 /* All Ys are the same, will fill any alignment padding but that's OK */ 380 memset(mCurrentFrame, changeExposure(adjustedColor.Y), 381 mFrameHeight * mYStride); 382 383 /* Fill U, and V panes. */ 384 for (int y = 0; y < mFrameHeight / 2; ++y) { 385 uint8_t* U = mFrameU + y * mUVStride; 386 uint8_t* V = mFrameV + y * mUVStride; 387 388 for (int x = 0; x < mFrameWidth / 2; ++x, U += mUVStep, V += mUVStep) { 389 *U = color->U; 390 *V = color->V; 391 } 392 } 393 } 394 395 void EmulatedFakeCameraDevice::drawStripes(void* buffer) 396 { 397 /* Divide frame into 4 stripes. */ 398 const int change_color_at = mFrameHeight / 4; 399 const int each_in_row = mUVInRow / mUVStep; 400 uint8_t* pY = mCurrentFrame; 401 for (int y = 0; y < mFrameHeight; y++, pY += mYStride) { 402 /* Select the color. */ 403 YUVPixel* color; 404 const int color_index = y / change_color_at; 405 if (color_index == 0) { 406 /* White stripe on top. */ 407 color = &mWhiteYUV; 408 } else if (color_index == 1) { 409 /* Then the red stripe. */ 410 color = &mRedYUV; 411 } else if (color_index == 2) { 412 /* Then the green stripe. */ 413 color = &mGreenYUV; 414 } else { 415 /* And the blue stripe at the bottom. */ 416 color = &mBlueYUV; 417 } 418 changeWhiteBalance(color->Y, color->U, color->V); 419 420 /* All Ys at the row are the same. */ 421 memset(pY, changeExposure(color->Y), mFrameWidth); 422 423 /* Offset of the current row inside U/V panes. */ 424 const int uv_off = (y / 2) * mUVStride; 425 /* Fill U, and V panes. */ 426 uint8_t* U = mFrameU + uv_off; 427 uint8_t* V = mFrameV + uv_off; 428 for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) { 429 *U = color->U; 430 *V = color->V; 431 } 432 } 433 } 434 435 int EmulatedFakeCameraDevice::rotateFrame() 436 { 437 if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRotatedAt) >= mRotateFreq) { 438 mLastRotatedAt = systemTime(SYSTEM_TIME_MONOTONIC); 439 mCurrentFrameType++; 440 if (mCurrentFrameType > 2) { 441 mCurrentFrameType = 0; 442 } 443 if (mCurrentFrameType == 2) { 444 ALOGD("********** Rotated to the SOLID COLOR frame **********"); 445 /* Solid color: lets rotate color too. */ 446 if (mCurrentColor == &mWhiteYUV) { 447 ALOGD("----- Painting a solid RED frame -----"); 448 mCurrentColor = &mRedYUV; 449 } else if (mCurrentColor == &mRedYUV) { 450 ALOGD("----- Painting a solid GREEN frame -----"); 451 mCurrentColor = &mGreenYUV; 452 } else if (mCurrentColor == &mGreenYUV) { 453 ALOGD("----- Painting a solid BLUE frame -----"); 454 mCurrentColor = &mBlueYUV; 455 } else { 456 /* Back to white. */ 457 ALOGD("----- Painting a solid WHITE frame -----"); 458 mCurrentColor = &mWhiteYUV; 459 } 460 } else if (mCurrentFrameType == 0) { 461 ALOGD("********** Rotated to the CHECKERBOARD frame **********"); 462 } else if (mCurrentFrameType == 1) { 463 ALOGD("********** Rotated to the STRIPED frame **********"); 464 } 465 } 466 467 return mCurrentFrameType; 468 } 469 470 #endif // EFCD_ROTATE_FRAME 471 472 }; /* namespace android */ 473