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