/* * 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 PreviewWindow that encapsulates * functionality of a preview window set via set_preview_window camera HAL API. */ #define LOG_NDEBUG 0 #define LOG_TAG "EmulatedCamera_Preview" #include <cutils/log.h> #include <ui/Rect.h> #include <ui/GraphicBufferMapper.h> #include "EmulatedCameraDevice.h" #include "PreviewWindow.h" namespace android { PreviewWindow::PreviewWindow() : mPreviewWindow(NULL), mLastPreviewed(0), mPreviewFrameWidth(0), mPreviewFrameHeight(0), mPreviewEnabled(false) { } PreviewWindow::~PreviewWindow() { } /**************************************************************************** * Camera API ***************************************************************************/ status_t PreviewWindow::setPreviewWindow(struct preview_stream_ops* window, int preview_fps) { ALOGV("%s: current: %p -> new: %p", __FUNCTION__, mPreviewWindow, window); status_t res = NO_ERROR; Mutex::Autolock locker(&mObjectLock); /* Reset preview info. */ mPreviewFrameWidth = mPreviewFrameHeight = 0; mPreviewAfter = 0; mLastPreviewed = 0; if (window != NULL) { /* The CPU will write each frame to the preview window buffer. * Note that we delay setting preview window buffer geometry until * frames start to come in. */ res = window->set_usage(window, GRALLOC_USAGE_SW_WRITE_OFTEN); if (res == NO_ERROR) { /* Set preview frequency. */ mPreviewAfter = 1000000 / preview_fps; } else { window = NULL; res = -res; // set_usage returns a negative errno. ALOGE("%s: Error setting preview window usage %d -> %s", __FUNCTION__, res, strerror(res)); } } mPreviewWindow = window; return res; } status_t PreviewWindow::startPreview() { ALOGV("%s", __FUNCTION__); Mutex::Autolock locker(&mObjectLock); mPreviewEnabled = true; return NO_ERROR; } void PreviewWindow::stopPreview() { ALOGV("%s", __FUNCTION__); Mutex::Autolock locker(&mObjectLock); mPreviewEnabled = false; } /**************************************************************************** * Public API ***************************************************************************/ void PreviewWindow::onNextFrameAvailable(const void* frame, nsecs_t timestamp, EmulatedCameraDevice* camera_dev) { int res; Mutex::Autolock locker(&mObjectLock); if (!isPreviewEnabled() || mPreviewWindow == NULL || !isPreviewTime()) { return; } /* Make sure that preview window dimensions are OK with the camera device */ if (adjustPreviewDimensions(camera_dev)) { /* Need to set / adjust buffer geometry for the preview window. * Note that in the emulator preview window uses only RGB for pixel * formats. */ ALOGV("%s: Adjusting preview windows %p geometry to %dx%d", __FUNCTION__, mPreviewWindow, mPreviewFrameWidth, mPreviewFrameHeight); res = mPreviewWindow->set_buffers_geometry(mPreviewWindow, mPreviewFrameWidth, mPreviewFrameHeight, HAL_PIXEL_FORMAT_RGBA_8888); if (res != NO_ERROR) { ALOGE("%s: Error in set_buffers_geometry %d -> %s", __FUNCTION__, -res, strerror(-res)); return; } } /* * Push new frame to the preview window. */ /* Dequeue preview window buffer for the frame. */ buffer_handle_t* buffer = NULL; int stride = 0; res = mPreviewWindow->dequeue_buffer(mPreviewWindow, &buffer, &stride); if (res != NO_ERROR || buffer == NULL) { ALOGE("%s: Unable to dequeue preview window buffer: %d -> %s", __FUNCTION__, -res, strerror(-res)); return; } /* Let the preview window to lock the buffer. */ res = mPreviewWindow->lock_buffer(mPreviewWindow, buffer); if (res != NO_ERROR) { ALOGE("%s: Unable to lock preview window buffer: %d -> %s", __FUNCTION__, -res, strerror(-res)); mPreviewWindow->cancel_buffer(mPreviewWindow, buffer); return; } /* Now let the graphics framework to lock the buffer, and provide * us with the framebuffer data address. */ void* img = NULL; const Rect rect(mPreviewFrameWidth, mPreviewFrameHeight); GraphicBufferMapper& grbuffer_mapper(GraphicBufferMapper::get()); res = grbuffer_mapper.lock(*buffer, GRALLOC_USAGE_SW_WRITE_OFTEN, rect, &img); if (res != NO_ERROR) { ALOGE("%s: grbuffer_mapper.lock failure: %d -> %s", __FUNCTION__, res, strerror(res)); mPreviewWindow->cancel_buffer(mPreviewWindow, buffer); return; } /* Frames come in in YV12/NV12/NV21 format. Since preview window doesn't * supports those formats, we need to obtain the frame in RGB565. */ res = camera_dev->getCurrentPreviewFrame(img); if (res == NO_ERROR) { /* Show it. */ mPreviewWindow->set_timestamp(mPreviewWindow, timestamp); mPreviewWindow->enqueue_buffer(mPreviewWindow, buffer); } else { ALOGE("%s: Unable to obtain preview frame: %d", __FUNCTION__, res); mPreviewWindow->cancel_buffer(mPreviewWindow, buffer); } grbuffer_mapper.unlock(*buffer); } /*************************************************************************** * Private API **************************************************************************/ bool PreviewWindow::adjustPreviewDimensions(EmulatedCameraDevice* camera_dev) { /* Match the cached frame dimensions against the actual ones. */ if (mPreviewFrameWidth == camera_dev->getFrameWidth() && mPreviewFrameHeight == camera_dev->getFrameHeight()) { /* They match. */ return false; } /* They don't match: adjust the cache. */ mPreviewFrameWidth = camera_dev->getFrameWidth(); mPreviewFrameHeight = camera_dev->getFrameHeight(); return true; } bool PreviewWindow::isPreviewTime() { timeval cur_time; gettimeofday(&cur_time, NULL); const uint64_t cur_mks = cur_time.tv_sec * 1000000LL + cur_time.tv_usec; if ((cur_mks - mLastPreviewed) >= mPreviewAfter) { mLastPreviewed = cur_mks; return true; } return false; } }; /* namespace android */