Home | History | Annotate | Download | only in camera
      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((mCheckX / size) & 1)
    247         black = false;
    248     if((mCheckY / size) & 1)
    249         black = !black;
    250 
    251     int county = mCheckY % size;
    252     int checkxremainder = mCheckX % size;
    253     uint8_t* Y = mCurrentFrame;
    254     uint8_t* U_pos = mFrameU;
    255     uint8_t* V_pos = mFrameV;
    256     uint8_t* U = U_pos;
    257     uint8_t* V = V_pos;
    258 
    259     YUVPixel adjustedWhite = YUVPixel(mWhiteYUV);
    260     changeWhiteBalance(adjustedWhite.Y, adjustedWhite.U, adjustedWhite.V);
    261 
    262     for(int y = 0; y < mFrameHeight; y++) {
    263         int countx = checkxremainder;
    264         bool current = black;
    265         for(int x = 0; x < mFrameWidth; x += 2) {
    266             if (current) {
    267                 mBlackYUV.get(Y, U, V);
    268             } else {
    269                 adjustedWhite.get(Y, U, V);
    270             }
    271             *Y = changeExposure(*Y);
    272             Y[1] = *Y;
    273             Y += 2; U += mUVStep; V += mUVStep;
    274             countx += 2;
    275             if(countx >= size) {
    276                 countx = 0;
    277                 current = !current;
    278             }
    279         }
    280         if (y & 0x1) {
    281             U_pos = U;
    282             V_pos = V;
    283         } else {
    284             U = U_pos;
    285             V = V_pos;
    286         }
    287         if(county++ >= size) {
    288             county = 0;
    289             black = !black;
    290         }
    291     }
    292     mCheckX += 3;
    293     mCheckY++;
    294 
    295     /* Run the square. */
    296     int sqx = ((mCcounter * 3) & 255);
    297     if(sqx > 128) sqx = 255 - sqx;
    298     int sqy = ((mCcounter * 5) & 255);
    299     if(sqy > 128) sqy = 255 - sqy;
    300     const int sqsize = mFrameWidth / 10;
    301     drawSquare(sqx * sqsize / 32, sqy * sqsize / 32, (sqsize * 5) >> 1,
    302                (mCcounter & 0x100) ? &mRedYUV : &mGreenYUV);
    303     mCcounter++;
    304 }
    305 
    306 void EmulatedFakeCameraDevice::drawSquare(int x,
    307                                           int y,
    308                                           int size,
    309                                           const YUVPixel* color)
    310 {
    311     const int square_xstop = min(mFrameWidth, x + size);
    312     const int square_ystop = min(mFrameHeight, y + size);
    313     uint8_t* Y_pos = mCurrentFrame + y * mFrameWidth + x;
    314 
    315     YUVPixel adjustedColor = *color;
    316     changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V);
    317 
    318     // Draw the square.
    319     for (; y < square_ystop; y++) {
    320         const int iUV = (y / 2) * mUVInRow + (x / 2) * mUVStep;
    321         uint8_t* sqU = mFrameU + iUV;
    322         uint8_t* sqV = mFrameV + iUV;
    323         uint8_t* sqY = Y_pos;
    324         for (int i = x; i < square_xstop; i += 2) {
    325             adjustedColor.get(sqY, sqU, sqV);
    326             *sqY = changeExposure(*sqY);
    327             sqY[1] = *sqY;
    328             sqY += 2; sqU += mUVStep; sqV += mUVStep;
    329         }
    330         Y_pos += mFrameWidth;
    331     }
    332 }
    333 
    334 #if EFCD_ROTATE_FRAME
    335 
    336 void EmulatedFakeCameraDevice::drawSolid(YUVPixel* color)
    337 {
    338     YUVPixel adjustedColor = *color;
    339     changeWhiteBalance(adjustedColor.Y, adjustedColor.U, adjustedColor.V);
    340 
    341     /* All Ys are the same. */
    342     memset(mCurrentFrame, changeExposure(adjustedColor.Y), mTotalPixels);
    343 
    344     /* Fill U, and V panes. */
    345     uint8_t* U = mFrameU;
    346     uint8_t* V = mFrameV;
    347     for (int k = 0; k < mUVTotalNum; k++, U += mUVStep, V += mUVStep) {
    348         *U = color->U;
    349         *V = color->V;
    350     }
    351 }
    352 
    353 void EmulatedFakeCameraDevice::drawStripes()
    354 {
    355     /* Divide frame into 4 stripes. */
    356     const int change_color_at = mFrameHeight / 4;
    357     const int each_in_row = mUVInRow / mUVStep;
    358     uint8_t* pY = mCurrentFrame;
    359     for (int y = 0; y < mFrameHeight; y++, pY += mFrameWidth) {
    360         /* Select the color. */
    361         YUVPixel* color;
    362         const int color_index = y / change_color_at;
    363         if (color_index == 0) {
    364             /* White stripe on top. */
    365             color = &mWhiteYUV;
    366         } else if (color_index == 1) {
    367             /* Then the red stripe. */
    368             color = &mRedYUV;
    369         } else if (color_index == 2) {
    370             /* Then the green stripe. */
    371             color = &mGreenYUV;
    372         } else {
    373             /* And the blue stripe at the bottom. */
    374             color = &mBlueYUV;
    375         }
    376         changeWhiteBalance(color->Y, color->U, color->V);
    377 
    378         /* All Ys at the row are the same. */
    379         memset(pY, changeExposure(color->Y), mFrameWidth);
    380 
    381         /* Offset of the current row inside U/V panes. */
    382         const int uv_off = (y / 2) * mUVInRow;
    383         /* Fill U, and V panes. */
    384         uint8_t* U = mFrameU + uv_off;
    385         uint8_t* V = mFrameV + uv_off;
    386         for (int k = 0; k < each_in_row; k++, U += mUVStep, V += mUVStep) {
    387             *U = color->U;
    388             *V = color->V;
    389         }
    390     }
    391 }
    392 
    393 int EmulatedFakeCameraDevice::rotateFrame()
    394 {
    395     if ((systemTime(SYSTEM_TIME_MONOTONIC) - mLastRotatedAt) >= mRotateFreq) {
    396         mLastRotatedAt = systemTime(SYSTEM_TIME_MONOTONIC);
    397         mCurrentFrameType++;
    398         if (mCurrentFrameType > 2) {
    399             mCurrentFrameType = 0;
    400         }
    401         if (mCurrentFrameType == 2) {
    402             ALOGD("********** Rotated to the SOLID COLOR frame **********");
    403             /* Solid color: lets rotate color too. */
    404             if (mCurrentColor == &mWhiteYUV) {
    405                 ALOGD("----- Painting a solid RED frame -----");
    406                 mCurrentColor = &mRedYUV;
    407             } else if (mCurrentColor == &mRedYUV) {
    408                 ALOGD("----- Painting a solid GREEN frame -----");
    409                 mCurrentColor = &mGreenYUV;
    410             } else if (mCurrentColor == &mGreenYUV) {
    411                 ALOGD("----- Painting a solid BLUE frame -----");
    412                 mCurrentColor = &mBlueYUV;
    413             } else {
    414                 /* Back to white. */
    415                 ALOGD("----- Painting a solid WHITE frame -----");
    416                 mCurrentColor = &mWhiteYUV;
    417             }
    418         } else if (mCurrentFrameType == 0) {
    419             ALOGD("********** Rotated to the CHECKERBOARD frame **********");
    420         } else if (mCurrentFrameType == 1) {
    421             ALOGD("********** Rotated to the STRIPED frame **********");
    422         }
    423     }
    424 
    425     return mCurrentFrameType;
    426 }
    427 
    428 #endif  // EFCD_ROTATE_FRAME
    429 
    430 }; /* namespace android */
    431