C++程序  |  552行  |  16.44 KB

/*
 * 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 EmulatedFakeRotatingCameraDevice that encapsulates
 * fake camera device.
 */

#define GL_GLEXT_PROTOTYPES
#define LOG_NDEBUG 0
#define LOG_TAG "EmulatedCamera_FakeDevice"
#define FAKE_CAMERA_SENSOR "FakeRotatingCameraSensor"
#include <cutils/log.h>
#include "EmulatedFakeCamera.h"
#include "EmulatedFakeRotatingCameraDevice.h"
#include "qemud.h"

#include <EGL/egl.h>
#include <GLES/gl.h>
#include <GLES/glext.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <ui/DisplayInfo.h>
#include <fcntl.h>

#undef min
#undef max
#include <algorithm>

namespace android {

// include the dots pattern directly, it is NV21 format
#include "acircles_pattern_1280_720.c"

// ----------------------------------------------------------------------------

static void checkEglError(const char* op, EGLBoolean returnVal = EGL_TRUE) {
    if (returnVal != EGL_TRUE) {
        ALOGE("%s() returned %d\n", op, returnVal);
    }

    for (EGLint error = eglGetError(); error != EGL_SUCCESS; error
            = eglGetError()) {
        ALOGE("after %s() eglError (0x%x)\n", op, error);
    }
}

static signed clamp_rgb(signed value) {
    if (value > 255) {
        value = 255;
    } else if (value < 0) {
        value = 0;
    }
    return value;
}

static void rgba8888_to_nv21(uint8_t* input, uint8_t* output, int width, int height) {
    int align = 16;
    int yStride = (width + (align -1)) & ~(align-1);
    uint8_t* outputVU = output + height*yStride;
    for (int j = 0; j < height; ++j) {
        uint8_t* outputY = output + j*yStride;
        for (int i = 0; i < width; ++i) {
            uint8_t R = input[j*width*4 + i*4];
            uint8_t G = input[j*width*4 + i*4 + 1];
            uint8_t B = input[j*width*4 + i*4 + 2];
            uint8_t Y = clamp_rgb((77 * R + 150 * G +  29 * B) >> 8);
            *outputY++ = Y;
            bool jeven = (j & 1) == 0;
            bool ieven = (i & 1) == 0;
            if (jeven && ieven) {
                uint8_t V = clamp_rgb((( 128 * R - 107 * G - 21 * B) >> 8) + 128);
                uint8_t U = clamp_rgb((( -43 * R - 85 * G + 128 * B) >> 8) + 128);
                *outputVU++ = V;
                *outputVU++ = U;
            }
        }
    }
}

static void nv21_to_rgba8888(uint8_t* input, uint32_t * output, int width, int height) {
    int align = 16;
    uint32_t* output0 = output;
    int yStride = (width + (align -1)) & ~(align-1);
    uint8_t* inputVU = input + height*yStride;
    uint8_t Y, U, V;
    for (int j = 0; j < height; ++j) {
        uint8_t* inputY = input + j*yStride;
        for (int i = 0; i < width; ++i) {
            Y = *inputY++;
            bool jeven = (j & 1) == 0;
            bool ieven = (i & 1) == 0;
            if (jeven && ieven) {
                V = *inputVU++;
                U = *inputVU++;
            }
            *output++ = YUVToRGB32(Y,U,V);
        }
    }
}

void EmulatedFakeRotatingCameraDevice::render(int width, int height)
{
    update_scene((float)width, (float)height);
    create_texture_dotx(1280, 720);
    int i, j;
    int quads = 1;

    int w= 992/2;
    int h = 1280/2;
    const GLfloat verticesfloat[] = {
             -w,  -h,  0,
              w,  -h,  0,
              w,   h,  0,
             -w,   h,  0
    };

    const GLfloat texCoordsfloat[] = {
            0,       0,
            1.0f,    0,
            1.0f,    1.0f,
            0,       1.0f
    };

    const GLushort indices[] = { 0, 1, 2,  0, 2, 3 };

    glVertexPointer(3, GL_FLOAT, 0, verticesfloat);
    glTexCoordPointer(2, GL_FLOAT, 0, texCoordsfloat);
    glClearColor(0.5, 0.5, 0.5, 1.0);
    int nelem = sizeof(indices)/sizeof(indices[0]);
    glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
    glDrawElements(GL_TRIANGLES, nelem, GL_UNSIGNED_SHORT, indices);
    glFinish();
    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, mPixelBuf);
}

