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 32namespace android { 33 34static const double kCheckXSpeed = 0.00000000096; 35static const double kCheckYSpeed = 0.00000000032; 36 37static const double kSquareXSpeed = 0.000000000096; 38static const double kSquareYSpeed = 0.000000000160; 39 40static const nsecs_t kSquareColorChangeIntervalNs = seconds(5); 41 42EmulatedFakeCameraDevice::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 73EmulatedFakeCameraDevice::~EmulatedFakeCameraDevice() 74{ 75} 76 77/**************************************************************************** 78 * Emulated camera device abstract interface implementation. 79 ***************************************************************************/ 80 81status_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 101status_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 121status_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 186status_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 206bool EmulatedFakeCameraDevice::produceFrame(void* buffer, int64_t* timestamp) 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 if (timestamp != nullptr) { 225 *timestamp = 0L; 226 } 227 return true; 228} 229 230/**************************************************************************** 231 * Fake camera device private API 232 ***************************************************************************/ 233 234void EmulatedFakeCameraDevice::drawCheckerboard(void* buffer) 235{ 236 nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); 237 nsecs_t elapsed = now - mLastRedrawn; 238 uint8_t* currentFrame = reinterpret_cast<uint8_t*>(buffer); 239 uint8_t* frameU = currentFrame + mFrameUOffset; 240 uint8_t* frameV = currentFrame + mFrameVOffset; 241 242 const int size = std::min(mFrameWidth, mFrameHeight) / 10; 243 bool black = true; 244 245 if (size == 0) { 246 // When this happens, it happens at a very high rate, 247 // so don't log any messages and just return. 248 return; 249 } 250 251 mCheckX += kCheckXSpeed * elapsed; 252 mCheckY += kCheckYSpeed * elapsed; 253 254 // Allow the X and Y values to transition across two checkerboard boxes 255 // before resetting it back. This allows for the gray to black transition. 256 // Note that this is in screen size independent coordinates so that frames 257 // will look similar regardless of resolution 258 if (mCheckX > 2.0) { 259 mCheckX -= 2.0; 260 } 261 if (mCheckY > 2.0) { 262 mCheckY -= 2.0; 263 } 264 265 // Are we in the gray or black zone? 266 if (mCheckX >= 1.0) 267 black = false; 268 if (mCheckY >= 1.0) 269 black = !black; 270 271 int county = static_cast<int>(mCheckY * size) % size; 272 int checkxremainder = static_cast<int>(mCheckX * size) % size; 273 274 YUVPixel adjustedWhite = YUVPixel(mWhiteYUV); 275 changeWhiteBalance(adjustedWhite.Y, adjustedWhite.U, adjustedWhite.V); 276 adjustedWhite.Y = changeExposure(adjustedWhite.Y); 277 YUVPixel adjustedBlack = YUVPixel(mBlackYUV); 278 adjustedBlack.Y = changeExposure(adjustedBlack.Y); 279 280 for(int y = 0; y < mFrameHeight; y++) { 281 int countx = checkxremainder; 282 bool current = black; 283 uint8_t* Y = currentFrame + mYStride * y; 284 uint8_t* U = frameU + mUVStride * (y / 2); 285 uint8_t* V = frameV + mUVStride * (y / 2); 286 for(int x = 0; x < mFrameWidth; x += 2) { 287 if (current) { 288 adjustedBlack.get(Y, U, V); 289 } else { 290 adjustedWhite.get(Y, U, V); 291 } 292 Y[1] = *Y; 293 Y += 2; U += mUVStep; V += mUVStep; 294 countx += 2; 295 if(countx >= size) { 296 countx = 0; 297 current = !current; 298 } 299 } 300 if(county++ >= size) { 301 county = 0; 302 black = !black; 303 } 304 } 305 306 /* Run the square. */ 307 const int squareSize = std::min(mFrameWidth, mFrameHeight) / 4; 308 mSquareX += mSquareXSpeed * elapsed; 309 mSquareY += mSquareYSpeed * elapsed; 310 int squareX = mSquareX * mFrameWidth; 311 int squareY = mSquareY * mFrameHeight; 312 if (squareX + squareSize > mFrameWidth) { 313 mSquareXSpeed = -mSquareXSpeed; 314 double relativeWidth = static_cast<double>(squareSize) / mFrameWidth; 315 mSquareX -= 2.0 * (mSquareX + relativeWidth - 1.0); 316 squareX = mSquareX * mFrameWidth; 317 } else if (squareX < 0) { 318 mSquareXSpeed = -mSquareXSpeed; 319 mSquareX = -mSquareX; 320 squareX = mSquareX * mFrameWidth; 321 } 322 if (squareY + squareSize > mFrameHeight) { 323 mSquareYSpeed = -mSquareYSpeed; 324 double relativeHeight = static_cast<double>(squareSize) / mFrameHeight; 325 mSquareY -= 2.0 * (mSquareY + relativeHeight - 1.0); 326 squareY = mSquareY * mFrameHeight; 327 } else if (squareY < 0) { 328 mSquareYSpeed = -mSquareYSpeed; 329 mSquareY = -mSquareY; 330 squareY = mSquareY * mFrameHeight; 331 } 332 333 if (now - mLastColorChange > kSquareColorChangeIntervalNs) { 334 mLastColorChange = now; 335 mSquareColor = mSquareColor == &mRedYUV ? &mGreenYUV : &mRedYUV; 336 } 337 338 drawSquare(buffer, squareX, squareY, squareSize, mSquareColor); 339 mLastRedrawn = now; 340} 341 342void EmulatedFakeCameraDevice::drawSquare(void* buffer, 343 int x, 344 int y, 345 int size, 346 const YUVPixel* color) 347{ 348 uint8_t* currentFrame = reinterpret_cast<uint8_t*>(buffer); 349 uint8_t* frameU = currentFrame + mFrameUOffset; 350 uint8_t* frameV = currentFrame + mFrameVOffset; 351 352 const int square_xstop = std::min(mFrameWidth, x + size); 353 const int square_ystop = std::min(mFrameHeight, y + size); 354 uint8_t* Y_pos = currentFrame + y * mYStride + x; 355 356 YUVPixel adjustedColor = *color; 357 changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V); 358 359 // Draw the square. 360 for (; y < square_ystop; y++) { 361 const int iUV = (y / 2) * mUVStride + (x / 2) * mUVStep; 362 uint8_t* sqU = frameU + iUV; 363 uint8_t* sqV = frameV + iUV; 364 uint8_t* sqY = Y_pos; 365 for (int i = x; i < square_xstop; i += 2) { 366 adjustedColor.get(sqY, sqU, sqV); 367 *sqY = changeExposure(*sqY); 368 sqY[1] = *sqY; 369 sqY += 2; sqU += mUVStep; sqV += mUVStep; 370 } 371 Y_pos += mYStride; 372 } 373} 374 375#if EFCD_ROTATE_FRAME 376 377void EmulatedFakeCameraDevice::drawSolid(void* buffer, YUVPixel* color) 378{ 379 YUVPixel adjustedColor = *color; 380 changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V); 381 382 /* All Ys are the same, will fill any alignment padding but that's OK */ 383 memset(mCurrentFrame, changeExposure(adjustedColor.Y), 384 mFrameHeight * mYStride); 385 386 /* Fill U, and V panes. */ 387 for (int y = 0; y < mFrameHeight / 2; ++y) { 388 uint8_t* U = mFrameU + y * mUVStride; 389 uint8_t* V = mFrameV + y * mUVStride; 390 391 for (int x = 0; x < mFrameWidth / 2; ++x, U += mUVStep, V += mUVStep) { 392 *U = color->U; 393 *V = color->V; 394 } 395 } 396} 397 398void EmulatedFakeCameraDevice::drawStripes(void* buffer) 399{ 400 /* Divide frame into 4 stripes. */ 401 const int change_color_at = mFrameHeight / 4; 402 const int each_in_row = mUVInRow / mUVStep; 403 uint8_t* pY = mCurrentFrame; 404 for (int y = 0; y < mFrameHeight; y++, pY += mYStride) { 405 /* Select the color. */ 406 YUVPixel* color; 407 const int color_index = y / change_color_at; 408 if (color_index == 0) { 409 /* White stripe on top. */ 410 color = &mWhiteYUV; 411 } else if (color_index == 1) { 412 /* Then the red stripe. */ 413 color = &mRedYUV; 414 } else if (color_index == 2) { 415 /* Then the green stripe. */ 416 color = &mGreenYUV; 417 } else { 418 /* And the blue stripe at the bottom. */ 419 color = &mBlueYUV; 420 } 421 changeWhiteBalance(color->Y, color->U, color->V); 422 423 /* All Ys at the row are the same. */ 424 memset(pY, changeExposure(color->Y), mFrameWidth); 425 426 /* Offset of the current row inside U/V panes. */ 427 const int uv_off = (y / 2) * mUVStride; 428 /* Fill U, and V panes. */ 429 uint8_t* U = mFrameU + uv_off; 430 uint8_t* V = mFrameV + uv_off; 431 for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) { 432 *U = color->U; 433 *V = color->V; 434 } 435 } 436} 437 438int EmulatedFakeCameraDevice::rotateFrame() 439{ 440 if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRotatedAt) >= mRotateFreq) { 441 mLastRotatedAt = systemTime(SYSTEM_TIME_MONOTONIC); 442 mCurrentFrameType++; 443 if (mCurrentFrameType > 2) { 444 mCurrentFrameType = 0; 445 } 446 if (mCurrentFrameType == 2) { 447 ALOGD("********** Rotated to the SOLID COLOR frame **********"); 448 /* Solid color: lets rotate color too. */ 449 if (mCurrentColor == &mWhiteYUV) { 450 ALOGD("----- Painting a solid RED frame -----"); 451 mCurrentColor = &mRedYUV; 452 } else if (mCurrentColor == &mRedYUV) { 453 ALOGD("----- Painting a solid GREEN frame -----"); 454 mCurrentColor = &mGreenYUV; 455 } else if (mCurrentColor == &mGreenYUV) { 456 ALOGD("----- Painting a solid BLUE frame -----"); 457 mCurrentColor = &mBlueYUV; 458 } else { 459 /* Back to white. */ 460 ALOGD("----- Painting a solid WHITE frame -----"); 461 mCurrentColor = &mWhiteYUV; 462 } 463 } else if (mCurrentFrameType == 0) { 464 ALOGD("********** Rotated to the CHECKERBOARD frame **********"); 465 } else if (mCurrentFrameType == 1) { 466 ALOGD("********** Rotated to the STRIPED frame **********"); 467 } 468 } 469 470 return mCurrentFrameType; 471} 472 473#endif // EFCD_ROTATE_FRAME 474 475}; /* namespace android */ 476