It implemented using a Qt library.
And it supports image files that QImage can support by qt plugin.
It displays each image every a 100ms.
Also implemented simple feature that displays solid color image for SWT UI without Qt.
Change-Id: Ida76b5c5ab6bda17ee2bfc2414c24d1e308ef4c5
Signed-off-by: Jinhyung Jo <jinhyung.jo@samsung.com>
obj-y += maru_brightness.o
-obj-y += maru_camera.o
+obj-y += maru_camera.o maru_camera_convert.o
+obj-y += maru_camera_virtual.o
+ifdef CONFIG_QT
+obj-y += maru_camera_qtimage.o
+$(obj)/maru_camera_qtimage.o: QEMU_CFLAGS += $(QT_CFLAGS)
+else
+obj-y += maru_camera_swimage.o
+endif
ifdef CONFIG_LINUX
obj-y += maru_camera_linux.o
LIBS += -lv4l2 -lv4lconvert
endif
ifdef CONFIG_WIN32
-obj-y += maru_camera_win32.o maru_camera_convert.o
+obj-y += maru_camera_win32.o
LIBS += -lole32 -loleaut32 -luuid -lstrmiids
endif
ifdef CONFIG_DARWIN
-obj-y += maru_camera_darwin.o maru_camera_convert.o
+obj-y += maru_camera_darwin.o
LIBS += -framework Foundation -framework SystemConfiguration
LIBS += -framework Cocoa -framework QTKit -framework CoreVideo
LIBS += -framework AppKit
#define MARU_PCI_CAMERA_DEVICE_NAME "maru-camera"
/* PCI region size must be pow2. */
-/* set to 1048(2^10) * 1048(2^10) * 4(2^2) = 4,194,304(2^22) */
+/* set to 1024(2^10) * 1024(2^10) * 4(2^2) = 4,194,304(2^22) */
#define MARUCAM_MEM_SIZE (1 << 22)
#define MARUCAM_REG_SIZE (1 << 8) /* 64 * 4Byte = 256(2^8) */
/* RAM type region must be at least 4KB */
MaruCamState *state = (MaruCamState *)opaque;
switch (offset & 0xFF) {
+ /* Passes the index into the kernel driver */
+ case MARUCAM_CMD_INIT:
+ ret = state->index;
+ break;
+ /* return isr value and clear the interrupt signal */
case MARUCAM_CMD_ISR:
qemu_mutex_lock(&state->thread_mutex);
ret = state->isr;
{
MaruCamState *s = DO_UPCAST(MaruCamState, dev, dev);
uint8_t *pci_conf = s->dev.config;
-
- /* Check available webcam
- * If there is not one, you can't use the camera.
- */
- if (!marucam_device_check()) {
- s->initialized = false;
- INFO("Failed to check the camera device, "
- "You can *not* use the camera\n");
+ static bool is_used_index[2];
+ static bool has_native;
+
+ /* If there is no index property in 'maru-camera' option,
+ it gives an index by the following routine. */
+ TRACE("[%s] Input index(%d), path(%s)\n", __func__,
+ s->index, s->path ? s->path : NULL);
+ if (s->index == 0xFF) {
+ if (!is_used_index[0]) {
+ s->index = 0;
+ is_used_index[s->index] = true;
+ } else if (!is_used_index[1]) {
+ s->index = 1;
+ is_used_index[s->index] = true;
+ } else {
+ WARN("No more available, there are already 2 instances\n");
+ return 0;
+ }
+ } else if (s->index == 0 || s->index == 1) {
+ if (is_used_index[s->index]) {
+ WARN("Input index(%u) is already in use\n", s->index);
+ return 0;
+ } else {
+ is_used_index[s->index] = true;
+ }
+ } else {
+ WARN("Input index(%u) is out of range[0~1]\n", s->index);
return 0;
}
- pci_config_set_interrupt_pin(pci_conf, 0x03);
+ /* Modify the pin number to avoid interrupt conflicts */
+ pci_config_set_interrupt_pin(pci_conf, (0x03 - s->index));
memory_region_init_ram(&s->fbmem, OBJECT(s), "marucamera.fbmem",
MARUCAM_MEM_SIZE, &error_abort);
pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_MEM_PREFETCH, &s->iomem);
pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->ioreg);
- /* for worker thread */
- qemu_cond_init(&s->thread_cond);
- qemu_mutex_init(&s->thread_mutex);
+ /* Check available webcam
+ * If there is not one, you can use the virtual camera.
+ */
+ if (s->path || has_native || !marucam_device_check()) {
+ INFO("[cam%d] Virtual Camera mode\n", s->index);
+ s->backend = marucam_virtual_backend_create(s);
+ } else {
+ INFO("[cam%d] Native Camera mode\n", s->index);
+ s->backend = marucam_native_backend_create(s);
+ has_native = true;
+ }
- s->backend = marucam_backend_create(s);
- s->backend->init(s);
+ if (s->backend) {
+ /* for worker thread */
+ qemu_cond_init(&s->thread_cond);
+ qemu_mutex_init(&s->thread_mutex);
- s->tx_bh = qemu_bh_new(marucam_tx_bh, s);
- INFO("initialize maru-camera device\n");
+ s->backend->init(s);
+
+ s->tx_bh = qemu_bh_new(marucam_tx_bh, s);
+ INFO("initialize the maru-camera%d device\n", s->index);
+ } else {
+ s->initialized = false;
+ ERR("failed to initialize the maru-camera%d device\n", s->index);
+ }
return 0;
}
qemu_mutex_destroy(&s->thread_mutex);
}
- INFO("finalize maru-camera device\n");
+ INFO("finalize maru-camera%d device\n", s->index);
}
static void marucam_resetfn(DeviceState *d)
qemu_mutex_unlock(&s->thread_mutex);
memset(s->fb_ptr, 0, MARUCAM_MEM_SIZE);
memset(s->io_ptr, 0, MARUCAM_IOMEM_SIZE);
- INFO("reset maru-camera device\n");
+ INFO("reset maru-camera%d device\n", s->index);
}
}
return 0;
}
+static Property marucam_props[] = {
+ DEFINE_PROP_UINT8("index", MaruCamState, index, 0xFF),
+ DEFINE_PROP_STRING("path", MaruCamState, path),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void maru_camera_pci_class_init(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
k->device_id = PCI_DEVICE_ID_VIRTUAL_CAMERA;
k->class_id = PCI_CLASS_OTHERS;
dc->reset = marucam_resetfn;
+ dc->props = marucam_props;
dc->desc = "MARU Virtual Camera device for Tizen emulator";
}
uint32_t req_frame;
uint32_t ret_val;
+ /* Properties */
+ uint8_t index; /* device index (0 or 1)*/
+ char *path; /* image file path */
+
MemoryRegion fbmem;
MemoryRegion iomem;
MemoryRegion ioreg;
MaruCamBackend *backend;
};
-MaruCamBackend *marucam_backend_create(MaruCamState *state);
+MaruCamBackend *marucam_native_backend_create(MaruCamState *state);
+MaruCamBackend *marucam_virtual_backend_create(MaruCamState *state);
int marucam_device_check(void);
if (backend->driver != nil) {
[backend->driver dealloc];
backend->driver = nil;
+ INFO("Closed\n");
}
-
- INFO("Closed\n");
}
static void backend_mac_stream_on(MaruCamState *state)
}
}
-MaruCamBackend *marucam_backend_create(MaruCamState *state)
+MaruCamBackend *marucam_native_backend_create(MaruCamState *state)
{
MCBackendMac *backend_mac;
--- /dev/null
+/*
+ * Qt5 module for the virtual camera
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * with Qt5 QImage class
+ */
+
+#ifndef __MARU_CAMERA_IMAGE_H__
+#define __MARU_CAMERA_IMAGE_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+void *imagemgr_create(void *parent);
+int imagemgr_init(void *mgr, const char *path);
+void imagemgr_reset(void *mgr);
+void imagemgr_release(void *mgr);
+void imagemgr_prepare(void *mgr);
+bool imagemgr_is_init(void *mgr);
+
+void imagemgr_set_resolution(void *mgr, int w, int h);
+void *imagemgr_get_data(void *mgr);
+void imagemgr_adjust_ctrl(void *mgr,
+ unsigned int ctrl_id,
+ int ctrl_value);
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __MARU_CAMERA_IMAGE_H__ */
+
}
}
-MaruCamBackend *marucam_backend_create(MaruCamState *state)
+MaruCamBackend *marucam_native_backend_create(MaruCamState *state)
{
MCBackendV4l2 *backend_v4l2;
--- /dev/null
+/*
+ * Qt5 module for the virtual camera
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * with Qt5 QImage class
+ */
+
+#include <QFileInfo>
+#include <QDirIterator>
+#include <QImageReader>
+#include "maru_camera_image.h"
+#include "ui/qemu-pixman.h"
+
+// Internal classes
+// TODO: Add debug info and logs
+
+// video test colors
+enum pim_color {
+ COLOR_WHITE = 0,
+ COLOR_YELLOW,
+ COLOR_CYAN,
+ COLOR_GREEN,
+ COLOR_MAGENTA,
+ COLOR_RED,
+ COLOR_BLUE,
+ COLOR_BLACK,
+ COLOR_GRAY19,
+ COLOR_GRAY7,
+ COLOR_MAX
+};
+
+static const pixman_color_t colors[] = {
+ // WHITE
+ { .red = 0xFF << 8,
+ .green = 0xFF << 8,
+ .blue = 0xFF << 8,
+ .alpha = 0 },
+ // YELLOW
+ { .red = 0xFF << 8,
+ .green = 0xFF << 8,
+ .blue = 0,
+ .alpha = 0 },
+ // CYAN
+ { .red = 0,
+ .green = 0xFF << 8,
+ .blue = 0xFF << 8,
+ .alpha = 0 },
+ // GREEN
+ { .red = 0,
+ .green = 0xFF << 8,
+ .blue = 0,
+ .alpha = 0 },
+ // MAGENTA
+ { .red = 0xFF << 8,
+ .green = 0,
+ .blue = 0xFF << 8,
+ .alpha = 0 },
+ // RED
+ { .red = 0xFF << 8,
+ .green = 0,
+ .blue = 0,
+ .alpha = 0 },
+ // BLUE
+ { .red = 0,
+ .green = 0,
+ .blue = 0xFF << 8,
+ .alpha = 0 },
+ // BLACK
+ { .red = 0,
+ .green = 0,
+ .blue = 0,
+ .alpha = 0 },
+ // GRAY (#202020)
+ { .red = 0x20 << 8,
+ .green = 0x20 << 8,
+ .blue = 0x20 << 8,
+ .alpha = 0 },
+ // GREY (#505050)
+ { .red = 0x50 << 8,
+ .green = 0x50 << 8,
+ .blue = 0x50 << 8,
+ .alpha = 0 },
+};
+
+class PixmanImageManager
+{
+public:
+ explicit PixmanImageManager();
+ ~PixmanImageManager();
+
+ void makeImages(int w, int h);
+ void *getImageData();
+
+private:
+ int width;
+ int height;
+ int idx;
+ static const int MAX_DST_IMAGES = 3;
+ pixman_image_t *dstImages[MAX_DST_IMAGES];
+ pixman_image_t *srcImages[COLOR_MAX];
+};
+
+PixmanImageManager::PixmanImageManager()
+{
+ this->width = 0;
+ this->height = 0;
+ this->idx = 0;
+
+ for (int i = 0; i < COLOR_MAX; i++) {
+ srcImages[i] = pixman_image_create_solid_fill(&colors[i]);
+ }
+ for (int i = 0; i < MAX_DST_IMAGES; i++) {
+ dstImages[i] = NULL;
+ }
+}
+
+PixmanImageManager::~PixmanImageManager()
+{
+ for (int i = 0; i < COLOR_MAX; i++) {
+ pixman_image_unref(srcImages[i]);
+ srcImages[i] = NULL;
+ }
+ for (int i = 0; i < MAX_DST_IMAGES; i++) {
+ pixman_image_unref(dstImages[i]);
+ dstImages[i] = NULL;
+ }
+}
+
+void PixmanImageManager::makeImages(int w, int h)
+{
+ if (w == this->width && h == this->height) {
+ return;
+ } else {
+ this->width = w;
+ this->height = h;
+ }
+
+ for (int i = 0; i < MAX_DST_IMAGES; i++) {
+ if (this->dstImages[i]) {
+ pixman_image_unref(this->dstImages[i]);
+ }
+ this->dstImages[i] = pixman_image_create_bits(PIXMAN_r8g8b8,
+ w, h, NULL, w * 3);
+ int h1 = 3 * h / 4;
+ for (int j = 0; j < COLOR_MAX; j++) {
+ int x1 = j * w / 7;
+ int x2 = (j + 1) * w / 7;
+ pixman_image_composite(PIXMAN_OP_SRC,
+ this->srcImages[j],
+ NULL, this->dstImages[i],
+ 0, 0, 0, 0, x1, 0, (x2 - x1), h1);
+ }
+ for (int j = 0; j < COLOR_MAX - COLOR_BLACK; j++) {
+ int x1 = j * w / 3;
+ int x2 = (j + 1) * w / 3;
+ int k = (i + j) % 3;
+ pixman_image_composite(PIXMAN_OP_SRC,
+ this->srcImages[COLOR_BLACK + k],
+ NULL, this->dstImages[i],
+ 0, 0, 0, 0, x1, h1, (x2 - x1), (h - h1));
+ }
+ }
+}
+
+void *PixmanImageManager::getImageData()
+{
+ void *buf = pixman_image_get_data(this->dstImages[this->idx++]);
+ if (this->idx == MAX_DST_IMAGES) {
+ this->idx = 0;
+ }
+
+ return buf;
+}
+
+class VCImageManager
+{
+public:
+ explicit VCImageManager(void *parent);
+ ~VCImageManager();
+
+ int init(const char *inPath);
+ void loadImages();
+ int findAnimatedImage();
+ void loadAnimatedImage(int idx);
+ void makeConvertedData(int w, int h);
+ void *getImageData();
+ bool isAvail();
+
+private:
+ void *parent;
+
+ int width;
+ int height;
+ int idxOfCurData;
+
+ bool isInit;
+ bool usePixman;
+ bool loadDone;
+
+ QList<QString> *fileList;
+ QList<QImage> *imageList;
+ QList<QByteArray *> *dataList;
+
+ void createPixmanImageManager();
+ PixmanImageManager *pim;
+};
+
+VCImageManager::VCImageManager(void *parent)
+{
+ this->parent = parent;
+ this->width = 0;
+ this->height = 0;
+ this->idxOfCurData = 0;
+
+ this->isInit = false;
+ this->usePixman = false;
+ this->loadDone = false;
+
+ this->fileList = new QList<QString>;
+ this->imageList = new QList<QImage>;
+ this->dataList = new QList<QByteArray *>;
+ this->pim = NULL;
+}
+
+VCImageManager::~VCImageManager()
+{
+ if (this->imageList) {
+ this->imageList->clear();
+ delete this->imageList;
+ this->imageList = NULL;
+ }
+ if (this->fileList) {
+ this->fileList->clear();
+ delete this->fileList;
+ this->fileList = NULL;
+ }
+ if (this->dataList) {
+ for (int i = 0; i < this->dataList->count(); i++) {
+ delete this->dataList->at(i);
+ }
+ this->dataList->clear();
+ delete this->dataList;
+ this->dataList = NULL;
+ }
+}
+
+int VCImageManager::init(const char *inPath)
+{
+ if (inPath) {
+ // If the inPath is enclosed with double quotes, remove them.
+ QFileInfo fi(QString(inPath).remove(QChar('\"')));
+ QString canonFilePath = fi.canonicalFilePath();
+ // single image file
+ if (fi.exists() && fi.isFile()) {
+ QImageReader ir(canonFilePath);
+ if (ir.canRead() && ir.imageCount() > 1) {
+ this->fileList->append(QDir::cleanPath(canonFilePath));
+ } else { // not animated, use generator
+ createPixmanImageManager();
+ }
+ // directory
+ } else if (fi.exists() && fi.isDir()) {
+ QDirIterator it(QDir::cleanPath(canonFilePath), QDir::Files);
+ if (!it.hasNext()) { // there is no file
+ createPixmanImageManager();
+ } else {
+ do {
+ it.next();
+ if (!it.fileName().isEmpty()) {
+ QByteArray format =
+ QImageReader::imageFormat(it.filePath());
+ // QImageReader wrong recognizing the file format
+ // some file what is compressed by the gzip
+ // so ignore it
+ if (!format.isEmpty() && !format.startsWith("svgz")) {
+ this->fileList->append(it.filePath());
+ }
+ }
+ } while (it.hasNext());
+ if (this->fileList->isEmpty()) {
+ // there is no image file
+ // use generator
+ createPixmanImageManager();
+ }
+ }
+ } else {
+ createPixmanImageManager();
+ }
+ } else {
+ createPixmanImageManager();
+ }
+
+ this->isInit = true;
+
+ return 1;
+}
+
+int VCImageManager::findAnimatedImage()
+{
+ if (this->usePixman) {
+ return -1;
+ }
+
+ for (int i = 0; i < this->fileList->count(); i++) {
+ QImageReader ir(this->fileList->at(i));
+ if (ir.canRead() && ir.imageCount() > 1) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void VCImageManager::loadAnimatedImage(int idx)
+{
+ if (this->loadDone || this->usePixman) {
+ return;
+ }
+
+ QImageReader ir(this->fileList->at(idx));
+ while (ir.canRead()) {
+ this->imageList->append(ir.read());
+ }
+ this->loadDone = true;
+}
+
+void VCImageManager::loadImages()
+{
+ if (this->loadDone) {
+ return;
+ }
+
+ if (this->usePixman) {
+ this->loadDone = true;
+ return;
+ }
+
+ for (int i = 0; i < this->fileList->count(); i++) {
+ QImage loadedImage = QImage(this->fileList->at(i));
+ if (!loadedImage.isNull()) {
+ this->imageList->append(loadedImage);
+ }
+ }
+ this->loadDone = true;
+}
+
+void VCImageManager::makeConvertedData(int w, int h)
+{
+ if (!isAvail()) {
+ return;
+ }
+
+ if (w == this->width && h == this->height) {
+ return;
+ } else {
+ this->width = w;
+ this->height = h;
+ }
+ if (this->usePixman) {
+ this->pim->makeImages(w, h);
+ } else {
+ bool dstListIsEmpty = this->dataList->empty();
+ for (int i = 0; i < this->imageList->count(); i++) {
+ QImage img = this->imageList->at(i).rgbSwapped();
+ img = img.scaled(this->width,
+ this->height,
+ Qt::IgnoreAspectRatio,
+ Qt::SmoothTransformation);
+ img = img.convertToFormat(QImage::Format_RGB888);
+
+ if (dstListIsEmpty) {
+ this->dataList->append(new QByteArray((char *)img.bits(),
+ img.byteCount()));
+ } else {
+ delete this->dataList->at(i);
+ this->dataList->replace(i, new QByteArray((char *)img.bits(),
+ img.byteCount()));
+ }
+ }
+ }
+}
+
+void *VCImageManager::getImageData()
+{
+ if (!isAvail()) {
+ return NULL;
+ }
+ if (usePixman) {
+ return this->pim->getImageData();
+ } else {
+ QByteArray *ba = this->dataList->at(this->idxOfCurData++);
+ if (this->idxOfCurData == this->dataList->count()) {
+ this->idxOfCurData = 0;
+ }
+ return reinterpret_cast<void *>(ba->data());
+ }
+}
+
+bool VCImageManager::isAvail()
+{
+ return this->isInit;
+}
+
+void VCImageManager::createPixmanImageManager()
+{
+ if (this->pim) {
+ return;
+ }
+ pim = new PixmanImageManager();
+ this->usePixman = true;
+}
+
+// Extern Functions
+void *imagemgr_create(void *parent)
+{
+ VCImageManager *mgr = new VCImageManager(parent);
+
+ return static_cast<void *>(mgr);
+}
+
+int imagemgr_init(void *mgr, const char *inPath)
+{
+ VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+ return imagemgr->init(inPath);
+}
+
+void imagemgr_reset(void *mgr)
+{
+}
+
+void imagemgr_release(void *mgr)
+{
+ VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+ delete imagemgr;
+}
+
+void imagemgr_prepare(void *mgr)
+{
+
+ VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+ if (!imagemgr->isAvail()) {
+ return;
+ }
+
+ int aniIdx = imagemgr->findAnimatedImage();
+ if (aniIdx < 0) {
+ imagemgr->loadImages();
+ } else {
+ imagemgr->loadAnimatedImage(aniIdx);
+ }
+}
+
+bool imagemgr_is_init(void *mgr)
+{
+ VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+ return imagemgr->isAvail();
+}
+
+void imagemgr_set_resolution(void *mgr, int w, int h)
+{
+ VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+ imagemgr->makeConvertedData(w, h);
+}
+
+void *imagemgr_get_data(void *mgr)
+{
+ VCImageManager *imagemgr = static_cast<VCImageManager *>(mgr);
+
+ return imagemgr->getImageData();
+}
+
+void imagemgr_adjust_ctrl(void *mgr,
+ unsigned int ctrl_id,
+ int ctrl_value)
+{
+}
--- /dev/null
+/*
+ * Qt5 module for the virtual camera
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * with Qt5 QImage class
+ */
+
+#include "qemu-common.h"
+#include "ui/qemu-pixman.h"
+#include "maru_camera_image.h"
+
+#define VCSWIMAGE_MAX_BUF 20
+
+typedef struct VCSWImage {
+ void *parent;
+ bool is_init;
+ uint32_t width;
+ uint32_t height;
+ uint32_t idx;
+
+ pixman_image_t *srcs[VCSWIMAGE_MAX_BUF];
+ pixman_image_t *dsts[VCSWIMAGE_MAX_BUF];
+} VCSWImage;
+
+void *imagemgr_create(void *parent)
+{
+ VCSWImage *swimage = g_new0(VCSWImage, 1);
+ swimage->parent = parent;
+
+ return (void *)swimage;
+}
+
+int imagemgr_init(void *mgr, const char *inPath)
+{
+ return 1;
+}
+
+void imagemgr_reset(void *mgr)
+{
+}
+
+void imagemgr_release(void *mgr)
+{
+ int i;
+ VCSWImage *swimage = (VCSWImage *)mgr;
+
+ for (i = 0; i < VCSWIMAGE_MAX_BUF; i++) {
+ if (swimage->srcs[i]) {
+ pixman_image_unref(swimage->srcs[i]);
+ }
+ if (swimage->dsts[i]) {
+ pixman_image_unref(swimage->dsts[i]);
+ }
+ }
+ g_free(swimage);
+ swimage = NULL;
+}
+
+void imagemgr_prepare(void *mgr)
+{
+ int i, val;
+ time_t t;
+ pixman_color_t color;
+ VCSWImage *swimage = (VCSWImage *)mgr;
+
+ srand((unsigned) time(&t));
+
+ for (i = 0; i < VCSWIMAGE_MAX_BUF; i++) {
+ val = rand();
+
+ switch (val % 3) {
+ case 0:
+ color.alpha = 0x0000;
+ color.red = (val % 256) << 8;
+ color.green = 0x0000;
+ color.blue = 0x0000;
+ break;
+ case 1:
+ color.alpha = 0x0000;
+ color.red = 0x0000;
+ color.green = (val % 256) << 8;
+ color.blue = 0x0000;
+ break;
+ case 2:
+ color.alpha = 0x0000;
+ color.red = 0x0000;
+ color.green = 0x0000;
+ color.blue = (val % 256) << 8;
+ break;
+ default:
+ /* error */
+ break;
+ }
+
+ if (swimage->srcs[i]) {
+ pixman_image_unref(swimage->srcs[i]);
+ }
+ swimage->srcs[i] = pixman_image_create_solid_fill(&color);
+ }
+
+}
+
+bool imagemgr_is_init(void *mgr)
+{
+ VCSWImage *swimage = (VCSWImage *)mgr;
+ return swimage->is_init;
+}
+
+void imagemgr_set_resolution(void *mgr, int w, int h)
+{
+ int i;
+ VCSWImage *swimage = (VCSWImage *)mgr;
+
+ if (w == swimage->width && h == swimage->height) {
+ return;
+ } else {
+ swimage->width = w;
+ swimage->height = h;
+ }
+
+ for (i = 0; i < VCSWIMAGE_MAX_BUF; i++) {
+ if (swimage->dsts[i]) {
+ pixman_image_unref(swimage->dsts[i]);
+ }
+ swimage->dsts[i] = pixman_image_create_bits(PIXMAN_r8g8b8,
+ w, h, NULL, w * 3);
+ pixman_image_composite(PIXMAN_OP_SRC,
+ swimage->srcs[i], NULL, swimage->dsts[i],
+ 0, 0, 0, 0, 0, 0, w, h);
+ }
+}
+
+void *imagemgr_get_data(void *mgr)
+{
+ void *buf = NULL;
+ VCSWImage *swimage = (VCSWImage *)mgr;
+
+ buf = pixman_image_get_data(swimage->dsts[swimage->idx++]);
+ if (swimage->idx == VCSWIMAGE_MAX_BUF) {
+ swimage->idx = 0;
+ }
+
+ return buf;
+}
+
+void imagemgr_adjust_ctrl(void *mgr,
+ unsigned int ctrl_id,
+ int ctrl_value)
+{
+}
--- /dev/null
+/*
+ * Implementation of MARU Virtual Camera device by PCI bus
+ *
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * Contact:
+ * Jinhyung Jo <jinhyung.jo@samsung.com>
+ * Sangho Park <sangho1206.park@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ * Contributors:
+ * - S-Core Co., Ltd
+ *
+ * with Qt5 QImage class
+ */
+
+#include "qemu-common.h"
+#include "qemu/timer.h"
+#include "sysemu/kvm.h"
+#include "emul_state.h"
+#include "maru_camera.h"
+#include "videodev2_min.h"
+#include "maru_camera_convert.h"
+#include "maru_camera_image.h"
+#include "debug_ch.h"
+
+MULTI_DEBUG_CHANNEL(tizen, camera);
+
+/* Controls the frame rate
+ Delay time for the next image display */
+#define MC_DEFAULT_MS 100
+
+static uint32_t support_fmts[] = {
+ V4L2_PIX_FMT_YUYV,
+ V4L2_PIX_FMT_YUV420,
+ V4L2_PIX_FMT_YVU420,
+};
+
+struct MCFrame {
+ uint32_t width;
+ uint32_t height;
+};
+
+static struct MCFrame support_frames[] = {
+ { 640, 480 },
+ { 352, 288 },
+ { 320, 240 },
+ { 176, 144 },
+ { 160, 120 },
+};
+
+struct MCControls {
+ uint32_t id;
+ int32_t min;
+ int32_t max;
+ int32_t step;
+ int32_t init_val;
+ int32_t cur_val;
+};
+
+enum {
+ VC_CID_BRIGHTNESS, /* V4L2_CID_BRIGHTNESS */
+ VC_CID_CONTRAST, /* V4L2_CID_CONTRAST */
+ VC_CID_SATURATION, /* V4L2_CID_SATURATION */
+ VC_CID_SHARPNESS, /* V4L2_CID_SHARPNESS */
+ VC_CID_MAX,
+};
+
+static uint32_t idx_to_cid[VC_CID_MAX] = {
+ V4L2_CID_BRIGHTNESS,
+ V4L2_CID_CONTRAST,
+ V4L2_CID_SATURATION,
+ V4L2_CID_SHARPNESS,
+};
+
+typedef struct MCBuffer {
+ void *data;
+ size_t size;
+} MCBuffer;
+
+struct MCStoredFrame {
+ void *data;
+ uint32_t pixelformat;
+ uint32_t width;
+ uint32_t height;
+ uint32_t size;
+};
+
+typedef struct MCBackendVirt {
+ MaruCamBackend base;
+
+ QEMUTimer *timer;
+ struct MCControls ctrl_tbl[VC_CID_MAX];
+ uint32_t src_fmt;
+ uint32_t dst_fmt;
+ uint32_t width;
+ uint32_t height;
+ void *imagemgr;
+} MCBackendVirt;
+
+static void backend_virt_reset_ctrls(MCBackendVirt *backend)
+{
+ uint32_t i;
+ for (i = 0; i < VC_CID_MAX; i++) {
+ backend->ctrl_tbl[i].id = idx_to_cid[i];
+ backend->ctrl_tbl[i].min = MARUCAM_CTRL_VALUE_MIN;
+ backend->ctrl_tbl[i].max = MARUCAM_CTRL_VALUE_MAX;
+ backend->ctrl_tbl[i].step = MARUCAM_CTRL_VALUE_STEP;
+ backend->ctrl_tbl[i].init_val = MARUCAM_CTRL_VALUE_MID;
+ backend->ctrl_tbl[i].cur_val = MARUCAM_CTRL_VALUE_MID;
+ }
+}
+
+static int is_streamon(MaruCamState *state)
+{
+ int st;
+ qemu_mutex_lock(&state->thread_mutex);
+ st = state->streamon;
+ qemu_mutex_unlock(&state->thread_mutex);
+ return (st == _MC_THREAD_STREAMON);
+}
+
+static void backend_virt_update(void *opaque)
+{
+ void *src_buf, *dst_buf;
+ MaruCamState *state = (MaruCamState *)opaque;
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ qemu_mutex_lock(&state->thread_mutex);
+ if (state->streamon) {
+ if (state->req_frame == 0) {
+ TRACE("there is no request\n");
+ goto update_out;
+ }
+ src_buf = imagemgr_get_data(backend->imagemgr);
+ if (src_buf == NULL) {
+ ERR("[%s:%d] Failed to get buffer\n", __func__, __LINE__);
+ goto update_out;
+ }
+
+ dst_buf = state->fb_ptr + state->buf_size * (state->req_frame - 1);
+ if (convert_frame(backend->src_fmt, backend->dst_fmt,
+ backend->width, backend->height,
+ (size_t)state->buf_size, src_buf, dst_buf, false) > 0) {
+ ERR("[%s:%d] Failed to convert buffer\n", __func__, __LINE__);
+ goto update_out;
+ }
+ state->req_frame = 0; /* clear request */
+ state->isr |= 0x01; /* set a flag of rasing a interrupt */
+ qemu_bh_schedule(state->tx_bh);
+ }
+update_out:
+ qemu_mutex_unlock(&state->thread_mutex);
+
+ timer_mod(backend->timer,
+ qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + MC_DEFAULT_MS);
+}
+
+static void *manager_init_func(void *thread_param)
+{
+ MaruCamState *state = (MaruCamState *)thread_param;
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ backend->imagemgr = imagemgr_create(backend);
+
+ if (state->path && state->path[0] != '\0') {
+ /* using the image data from input files including default images */
+ imagemgr_init(backend->imagemgr, state->path);
+ } else {
+ /* using the image data to be generated internally */
+ imagemgr_init(backend->imagemgr, NULL);
+ }
+
+ backend->height = support_frames[0].height;
+ backend->width = support_frames[0].width;
+ backend->dst_fmt = support_fmts[0];
+ backend->src_fmt = V4L2_PIX_FMT_RGB24;
+
+ imagemgr_prepare(backend->imagemgr);
+ imagemgr_set_resolution(backend->imagemgr,
+ backend->width,
+ backend->height);
+
+ return NULL;
+}
+
+static void backend_virt_init(MaruCamState *state)
+{
+ qemu_thread_create(&state->thread_id,
+ "marucam-init-virtual",
+ manager_init_func,
+ (void *)state,
+ QEMU_THREAD_DETACHED);
+}
+
+static void backend_virt_reset(MaruCamState *state)
+{
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ imagemgr_reset(backend->imagemgr);
+}
+
+static void backend_virt_release(MaruCamState *state)
+{
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ imagemgr_release(backend->imagemgr);
+ g_free(backend);
+ backend = NULL;
+}
+
+static void backend_virt_open(MaruCamState *state)
+{
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ if (backend->imagemgr == NULL) {
+ ERR("There is no camera engine\n");
+ state->ret_val = ENODEV;
+ return;
+ }
+
+ INFO("Opened\n");
+ backend->timer = timer_new_ms(QEMU_CLOCK_REALTIME,
+ backend_virt_update,
+ state);
+
+ /* Set default values */
+ backend->height = support_frames[0].height;
+ backend->width = support_frames[0].width;
+ backend->dst_fmt = support_fmts[0];
+
+ backend_virt_reset_ctrls(backend);
+}
+
+static void backend_virt_close(MaruCamState *state)
+{
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ if (is_streamon(state)) {
+ state->backend->stream_off(state);
+ }
+
+ timer_free(backend->timer);
+ backend->timer = NULL;
+
+ INFO("Closed\n");
+}
+
+static void backend_virt_stream_on(MaruCamState *state)
+{
+ uint32_t width, height, pixfmt;
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ width = backend->width;
+ height = backend->height;
+ pixfmt = backend->dst_fmt;
+ state->buf_size = get_sizeimage(pixfmt, width, height);
+
+ INFO("Pixfmt(%c%c%c%c), W:H(%d:%d), buf size(%u)\n",
+ (char)(pixfmt), (char)(pixfmt >> 8),
+ (char)(pixfmt >> 16), (char)(pixfmt >> 24),
+ width, height, state->buf_size);
+ INFO("Starting preview\n");
+
+ timer_mod(backend->timer,
+ qemu_clock_get_ms(QEMU_CLOCK_REALTIME) + MC_DEFAULT_MS);
+
+ qemu_mutex_lock(&state->thread_mutex);
+ state->streamon = _MC_THREAD_STREAMON;
+ qemu_mutex_unlock(&state->thread_mutex);
+
+ INFO("Streaming on ......\n");
+}
+
+static void backend_virt_stream_off(MaruCamState *state)
+{
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ INFO("...... Streaming off\n");
+ qemu_mutex_lock(&state->thread_mutex);
+ state->streamon = _MC_THREAD_STREAMOFF;
+ qemu_mutex_unlock(&state->thread_mutex);
+
+ state->buf_size = 0;
+
+ INFO("Stopping preview\n");
+ timer_del(backend->timer);
+}
+
+
+static void backend_virt_s_parm(MaruCamState *state)
+{
+}
+
+static void backend_virt_g_parm(MaruCamState *state)
+{
+ struct v4l2_captureparm *cp =
+ (struct v4l2_captureparm *)state->io_ptr;
+
+ /* We use default FPS of the webcam
+ * return a fixed value on guest ini file (1/30).
+ */
+ cp->capability = V4L2_CAP_TIMEPERFRAME;
+ cp->timeperframe.numerator = 1;
+ cp->timeperframe.denominator = 30;
+}
+
+static void backend_virt_s_fmt(MaruCamState *state)
+{
+ uint32_t pidx, fidx;
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+ struct v4l2_pix_format *f = (struct v4l2_pix_format *)state->io_ptr;
+
+ for (fidx = 0; fidx < ARRAY_SIZE(support_frames); fidx++) {
+ if ((support_frames[fidx].width == f->width) &&
+ (support_frames[fidx].height == f->height)) {
+ break;
+ }
+ }
+ if (fidx == ARRAY_SIZE(support_frames)) {
+ state->ret_val = EINVAL;
+ return;
+ }
+ for (pidx = 0; pidx < ARRAY_SIZE(support_fmts); pidx++) {
+ if (support_fmts[pidx] == f->pixelformat) {
+ break;
+ }
+ }
+ if (pidx == ARRAY_SIZE(support_fmts)) {
+ state->ret_val = EINVAL;
+ return;
+ }
+
+ f->width = backend->width = support_frames[fidx].width;
+ f->height = backend->height = support_frames[fidx].height;
+ f->pixelformat = backend->dst_fmt = support_fmts[pidx];
+
+ f->field = V4L2_FIELD_NONE;
+ f->bytesperline = get_bytesperline(backend->dst_fmt,
+ backend->width);
+ f->sizeimage = get_sizeimage(backend->dst_fmt,
+ backend->width,
+ backend->height);
+ f->colorspace = 0;
+ f->priv = 0;
+
+ imagemgr_set_resolution(backend->imagemgr,
+ backend->width,
+ backend->height);
+
+ TRACE("Set the format: w:h(%dx%d), fmt(0x%x), "
+ "bytesperline(%d), sizeimage(%d), colorspace(%d)\n",
+ f->width, f->height, f->pixelformat, f->bytesperline,
+ f->sizeimage, f->colorspace);
+}
+
+static void backend_virt_g_fmt(MaruCamState *state)
+{
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+ struct v4l2_pix_format *f = (struct v4l2_pix_format *)state->io_ptr;
+
+ f->width = backend->width;
+ f->height = backend->height;
+ f->pixelformat = backend->dst_fmt;
+ f->field = V4L2_FIELD_NONE;
+ f->bytesperline = get_bytesperline(backend->dst_fmt,
+ backend->width);
+ f->sizeimage = get_sizeimage(backend->dst_fmt,
+ backend->width,
+ backend->height);
+ f->colorspace = 0;
+ f->priv = 0;
+
+ TRACE("Get the format: w:h(%dx%d), fmt(0x%x), "
+ "bytesperline(%d), sizeimage(%d), colorspace(%d)\n",
+ f->width, f->height, f->pixelformat, f->bytesperline,
+ f->sizeimage, f->colorspace);
+}
+
+static void backend_virt_try_fmt(MaruCamState *state)
+{
+ uint32_t i;
+ struct v4l2_pix_format *f = (struct v4l2_pix_format *)state->io_ptr;
+
+ for (i = 0; i < ARRAY_SIZE(support_frames); i++) {
+ if ((support_frames[i].width == f->width) &&
+ (support_frames[i].height == f->height)) {
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(support_frames)) {
+ state->ret_val = EINVAL;
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(support_fmts); i++) {
+ if (support_fmts[i] == f->pixelformat) {
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(support_fmts)) {
+ state->ret_val = EINVAL;
+ return;
+ }
+
+ f->field = V4L2_FIELD_NONE;
+ f->bytesperline = get_bytesperline(f->pixelformat,
+ f->width);
+ f->sizeimage = get_sizeimage(f->pixelformat,
+ f->width,
+ f->height);
+ f->colorspace = 0;
+ f->priv = 0;
+
+
+ TRACE("Check the format: w:h(%dx%d), pix_fmt(0x%x), "
+ "bytesperline(%d), sizeimage(%d), colorspace(%d)\n",
+ f->width, f->height, f->pixelformat, f->bytesperline,
+ f->sizeimage, f->colorspace);
+}
+
+static void backend_virt_enum_fmt(MaruCamState *state)
+{
+ struct v4l2_fmtdesc *f = (struct v4l2_fmtdesc *)state->io_ptr;
+
+ if (f->index >= ARRAY_SIZE(support_fmts)) {
+ state->ret_val = EINVAL;
+ return;
+ }
+
+ f->flags = 0; /* flags = NONE */
+ f->pixelformat = support_fmts[f->index]; /* pixelformat */
+
+ /* set description */
+ switch (support_fmts[f->index]) {
+ case V4L2_PIX_FMT_YUYV:
+ pstrcpy((char *)f->description, sizeof(f->description), "YUYV");
+ break;
+ case V4L2_PIX_FMT_YUV420:
+ pstrcpy((char *)f->description, sizeof(f->description), "YU12");
+ break;
+ case V4L2_PIX_FMT_YVU420:
+ pstrcpy((char *)f->description, sizeof(f->description), "YV12");
+ break;
+ default:
+ ERR("Invalid fixel format\n");
+ state->ret_val = EINVAL;
+ break;
+ }
+}
+
+static void backend_virt_query_ctrl(MaruCamState *state)
+{
+ struct v4l2_queryctrl *qc = (struct v4l2_queryctrl *)state->io_ptr;
+
+ /* NOTICE: Tizen MMFW hardcoded for control name
+ Do Not Modified the name
+ */
+ switch (qc->id) {
+ case V4L2_CID_BRIGHTNESS:
+ TRACE("Query : BRIGHTNESS\n");
+ pstrcpy((char *)qc->name, sizeof(qc->name), "brightness");
+ break;
+ case V4L2_CID_CONTRAST:
+ TRACE("Query : CONTRAST\n");
+ pstrcpy((char *)qc->name, sizeof(qc->name), "contrast");
+ break;
+ case V4L2_CID_SATURATION:
+ TRACE("Query : SATURATION\n");
+ pstrcpy((char *)qc->name, sizeof(qc->name), "saturation");
+ break;
+ case V4L2_CID_SHARPNESS:
+ TRACE("Query : SHARPNESS\n");
+ pstrcpy((char *)qc->name, sizeof(qc->name), "sharpness");
+ break;
+ default:
+ ERR("[%s] Invalid control ID: 0x%x\n", __func__, qc->id);
+ state->ret_val = EINVAL;
+ return;
+ }
+
+ /* set fixed values by FW configuration file */
+ qc->minimum = MARUCAM_CTRL_VALUE_MIN; /* minimum */
+ qc->maximum = MARUCAM_CTRL_VALUE_MAX; /* maximum */
+ qc->step = MARUCAM_CTRL_VALUE_STEP; /* step */
+ qc->default_value = MARUCAM_CTRL_VALUE_MID; /* default_value */
+ qc->flags = V4L2_CTRL_FLAG_SLIDER;
+
+ TRACE("Query Control: id(0x%x), name(%s), min(%d), max(%d), step(%d), "
+ "def_value(%d), flags(0x%x)\n",
+ qc->id, qc->name, qc->minimum, qc->maximum,
+ qc->step, qc->default_value, qc->flags);
+}
+
+static void backend_virt_s_ctrl(MaruCamState *state)
+{
+ uint32_t i;
+ struct v4l2_control *ctrl = (struct v4l2_control *)state->io_ptr;
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ i = VC_CID_BRIGHTNESS;
+ TRACE("%d is set to the value of the BRIGHTNESS\n", ctrl->value);
+ break;
+ case V4L2_CID_CONTRAST:
+ i = VC_CID_CONTRAST;
+ TRACE("%d is set to the value of the CONTRAST\n", ctrl->value);
+ break;
+ case V4L2_CID_SATURATION:
+ i = VC_CID_SATURATION;
+ TRACE("%d is set to the value of the SATURATION\n", ctrl->value);
+ break;
+ case V4L2_CID_SHARPNESS:
+ i = VC_CID_SHARPNESS;
+ TRACE("%d is set to the value of the SHARPNESS\n", ctrl->value);
+ break;
+ default:
+ ERR("[%s] Not supported control: 0x%x\n", __func__, ctrl->id);
+ state->ret_val = EINVAL;
+ return;
+ }
+ backend->ctrl_tbl[i].cur_val = ctrl->value;
+ imagemgr_adjust_ctrl(backend->imagemgr, ctrl->id, ctrl->value);
+}
+
+static void backend_virt_g_ctrl(MaruCamState *state)
+{
+ uint32_t i;
+ struct v4l2_control *ctrl = (struct v4l2_control *)state->io_ptr;
+ MCBackendVirt *backend = (MCBackendVirt *)(state->backend);
+
+ switch (ctrl->id) {
+ case V4L2_CID_BRIGHTNESS:
+ i = VC_CID_BRIGHTNESS;
+ TRACE("Gets the value of the BRIGHTNESS\n");
+ break;
+ case V4L2_CID_CONTRAST:
+ i = VC_CID_CONTRAST;
+ TRACE("Gets the value of the CONTRAST\n");
+ break;
+ case V4L2_CID_SATURATION:
+ i = VC_CID_SATURATION;
+ TRACE("Gets the value of the SATURATION\n");
+ break;
+ case V4L2_CID_SHARPNESS:
+ i = VC_CID_SHARPNESS;
+ TRACE("Gets the value of the SHARPNESS\n");
+ break;
+ default:
+ ERR("[%s] Not supported control: 0x%x\n", __func__, ctrl->id);
+ state->ret_val = EINVAL;
+ return;
+ }
+
+ ctrl->value = backend->ctrl_tbl[i].cur_val;
+ TRACE("Value: %d\n", ctrl->value);
+}
+
+static void backend_virt_enum_fsizes(MaruCamState *state)
+{
+ uint32_t i;
+ struct v4l2_frmsizeenum *fsize =
+ (struct v4l2_frmsizeenum *)state->io_ptr;
+
+ if (fsize->index >= ARRAY_SIZE(support_frames)) {
+ state->ret_val = EINVAL;
+ return;
+ }
+ for (i = 0; i < ARRAY_SIZE(support_fmts); i++) {
+ if (support_fmts[i] == fsize->pixel_format) {
+ break;
+ }
+ }
+
+ if (i == ARRAY_SIZE(support_fmts)) {
+ state->ret_val = EINVAL;
+ return;
+ }
+
+ fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
+ fsize->discrete.width = support_frames[fsize->index].width;
+ fsize->discrete.height = support_frames[fsize->index].height;
+}
+
+static void backend_virt_enum_fintv(MaruCamState *state)
+{
+ struct v4l2_frmivalenum *fival =
+ (struct v4l2_frmivalenum *)state->io_ptr;
+
+ /* switch by index) */
+ switch (fival->index) {
+ case 0:
+ /* we only use 1/30 frame interval */
+ fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
+ fival->discrete.numerator = 1;
+ fival->discrete.denominator = 30;
+ break;
+ default:
+ state->ret_val = EINVAL;
+ return;
+ }
+}
+
+MaruCamBackend *marucam_virtual_backend_create(MaruCamState *state)
+{
+ MCBackendVirt *backend_virt;
+
+ backend_virt = g_new0(MCBackendVirt, 1);
+ if (backend_virt == NULL) {
+ ERR("Failed to allocate memory for the virtual backend\n");
+ return NULL;
+ }
+
+
+ backend_virt->base.state = state;
+ backend_virt->base.init = backend_virt_init;
+ backend_virt->base.reset = backend_virt_reset;
+ backend_virt->base.release = backend_virt_release;
+ backend_virt->base.open = backend_virt_open;
+ backend_virt->base.close = backend_virt_close;
+ backend_virt->base.stream_on = backend_virt_stream_on;
+ backend_virt->base.stream_off = backend_virt_stream_off;
+ backend_virt->base.enum_fmt = backend_virt_enum_fmt;
+ backend_virt->base.try_fmt = backend_virt_try_fmt;
+ backend_virt->base.s_fmt = backend_virt_s_fmt;
+ backend_virt->base.g_fmt = backend_virt_g_fmt;
+ backend_virt->base.s_parm = backend_virt_s_parm;
+ backend_virt->base.g_parm = backend_virt_g_parm;
+ backend_virt->base.query_ctrl = backend_virt_query_ctrl;
+ backend_virt->base.s_ctrl = backend_virt_s_ctrl;
+ backend_virt->base.g_ctrl = backend_virt_g_ctrl;
+ backend_virt->base.enum_framesizes = backend_virt_enum_fsizes;
+ backend_virt->base.enum_frameintervals = backend_virt_enum_fintv;
+
+ return &backend_virt->base;
+}
}
}
-MaruCamBackend *marucam_backend_create(MaruCamState *state)
+MaruCamBackend *marucam_native_backend_create(MaruCamState *state)
{
MCBackendWin *backend_win;