static void get_color(uint32_t* img, int i, int j, int w, int h, int dw, uint32_t * color) {
    int mini = dw/2 - w/2;
    int minj = dw/2 - h/2;
    int maxi = mini + w -1;
    int maxj = minj + h -1;

    if ( i >= mini && i <= maxi && j >= minj && j <= maxj) {
        *color = img[i-mini + dw*(j-minj)];
    }
}

static void convert_to_square(uint32_t* src, uint32_t* dest, int sw, int sh, int dw) {
    for (int i=0; i < dw; ++i) {
        for (int j=0; j < dw; ++j) {
            uint32_t color=0;
            get_color(src, i, j, sw, sh, dw, &color);
            dest[i+j*dw] = color;
        }
    }
}

void EmulatedFakeRotatingCameraDevice::create_texture_dotx(int width, int height) {
    uint32_t* myrgba = new uint32_t[width * height];
    nv21_to_rgba8888(rawData, myrgba, width, height);
    uint32_t* myrgba2 = new uint32_t[width * width];
    convert_to_square(myrgba, myrgba2, width, height, width);

    glGenTextures(1, &mTexture);
    glBindTexture(GL_TEXTURE_2D, mTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, width, 0, GL_RGBA, GL_UNSIGNED_BYTE, myrgba2);
    //glGenerateMipmapOES does not work on mac, dont use it.
    //glGenerateMipmapOES(GL_TEXTURE_2D);
    // need to use linear, otherwise the dots will have sharp edges
    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
    delete[] myrgba;
    delete[] myrgba2;
}


static void gluLookAt(float eyeX, float eyeY, float eyeZ,
        float centerX, float centerY, float centerZ, float upX, float upY,
        float upZ)
{
    // See the OpenGL GLUT documentation for gluLookAt for a description
    // of the algorithm. We implement it in a straightforward way:

    float fx = centerX - eyeX;
    float fy = centerY - eyeY;
    float fz = centerZ - eyeZ;
    float flf = 1.0f / sqrt(fx * fx + fy * fy + fz * fz);
    fx *= flf;
    fy *= flf;
    fz *= flf;

    // compute s = f x up (x means "cross product")

    float sx = fy * upZ - fz * upY;
    float sy = fz * upX - fx * upZ;
    float sz = fx * upY - fy * upX;
    float slf = 1.0f / sqrt(sx * sx + sy * sy + sz * sz);
    sx *= slf;
    sy *= slf;
    sz *= slf;

    // compute u = s x f
    float ux = sy * fz - sz * fy;
    float uy = sz * fx - sx * fz;
    float uz = sx * fy - sy * fx;
    float ulf = 1.0f / sqrt(ux * ux + uy * uy + uz * uz);
    ux *= ulf;
    uy *= ulf;
    uz *= ulf;

    float m[16] ;
    m[0] = sx;
    m[1] = ux;
    m[2] = -fx;
    m[3] = 0.0f;

    m[4] = sy;
    m[5] = uy;
    m[6] = -fy;
    m[7] = 0.0f;

    m[8] = sz;
    m[9] = uz;
    m[10] = -fz;
    m[11] = 0.0f;

    m[12] = 0.0f;
    m[13] = 0.0f;
    m[14] = 0.0f;
    m[15] = 1.0f;

    glMultMatrixf(m);
    glTranslatef(-eyeX, -eyeY, -eyeZ);
}

void EmulatedFakeRotatingCameraDevice::update_scene(float width, float height)
{
    float ratio = width / height;
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glFrustumf(-ratio/2.0, ratio/2.0, -1/2.0, 1/2.0, 1, 40000);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    float up_x=-1;
    float up_y=0;
    float up_z=0;
    get_yawing(&up_x, &up_y, &up_z);
    float eye_x=0;
    float eye_y=0;
    float eye_z=2000;
    get_eye_x_y_z(&eye_x, &eye_y, &eye_z);
    gluLookAt( eye_x, eye_y, eye_z, 0, 0, 0, up_x, up_y, up_z);
    glEnable(GL_TEXTURE_2D);
    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_TEXTURE_COORD_ARRAY);
}

