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 EmulatedQemuCameraDevice that encapsulates
19 * an emulated camera device connected to the host.
20 */
21
22#define LOG_NDEBUG 0
23#define LOG_TAG "EmulatedCamera_QemuDevice"
24#include <cutils/log.h>
25#include "EmulatedQemuCamera.h"
26#include "EmulatedQemuCameraDevice.h"
27
28namespace android {
29
30EmulatedQemuCameraDevice::EmulatedQemuCameraDevice(EmulatedQemuCamera* camera_hal)
31    : EmulatedCameraDevice(camera_hal),
32      mQemuClient(),
33      mPreviewFrame(NULL)
34{
35}
36
37EmulatedQemuCameraDevice::~EmulatedQemuCameraDevice()
38{
39    if (mPreviewFrame != NULL) {
40        delete[] mPreviewFrame;
41    }
42}
43
44/****************************************************************************
45 * Public API
46 ***************************************************************************/
47
48status_t EmulatedQemuCameraDevice::Initialize(const char* device_name)
49{
50    /* Connect to the service. */
51    char connect_str[256];
52    snprintf(connect_str, sizeof(connect_str), "name=%s", device_name);
53    status_t res = mQemuClient.connectClient(connect_str);
54    if (res != NO_ERROR) {
55        return res;
56    }
57
58    /* Initialize base class. */
59    res = EmulatedCameraDevice::Initialize();
60    if (res == NO_ERROR) {
61        ALOGV("%s: Connected to the emulated camera service '%s'",
62             __FUNCTION__, device_name);
63        mDeviceName = device_name;
64    } else {
65        mQemuClient.queryDisconnect();
66    }
67
68    return res;
69}
70
71/****************************************************************************
72 * Emulated camera device abstract interface implementation.
73 ***************************************************************************/
74
75status_t EmulatedQemuCameraDevice::connectDevice()
76{
77    ALOGV("%s", __FUNCTION__);
78
79    Mutex::Autolock locker(&mObjectLock);
80    if (!isInitialized()) {
81        ALOGE("%s: Qemu camera device is not initialized.", __FUNCTION__);
82        return EINVAL;
83    }
84    if (isConnected()) {
85        ALOGW("%s: Qemu camera device '%s' is already connected.",
86             __FUNCTION__, (const char*)mDeviceName);
87        return NO_ERROR;
88    }
89
90    /* Connect to the camera device via emulator. */
91    const status_t res = mQemuClient.queryConnect();
92    if (res == NO_ERROR) {
93        ALOGV("%s: Connected to device '%s'",
94             __FUNCTION__, (const char*)mDeviceName);
95        mState = ECDS_CONNECTED;
96    } else {
97        ALOGE("%s: Connection to device '%s' failed",
98             __FUNCTION__, (const char*)mDeviceName);
99    }
100
101    return res;
102}
103
104status_t EmulatedQemuCameraDevice::disconnectDevice()
105{
106    ALOGV("%s", __FUNCTION__);
107
108    Mutex::Autolock locker(&mObjectLock);
109    if (!isConnected()) {
110        ALOGW("%s: Qemu camera device '%s' is already disconnected.",
111             __FUNCTION__, (const char*)mDeviceName);
112        return NO_ERROR;
113    }
114    if (isStarted()) {
115        ALOGE("%s: Cannot disconnect from the started device '%s.",
116             __FUNCTION__, (const char*)mDeviceName);
117        return EINVAL;
118    }
119
120    /* Disconnect from the camera device via emulator. */
121    const status_t res = mQemuClient.queryDisconnect();
122    if (res == NO_ERROR) {
123        ALOGV("%s: Disonnected from device '%s'",
124             __FUNCTION__, (const char*)mDeviceName);
125        mState = ECDS_INITIALIZED;
126    } else {
127        ALOGE("%s: Disconnection from device '%s' failed",
128             __FUNCTION__, (const char*)mDeviceName);
129    }
130
131    return res;
132}
133
134status_t EmulatedQemuCameraDevice::startDevice(int width,
135                                               int height,
136                                               uint32_t pix_fmt)
137{
138    ALOGV("%s", __FUNCTION__);
139
140    Mutex::Autolock locker(&mObjectLock);
141    if (!isConnected()) {
142        ALOGE("%s: Qemu camera device '%s' is not connected.",
143             __FUNCTION__, (const char*)mDeviceName);
144        return EINVAL;
145    }
146    if (isStarted()) {
147        ALOGW("%s: Qemu camera device '%s' is already started.",
148             __FUNCTION__, (const char*)mDeviceName);
149        return NO_ERROR;
150    }
151
152    status_t res = EmulatedCameraDevice::commonStartDevice(width, height, pix_fmt);
153    if (res != NO_ERROR) {
154        ALOGE("%s: commonStartDevice failed", __FUNCTION__);
155        return res;
156    }
157
158    /* Allocate preview frame buffer. */
159    /* TODO: Watch out for preview format changes! At this point we implement
160     * RGB32 only.*/
161    mPreviewFrame = new uint32_t[mTotalPixels];
162    if (mPreviewFrame == NULL) {
163        ALOGE("%s: Unable to allocate %d bytes for preview frame",
164             __FUNCTION__, mTotalPixels);
165        return ENOMEM;
166    }
167
168    /* Start the actual camera device. */
169    res = mQemuClient.queryStart(mPixelFormat, mFrameWidth, mFrameHeight);
170    if (res == NO_ERROR) {
171        ALOGV("%s: Qemu camera device '%s' is started for %.4s[%dx%d] frames",
172             __FUNCTION__, (const char*)mDeviceName,
173             reinterpret_cast<const char*>(&mPixelFormat),
174             mFrameWidth, mFrameHeight);
175        mState = ECDS_STARTED;
176    } else {
177        ALOGE("%s: Unable to start device '%s' for %.4s[%dx%d] frames",
178             __FUNCTION__, (const char*)mDeviceName,
179             reinterpret_cast<const char*>(&pix_fmt), width, height);
180    }
181
182    return res;
183}
184
185status_t EmulatedQemuCameraDevice::stopDevice()
186{
187    ALOGV("%s", __FUNCTION__);
188
189    Mutex::Autolock locker(&mObjectLock);
190    if (!isStarted()) {
191        ALOGW("%s: Qemu camera device '%s' is not started.",
192             __FUNCTION__, (const char*)mDeviceName);
193        return NO_ERROR;
194    }
195
196    /* Stop the actual camera device. */
197    status_t res = mQemuClient.queryStop();
198    if (res == NO_ERROR) {
199        if (mPreviewFrame == NULL) {
200            delete[] mPreviewFrame;
201            mPreviewFrame = NULL;
202        }
203        EmulatedCameraDevice::commonStopDevice();
204        mState = ECDS_CONNECTED;
205        ALOGV("%s: Qemu camera device '%s' is stopped",
206             __FUNCTION__, (const char*)mDeviceName);
207    } else {
208        ALOGE("%s: Unable to stop device '%s'",
209             __FUNCTION__, (const char*)mDeviceName);
210    }
211
212    return res;
213}
214
215/****************************************************************************
216 * EmulatedCameraDevice virtual overrides
217 ***************************************************************************/
218
219status_t EmulatedQemuCameraDevice::getCurrentPreviewFrame(void* buffer)
220{
221    ALOGW_IF(mPreviewFrame == NULL, "%s: No preview frame", __FUNCTION__);
222    if (mPreviewFrame != NULL) {
223        memcpy(buffer, mPreviewFrame, mTotalPixels * 4);
224        return 0;
225    } else {
226        return EmulatedCameraDevice::getCurrentPreviewFrame(buffer);
227    }
228}
229
230/****************************************************************************
231 * Worker thread management overrides.
232 ***************************************************************************/
233
234bool EmulatedQemuCameraDevice::inWorkerThread()
235{
236    /* Wait till FPS timeout expires, or thread exit message is received. */
237    WorkerThread::SelectRes res =
238        getWorkerThread()->Select(-1, 1000000 / mEmulatedFPS);
239    if (res == WorkerThread::EXIT_THREAD) {
240        ALOGV("%s: Worker thread has been terminated.", __FUNCTION__);
241        return false;
242    }
243
244    /* Query frames from the service. */
245    status_t query_res = mQemuClient.queryFrame(mCurrentFrame, mPreviewFrame,
246                                                 mFrameBufferSize,
247                                                 mTotalPixels * 4,
248                                                 mWhiteBalanceScale[0],
249                                                 mWhiteBalanceScale[1],
250                                                 mWhiteBalanceScale[2],
251                                                 mExposureCompensation);
252    if (query_res == NO_ERROR) {
253        /* Timestamp the current frame, and notify the camera HAL. */
254        mCurFrameTimestamp = systemTime(SYSTEM_TIME_MONOTONIC);
255        mCameraHAL->onNextFrameAvailable(mCurrentFrame, mCurFrameTimestamp, this);
256        return true;
257    } else {
258        ALOGE("%s: Unable to get current video frame: %s",
259             __FUNCTION__, strerror(query_res));
260        mCameraHAL->onCameraDeviceError(CAMERA_ERROR_SERVER_DIED);
261        return false;
262    }
263}
264
265}; /* namespace android */
266