summaryrefslogtreecommitdiff
path: root/v4l2-mfc-encoder
diff options
context:
space:
mode:
authorAndrzej Hajda <a.hajda@samsung.com>2012-07-02 10:50:47 +0200
committerMarek Szyprowski <m.szyprowski@samsung.com>2012-07-02 11:17:43 +0200
commit8b8182da6e16dfd0820ef2418c1bb66d4bcc8079 (patch)
treee1e318e1ebc676a16d6eea7b1f659482f2d93ebf /v4l2-mfc-encoder
parenta7e755629a74a7ac137882032a0f7b2480fa1490 (diff)
v4l-mfc-encoder: version 0.1
Signed-off-by: Andrzej Hajda <a.hajda@samsung.com> Signed-off-by: Kamil Debski <k.debski@samsung.com>
Diffstat (limited to 'v4l2-mfc-encoder')
-rw-r--r--v4l2-mfc-encoder/Makefile52
-rw-r--r--v4l2-mfc-encoder/README.txt153
-rw-r--r--v4l2-mfc-encoder/args.c128
-rw-r--r--v4l2-mfc-encoder/args.h43
-rw-r--r--v4l2-mfc-encoder/common.h66
-rw-r--r--v4l2-mfc-encoder/func_dev.c132
-rw-r--r--v4l2-mfc-encoder/func_dev.h33
-rw-r--r--v4l2-mfc-encoder/in_camera.c83
-rw-r--r--v4l2-mfc-encoder/in_camera.h30
-rw-r--r--v4l2-mfc-encoder/in_demo.c111
-rw-r--r--v4l2-mfc-encoder/in_demo.h30
-rw-r--r--v4l2-mfc-encoder/io_dev.c172
-rw-r--r--v4l2-mfc-encoder/io_dev.h91
-rw-r--r--v4l2-mfc-encoder/main.c106
-rw-r--r--v4l2-mfc-encoder/mfc.c187
-rw-r--r--v4l2-mfc-encoder/mfc.h41
-rw-r--r--v4l2-mfc-encoder/out_file.c57
-rw-r--r--v4l2-mfc-encoder/out_file.h30
-rw-r--r--v4l2-mfc-encoder/v4l_dev.c399
-rw-r--r--v4l2-mfc-encoder/v4l_dev.h43
20 files changed, 1987 insertions, 0 deletions
diff --git a/v4l2-mfc-encoder/Makefile b/v4l2-mfc-encoder/Makefile
new file mode 100644
index 0000000..d18ba17
--- /dev/null
+++ b/v4l2-mfc-encoder/Makefile
@@ -0,0 +1,52 @@
+# V4L2 Codec encoding example application
+# Andrzej Hajda <a.hajda@samsung.com>
+#
+# Copyright 2012 Samsung Electronics Co., Ltd.
+#
+# 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.
+#
+
+# Toolchain path
+TCPATH = arm-linux-gnueabi-
+KERNELHEADERS = /home/kamil/praca/w1-party/kernel/headers/include
+
+CC = ${TCPATH}gcc
+AR = "${TCPATH}ar rc"
+AR2 = ${TCPATH}ranlib make -j4
+
+
+INCLUDES = -I$(KERNELHEADERS)
+
+SOURCES = main.c args.c in_demo.c out_file.c mfc.c io_dev.c func_dev.c v4l_dev.c in_camera.c
+OBJECTS := $(SOURCES:.c=.o)
+EXEC = mfc-encode
+CFLAGS = -Wall -g -DS5PC1XX_FIMC
+DEP = .dep
+
+all: $(EXEC) $(DEP)
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(INCLUDES) $<
+
+$(EXEC): $(OBJECTS)
+ $(CC) $(CFLAGS) -o $(EXEC) $(OBJECTS) -lm -lrt
+
+clean:
+ rm -f *.o $(EXEC) $(DEP)
+
+$(DEP): $(SOURCES) Makefile
+ $(CC) -c $(CFLAGS) $(INCLUDES) -MM $(SOURCES) > $(DEP)
+
+.PHONY: clean all
+
+-include $(DEP)
diff --git a/v4l2-mfc-encoder/README.txt b/v4l2-mfc-encoder/README.txt
new file mode 100644
index 0000000..9b0f107
--- /dev/null
+++ b/v4l2-mfc-encoder/README.txt
@@ -0,0 +1,153 @@
+V4L2 Codec encoding example application
+by Andrzej Hajda <a.hajda@samsung.com>
+
+===========
+* License *
+===========
+Copyright 2012 Samsung Electronics Co., Ltd.
+
+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.
+
+=============
+* About MFC *
+=============
+
+MFC stands for Multi Format Codec. It is a hardware module available in the
+Exynos family of SoCs. It is capable of decoding a range of compressed
+streams with resolution up to 1080p at 30 fps. This includes H264, H264,
+MPEG1, MPEG2, MPEG4 and VC1. It also supports encoding in H264 and H263 format
+up to 1080p and MPEG up to D1 resolution.
+
+==============
+* About FIMC *
+==============
+
+Although this is the example application for MFC it also uses the FIMC camera
+module.
+
+=======================
+* About the interface *
+=======================
+
+The interface used by the MFC 5.1 driver is the Video4Linux2 framework.
+It acts as a memory to memory device. It supports up to 16 contexts, each of
+them can be setup to do encoding or decoding.
+
+=========================
+* About the application *
+=========================
+
+The purpose of this applications is to present and explain the usage of hardware
+video codecs with the V4L2 interface. Application also presents usage of poll
+interface with V4L2 devices as an alternative to threads.
+
+=================================
+* Encoding in a few words *
+=================================
+
+MFC codec is a memory to memory V4L2 device. Following steps should be performed
+to encode video stream:
+1. Open device.
+2. Set image size and format on OUTPUT. MFC supports V4L2_PIX_FMT_NV12M format
+ with bytes per line aligned to 128 and image size aligned to 2048.
+3. Set codec and buffer size on CAPTURE. Size should be not greater than 2MB.
+ Supported codecs: MPEG4, H263, H264.
+4. Optionally set additional parameters (bitrate, fps,....).
+5. Request CAPTURE and OUTPUT buffers. Due to hardware limitations of MFC on
+ some platforms it is recommended to use V4L2_MEMORY_MMAP buffers.
+6. Enqueue CAPTURE buffers.
+7. Enqueue OUTPUT buffer with first frame.
+8. Start streaming (VIDIOC_STREAMON) on both ends.
+9. Simultaneously:
+ - enqueue buffers with next frames,
+ - dequeue used OUTPUT buffers (blocking operation),
+ - dequeue buffers with encoded stream (blocking operation),
+ - enqueue free CAPTURE buffers.
+10. Last frame should have set flags field to V4L2_BUF_FLAG_LAST to
+ inform codec about the end of the stream.
+11. Stop dequeuing CAPTURE frames after dequeuing frame with luma plane
+ bytesused equal 0.
+12. Stop streaming (VIDIOC_STREAMOFF) on both ends.
+13. Close device.
+
+Encoded stream is in elementary stream format.
+
+==============================
+* Application in a few words *
+==============================
+
+The application uses pattern of device chain. Single device has input and output.
+Chained devices have connected output of one device with input of the following
+device. Devices uses buffers to pass data between them.
+
+Video4Linux2 devices uses this pattern natively. The application uses also this
+pattern for pseudo devices:
+ in_demo - input device providing stream of NV12M frames with animation of
+ white square flying over 'noisy background'.
+ out_file - writes incoming data to file.
+
+Application uses following strategy regarding queuing buffers:
+1. At the beginning all buffers are enqueued in output queues of devices.
+2. Every time buffer is dequeued from out queue it is immediately enqueued
+ in the input queue of the next device.
+3. Every time buffer is dequeued from input queue it is immediately enqueued
+ in the output queue of the previous device.
+
+Application uses poll interface to wait for devices which can be dequeued
+without blocking. When device is ready dequeue/enqueue operation is performed.
+
+There are two types of devices:
+1. V4L2 devices:
+ - mfc,
+ - in_camera.
+2. Function devices - they use read or write function to pass data:
+ - in_demo,
+ - out_file.
+
+Implementation of devices: mfc.c, in_camera.c, in.demo.c out_file.c shows that
+extending application with new devices should be quite easy.
+
+===========================
+* Running the application *
+===========================
+
+The application takes a few necessary arguments. The only required argument is
+MFC device (-m).
+
+Options:
+ -i <device> - FIMC camera device (e.g. /dev/video1)
+ If not specified demo input device is used
+ -m <device> - (required) MFC device (e.g. /dev/video8)
+ -o <file> - Output file name
+ -c <codec> - The codec of the encoded stream
+ Available codecs: mpeg4, h263, h264
+ -d <duration> - Number of frames to encode
+ -r <rate> - Frame rate
+ -b <bitrate> - Bitrate
+ -s <size> - Size of frame in format WxH
+
+To determine which devices to use you can try the following commands.
+
+For MFC:
+dmesg | grep -e s5p-mfc.*encoder.*/dev/video
+which outputs:
+[ 2.160683] s5p-mfc s5p-mfc: encoder registered as /dev/video9
+
+For FIMC camera:
+dmesg | grep -e fimc...capture.*/dev/video
+which outputs:
+[ 2.484000] s5p-fimc-md: Registered exynos4-fimc.0.capture as /dev/video1
+[ 2.497174] s5p-fimc-md: Registered exynos4-fimc.1.capture as /dev/video3
+[ 2.510368] s5p-fimc-md: Registered exynos4-fimc.2.capture as /dev/video5
+[ 2.523578] s5p-fimc-md: Registered exynos4-fimc.3.capture as /dev/video7
+
diff --git a/v4l2-mfc-encoder/args.c b/v4l2-mfc-encoder/args.c
new file mode 100644
index 0000000..98ef886
--- /dev/null
+++ b/v4l2-mfc-encoder/args.c
@@ -0,0 +1,128 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Command line helper functions.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <linux/videodev2.h>
+#include <stddef.h>
+
+#include "common.h"
+#include "args.h"
+
+void print_usage(char const *name)
+{
+ printf("Usage: %s [args]\n"
+ "\t-i <device> - FIMC camera device (e.g. /dev/video1)\n"
+ "\t If not specified demo input device is used\n"
+ "\t-m <device> - (required) MFC device (e.g. /dev/video8)\n"
+ "\t-o <file> - Output file name\n"
+ "\t-c <codec> - The codec of the encoded stream\n"
+ "\t Available codecs: mpeg4, h263, h264\n"
+ "\t-d <duration> - Number of frames to encode\n"
+ "\t-r <rate> - Frame rate\n"
+ "\t-b <bitrate> - Bitrate\n"
+ "\t-s <size> - Size of frame in format WxH\n"
+ , name);
+}
+
+int get_codec(char *str)
+{
+ if (strncasecmp("mpeg4", str, 5) == 0)
+ return V4L2_PIX_FMT_MPEG4;
+ else if (strncasecmp("h263", str, 5) == 0)
+ return V4L2_PIX_FMT_H263;
+ else if (strncasecmp("h264", str, 5) == 0)
+ return V4L2_PIX_FMT_H264;
+
+ return 0;
+}
+
+void set_options_default(struct options *o)
+{
+ memset(o, 0, sizeof(*o));
+ o->width = 176;
+ o->height = 144;
+ o->duration = 250;
+ o->rate = 25;
+ o->out_name = "demo.out";
+ o->codec = V4L2_PIX_FMT_H264;
+ o->bitrate = 1000;
+}
+
+int parse_args(struct options *opts, int argc, char **argv)
+{
+ int c;
+
+ set_options_default(opts);
+
+ while ((c = getopt(argc, argv, "i:m:o:c:d:r:s:b:")) != -1) {
+ switch (c) {
+ case 'i':
+ opts->in_name = optarg;
+ break;
+ case 'm':
+ opts->mfc_name = optarg;
+ break;
+ case 'o':
+ opts->out_name = optarg;
+ break;
+ case 'c':
+ opts->codec = get_codec(optarg);
+ if (opts->codec == 0) {
+ err("Unknown codec");
+ return -1;
+ }
+ break;
+ case 'd':
+ opts->duration = atoi(optarg);
+ break;
+ case 'r':
+ opts->rate = atoi(optarg);
+ break;
+ case 's': {
+ char *sep = NULL;
+ opts->width = strtol(optarg, &sep, 10);
+ if (!sep || *sep != 'x') {
+ err("Bad size, should be like 320x200");
+ return -1;
+ }
+ opts->height = atoi(++sep);
+ break;
+ case 'b':
+ opts->bitrate = atoi(optarg);
+ break;
+ }
+ default:
+ return -1;
+ }
+ }
+
+ if (opts->mfc_name == NULL) {
+ err("Please provide MFC device");
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/v4l2-mfc-encoder/args.h b/v4l2-mfc-encoder/args.h
new file mode 100644
index 0000000..757cb2f
--- /dev/null
+++ b/v4l2-mfc-encoder/args.h
@@ -0,0 +1,43 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Argument parser header file.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef ARGS_H
+#define ARGS_H
+
+#include "common.h"
+
+struct options {
+ char *in_name;
+ char *mfc_name;
+ char *out_name;
+ int codec;
+ int width;
+ int height;
+ int duration;
+ int rate;
+ int bitrate;
+};
+
+void print_usage(char const *name);
+int parse_args(struct options *opts, int argc, char **argv);
+
+#endif
diff --git a/v4l2-mfc-encoder/common.h b/v4l2-mfc-encoder/common.h
new file mode 100644
index 0000000..ef5276f
--- /dev/null
+++ b/v4l2-mfc-encoder/common.h
@@ -0,0 +1,66 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Common structures.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef COMMON_H
+#define COMMON_H
+
+#include <stdio.h>
+#include <string.h>
+
+/* When ADD_DETAILS is defined every debug and error message contains
+ * information about the file, function and line of code where it has
+ * been called */
+#define ADD_DETAILS
+/* When DEBUG is defined debug messages are printed on the screen.
+ * Otherwise only error messages are displayed. */
+#define DEBUG
+
+#ifdef ADD_DETAILS
+#define err(msg, ...) \
+ fprintf(stderr, "%s:%s:%d: error: " msg "\n", __FILE__, \
+ __func__, __LINE__, ##__VA_ARGS__)
+#else
+#define err(msg, ...) \
+ fprintf(stderr, "Error: " msg "\n", __FILE__, ##__VA_ARGS__)
+#endif /* ADD_DETAILS */
+
+#ifdef DEBUG
+#ifdef ADD_DETAILS
+#include <time.h>
+#define dbg(msg, ...) { \
+ struct timespec t; \
+ clock_gettime(CLOCK_MONOTONIC, &t); \
+ fprintf(stderr, "%ld.%ld:%s:%s:%d: " msg "\n", t.tv_sec, t.tv_nsec, __FILE__, \
+ __func__, __LINE__, ##__VA_ARGS__); \
+}
+#else
+#define dbg(msg, ...) \
+ fprintf(stderr, msg "\n", ##__VA_ARGS__)
+#endif /* ADD_DETAILS */
+#else /* DEBUG */
+#define dbg(...) {}
+#endif /* DEBUG */
+
+#define array_len(arr) (sizeof(arr) / sizeof(arr[0]))
+#define memzero(d) memset(&(d), 0, sizeof(d))
+
+#endif
diff --git a/v4l2-mfc-encoder/func_dev.c b/v4l2-mfc-encoder/func_dev.c
new file mode 100644
index 0000000..c3fff54
--- /dev/null
+++ b/v4l2-mfc-encoder/func_dev.c
@@ -0,0 +1,132 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * func I/O device implementation.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "io_dev.h"
+#include "func_dev.h"
+#include "mfc.h"
+
+int func_req_bufs(struct io_dev *dev, enum io_dir dir, int nelem)
+{
+ struct ring_buffer *rb;
+
+ rb = malloc(sizeof(*rb) + (nelem + 1) * sizeof(rb->data[0]));
+ rb->begin = 0;
+ rb->end = 0;
+ rb->size = nelem + 1;
+
+ dev->io[dir].queue = rb;
+
+ dbg("Succesfully requested %d buffers for device %d:%d", nelem,
+ dev->fd, dir);
+
+ return nelem;
+}
+
+int func_deq_buf(struct io_dev *dev, enum io_dir dir)
+{
+ int i;
+ struct dev_buffers *bufs;
+ int ret;
+ int lens[MFC_MAX_PLANES];
+ struct ring_buffer *q;
+ int idx;
+ int (*func)(struct io_dev *dev, int nbufs, char **bufs, int *lens);
+
+ q = dev->io[dir].queue;
+
+ if (q->begin == q->end)
+ return -1;
+
+ idx = q->data[q->begin++];
+ q->begin %= q->size;
+
+ bufs = dev->io[dir].bufs;
+
+ for (i = 0; i < bufs->nplanes; ++i)
+ lens[i] = (dir == DIR_IN) ?
+ bufs->bytesused[bufs->nplanes * idx + i] :
+ bufs->lengths[i];
+
+ func = (dir == DIR_OUT) ? dev->ops->read : dev->ops->write;
+
+ ret = func(dev, bufs->nplanes, &bufs->addr[bufs->nplanes * idx], lens);
+
+ for (i = 0; i < bufs->nplanes; ++i)
+ bufs->bytesused[bufs->nplanes * idx + i] = lens[i];
+
+ dbg("Dequeued buffer %d/%d from %d:%d", idx, bufs->count, dev->fd, dir);
+
+ --dev->io[dir].nbufs;
+
+ ++dev->io[dir].counter;
+
+ if (ret <= 0 || (dev->io[dir].limit &&
+ dev->io[dir].limit <= dev->io[dir].counter)) {
+ dev->io[dir].state = FS_END;
+ dbg("End on %d:%d", dev->fd, dir);
+ } else if (q->begin == q->end) {
+ dev->io[dir].state = FS_OFF;
+ } else if (dev->fd >= 0) {
+ dev->io[dir].state = FS_BUSY;
+ }
+
+ return idx;
+}
+
+int func_enq_buf(struct io_dev *dev, enum io_dir dir, int idx)
+{
+ struct ring_buffer *q;
+
+ q = dev->io[dir].queue;
+ q->data[q->end++] = idx;
+ q->end %= q->size;
+
+ ++dev->io[dir].nbufs;
+
+ if (dev->io[dir].state == FS_OFF)
+ dev->io[dir].state = dev->fd >= 0 ? FS_BUSY : FS_READY;
+ else if (dev->io[dir].state == FS_BUSY && dev->fd < 0)
+ dev->io[dir].state = FS_READY;
+
+ dbg("Enqueued buffer %d/%d to %d:%d", idx, q->size - 1, dev->fd, dir);
+
+ return 0;
+}
+
+int func_destroy(struct io_dev *dev)
+{
+ if (dev->io[DIR_IN].type == IO_FUNC)
+ free(dev->io[DIR_IN].queue);
+
+ if (dev->io[DIR_OUT].type == IO_FUNC)
+ free(dev->io[DIR_OUT].queue);
+
+ if (dev->fd >= 0)
+ close(dev->fd);
+
+ free(dev);
+
+ return 0;
+}
diff --git a/v4l2-mfc-encoder/func_dev.h b/v4l2-mfc-encoder/func_dev.h
new file mode 100644
index 0000000..6c2621e
--- /dev/null
+++ b/v4l2-mfc-encoder/func_dev.h
@@ -0,0 +1,33 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * func I/O device header.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef FUNC_DEV_H
+#define FUNC_DEV_H
+
+#include "io_dev.h"
+
+int func_req_bufs(struct io_dev *dev, enum io_dir dir, int nelem);
+int func_deq_buf(struct io_dev *dev, enum io_dir dir);
+int func_enq_buf(struct io_dev *dev, enum io_dir dir, int idx);
+int func_destroy(struct io_dev *dev);
+
+#endif
diff --git a/v4l2-mfc-encoder/in_camera.c b/v4l2-mfc-encoder/in_camera.c
new file mode 100644
index 0000000..2ad840b
--- /dev/null
+++ b/v4l2-mfc-encoder/in_camera.c
@@ -0,0 +1,83 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Input camera device.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+
+#include "common.h"
+#include "in_demo.h"
+#include "v4l_dev.h"
+
+#define SQUARE_SIZE 5
+
+static struct io_dev_ops in_camera_ops = { .req_bufs = v4l_req_bufs,
+ .enq_buf = v4l_enq_buf,
+ .deq_buf = v4l_deq_buf,
+ .destroy = v4l_destroy
+ };
+
+int in_camera_set_autofocus(struct io_dev *dev, int val)
+{
+ struct v4l2_ext_control ctrl;
+ struct v4l2_ext_controls ctrls;
+ int ret;
+
+ ctrl.id = V4L2_CID_FOCUS_AUTO;
+ ctrl.value = val;
+
+ ctrls.ctrl_class = V4L2_CID_CAMERA_CLASS;
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+
+ ret = ioctl(dev->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
+ if (ret < 0)
+ err("Cannot set autofocus on %d", dev->fd);
+
+ return ret;
+}
+
+struct io_dev *in_camera_create(char const *name)
+{
+ struct io_dev *dev;
+
+ dev = malloc(sizeof(*dev));
+ memzero(*dev);
+
+ dev->fd = open(name, O_RDWR, 0);
+ if (dev->fd < 0) {
+ free(dev);
+ err("Cannot open camera(%s) device", name);
+ return NULL;
+ }
+
+ dev->io[0].type = IO_NONE;
+ dev->io[1].type = IO_USERPTR;
+
+ dev->ops = &in_camera_ops;
+
+ in_camera_set_autofocus(dev, 1);
+
+ return dev;
+}
diff --git a/v4l2-mfc-encoder/in_camera.h b/v4l2-mfc-encoder/in_camera.h
new file mode 100644
index 0000000..b72d9d2
--- /dev/null
+++ b/v4l2-mfc-encoder/in_camera.h
@@ -0,0 +1,30 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Camera input header file.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef IN_CAMERA_H
+#define IN_CAMERA_H
+
+#include "io_dev.h"
+
+struct io_dev *in_camera_create(char const *name);
+
+#endif
diff --git a/v4l2-mfc-encoder/in_demo.c b/v4l2-mfc-encoder/in_demo.c
new file mode 100644
index 0000000..78d58f0
--- /dev/null
+++ b/v4l2-mfc-encoder/in_demo.c
@@ -0,0 +1,111 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Sample input device.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "common.h"
+#include "in_demo.h"
+#include "func_dev.h"
+
+#define SQUARE_SIZE 5
+#define MIN(X,Y) ((X) < (Y) ? (X) : (Y))
+
+struct in_demo_priv {
+ int width;
+ int height;
+};
+
+static int in_demo_read(struct io_dev *dev, int nbufs, char **bufs, int *lens)
+{
+ struct in_demo_priv *p;
+ int x, y;
+ int i, j;
+ int size;
+ double rx, ry;
+ int t;
+
+ p = dev->priv;
+
+ if (nbufs != 2)
+ return -1;
+
+ t = dev->io[DIR_OUT].counter;
+
+ size = p->width * p->height;
+ if (size > lens[0] || size > 2 * lens[1]) {
+ err("Size=%d len=%d,%d", size, lens[0], lens[1]);
+ return -1;
+ }
+
+ memset(bufs[0], 0, size);
+ memset(bufs[1], 128, size / 2);
+
+ rx = cos(7 * t / 3.14 / 25 * 100 / p->width);
+ ry = sin(6 * t / 3.14 / 25 * 100 / p->width);
+
+ x = (rx + 1) / 2 * (p->width - 2 * SQUARE_SIZE) + SQUARE_SIZE;
+ y = (ry + 1) / 2 * (p->height - 2 * SQUARE_SIZE) + SQUARE_SIZE;
+
+ for (i = MIN(SQUARE_SIZE, p->width) - 1; i >= 0; --i)
+ for (j = MIN(SQUARE_SIZE, p->height) - 1; j >= 0; --j)
+ bufs[0][x + i + (y + j) * p->width] = 255;
+
+ return size;
+}
+
+static int in_demo_destroy(struct io_dev *dev)
+{
+ free(dev->priv);
+
+ return func_destroy(dev);
+}
+
+static struct io_dev_ops in_demo_ops = { .read = in_demo_read,
+ .req_bufs = func_req_bufs,
+ .enq_buf = func_enq_buf,
+ .deq_buf = func_deq_buf,
+ .destroy = in_demo_destroy
+ };
+
+struct io_dev *in_demo_create(int width, int height)
+{
+ struct io_dev *dev;
+ struct in_demo_priv *priv;
+
+ dev = malloc(sizeof(*dev));
+ memzero(*dev);
+
+ priv = malloc(sizeof(struct in_demo_priv));
+ priv->width = width;
+ priv->height = height;
+
+ dev->fd = -1;
+ dev->io[DIR_IN].type = IO_NONE;
+ dev->io[DIR_OUT].type = IO_FUNC;
+ dev->ops = &in_demo_ops;
+
+ dev->priv = priv;
+
+ return dev;
+}
diff --git a/v4l2-mfc-encoder/in_demo.h b/v4l2-mfc-encoder/in_demo.h
new file mode 100644
index 0000000..d860f69
--- /dev/null
+++ b/v4l2-mfc-encoder/in_demo.h
@@ -0,0 +1,30 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Sample input header file.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef IN_DEMO_H
+#define IN_DEMO_H
+
+#include "io_dev.h"
+
+struct io_dev *in_demo_create(int width, int height);
+
+#endif
diff --git a/v4l2-mfc-encoder/io_dev.c b/v4l2-mfc-encoder/io_dev.c
new file mode 100644
index 0000000..24e017f
--- /dev/null
+++ b/v4l2-mfc-encoder/io_dev.c
@@ -0,0 +1,172 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * I/O device implementation.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+#include <linux/videodev2.h>
+#include <errno.h>
+
+#include "io_dev.h"
+#include "mfc.h"
+#include "func_dev.h"
+
+/* return immediately if there is at least one device ready,
+ waits until at least one device is ready, returns number of ready devices */
+int wait_for_ready_devs(struct io_dev *chain[], int ndev)
+{
+ struct pollfd fds[ndev];
+ int nfds;
+ int i, j;
+ int ret;
+
+ nfds = 0;
+
+ for (i = 0; i < ndev; ++i) {
+ if (chain[i]->io[DIR_IN].state == FS_READY)
+ return 1;
+
+ if (chain[i]->io[DIR_OUT].state == FS_READY)
+ return 1;
+
+ if (chain[i]->fd < 0)
+ continue;
+
+ fds[nfds].events = 0;
+
+ if (chain[i]->io[DIR_IN].state == FS_BUSY)
+ fds[nfds].events |= POLLOUT;
+
+ if (chain[i]->io[DIR_OUT].state == FS_BUSY)
+ fds[nfds].events |= POLLIN | POLLPRI;
+
+ if (fds[nfds].events != 0) {
+ fds[nfds].fd = chain[i]->fd;
+ dbg("Will poll fd=%d events=%d", fds[nfds].fd, fds[nfds].events);
+ ++nfds;
+ }
+ }
+
+ if (nfds == 0)
+ return 0;
+
+ ret = poll(fds, nfds, -1);
+ if (ret <= 0)
+ return ret;
+
+ for (i = 0, j = 0; j < nfds; ++j) {
+ while (fds[j].fd != chain[i]->fd)
+ ++i;
+
+ if (fds[j].revents & POLLOUT)
+ chain[i]->io[DIR_IN].state = FS_READY;
+
+ if (fds[j].revents & POLLIN)
+ chain[i]->io[DIR_OUT].state = FS_READY;
+
+ if (fds[j].revents & POLLPRI)
+ chain[i]->event = 1;
+ }
+
+ return ret;
+}
+
+void print_chain(struct io_dev *chain[], int ndev)
+{
+ int i;
+ struct io_port *in, *out;
+ static char *ch_state[] = {"Off", "Bus", "Rdy", "Evt", "End"};
+
+ fprintf(stderr, "State [enq cnt/max]: ");
+
+ for (i = 0; i < ndev; ++i) {
+ in = &chain[i]->io[DIR_IN];
+ out = &chain[i]->io[DIR_OUT];
+ fprintf(stderr, "[%s%s %d %d/%d|%s %d %d/%d] ",
+ ch_state[in->state], chain[i]->event ? "+ev" : "", in->nbufs, in->counter,
+ in->limit, ch_state[out->state], out->nbufs,
+ out->counter, out->limit);
+ }
+ fprintf(stderr, "\n");
+}
+
+int process_pair(struct io_dev *in, struct io_dev *out)
+{
+ int idx;
+
+ idx = 0;
+ if (in->io[DIR_OUT].state == FS_READY) {
+ idx = in->ops->deq_buf(in, DIR_OUT);
+ if (out->io[DIR_IN].state != FS_END) {
+ if (in->io[DIR_OUT].state == FS_END &&
+ !out->io[DIR_IN].limit)
+ out->io[DIR_IN].limit = out->io[DIR_IN].counter
+ + out->io[DIR_IN].nbufs
+ + (idx >= 0 ? 1 : 0);
+ if (idx >= 0)
+ idx = out->ops->enq_buf(out, DIR_IN, idx);
+ }
+ }
+
+ if (idx < 0)
+ return idx;
+
+ if (out->io[DIR_IN].state == FS_READY) {
+ idx = out->ops->deq_buf(out, DIR_IN);
+ if (in->io[DIR_OUT].state != FS_END && idx >= 0)
+ idx = in->ops->enq_buf(in, DIR_OUT, idx);
+ }
+
+ if (idx < 0)
+ return idx;
+
+ if (in->event)
+ idx = in->ops->deq_event(in);
+
+ return idx >= 0 ? 0 : -1;
+}
+
+int process_chain(struct io_dev *chain[], int ndev)
+{
+ int ret;
+ int i;
+
+ if (ndev < 2)
+ return 1;
+
+ do {
+ print_chain(chain, ndev);
+ ret = wait_for_ready_devs(chain, ndev);
+ print_chain(chain, ndev);
+ if (ret <= 0)
+ break;
+ for (i = 1; i < ndev; ++i) {
+ ret = process_pair(chain[i - 1], chain[i]);
+ if (ret != 0) {
+ dbg("pair %d:%d ret=%d", i-1, i, ret);
+ return -1;
+ }
+ }
+ } while (1);
+
+ return 0;
+}
diff --git a/v4l2-mfc-encoder/io_dev.h b/v4l2-mfc-encoder/io_dev.h
new file mode 100644
index 0000000..37e4364
--- /dev/null
+++ b/v4l2-mfc-encoder/io_dev.h
@@ -0,0 +1,91 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * I/O device header file.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef IO_DEV_H
+#define IO_DEV_H
+
+#include <linux/videodev2.h>
+
+#include "common.h"
+
+struct dev_buffers {
+ int count; /* number of buffers */
+ int nplanes; /* number of planes per buffer */
+ int *lengths; /* array of plane lengths */
+ /* array of plane addresses, address of plane p in buffer b
+ is at addr[nplanes * b + p] */
+ char **addr;
+ /* array of bytes used by plane, bytesused of plane p in buffer b
+ is at bytesused[nplanes * b + p] */
+ int *bytesused;
+};
+
+struct ring_buffer {
+ int begin;
+ int end;
+ int size;
+ int data[0];
+};
+
+enum io_type { IO_NONE, IO_FUNC, IO_MMAP, IO_USERPTR };
+enum io_dir { DIR_IN = 0, DIR_OUT = 1};
+enum func_state { FS_OFF, FS_BUSY, FS_READY, FS_EVENT, FS_END };
+
+struct io_dev_ops;
+struct io_dev;
+
+struct io_port {
+ enum io_type type;
+ enum func_state state;
+ int counter; /* total number of dequeued buffers */
+ int nbufs; /* number of buffers in queue */
+ int limit; /* after dequeuing limit buffers state is changed to END */
+ struct dev_buffers *bufs;
+ struct ring_buffer *queue; /* used by non V4L devices */
+};
+
+struct io_dev {
+ int fd;
+ int event;
+ /* in and out parts of device */
+ struct io_port io[2];
+ struct io_dev_ops *ops;
+ void *priv;
+};
+
+struct io_dev_ops {
+ int (*read)(struct io_dev *dev, int nbufs, char **bufs, int *lens);
+ int (*write)(struct io_dev *dev, int nbufs, char **bufs, int *lens);
+ int (*req_bufs)(struct io_dev *dev, enum io_dir dir, int nelem);
+ int (*deq_buf)(struct io_dev *dev, enum io_dir dir);
+ int (*enq_buf)(struct io_dev *dev, enum io_dir dir, int idx);
+ int (*deq_event)(struct io_dev *dev);
+ int (*destroy)(struct io_dev *dev);
+};
+
+enum v4l2_buf_type io_dir_to_type(enum io_dir dir);
+int dev_copy_fmt(int src_fd, enum io_dir src_dir, int dst_fd,
+ enum io_dir dst_dir);
+
+int process_chain(struct io_dev *chain[], int nelem);
+
+#endif
diff --git a/v4l2-mfc-encoder/main.c b/v4l2-mfc-encoder/main.c
new file mode 100644
index 0000000..5681bab
--- /dev/null
+++ b/v4l2-mfc-encoder/main.c
@@ -0,0 +1,106 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Main file of the application.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "common.h"
+#include "args.h"
+#include "in_demo.h"
+#include "in_camera.h"
+#include "out_file.h"
+#include "io_dev.h"
+#include "mfc.h"
+#include "v4l_dev.h"
+
+int main(int argc, char *argv[])
+{
+ struct options opts;
+
+ struct io_dev *input;
+ struct io_dev *mfc;
+ struct io_dev *output;
+ int i;
+
+ struct io_dev *chain[3] = {};
+
+ printf("mfc codec encoding example application\n"
+ "Andrzej Hajda <a.hajda@samsung.com>\n"
+ "Copyright 2012 Samsung Electronics Co., Ltd.\n\n");
+
+ if (parse_args(&opts, argc, argv)) {
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ if (opts.in_name)
+ input = in_camera_create(opts.in_name);
+ else
+ input = in_demo_create(opts.width, opts.height);
+ if (input == NULL)
+ return 1;
+ chain[0] = input;
+
+ input->io[DIR_OUT].limit = opts.duration;
+
+ mfc = mfc_create(opts.mfc_name);
+ if (mfc == NULL)
+ return 1;
+ chain[1] = mfc;
+
+ if (mfc_set_fmt(mfc, DIR_IN, opts.width, opts.height))
+ return 1;
+
+ if (mfc_set_codec(mfc, DIR_OUT, opts.codec))
+ return 1;
+
+ if (mfc_set_rate(mfc, opts.rate))
+ return 1;
+
+ if (mfc_set_bitrate(mfc, opts.bitrate))
+ return 1;
+
+ mfc_set_mpeg_control(mfc, V4L2_CID_MPEG_VIDEO_B_FRAMES, 2);
+
+ if (opts.in_name)
+ if (v4l_copy_fmt(mfc, DIR_IN, input, DIR_OUT))
+ return 1;
+
+ output = out_file_create(opts.out_name);
+ if (output == NULL)
+ return 1;
+ chain[2] = output;
+
+ if (dev_bufs_create(input, mfc, MFC_ENC_IN_NBUF))
+ return 1;
+
+ if (dev_bufs_create(mfc, output, MFC_ENC_OUT_NBUF))
+ return 1;
+
+ if (process_chain(chain, array_len(chain)))
+ return 1;
+
+ for (i = 0; i < array_len(chain); ++i)
+ chain[i]->ops->destroy(chain[i]);
+
+ return 0;
+}
diff --git a/v4l2-mfc-encoder/mfc.c b/v4l2-mfc-encoder/mfc.c
new file mode 100644
index 0000000..573ee8a
--- /dev/null
+++ b/v4l2-mfc-encoder/mfc.c
@@ -0,0 +1,187 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * MFC device.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <sys/ioctl.h>
+#include <stdlib.h>
+#include <fcntl.h>
+
+#include "common.h"
+#include "mfc.h"
+#include "v4l_dev.h"
+
+#define MAX_STREAM_SIZE (2*1024*1024)
+
+int align(int x, int a)
+{
+ return ((x + a - 1) / a) * a;
+}
+
+/* runs v4l_deq_buf and correct ending conditions
+ according to MFC driver */
+int mfc_deq_buf(struct io_dev *dev, enum io_dir dir)
+{
+ int idx;
+
+ idx = v4l_deq_buf(dev, dir);
+
+ if (dir == DIR_IN && dev->io[DIR_IN].state == FS_END
+ && dev->io[DIR_OUT].limit)
+ dev->io[DIR_OUT].limit = 0;
+
+ return idx;
+}
+
+struct io_dev_ops mfc_dev_ops = { .req_bufs = v4l_req_bufs,
+ .enq_buf = v4l_enq_buf,
+ .deq_buf = mfc_deq_buf,
+ .deq_event = v4l_deq_event,
+ .destroy = v4l_destroy
+ };
+
+struct io_dev *mfc_create(char const *name)
+{
+ struct io_dev *dev;
+ int ret;
+
+ dev = malloc(sizeof(*dev));
+ memzero(*dev);
+
+ dev->ops = &mfc_dev_ops;
+
+ dev->io[DIR_IN].type = IO_MMAP;
+ dev->io[DIR_OUT].type = IO_MMAP;
+
+ dev->fd = open(name, O_RDWR, 0);
+ if (dev->fd < 0) {
+ err("Cannot open MFC device %s", name);
+ free(dev);
+ return NULL;
+ }
+
+ struct v4l2_event_subscription ev_sub;
+ memzero(ev_sub);
+ ev_sub.type = V4L2_EVENT_EOS;
+ ret = ioctl(dev->fd, VIDIOC_SUBSCRIBE_EVENT, &ev_sub);
+ if (ret != 0)
+ err("Cannot subscribe EOS event for MFC");
+
+ dbg("MFC device %s opened with fd=%d", name, dev->fd);
+
+ return dev;
+}
+
+int mfc_set_codec(struct io_dev *dev, enum io_dir dir, int codec)
+{
+ struct v4l2_format fmt;
+ int ret;
+
+ memzero(fmt);
+ fmt.type = io_dir_to_type(dir);
+ fmt.fmt.pix_mp.pixelformat = codec;
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage = MAX_STREAM_SIZE;
+
+ ret = ioctl(dev->fd, VIDIOC_S_FMT, &fmt);
+
+ return ret;
+}
+
+/* set format with proper alignement */
+int mfc_set_fmt(struct io_dev *dev, enum io_dir dir, int width, int height)
+{
+ struct v4l2_format fmt;
+ int ret;
+
+ memzero(fmt);
+ fmt.type = io_dir_to_type(dir);
+ fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12M;
+ fmt.fmt.pix_mp.width = width;
+ fmt.fmt.pix_mp.height = height;
+
+ fmt.fmt.pix_mp.num_planes = 2;
+ fmt.fmt.pix_mp.plane_fmt[0].bytesperline = align(width, 128);
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage = align(width * height, 2048);
+ fmt.fmt.pix_mp.plane_fmt[1].bytesperline = align(width, 128);
+ fmt.fmt.pix_mp.plane_fmt[1].sizeimage = align(width * (height / 2),
+ 2048);
+
+ ret = ioctl(dev->fd, VIDIOC_S_FMT, &fmt);
+ if (ret != 0)
+ err("Cannot set format on %d:%d", dev->fd, dir);
+
+ return ret;
+}
+
+int mfc_set_rate(struct io_dev *dev, int rate)
+{
+ struct v4l2_streamparm fps;
+ int ret;
+
+ fps.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ fps.parm.output.timeperframe.numerator = 1000;
+ fps.parm.output.timeperframe.denominator = rate;
+
+ ret = ioctl(dev->fd, VIDIOC_S_PARM, &fps);
+ if (ret != 0)
+ err("Cannot set rate on %d", dev->fd);
+
+ return ret;
+}
+
+int mfc_set_mpeg_control(struct io_dev *dev, int id, int value)
+{
+ struct v4l2_ext_control ctrl;
+ struct v4l2_ext_controls ctrls;
+ int ret;
+
+ ctrl.id = id;
+ ctrl.value = value;
+
+ ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+
+ ret = ioctl(dev->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
+ if (ret < 0)
+ err("Cannot set control %d to %d on %d", id, value, dev->fd);
+
+ return ret;
+}
+
+int mfc_set_bitrate(struct io_dev *dev, int bitrate)
+{
+ struct v4l2_ext_control ctrl;
+ struct v4l2_ext_controls ctrls;
+ int ret;
+
+ ctrl.id = V4L2_CID_MPEG_VIDEO_BITRATE;
+ ctrl.value = bitrate;
+
+ ctrls.ctrl_class = V4L2_CTRL_CLASS_MPEG;
+ ctrls.count = 1;
+ ctrls.controls = &ctrl;
+
+ ret = ioctl(dev->fd, VIDIOC_S_EXT_CTRLS, &ctrls);
+ if (ret < 0)
+ err("Cannot set bitrate on %d", dev->fd);
+
+ return ret;
+}
diff --git a/v4l2-mfc-encoder/mfc.h b/v4l2-mfc-encoder/mfc.h
new file mode 100644
index 0000000..06849a6
--- /dev/null
+++ b/v4l2-mfc-encoder/mfc.h
@@ -0,0 +1,41 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * MFC device header file.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef MFC_H
+#define MFC_H
+
+#include <linux/videodev2.h>
+
+#include "io_dev.h"
+
+#define MFC_ENC_IN_NBUF 16
+#define MFC_ENC_OUT_NBUF 4
+#define MFC_MAX_PLANES 2
+
+struct io_dev *mfc_create(char const *name);
+int mfc_set_codec(struct io_dev *dev, enum io_dir dir, int codec);
+int mfc_set_fmt(struct io_dev *dev, enum io_dir dir, int width, int height);
+int mfc_set_rate(struct io_dev *dev, int rate);
+int mfc_set_bitrate(struct io_dev *dev, int bitrate);
+int mfc_set_mpeg_control(struct io_dev *dev, int id, int value);
+
+#endif
diff --git a/v4l2-mfc-encoder/out_file.c b/v4l2-mfc-encoder/out_file.c
new file mode 100644
index 0000000..7a4bde5
--- /dev/null
+++ b/v4l2-mfc-encoder/out_file.c
@@ -0,0 +1,57 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Output file device.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "common.h"
+#include "out_file.h"
+#include "func_dev.h"
+
+int out_file_write(struct io_dev *dev, int nbufs, char **bufs, int *lens)
+{
+ return write(dev->fd, bufs[0], lens[0]);
+}
+
+static struct io_dev_ops out_file_ops = { .write = out_file_write,
+ .req_bufs = func_req_bufs,
+ .enq_buf = func_enq_buf,
+ .deq_buf = func_deq_buf,
+ .destroy = func_destroy
+ };
+
+struct io_dev *out_file_create(char const *name)
+{
+ struct io_dev *dev;
+
+ dev = malloc(sizeof(*dev));
+ memzero(*dev);
+
+ dev->fd = open(name, O_CREAT | O_TRUNC | O_WRONLY, 0666);
+ dev->io[DIR_IN].type = IO_FUNC;
+ dev->io[DIR_OUT].type = IO_NONE;
+ dev->ops = &out_file_ops;
+
+ return dev;
+}
diff --git a/v4l2-mfc-encoder/out_file.h b/v4l2-mfc-encoder/out_file.h
new file mode 100644
index 0000000..1e740c4
--- /dev/null
+++ b/v4l2-mfc-encoder/out_file.h
@@ -0,0 +1,30 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * Output file device header.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef OUT_FILE_H
+#define OUT_FILE_H
+
+#include "io_dev.h"
+
+struct io_dev *out_file_create(char const *name);
+
+#endif
diff --git a/v4l2-mfc-encoder/v4l_dev.c b/v4l2-mfc-encoder/v4l_dev.c
new file mode 100644
index 0000000..4aaa3ca
--- /dev/null
+++ b/v4l2-mfc-encoder/v4l_dev.c
@@ -0,0 +1,399 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * V4L I/O device implementation.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#include <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/mman.h>
+
+#include "io_dev.h"
+#include "v4l_dev.h"
+#include "mfc.h"
+
+enum v4l2_memory io_type_to_memory(enum io_type type)
+{
+ switch (type) {
+ case IO_USERPTR: return V4L2_MEMORY_USERPTR;
+ case IO_MMAP: return V4L2_MEMORY_MMAP;
+ default: return 0;
+ }
+}
+
+int is_buf_type(enum io_type type)
+{
+ return (type == IO_USERPTR) || (type == IO_MMAP);
+}
+
+enum v4l2_buf_type io_dir_to_type(enum io_dir dir)
+{
+ switch (dir) {
+ case DIR_IN: return V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ case DIR_OUT: return V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ default: return 0;
+ }
+}
+
+/* switch on/off streaming on v4l device */
+int v4l_stream_set(struct io_dev *dev, int op)
+{
+ int ret;
+ int buf_type;
+
+ if (dev->io[DIR_IN].type != IO_NONE) {
+ buf_type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ ret = ioctl(dev->fd, op ? VIDIOC_STREAMON : VIDIOC_STREAMOFF,
+ &buf_type);
+ if (ret != 0) {
+ err("Cannot %s stream on fd=%d:0",
+ op ? "start" : "stop", dev->fd);
+ return -1;
+ }
+
+ dev->io[DIR_IN].state = op ? FS_BUSY : FS_END;
+
+ dbg("Stream %s on fd=%d:0", op ? "started" : "stopped",
+ dev->fd);
+ }
+
+ if (dev->io[DIR_OUT].type != IO_NONE) {
+ buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ ret = ioctl(dev->fd, op ? VIDIOC_STREAMON : VIDIOC_STREAMOFF,
+ &buf_type);
+ if (ret != 0) {
+ err("Cannot %s stream on fd=%d:1",
+ op ? "start" : "stop", dev->fd);
+ return -1;
+ }
+
+ dev->io[DIR_OUT].state = op ? FS_BUSY : FS_END;
+
+ dbg("Stream %s on fd=%d:1", op ? "started" : "stopped",
+ dev->fd);
+ }
+
+ return 0;
+}
+
+int v4l_req_bufs(struct io_dev *dev, enum io_dir dir, int nelem)
+{
+ int ret;
+ struct v4l2_requestbuffers reqbuf;
+
+ memzero(reqbuf);
+ reqbuf.count = nelem;
+ reqbuf.type = io_dir_to_type(dir);
+ reqbuf.memory = io_type_to_memory(dev->io[dir].type);
+
+ ret = ioctl(dev->fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret != 0) {
+ err("Failed to request %d buffers for device %d:%d)", nelem,
+ dev->fd, dir);
+ return -1;
+ }
+
+ dbg("Succesfully requested %d buffers for device %d:%d", nelem,
+ dev->fd, dir);
+
+ return reqbuf.count;
+}
+
+/* dequeue v4l buffer, adjust io_port fields, stop stream when needed,
+ set end state when counter reaches limit,
+ set limit on out port if in port is stopped */
+int v4l_deq_buf(struct io_dev *dev, enum io_dir dir)
+{
+ struct v4l2_plane planes[MFC_MAX_PLANES];
+ struct v4l2_buffer buf;
+ int ret;
+ int idx;
+ struct dev_buffers *bufs;
+ int i;
+
+ bufs = dev->io[dir].bufs;
+
+ memzero(buf);
+
+ buf.type = io_dir_to_type(dir);
+ buf.memory = io_type_to_memory(dev->io[dir].type);
+ buf.m.planes = planes;
+ buf.length = MFC_MAX_PLANES;
+
+ ret = ioctl(dev->fd, VIDIOC_DQBUF, &buf);
+ if (ret != 0) {
+ dbg("Dequeue buffer error for %d:%d", dev->fd, dir);
+ return -1;
+ } else
+ dbg("Dequeued %d/%d buffer(flags=%02X,s=%d) from %d:%d",
+ buf.index, bufs->count, buf.flags,
+ buf.m.planes[0].bytesused, dev->fd,
+ dir);
+
+ idx = buf.index;
+
+ for (i = 0; i < bufs->nplanes; ++i)
+ bufs->bytesused[idx * bufs->nplanes + i] =
+ buf.m.planes[i].bytesused;
+
+ --dev->io[dir].nbufs;
+
+ ++dev->io[dir].counter;
+
+ if (dev->io[dir].limit && dev->io[dir].limit <= dev->io[dir].counter) {
+ dev->io[dir].state = FS_END;
+ if (dev->io[DIR_OUT - dir].type == IO_NONE ||
+ dev->io[DIR_OUT - dir].state == FS_END)
+ v4l_stream_set(dev, 0);
+ dbg("End on %d:%d", dev->fd, dir);
+ } else {
+ dev->io[dir].state = FS_BUSY;
+ }
+
+ if (dir == DIR_IN && dev->io[DIR_IN].state == FS_END
+ && !dev->io[DIR_OUT].limit)
+ dev->io[DIR_OUT].limit = dev->io[DIR_IN].counter;
+
+ return idx;
+}
+
+/* enqueue buffer, start stream when needed */
+int v4l_enq_buf(struct io_dev *dev, enum io_dir dir, int idx)
+{
+ struct v4l2_plane planes[MFC_MAX_PLANES];
+ struct v4l2_buffer buf;
+ int i;
+ int ret;
+ struct dev_buffers *bufs;
+
+ bufs = dev->io[dir].bufs;
+
+ memzero(buf);
+
+ buf.type = io_dir_to_type(dir);
+ buf.memory = io_type_to_memory(dev->io[dir].type);
+ buf.index = idx;
+ buf.m.planes = planes;
+ buf.length = bufs->nplanes;
+ for (i = 0; i < bufs->nplanes; ++i) {
+ planes[i].bytesused = bufs->bytesused[idx * bufs->nplanes + i];
+ planes[i].length = bufs->lengths[i];
+ planes[i].m.userptr = (unsigned long)bufs->addr[
+ idx * bufs->nplanes + i];
+ }
+
+ ret = ioctl(dev->fd, VIDIOC_QBUF, &buf);
+ if (ret != 0) {
+ err("Error %d enq buffer %d/%d to %d:%d", errno, idx,
+ bufs->count, dev->fd, dir);
+ return -1;
+ } else {
+ dbg("Enqueued buffer %d/%d to %d:%d", idx, bufs->count,
+ dev->fd, dir);
+ }
+
+ ++dev->io[dir].nbufs;
+
+ if (dev->io[dir].state == FS_OFF)
+ if (dir == DIR_IN || dev->io[DIR_IN].type == IO_NONE)
+ v4l_stream_set(dev, 1);
+
+ if (dir == DIR_IN && dev->io[dir].limit && dev->io[dir].limit
+ <= dev->io[dir].counter + dev->io[dir].nbufs) {
+ struct v4l2_encoder_cmd cmd;
+ memset(&cmd, 0, sizeof cmd);
+ cmd.cmd = V4L2_ENC_CMD_STOP;
+ ret = ioctl(dev->fd, VIDIOC_ENCODER_CMD, &cmd);
+ dbg("EOS sent to %d:%d, ret=%d", dev->fd, dir, ret);
+ }
+
+ return 0;
+}
+
+int v4l_deq_event(struct io_dev *dev)
+{
+ struct v4l2_event ev;
+ int ret;
+
+ memzero(ev);
+ ret = ioctl(dev->fd, VIDIOC_DQEVENT, &ev);
+ if (ret != 0)
+ return ret;
+
+ switch (ev.type) {
+ case V4L2_EVENT_EOS:
+ dev->io[DIR_OUT].state = FS_END;
+ dbg("EOS event received on %d", dev->fd);
+ }
+
+ dev->event = 0;
+
+ return 0;
+}
+
+/* set format on dst device according to src device */
+int v4l_copy_fmt(struct io_dev *src, enum io_dir sdir,
+ struct io_dev *dst, enum io_dir ddir)
+{
+ struct v4l2_format fmt;
+ int ret;
+
+ memzero(fmt);
+ fmt.type = io_dir_to_type(sdir);
+ ret = ioctl(src->fd, VIDIOC_G_FMT, &fmt);
+ if (ret != 0) {
+ err("Failed to get format");
+ return -1;
+ }
+
+ fmt.type = io_dir_to_type(ddir);
+ ret = ioctl(dst->fd, VIDIOC_S_FMT, &fmt);
+ if (ret != 0) {
+ err("Failed to set format");
+ return -1;
+ }
+
+ return 0;
+}
+
+int v4l_destroy(struct io_dev *dev)
+{
+ if (dev->io[DIR_IN].type == IO_MMAP)
+ dev_bufs_destroy(dev->io[DIR_IN].bufs);
+
+ if (dev->io[DIR_OUT].type == IO_MMAP)
+ dev_bufs_destroy(dev->io[DIR_OUT].bufs);
+
+ if (dev->fd >= 0)
+ close(dev->fd);
+
+ free(dev);
+
+ return 0;
+}
+
+/* requests mmap buffers from device with proper port type, mmap them
+ and initialize struct dev_buffers
+*/
+int dev_bufs_create(struct io_dev *in, struct io_dev *out, int nelem)
+{
+ enum io_dir dir;
+ struct io_dev *master, *slave;
+ struct v4l2_buffer qbuf;
+ int ret;
+ int n, i;
+ struct dev_buffers *bufs;
+ struct v4l2_plane planes[MFC_MAX_PLANES];
+
+ if (in->io[DIR_OUT].type == IO_MMAP) {
+ master = in;
+ slave = out;
+ dir = DIR_OUT;
+ } else if (out->io[DIR_IN].type == IO_MMAP) {
+ master = out;
+ slave = in;
+ dir = DIR_IN;
+ } else {
+ err("At least one device must have MMAP buffers.");
+ return -1;
+ }
+
+ nelem = master->ops->req_bufs(master, dir, nelem);
+ if (nelem < 0)
+ return -1;
+
+ nelem = slave->ops->req_bufs(slave, DIR_OUT - dir, nelem);
+ if (nelem < 0)
+ return -1;
+
+ bufs = malloc(sizeof(struct dev_buffers));
+ in->io[DIR_OUT].bufs = bufs;
+ out->io[DIR_IN].bufs = bufs;
+
+ bufs->count = nelem;
+
+ memzero(qbuf);
+ qbuf.type = io_dir_to_type(dir);
+ qbuf.memory = V4L2_MEMORY_MMAP;
+ qbuf.m.planes = planes;
+ qbuf.length = MFC_MAX_PLANES;
+
+ for (n = 0; n < nelem; ++n) {
+ qbuf.index = n;
+ ret = ioctl(master->fd, VIDIOC_QUERYBUF, &qbuf);
+ if (ret != 0) {
+ err("QUERYBUF failed");
+ return -1;
+ }
+
+ if (n == 0) {
+ for (i = 0; i < qbuf.length; ++i)
+ if (qbuf.m.planes[i].length == 0)
+ break;
+ bufs->nplanes = i;
+ bufs->lengths = malloc(bufs->nplanes
+ * sizeof(*bufs->lengths));
+ for (i = 0; i < bufs->nplanes; ++i)
+ bufs->lengths[i] = qbuf.m.planes[i].length;
+
+ bufs->bytesused = malloc(nelem * bufs->nplanes
+ * sizeof(*bufs->bytesused));
+ bufs->addr = malloc(nelem * bufs->nplanes
+ * sizeof(*bufs->addr));
+ }
+
+ for (i = 0; i < bufs->nplanes; ++i) {
+ bufs->addr[n * bufs->nplanes + i] = mmap(NULL,
+ qbuf.m.planes[i].length,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED, master->fd,
+ qbuf.m.planes[i].m.mem_offset);
+ if (bufs->addr[n * bufs->nplanes + i] == MAP_FAILED) {
+ err("Failed mmap buffer %d for %d:%d", n,
+ master->fd, dir);
+ return -1;
+ }
+ }
+ ret = in->ops->enq_buf(in, DIR_OUT, n);
+ if (ret < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+int dev_bufs_destroy(struct dev_buffers *bufs)
+{
+ free(bufs->addr);
+ free(bufs->bytesused);
+ free(bufs->lengths);
+ free(bufs);
+
+ return 0;
+}
+
+struct io_dev_ops v4l_dev_ops = { .req_bufs = v4l_req_bufs,
+ .enq_buf = v4l_enq_buf,
+ .deq_buf = v4l_deq_buf,
+ .deq_event = v4l_deq_event,
+ .destroy = v4l_destroy
+ };
diff --git a/v4l2-mfc-encoder/v4l_dev.h b/v4l2-mfc-encoder/v4l_dev.h
new file mode 100644
index 0000000..cee63a8
--- /dev/null
+++ b/v4l2-mfc-encoder/v4l_dev.h
@@ -0,0 +1,43 @@
+/*
+ * mfc codec encoding example application
+ * Andrzej Hajda <a.hajda@samsung.com>
+ *
+ * V4L I/O device header.
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ *
+ */
+
+#ifndef V4L_DEV_H
+#define V4L_DEV_H
+
+#include "io_dev.h"
+
+int v4l_stream_set(struct io_dev *dev, int op);
+int v4l_req_bufs(struct io_dev *dev, enum io_dir dir, int nelem);
+int v4l_deq_buf(struct io_dev *dev, enum io_dir dir);
+int v4l_enq_buf(struct io_dev *dev, enum io_dir dir, int idx);
+int v4l_deq_event(struct io_dev *dev);
+int v4l_copy_fmt(struct io_dev *src, enum io_dir sdir,
+ struct io_dev *dst, enum io_dir ddir);
+int v4l_destroy(struct io_dev *dev);
+
+/* create common struct dev_buffers for two joined devices */
+int dev_bufs_create(struct io_dev *in, struct io_dev *out, int nelem);
+int dev_bufs_destroy(struct dev_buffers *bufs);
+
+extern struct io_dev_ops v4l_dev_ops;
+
+#endif