void EmulatedFakeRotatingCameraDevice::free_gl_surface(void)
{
    if (mEglDisplay != EGL_NO_DISPLAY)
    {
        eglMakeCurrent( EGL_NO_DISPLAY, EGL_NO_SURFACE,
                EGL_NO_SURFACE, EGL_NO_CONTEXT );
        eglDestroyContext( mEglDisplay, mEglContext );
        eglDestroySurface( mEglDisplay, mEglSurface );
        eglTerminate( mEglDisplay );
        mEglDisplay = EGL_NO_DISPLAY;
    }
}

void EmulatedFakeRotatingCameraDevice::init_sensor() {
    if (mSensorPipe >=0) return;
    // create a sensor pipe
    mSensorPipe = qemu_pipe_open(FAKE_CAMERA_SENSOR);
    if (mSensorPipe < 0) {
        ALOGE("cannot open %s", FAKE_CAMERA_SENSOR);
    } else {
        ALOGD("successfully opened %s", FAKE_CAMERA_SENSOR);
    }
}

void EmulatedFakeRotatingCameraDevice::read_sensor() {
    if (mSensorPipe < 0) return;
    char get[] = "get";
    int pipe_command_length = sizeof(get);
    WriteFully(mSensorPipe, &pipe_command_length, sizeof(pipe_command_length));
    WriteFully(mSensorPipe, get, pipe_command_length);
    ReadFully(mSensorPipe, &pipe_command_length, sizeof(pipe_command_length));
    ReadFully(mSensorPipe, &mSensorValues, pipe_command_length);
    assert(pipe_command_length == 9*sizeof(float));
    ALOGD("accel: %g %g %g; magnetic %g %g %g orientation %g %g %g",
            mSensorValues[SENSOR_VALUE_ACCEL_X], mSensorValues[SENSOR_VALUE_ACCEL_Y],
            mSensorValues[SENSOR_VALUE_ACCEL_Z],
            mSensorValues[SENSOR_VALUE_MAGNETIC_X], mSensorValues[SENSOR_VALUE_MAGNETIC_Y],
            mSensorValues[SENSOR_VALUE_MAGNETIC_Y],
            mSensorValues[SENSOR_VALUE_ROTATION_X], mSensorValues[SENSOR_VALUE_ROTATION_Y],
            mSensorValues[SENSOR_VALUE_ROTATION_Z]);
}

void EmulatedFakeRotatingCameraDevice::read_rotation_vector(double *yaw, double* pitch, double* roll) {
    read_sensor();
    *yaw = mSensorValues[SENSOR_VALUE_ROTATION_Z];
    *pitch = mSensorValues[SENSOR_VALUE_ROTATION_X];
    *roll = mSensorValues[SENSOR_VALUE_ROTATION_Y];
    return;
}

void EmulatedFakeRotatingCameraDevice::get_yawing(float* x, float* y, float*z) {
    double yaw, pitch, roll;
    read_rotation_vector(&yaw, &pitch, &roll);
    *x = sin((180+yaw)*3.14/180);
    *y = cos((180+yaw)*3.14/180);
    *z = 0;
    ALOGD("%s: yaw is %g, x %g y %g z %g", __func__, yaw, *x, *y, *z);
}

void EmulatedFakeRotatingCameraDevice::get_eye_x_y_z(float* x, float* y, float*z) {
    const float R=3500;
    //the coordinate of real camera is rotated (x-y swap)
    //and reverted (+/- swap)
    //
    //so rotation y is clockwise around x axis;
    //and rotation x is clockwise around y axis.
    const float theta_around_x = -mSensorValues[SENSOR_VALUE_ROTATION_Y];
    const float theta_around_y = -mSensorValues[SENSOR_VALUE_ROTATION_X];
    //apply x rotation first
    float y1 = -R*sin(theta_around_x*3.14/180);
    float z1 = R*cos(theta_around_x*3.14/180);
    //apply y rotation second
    float xz2 = z1 * sin(theta_around_y*3.14/180);
    float zz2 = z1 * cos(theta_around_y*3.14/180);
    *x = xz2;
    *y = y1;
    *z = zz2;

}

