/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /* * Contains implementation of a class EmulatedFakeCameraDevice that encapsulates * fake camera device. */ #define LOG_NDEBUG 0 #define LOG_TAG "EmulatedCamera_FakeDevice" #include "EmulatedFakeCameraDevice.h" #include <cutils/log.h> #include "EmulatedFakeCamera.h" namespace android { EmulatedFakeCameraDevice::EmulatedFakeCameraDevice( EmulatedFakeCamera* camera_hal) : EmulatedCameraDevice(camera_hal), mBlackYUV(kBlack32), mWhiteYUV(kWhite32), mRedYUV(kRed8), mGreenYUV(kGreen8), mBlueYUV(kBlue8), mLastRedrawn(0), mCheckX(0), mCheckY(0), mCcounter(0) #if EFCD_ROTATE_FRAME , mLastRotatedAt(0), mCurrentFrameType(0), mCurrentColor(&mWhiteYUV) #endif // EFCD_ROTATE_FRAME { // Makes the image with the original exposure compensation darker. // So the effects of changing the exposure compensation can be seen. mBlackYUV.Y = mBlackYUV.Y / 2; mWhiteYUV.Y = mWhiteYUV.Y / 2; mRedYUV.Y = mRedYUV.Y / 2; mGreenYUV.Y = mGreenYUV.Y / 2; mBlueYUV.Y = mBlueYUV.Y / 2; } EmulatedFakeCameraDevice::~EmulatedFakeCameraDevice() {} /**************************************************************************** * Emulated camera device abstract interface implementation. ***************************************************************************/ status_t EmulatedFakeCameraDevice::connectDevice() { ALOGV("%s", __FUNCTION__); Mutex::Autolock locker(&mObjectLock); if (!isInitialized()) { ALOGE("%s: Fake camera device is not initialized.", __FUNCTION__); return EINVAL; } if (isConnected()) { ALOGW("%s: Fake camera device is already connected.", __FUNCTION__); return NO_ERROR; } /* There is no device to connect to. */ mState = ECDS_CONNECTED; return NO_ERROR; } status_t EmulatedFakeCameraDevice::disconnectDevice() { ALOGV("%s", __FUNCTION__); Mutex::Autolock locker(&mObjectLock); if (!isConnected()) { ALOGW("%s: Fake camera device is already disconnected.", __FUNCTION__); return NO_ERROR; } if (isStarted()) { ALOGE("%s: Cannot disconnect from the started device.", __FUNCTION__); return EINVAL; } /* There is no device to disconnect from. */ mState = ECDS_INITIALIZED; return NO_ERROR; } status_t EmulatedFakeCameraDevice::startDevice(int width, int height, uint32_t pix_fmt, int fps) { ALOGV("%s", __FUNCTION__); Mutex::Autolock locker(&mObjectLock); if (!isConnected()) { ALOGE("%s: Fake camera device is not connected.", __FUNCTION__); return EINVAL; } if (isStarted()) { ALOGE("%s: Fake camera device is already started.", __FUNCTION__); return EINVAL; } /* Initialize the base class. */ const status_t res = EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt, fps); if (res == NO_ERROR) { /* Calculate U/V panes inside the framebuffer. */ switch (mPixelFormat) { case V4L2_PIX_FMT_YVU420: mFrameV = mCurrentFrame + mTotalPixels; mFrameU = mFrameU + mTotalPixels / 4; mUVStep = 1; mUVTotalNum = mTotalPixels / 4; break; case V4L2_PIX_FMT_YUV420: mFrameU = mCurrentFrame + mTotalPixels; mFrameV = mFrameU + mTotalPixels / 4; mUVStep = 1; mUVTotalNum = mTotalPixels / 4; break; case V4L2_PIX_FMT_NV21: /* Interleaved UV pane, V first. */ mFrameV = mCurrentFrame + mTotalPixels; mFrameU = mFrameV + 1; mUVStep = 2; mUVTotalNum = mTotalPixels / 4; break; case V4L2_PIX_FMT_NV12: /* Interleaved UV pane, U first. */ mFrameU = mCurrentFrame + mTotalPixels; mFrameV = mFrameU + 1; mUVStep = 2; mUVTotalNum = mTotalPixels / 4; break; default: ALOGE("%s: Unknown pixel format %.4s", __FUNCTION__, reinterpret_cast<const char*>(&mPixelFormat)); return EINVAL; } /* Number of items in a single row inside U/V panes. */ mUVInRow = (width / 2) * mUVStep; mState = ECDS_STARTED; mCurFrameTimestamp = 0; } else { ALOGE("%s: commonStartDevice failed", __FUNCTION__); } return res; } status_t EmulatedFakeCameraDevice::stopDevice() { ALOGV("%s", __FUNCTION__); Mutex::Autolock locker(&mObjectLock); if (!isStarted()) { ALOGW("%s: Fake camera device is not started.", __FUNCTION__); return NO_ERROR; } mFrameU = mFrameV = NULL; EmulatedCameraDevice::commonStopDevice(); mState = ECDS_CONNECTED; return NO_ERROR; } /**************************************************************************** * Worker thread management overrides. ***************************************************************************/ bool EmulatedFakeCameraDevice::inWorkerThread() { /* Wait till FPS timeout expires, or thread exit message is received. */ WorkerThread::SelectRes res = getWorkerThread()->Select(-1, 1000000 / (mTargetFps / 1000)); if (res == WorkerThread::EXIT_THREAD) { ALOGV("%s: Worker thread has been terminated.", __FUNCTION__); return false; } /* Lets see if we need to generate a new frame. */ if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRedrawn) >= mRedrawAfter) { /* * Time to generate a new frame. */ #if EFCD_ROTATE_FRAME const int frame_type = rotateFrame(); switch (frame_type) { case 0: drawCheckerboard(); break; case 1: drawStripes(); break; case 2: drawSolid(mCurrentColor); break; } #else /* Draw the checker board. */ drawCheckerboard(); #endif // EFCD_ROTATE_FRAME // mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC); mCurFrameTimestamp += (1000000000 / (mTargetFps / 1000)); /* Timestamp the current frame, and notify the camera HAL about new frame. */ mLastRedrawn = systemTime(SYSTEM_TIME_MONOTONIC); mCameraHAL->onNextFrameAvailable(mCurrentFrame, mCurFrameTimestamp, this); } return true; } /**************************************************************************** * Fake camera device private API ***************************************************************************/ void EmulatedFakeCameraDevice::drawCheckerboard() { const int size = mFrameWidth / 10; bool black = true; if (size == 0) { // When this happens, it happens at a very high rate, // so don't log any messages and just return. return; } if ((mCheckX / size) & 1) black = false; if ((mCheckY / size) & 1) black = !black; int county = mCheckY % size; int checkxremainder = mCheckX % size; uint8_t* Y = mCurrentFrame; uint8_t* U_pos = mFrameU; uint8_t* V_pos = mFrameV; uint8_t* U = U_pos; uint8_t* V = V_pos; YUVPixel adjustedWhite = YUVPixel(mWhiteYUV); changeWhiteBalance(adjustedWhite.Y, adjustedWhite.U, adjustedWhite.V); for (int y = 0; y < mFrameHeight; y++) { int countx = checkxremainder; bool current = black; for (int x = 0; x < mFrameWidth; x += 2) { if (current) { mBlackYUV.get(Y, U, V); } else { adjustedWhite.get(Y, U, V); } *Y = changeExposure(*Y); Y[1] = *Y; Y += 2; U += mUVStep; V += mUVStep; countx += 2; if (countx >= size) { countx = 0; current = !current; } } if (y & 0x1) { U_pos = U; V_pos = V; } else { U = U_pos; V = V_pos; } if (county++ >= size) { county = 0; black = !black; } } mCheckX += 3; mCheckY++; /* Run the square. */ int sqx = ((mCcounter * 3) & 255); if (sqx > 128) sqx = 255 - sqx; int sqy = ((mCcounter * 5) & 255); if (sqy > 128) sqy = 255 - sqy; const int sqsize = mFrameWidth / 10; drawSquare(sqx * sqsize / 32, sqy * sqsize / 32, (sqsize * 5) >> 1, (mCcounter & 0x100) ? &mRedYUV : &mGreenYUV); mCcounter++; } void EmulatedFakeCameraDevice::drawSquare(int x, int y, int size, const YUVPixel* color) { const int square_xstop = std::min(mFrameWidth, x + size); const int square_ystop = std::min(mFrameHeight, y + size); uint8_t* Y_pos = mCurrentFrame + y * mFrameWidth + x; YUVPixel adjustedColor = *color; changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V); // Draw the square. for (; y < square_ystop; y++) { const int iUV = (y / 2) * mUVInRow + (x / 2) * mUVStep; uint8_t* sqU = mFrameU + iUV; uint8_t* sqV = mFrameV + iUV; uint8_t* sqY = Y_pos; for (int i = x; i < square_xstop; i += 2) { adjustedColor.get(sqY, sqU, sqV); *sqY = changeExposure(*sqY); sqY[1] = *sqY; sqY += 2; sqU += mUVStep; sqV += mUVStep; } Y_pos += mFrameWidth; } } #if EFCD_ROTATE_FRAME void EmulatedFakeCameraDevice::drawSolid(YUVPixel* color) { YUVPixel adjustedColor = *color; changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V); /* All Ys are the same. */ memset(mCurrentFrame, changeExposure(adjustedColor.Y), mTotalPixels); /* Fill U, and V panes. */ uint8_t* U = mFrameU; uint8_t* V = mFrameV; for (int k = 0; k < mUVTotalNum; k++, U += mUVStep, V += mUVStep) { *U = color->U; *V = color->V; } } void EmulatedFakeCameraDevice::drawStripes() { /* Divide frame into 4 stripes. */ const int change_color_at = mFrameHeight / 4; const int each_in_row = mUVInRow / mUVStep; uint8_t* pY = mCurrentFrame; for (int y = 0; y < mFrameHeight; y++, pY += mFrameWidth) { /* Select the color. */ YUVPixel* color; const int color_index = y / change_color_at; if (color_index == 0) { /* White stripe on top. */ color = &mWhiteYUV; } else if (color_index == 1) { /* Then the red stripe. */ color = &mRedYUV; } else if (color_index == 2) { /* Then the green stripe. */ color = &mGreenYUV; } else { /* And the blue stripe at the bottom. */ color = &mBlueYUV; } changeWhiteBalance(color->Y, color->U, color->V); /* All Ys at the row are the same. */ memset(pY, changeExposure(color->Y), mFrameWidth); /* Offset of the current row inside U/V panes. */ const int uv_off = (y / 2) * mUVInRow; /* Fill U, and V panes. */ uint8_t* U = mFrameU + uv_off; uint8_t* V = mFrameV + uv_off; for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) { *U = color->U; *V = color->V; } } } int EmulatedFakeCameraDevice::rotateFrame() { if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRotatedAt) >= mRotateFreq) { mLastRotatedAt = systemTime(SYSTEM_TIME_MONOTONIC); mCurrentFrameType++; if (mCurrentFrameType > 2) { mCurrentFrameType = 0; } if (mCurrentFrameType == 2) { ALOGD("********** Rotated to the SOLID COLOR frame **********"); /* Solid color: lets rotate color too. */ if (mCurrentColor == &mWhiteYUV) { ALOGD("----- Painting a solid RED frame -----"); mCurrentColor = &mRedYUV; } else if (mCurrentColor == &mRedYUV) { ALOGD("----- Painting a solid GREEN frame -----"); mCurrentColor = &mGreenYUV; } else if (mCurrentColor == &mGreenYUV) { ALOGD("----- Painting a solid BLUE frame -----"); mCurrentColor = &mBlueYUV; } else { /* Back to white. */ ALOGD("----- Painting a solid WHITE frame -----"); mCurrentColor = &mWhiteYUV; } } else if (mCurrentFrameType == 0) { ALOGD("********** Rotated to the CHECKERBOARD frame **********"); } else if (mCurrentFrameType == 1) { ALOGD("********** Rotated to the STRIPED frame **********"); } } return mCurrentFrameType; } #endif // EFCD_ROTATE_FRAME }; /* namespace android */