blob: 4afadc1b5054f539c2322015239cda8802f7bf3a
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 |