int EmulatedFakeRotatingCameraDevice::init_gl_surface(int width, int height)
{
    EGLint numConfigs = 1;
    EGLConfig myConfig = {0};

    if ( (mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY )
    {
        ALOGE("eglGetDisplay failed\n");
        return 0;
    }

    if ( eglInitialize(mEglDisplay, NULL, NULL) != EGL_TRUE )
    {
        ALOGE("eglInitialize failed\n");
        return 0;
    }

    {
        EGLint s_configAttribs[] = {
         EGL_SURFACE_TYPE, EGL_PBUFFER_BIT|EGL_WINDOW_BIT,
         EGL_RED_SIZE,       5,
         EGL_GREEN_SIZE,     6,
         EGL_BLUE_SIZE,      5,
         EGL_NONE
        };
        eglChooseConfig(mEglDisplay, s_configAttribs, &myConfig, 1, &numConfigs);
        EGLint attribs[] = { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE };
        mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, attribs);
        if (mEglSurface == EGL_NO_SURFACE) {
            ALOGE("eglCreatePbufferSurface error %x\n", eglGetError());
        }
    }

    if ( (mEglContext = eglCreateContext(mEglDisplay, myConfig, 0, 0)) == EGL_NO_CONTEXT )
    {
        ALOGE("eglCreateContext failed\n");
        return 0;
    }

    if ( eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext) != EGL_TRUE )
    {
        ALOGE("eglMakeCurrent failed\n");
        return 0;
    }

    int w, h;

    eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w);
    checkEglError("eglQuerySurface");
    eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h);
    checkEglError("eglQuerySurface");
    GLint dim = w < h ? w : h;

    ALOGD("Window dimensions: %d x %d\n", w, h);

    glDisable(GL_DITHER);
    glEnable(GL_CULL_FACE);

    return 1;
}

EmulatedFakeRotatingCameraDevice::EmulatedFakeRotatingCameraDevice(EmulatedFakeCamera* camera_hal)
    : EmulatedCameraDevice(camera_hal), mOpenglReady(false)
{
}

EmulatedFakeRotatingCameraDevice::~EmulatedFakeRotatingCameraDevice()
{
}

/****************************************************************************
 * Emulated camera device abstract interface implementation.
 ***************************************************************************/

status_t EmulatedFakeRotatingCameraDevice::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 EmulatedFakeRotatingCameraDevice::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 EmulatedFakeRotatingCameraDevice::startDevice(int width,
                                               int height,
                                               uint32_t pix_fmt)
{
    ALOGE("%s width %d height %d", __FUNCTION__, width, height);

    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);

    mState = ECDS_STARTED;

    return res;
}

status_t EmulatedFakeRotatingCameraDevice::stopDevice()
{
    ALOGV("%s", __FUNCTION__);

    Mutex::Autolock locker(&mObjectLock);
    if (!isStarted()) {
        ALOGW("%s: Fake camera device is not started.", __FUNCTION__);
        return NO_ERROR;
    }

    EmulatedCameraDevice::commonStopDevice();
    mState = ECDS_CONNECTED;

    if (mOpenglReady) {
        free_gl_surface();
        delete mPixelBuf;
        mOpenglReady=false;
    }
    if (mSensorPipe >= 0) {
        close(mSensorPipe);
        mSensorPipe = -1;
    }

    return NO_ERROR;
}

/****************************************************************************
 * Worker thread management overrides.
 ***************************************************************************/

bool EmulatedFakeRotatingCameraDevice::produceFrame(void* buffer,
                                                    int64_t* timestamp)
{
    if (mOpenglReady == false) {
        init_gl_surface(mFrameWidth, mFrameHeight);
        mOpenglReady = true;
        int width=mFrameWidth;
        int height = mFrameHeight;
        int kGlBytesPerPixel = 4;
        mPixelBuf = new uint8_t[width * height * kGlBytesPerPixel];
        init_sensor();
    }
    render(mFrameWidth, mFrameHeight);
    fillBuffer(buffer);
    return true;
}

/****************************************************************************
 * Fake camera device private API
 ***************************************************************************/

void EmulatedFakeRotatingCameraDevice::fillBuffer(void* buffer)
{
    uint8_t* currentFrame = reinterpret_cast<uint8_t*>(buffer);
    rgba8888_to_nv21(mPixelBuf, currentFrame, mFrameWidth, mFrameHeight);
    return;
}

}; /* namespace android */