summaryrefslogtreecommitdiff
path: root/v4l2-mfc-example
diff options
context:
space:
mode:
Diffstat (limited to 'v4l2-mfc-example')
-rw-r--r--v4l2-mfc-example/CHANGELOG.txt9
-rw-r--r--v4l2-mfc-example/Makefile54
-rw-r--r--v4l2-mfc-example/README.txt131
-rw-r--r--v4l2-mfc-example/args.c117
-rw-r--r--v4l2-mfc-example/args.h33
-rw-r--r--v4l2-mfc-example/common.h178
-rw-r--r--v4l2-mfc-example/fb.c118
-rw-r--r--v4l2-mfc-example/fb.h37
-rw-r--r--v4l2-mfc-example/fileops.c57
-rw-r--r--v4l2-mfc-example/fileops.h34
-rw-r--r--v4l2-mfc-example/fimc.c335
-rw-r--r--v4l2-mfc-example/fimc.h61
-rw-r--r--v4l2-mfc-example/main.c492
-rw-r--r--v4l2-mfc-example/mfc.c346
-rw-r--r--v4l2-mfc-example/mfc.h53
-rw-r--r--v4l2-mfc-example/parser.c349
-rw-r--r--v4l2-mfc-example/parser.h90
-rw-r--r--v4l2-mfc-example/queue.c80
-rw-r--r--v4l2-mfc-example/queue.h47
19 files changed, 2621 insertions, 0 deletions
diff --git a/v4l2-mfc-example/CHANGELOG.txt b/v4l2-mfc-example/CHANGELOG.txt
new file mode 100644
index 0000000..faca41a
--- /dev/null
+++ b/v4l2-mfc-example/CHANGELOG.txt
@@ -0,0 +1,9 @@
+Changelog
+
+============================
+* Version 0.1 - 2012-03-05 *
+============================
+
+Initial public release which supports decoding and contains
+the MPEG4 and H264 stream parsers.
+
diff --git a/v4l2-mfc-example/Makefile b/v4l2-mfc-example/Makefile
new file mode 100644
index 0000000..2117e1e
--- /dev/null
+++ b/v4l2-mfc-example/Makefile
@@ -0,0 +1,54 @@
+# V4L2 Codec decoding example application
+# Kamil Debski <k.debski@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-
+#TCPATH =
+KERNELHEADERS = /home/kamil/praca/w1-party/kernel/headers/include
+
+CC = ${TCPATH}gcc
+AR = "${TCPATH}ar rc"
+AR2 = ${TCPATH}ranlib make -j4
+
+
+INCLUDES = -I$(KERNELHEADERS)
+
+#INCLUDES = -I$(KERNELHEADERS)/include
+
+#-I$(TARGETROOT)/usr/include/linux
+
+SOURCES = main.c fileops.c args.c parser.c fb.c fimc.c mfc.c queue.c
+OBJECTS := $(SOURCES:.c=.o)
+EXEC = v4l2_decode
+CFLAGS = -Wall -g -DS5PC1XX_FIMC -lm
+#-Os
+
+all: $(EXEC)
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(INCLUDES) $<
+
+$(EXEC): $(OBJECTS)
+ $(CC) $(CFLAGS) -o $(EXEC) $(OBJECTS) -pthread
+
+clean:
+ rm -f *.o $(EXEC)
+
+install:
+
+.PHONY: clean all
diff --git a/v4l2-mfc-example/README.txt b/v4l2-mfc-example/README.txt
new file mode 100644
index 0000000..3c52396
--- /dev/null
+++ b/v4l2-mfc-example/README.txt
@@ -0,0 +1,131 @@
+V4L2 Codec decoding example application
+by Kamil Debski <k.debski@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 hardware
+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. Also the application demonstrates how
+should the stream be parsed and cut to successfully decode it with MFC.
+
+The application was written to make is easy to understand which steps are
+necessary to setup and conduct video decoding.
+
+
+=================================
+* Decoding in a few short words *
+=================================
+
+The main reference is the code which will guide you through the necessary steps.
+However here is a short summary.
+
+The header of the stream determines the resolution of the decompressed frames
+thus it has to be processed before the buffers are allocated. The setup of
+decoding is two-fold: first the OUTPUT queue is setup and it used to process the
+header. After the header is processed the resolution of the CAPTURE buffers is
+known and the application can safely setup the CAPTURE queue. To read the
+parameters of the decoded movie G_FMT ioctl call is used.
+Also the V4L2_CID_MIN_BUFFERS_FOR_CAPTURE control value is useful. It tells the
+minimum number of CAPTURE buffers that have to be queued in the queue to enable
+processing by the hardware. It is related to the number of frames that have
+to be kept as reference frames in the decoder. It depends on the coded used and
+its settings. The application can choose to allocate N numbers more to ensure
+that these N buffers can be accessed by the application while the MFC keeps
+processing new frames.
+
+After the decoding has been setup it the application has to supply stream
+buffers and processed the decoded frames. It is convenient to use three threads:
+one for stream parser, one for handling the decoded frame and on for processing
+the decoded frames.
+
+Finishing streaming - when the end of stream is detected it is necessary to
+extract and process all the remaining frames that have been kept as reference.
+To do this the application should queue an empty (bytesused = 0) buffer on
+OUTPUT after the end of stream has been detected and dequeue all remaining
+CAPTURE buffers have been dequeued. The first empty buffer signalises MFC that
+the end of stream has been reached and initializes decoding ending procedure.
+
+===========================
+* Running the application *
+===========================
+
+The application takes a few necessary arguments. Obviously you have to specify
+which file to play and the used codec. Also you need to provide information on
+which devices to use for processing.
+
+Options:
+-c <codec> - The codec of the encoded stream
+ Available codecs: mpeg4, h264
+-d <device> - Frame buffer device (e.g. /dev/fb0)
+-f <device> - FIMC device (e.g. /dev/video4)
+-i <file> - Input file name
+-m <device> - MFC device (e.g. /dev/video8)
+-V - synchronise to vsync
+
+For example the following command:
+
+./v4l2_decode -f /dev/video4 -m /dev/video8 -d /dev/fb0 -c mpeg4 -i shrek.m4v
+
+would play the shrek.m4v video clip using /dev/video4 FIMC, /dev/video8 MFC
+and /dev/fb0 frame buffer to display the movie. The -c option specifies the
+mpeg4 codec.
+
+To determine which devices to use you can try the following commands.
+The number next to /dev/video may depend on your kernel configuration.
+
+For MFC:
+dmesg | grep -e s5p-mfc.*decoder.*/dev/video
+which outputs:
+[ 2.160683] s5p-mfc s5p-mfc: decoder registered as /dev/video8
+
+For FIMC:
+dmesg | grep -e fimc...m2m
+which outputs:
+[ 2.108768] s5p-fimc-md: Registered exynos4-fimc.0.m2m as /dev/video0
+[ 2.120782] s5p-fimc-md: Registered exynos4-fimc.1.m2m as /dev/video2
+[ 2.133962] s5p-fimc-md: Registered exynos4-fimc.2.m2m as /dev/video4
+[ 2.147145] s5p-fimc-md: Registered exynos4-fimc.3.m2m as /dev/video6
+
diff --git a/v4l2-mfc-example/args.c b/v4l2-mfc-example/args.c
new file mode 100644
index 0000000..119bf22
--- /dev/null
+++ b/v4l2-mfc-example/args.c
@@ -0,0 +1,117 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Argument parser
+ *
+ * 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 <unistd.h>
+#include <linux/videodev2.h>
+
+#include "common.h"
+#include "parser.h"
+
+
+void print_usage(char *name)
+{
+ // "d:f:i:m:c:V"
+ printf("Usage:\n");
+ printf("\t./%s\n", name);
+ printf("\t-c <codec> - The codec of the encoded stream\n");
+ printf("\t\t Available codecs: mpeg4, h264\n");
+ printf("\t-d <device> - Frame buffer device (e.g. /dev/fb0)\n");
+ printf("\t-f <device> - FIMC device (e.g. /dev/video4)\n");
+ printf("\t-i <file> - Input file name\n");
+ printf("\t-m <device> - MFC device (e.g. /dev/video8)\n");
+ printf("\t-V - synchronise to vsync\n");
+ //printf("\t- <device> - \n");
+ printf("\tp2\n");
+ printf("\n");
+}
+
+void init_to_defaults(struct instance *i)
+{
+ memset(i, 0, sizeof(*i));
+}
+
+int get_codec(char *str)
+{
+ if (strncasecmp("mpeg4", str, 5) == 0) {
+ return V4L2_PIX_FMT_MPEG4;
+ } else if (strncasecmp("h264", str, 5) == 0) {
+ return V4L2_PIX_FMT_H264;
+ }
+ return 0;
+}
+
+int parse_args(struct instance *i, int argc, char **argv)
+{
+ int c;
+
+ init_to_defaults(i);
+
+ while ((c = getopt(argc, argv, "c:d:f:i:m:V")) != -1) {
+ switch (c) {
+ case 'c':
+ i->parser.codec = get_codec(optarg);
+ break;
+ case 'd':
+ i->fb.name = optarg;
+ break;
+ case 'f':
+ i->fimc.name = optarg;
+ break;
+ case 'i':
+ i->in.name = optarg;
+ break;
+ case 'm':
+ i->mfc.name = optarg;
+ break;
+ case 'V':
+ i->fb.double_buf = 1;
+ break;
+ default:
+ err("Bad argument");
+ return -1;
+ }
+ }
+
+ if (!i->in.name || !i->fb.name || !i->fimc.name || !i->mfc.name) {
+ err("The following arguments are required: -d -f -i -m -c");
+ return -1;
+ }
+
+ if (!i->parser.codec) {
+ err("Unknown or not set codec (-c)");
+ return -1;
+ }
+
+ switch (i->parser.codec) {
+ case V4L2_PIX_FMT_MPEG4:
+ i->parser.func = parse_mpeg4_stream;
+ break;
+ case V4L2_PIX_FMT_H264:
+ i->parser.func = parse_h264_stream;
+ break;
+ }
+
+ return 0;
+}
+
diff --git a/v4l2-mfc-example/args.h b/v4l2-mfc-example/args.h
new file mode 100644
index 0000000..93468e5
--- /dev/null
+++ b/v4l2-mfc-example/args.h
@@ -0,0 +1,33 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@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 INCLUDE_ARGS_H
+#define INCLUDE_ARGS_H
+
+#include "common.h"
+
+/* Pritn usage information of the application */
+void print_usage(char *name);
+/* Parse the arguments that have been given to the application */
+int parse_args(struct instance *i, int argc, char **argv);
+
+#endif /* INCLUDE_FILEOPS_H */
+
diff --git a/v4l2-mfc-example/common.h b/v4l2-mfc-example/common.h
new file mode 100644
index 0000000..f07c19a
--- /dev/null
+++ b/v4l2-mfc-example/common.h
@@ -0,0 +1,178 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Common stuff 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 INCLUDE_COMMON_H
+#define INCLUDE_COMMON_H
+
+#include <stdio.h>
+#include <semaphore.h>
+
+#include "parser.h"
+#include "queue.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, "Error (%s:%s:%d): " 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
+#define dbg(msg, ...) \
+ fprintf(stdout, "(%s:%s:%d): " msg "\n", __FILE__, \
+ __func__, __LINE__, ##__VA_ARGS__)
+#else
+#define dbg(msg, ...) \
+ fprintf(stdout, msg "\n", ##__VA_ARGS__)
+#endif /* ADD_DETAILS */
+#else /* DEBUG */
+#define dbg(...) {}
+#endif /* DEBUG */
+
+#define memzero(x)\
+ memset(&(x), 0, sizeof (x));
+
+/* Maximum number of output buffers */
+#define MFC_MAX_OUT_BUF 16
+/* Maximum number of capture buffers (32 is the limit imposed by MFC */
+#define MFC_MAX_CAP_BUF 32
+/* Number of output planes */
+#define MFC_OUT_PLANES 1
+/* Number of capture planes */
+#define MFC_CAP_PLANES 2
+/* Maximum number of planes used in the application */
+#define MFC_MAX_PLANES MFC_CAP_PLANES
+/* Number of FIMC capture planes = number of frame buffer planes */
+#define FIMC_CAP_PLANES 1
+/* Maximum number of frame buffers - used for double buffering and
+ * vsyns synchronisation */
+#define FB_MAX_BUFS 2
+
+/* The buffer is free to use by MFC */
+#define BUF_FREE 0
+/* The buffer is currently queued in MFC */
+#define BUF_MFC 1
+/* The buffer has been processed by MFC and is now queued
+ * to be processed by FIMC. */
+#define BUF_FIMC 2
+
+struct instance {
+ /* Input file related parameters */
+ struct {
+ char *name;
+ int fd;
+ char *p;
+ int size;
+ int offs;
+ } in;
+
+ /* Frame buffer related parameters */
+ struct {
+ char *name;
+ int fd;
+ char *p[FB_MAX_BUFS];
+ int cur_buf;
+ int buffers;
+ int width;
+ int height;
+ int virt_width;
+ int virt_height;
+ int bpp;
+ int stride;
+ int size;
+ int full_size;
+ int double_buf;
+ } fb;
+
+ /* FIMC related parameter */
+ struct {
+ char *name;
+ int fd;
+ struct queue queue;
+ sem_t todo;
+ /* Semaphores are used to synchronise FIMC thread with
+ * the MFC thread */
+ sem_t done;
+ } fimc;
+
+ /* MFC related parameters */
+ struct {
+ char *name;
+ int fd;
+
+ /* Output queue related */
+ int out_buf_cnt;
+ int out_buf_size;
+ int out_buf_off[MFC_MAX_OUT_BUF];
+ char *out_buf_addr[MFC_MAX_OUT_BUF];
+ int out_buf_flag[MFC_MAX_OUT_BUF];
+
+ /* Capture queue related */
+ int cap_w;
+ int cap_h;
+ int cap_crop_w;
+ int cap_crop_h;
+ int cap_crop_left;
+ int cap_crop_top;
+ int cap_buf_cnt;
+ int cap_buf_cnt_min;
+ int cap_buf_size[MFC_CAP_PLANES];
+ int cap_buf_off[MFC_MAX_CAP_BUF][MFC_CAP_PLANES];
+ char *cap_buf_addr[MFC_MAX_CAP_BUF][MFC_CAP_PLANES];
+ int cap_buf_flag[MFC_MAX_CAP_BUF];
+ int cap_buf_queued;
+ } mfc;
+
+ /* Parser related parameters */
+ struct {
+ struct mfc_parser_context ctx;
+ unsigned long codec;
+ /* Callback function to the real parsing function.
+ * Dependent on the codec used. */
+ int (*func)( struct mfc_parser_context *ctx,
+ char* in, int in_size, char* out, int out_size,
+ int *consumed, int *frame_size, char get_head);
+ /* Set when the parser has finished and end of file has
+ * been reached */
+ int finished;
+ } parser;
+
+
+ /* Control */
+ int error; /* The error flag */
+ int finish; /* Flag set when decoding has been completed and all
+ threads finish */
+};
+
+#endif /* INCLUDE_COMMON_H */
+
diff --git a/v4l2-mfc-example/fb.c b/v4l2-mfc-example/fb.c
new file mode 100644
index 0000000..a2d0bcb
--- /dev/null
+++ b/v4l2-mfc-example/fb.c
@@ -0,0 +1,118 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Framebuffer operations
+ *
+ * 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 <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <linux/fb.h>
+
+#include "common.h"
+#include "fb.h"
+
+int fb_open(struct instance *i, char *name)
+{
+ struct fb_var_screeninfo fbinfo;
+ int ret;
+
+ i->fb.fd = open(name, O_RDWR);
+ if (i->fb.fd < 0) {
+ err("Failed to open frame buffer: %s", name);
+ return -1;
+ }
+
+ ret = ioctl(i->fb.fd, FBIOGET_VSCREENINFO, &fbinfo);
+ if (ret != 0) {
+ err("Failed to get frame buffer properties");
+ return -1;
+ }
+ dbg("Framebuffer properties: xres=%d, yres=%d, bpp=%d",
+ fbinfo.xres, fbinfo.yres, fbinfo.bits_per_pixel);
+ dbg("Virtual resolution: vxres=%d vyres=%d",
+ fbinfo.xres_virtual, fbinfo.yres_virtual);
+
+ i->fb.width = fbinfo.xres;
+ i->fb.height = fbinfo.yres;
+ i->fb.virt_width = fbinfo.xres_virtual;
+ i->fb.virt_height = fbinfo.yres_virtual;
+ i->fb.bpp = fbinfo.bits_per_pixel;
+ i->fb.stride = i->fb.virt_width * i->fb.bpp / 8;
+ i->fb.full_size = i->fb.stride * i->fb.virt_height;
+ i->fb.size = i->fb.stride * fbinfo.yres;
+
+ i->fb.p[0] = mmap(0, i->fb.full_size, PROT_WRITE | PROT_READ,
+ MAP_SHARED, i->fb.fd, 0);
+
+ i->fb.buffers = 1;
+
+ if (i->fb.double_buf) {
+ i->fb.p[1] = i->fb.p[0] + i->fb.size;
+ i->fb.buffers = 2;
+ }
+
+ return fb_set_virt_y_offset(i, 0);
+}
+
+int fb_set_virt_y_offset(struct instance *i, int yoffs)
+{
+ struct fb_var_screeninfo var;
+ int ret;
+
+ ret = ioctl(i->fb.fd, FBIOGET_VSCREENINFO, &var);
+ if (ret != 0) {
+ err("Failed to get frame buffer screen information");
+ return -1;
+ }
+
+ var.yoffset = yoffs;
+
+ ret = ioctl(i->fb.fd, FBIOPAN_DISPLAY, &var);
+ if (ret != 0) {
+ err("Failed to set y_offset of frame buffer");
+ return -1;
+ }
+
+ return 0;
+}
+
+int fb_wait_for_vsync(struct instance *i)
+{
+ int ret;
+ unsigned long temp;
+
+ ret = ioctl(i->fb.fd, FBIO_WAITFORVSYNC, &temp);
+ if (ret < 0) {
+ err("Wait for vsync failed");
+ return -1;
+ }
+ return 0;
+}
+
+void fb_close(struct instance *i)
+{
+ fb_set_virt_y_offset(i, 0);
+ munmap(i->fb.p[0], i->fb.full_size);
+ close(i->fb.fd);
+}
+
diff --git a/v4l2-mfc-example/fb.h b/v4l2-mfc-example/fb.h
new file mode 100644
index 0000000..fcd0756
--- /dev/null
+++ b/v4l2-mfc-example/fb.h
@@ -0,0 +1,37 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Framebuffer operations 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 INCLUDE_FB_H
+#define INCLUDE_FB_H
+
+/* Open and mmap frame buffer. Also read its properties */
+int fb_open(struct instance *i, char *name);
+/* Unmap and close the framebuffer */
+void fb_close(struct instance *i);
+/* Set virtual y offset of the frame buffer, this is used for vsync
+ * synchronisation */
+int fb_set_virt_y_offset(struct instance *i, int yoffs);
+/* Wait for vsync synchronisation */
+int fb_wait_for_vsync(struct instance *i);
+
+#endif /* INCLUDE_FB_H */
+
diff --git a/v4l2-mfc-example/fileops.c b/v4l2-mfc-example/fileops.c
new file mode 100644
index 0000000..031cfa5
--- /dev/null
+++ b/v4l2-mfc-example/fileops.c
@@ -0,0 +1,57 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * File operations
+ *
+ * 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 <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "fileops.h"
+
+int input_open(struct instance *i, char *name)
+{
+ struct stat in_stat;
+
+ i->in.fd = open(name, O_RDONLY);
+ if (!i->in.fd) {
+ err("Failed to open file: %s", i->in.name);
+ return -1;
+ }
+ fstat(i->in.fd, &in_stat);
+ i->in.size = in_stat.st_size;
+ i->in.offs = 0;
+ i->in.p = mmap(0, i->in.size, PROT_READ, MAP_SHARED, i->in.fd, 0);
+ if (i->in.p == MAP_FAILED) {
+ err("Failed to map input file");
+ return -1;
+ }
+ return 0;
+}
+
+void input_close(struct instance *i)
+{
+ munmap(i->in.p, i->in.size);
+ close(i->in.fd);
+}
+
diff --git a/v4l2-mfc-example/fileops.h b/v4l2-mfc-example/fileops.h
new file mode 100644
index 0000000..5e1bb7c
--- /dev/null
+++ b/v4l2-mfc-example/fileops.h
@@ -0,0 +1,34 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * File operations 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 INCLUDE_FILEOPS_H
+#define INCLUDE_FILEOPS_H
+
+#include "common.h"
+
+/* Open and mmap the input file */
+int input_open(struct instance *i, char *name);
+/* Unmap and close the input file */
+void input_close(struct instance *i);
+
+#endif /* INCLUDE_FILEOPS_H */
+
diff --git a/v4l2-mfc-example/fimc.c b/v4l2-mfc-example/fimc.c
new file mode 100644
index 0000000..9f257d6
--- /dev/null
+++ b/v4l2-mfc-example/fimc.c
@@ -0,0 +1,335 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * FIMC operations
+ *
+ * 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 <fcntl.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "fimc.h"
+
+static char *dbg_type[2] = {"OUTPUT", "CAPTURE"};
+static char *dbg_status[2] = {"ON", "OFF"};
+
+int fimc_open(struct instance *i, char *name)
+{
+ struct v4l2_capability cap;
+ int ret;
+
+ i->fimc.fd = open(name, O_RDWR, 0);
+ if (i->fimc.fd < 0) {
+ err("Failed to open FIMC: %s", name);
+ return -1;
+ }
+
+ memzero(cap);
+ ret = ioctl(i->fimc.fd, VIDIOC_QUERYCAP, &cap);
+ if (ret != 0) {
+ err("Failed to verify capabilities");
+ return -1;
+ }
+
+ dbg("FIMC Info (%s): driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x",
+ name, cap.driver, cap.bus_info, cap.card, i->fimc.fd);
+
+ if ( !(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) ||
+ !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) ||
+ !(cap.capabilities & V4L2_CAP_STREAMING)) {
+ err("Insufficient capabilities of FIMC device (is %s correct?)",
+ name);
+ return -1;
+ }
+
+ return 0;
+}
+
+void fimc_close(struct instance *i)
+{
+ close(i->fimc.fd);
+}
+
+int fimc_sfmt(struct instance *i, int width, int height,
+ enum v4l2_buf_type type, unsigned long pix_fmt, int num_planes,
+ struct v4l2_plane_pix_format planes[])
+{
+ struct v4l2_format fmt;
+ int ret;
+ int n;
+
+ memzero(fmt);
+ fmt.fmt.pix_mp.pixelformat = pix_fmt;
+ fmt.type = type;
+ fmt.fmt.pix_mp.width = width;
+ fmt.fmt.pix_mp.height = height;
+ fmt.fmt.pix_mp.num_planes = num_planes;
+
+ for (n = 0; n < num_planes; n++)
+ memcpy(&fmt.fmt.pix_mp.plane_fmt[n], &planes[n],
+ sizeof(*planes));
+
+ fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
+
+ ret = ioctl(i->fimc.fd, VIDIOC_S_FMT, &fmt);
+
+ if (ret != 0) {
+ err("Failed to SFMT on %s of FIMC",
+ dbg_type[type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE]);
+ return -1;
+ }
+
+ if (fmt.fmt.pix_mp.width != width ||
+ fmt.fmt.pix_mp.height != height ||
+ fmt.fmt.pix_mp.num_planes != num_planes ||
+ fmt.fmt.pix_mp.pixelformat != pix_fmt) {
+ err("Format was changed by FIMC so we abort operations");
+ return -1;
+ }
+
+
+ dbg("Successful SFMT on %s of FIMC (%dx%d)",
+ dbg_type[type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE],
+ width, height);
+
+ return 0;
+}
+
+int fimc_setup_output_from_mfc(struct instance *i)
+{
+ struct v4l2_plane_pix_format planes[MFC_CAP_PLANES];
+ struct v4l2_requestbuffers reqbuf;
+ int ret;
+ int n;
+
+ for (n = 0; n < MFC_CAP_PLANES; n++) {
+ planes[n].sizeimage = i->mfc.cap_buf_size[n];
+ planes[n].bytesperline = i->mfc.cap_w;
+ }
+
+ ret = fimc_sfmt(i, i->mfc.cap_w, i->mfc.cap_h,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, V4L2_PIX_FMT_NV12MT,
+ MFC_CAP_PLANES, planes);
+
+ if (ret)
+ return ret;
+
+ memzero(reqbuf);
+ reqbuf.count = i->mfc.cap_buf_cnt;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_USERPTR;
+
+ ret = ioctl(i->fimc.fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret) {
+ err("REQBUFS failed on OUTPUT of FIMC");
+ return -1;
+ }
+
+ dbg("Succesfully setup OUTPUT of FIMC");
+
+ return 0;
+}
+
+int fimc_setup_capture_from_fb(struct instance *i)
+{
+ struct v4l2_plane_pix_format planes[MFC_OUT_PLANES];
+ struct v4l2_requestbuffers reqbuf;
+ unsigned long fmt;
+ int ret;
+
+ planes[0].sizeimage = i->fb.stride * i->fb.height;
+ planes[0].bytesperline = i->fb.stride;
+
+ switch (i->fb.bpp) {
+ case 16:
+ fmt = V4L2_PIX_FMT_RGB565;
+ break;
+ case 32:
+ fmt = V4L2_PIX_FMT_RGB32;
+ break;
+ default:
+ err("Framebuffer format in not recognized. Bpp=%d", i->fb.bpp);
+ return -1;
+ }
+
+ ret = fimc_sfmt(i, i->fb.width, i->fb.height,
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE, fmt, 1, planes);
+
+ if (ret)
+ return -1;
+
+ memzero(reqbuf);
+ reqbuf.count = i->fb.buffers;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_USERPTR;
+
+ ret = ioctl(i->fimc.fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret) {
+ err("REQBUFS failed on CAPTURE of FIMC");
+ return -1;
+ }
+
+ dbg("Succesfully setup CAPTURE of FIMC");
+
+ return 0;
+}
+
+int fimc_stream(struct instance *i, enum v4l2_buf_type type, int status)
+{
+ int ret;
+
+ ret = ioctl(i->fimc.fd, status, &type);
+ if (ret) {
+ err("Failed to change streaming on FIMC (type=%s, status=%s)",
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE],
+ dbg_status[status==VIDIOC_STREAMOFF]);
+ return -1;
+ }
+
+ dbg("Stream %s on %s queue\n", dbg_status[status==VIDIOC_STREAMOFF],
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE]);
+
+ return 0;
+}
+
+int fimc_dec_queue_buf_out_from_mfc(struct instance *i, int n)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[MFC_CAP_PLANES];
+ int ret;
+
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+ buf.index = n;
+ buf.m.planes = planes;
+ buf.length = MFC_CAP_PLANES;
+
+ buf.m.planes[0].bytesused = i->mfc.cap_buf_size[0];
+ buf.m.planes[0].length = i->mfc.cap_buf_size[0];
+ buf.m.planes[0].m.userptr = (unsigned long)i->mfc.cap_buf_addr[n][0];
+
+ buf.m.planes[1].bytesused = i->mfc.cap_buf_size[1];
+ buf.m.planes[1].length = i->mfc.cap_buf_size[1];
+ buf.m.planes[1].m.userptr = (unsigned long)i->mfc.cap_buf_addr[n][1];
+
+ ret = ioctl(i->fimc.fd, VIDIOC_QBUF, &buf);
+
+ if (ret) {
+ err("Failed to queue buffer (index=%d) on CAPTURE", n);
+ return -1;
+ }
+
+ dbg("Queued buffer on CAPTURE queue with index %d", n);
+
+ return 0;
+}
+
+int fimc_dec_queue_buf_cap_from_fb(struct instance *i, int n)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[FIMC_CAP_PLANES];
+ int ret;
+
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buf.memory = V4L2_MEMORY_USERPTR;
+ buf.index = n;
+ buf.m.planes = planes;
+ buf.length = FIMC_CAP_PLANES;
+
+ buf.m.planes[0].bytesused = i->fb.size;
+ buf.m.planes[0].length = i->fb.size;
+ buf.m.planes[0].m.userptr = (unsigned long)i->fb.p[n];
+
+ ret = ioctl(i->fimc.fd, VIDIOC_QBUF, &buf);
+
+ if (ret) {
+ err("Failed to queue buffer (index=%d) on OUTPUT", n);
+ return -1;
+ }
+
+ dbg("Queued buffer on OUTPUT queue with index %d", n);
+
+ return 0;
+}
+
+int fimc_dec_dequeue_buf(struct instance *i, int *n, int nplanes, int type)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[MFC_MAX_PLANES];
+ int ret;
+
+ memzero(buf);
+ buf.type = type;
+ buf.memory = V4L2_MEMORY_USERPTR;
+ buf.m.planes = planes;
+ buf.length = nplanes;
+
+ ret = ioctl(i->fimc.fd, VIDIOC_DQBUF, &buf);
+
+ if (ret) {
+ err("Failed to dequeue buffer");
+ return -1;
+ }
+
+ *n = buf.index;
+
+ dbg("Dequeued buffer with index %d on %s queue", buf.index,
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE]);
+
+ return 0;
+}
+
+int fimc_dec_dequeue_buf_cap(struct instance *i, int *n)
+{
+ return fimc_dec_dequeue_buf(i, n, 1, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE);
+}
+
+int fimc_dec_dequeue_buf_out(struct instance *i, int *n)
+{
+ return fimc_dec_dequeue_buf(i, n, 2, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE);
+}
+
+int fimc_set_crop(struct instance *i, int type, int width, int height, int left,
+ int top)
+{
+ struct v4l2_crop crop;
+
+ memzero(crop);
+ crop.type = type;
+ crop.c.width = width;
+ crop.c.height = height;
+ crop.c.left = left;
+ crop.c.top = top;
+
+ if (ioctl(i->fimc.fd, VIDIOC_S_CROP, &crop)) {
+ err("Failed to set CROP on %s",
+ dbg_type[type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE]);
+ return -1;
+ }
+
+ return 0;
+}
+
diff --git a/v4l2-mfc-example/fimc.h b/v4l2-mfc-example/fimc.h
new file mode 100644
index 0000000..7a3f08b
--- /dev/null
+++ b/v4l2-mfc-example/fimc.h
@@ -0,0 +1,61 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * FIMC operations 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 INCLUDE_FIMC_H
+#define INCLUDE_FIMC_H
+
+#include "common.h"
+
+/* Open the FIMC device */
+int fimc_open(struct instance *i, char *name);
+/* Close the FIMC device */
+void fimc_close(struct instance *i);
+/* Set format in FIMC */
+int fimc_sfmt(struct instance *i, int width, int height,
+ enum v4l2_buf_type type, unsigned long pix_fmt, int num_planes,
+ struct v4l2_plane_pix_format planes[]);
+/* Setup OUTPUT queue of FIMC basing on the configuration of MFC */
+int fimc_setup_output_from_mfc(struct instance *i);
+/* Setup CAPTURE queue of FIMC basing on the configuration of the frame buffer */
+int fimc_setup_capture_from_fb(struct instance *i);
+/* Control streaming status */
+int fimc_stream(struct instance *i, enum v4l2_buf_type type, int status);
+/* Convenience function for queueing buffers from MFC */
+int fimc_dec_queue_buf_out_from_mfc(struct instance *i, int n);
+/* Convenience function for queueing buffers from frame buffer*/
+int fimc_dec_queue_buf_cap_from_fb(struct instance *i, int n);
+/* Dequeue buffer */
+int fimc_dec_dequeue_buf(struct instance *i, int *n, int nplanes, int type);
+/* Dequeue buffer from the CAPTURE queue. The argument *n is set to the index of
+ * the dequeued buffer */
+int fimc_dec_dequeue_buf_cap(struct instance *i, int *n);
+/* Dequeue buffer from the OUTPUT queue. The argument *n is set to the index of
+ * the dequeued buffer. */
+int fimc_dec_dequeue_buf_out(struct instance *i, int *n);
+/* Setup crop in FIMC */
+int fimc_set_crop(struct instance *i, int type, int width, int height,
+ int left, int top);
+
+
+
+#endif /* INCLUDE_FIMC_H */
+
diff --git a/v4l2-mfc-example/main.c b/v4l2-mfc-example/main.c
new file mode 100644
index 0000000..dba04ef
--- /dev/null
+++ b/v4l2-mfc-example/main.c
@@ -0,0 +1,492 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@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 <linux/videodev2.h>
+#include <pthread.h>
+#include <semaphore.h>
+
+#include "args.h"
+#include "common.h"
+#include "fb.h"
+#include "fimc.h"
+#include "fileops.h"
+#include "mfc.h"
+#include "parser.h"
+
+/* This is the size of the buffer for the compressed stream.
+ * It limits the maximum compressed frame size. */
+#define STREAM_BUUFER_SIZE (128 * 1024)
+/* The number of compress4ed stream buffers */
+#define STREAM_BUFFER_CNT 2
+
+/* The number of extra buffers for the decoded output.
+ * This is the number of buffers that the application can keep
+ * used and still enable MFC to decode with the hardware. */
+#define RESULT_EXTRA_BUFFER_CNT 2
+
+void cleanup(struct instance *i)
+{
+ if (i->mfc.fd)
+ mfc_close(i);
+ if (i->fimc.fd)
+ fimc_close(i);
+ if (i->fb.fd)
+ fb_close(i);
+ if (i->in.fd)
+ input_close(i);
+ queue_free(&i->fimc.queue);
+}
+
+int extract_and_process_header(struct instance *i)
+{
+ int used, fs;
+ int ret;
+
+ ret = i->parser.func(&i->parser.ctx, i->in.p + i->in.offs,
+ i->in.size - i->in.offs, i->mfc.out_buf_addr[0],
+ i->mfc.out_buf_size, &used, &fs, 1);
+
+ if (ret == 0) {
+ err("Failed to extract header from stream");
+ return -1;
+ }
+
+ i->in.offs += used;
+
+ dbg("Extracted header of size %d", fs);
+
+ ret = mfc_dec_queue_buf_out(i, 0, fs);
+
+ if (ret)
+ return -1;
+
+ ret = mfc_stream(i, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, VIDIOC_STREAMON);
+
+ if (ret)
+ return -1;
+
+ return 0;
+}
+
+int dequeue_output(struct instance *i, int *n)
+{
+ struct v4l2_buffer qbuf;
+ struct v4l2_plane planes[MFC_OUT_PLANES];
+
+ memzero(qbuf);
+ qbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ qbuf.memory = V4L2_MEMORY_MMAP;
+ qbuf.m.planes = planes;
+ qbuf.length = 1;
+
+ if (mfc_dec_dequeue_buf(i, &qbuf))
+ return -1;
+
+ *n = qbuf.index;
+
+ return 0;
+}
+
+int dequeue_capture(struct instance *i, int *n, int *finished)
+{
+ struct v4l2_buffer qbuf;
+ struct v4l2_plane planes[MFC_CAP_PLANES];
+
+ memzero(qbuf);
+ qbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ qbuf.memory = V4L2_MEMORY_MMAP;
+ qbuf.m.planes = planes;
+ qbuf.length = 2;
+
+ if (mfc_dec_dequeue_buf(i, &qbuf))
+ return -1;
+
+ *finished = qbuf.m.planes[0].bytesused == 0;
+
+ *n = qbuf.index;
+
+ return 0;
+}
+
+/* This threads is responsible for parsing the stream and
+ * feeding MFC with consecutive frames to decode */
+void *parser_thread_func(void *args)
+{
+ struct instance *i = (struct instance *)args;
+ int ret;
+ int used, fs, n;
+
+ while (!i->error && !i->finish) {
+ n = 0;
+ while (n < i->mfc.out_buf_cnt && i->mfc.out_buf_flag[n])
+ n++;
+
+ if (n < i->mfc.out_buf_cnt && !i->parser.finished) {
+ ret = i->parser.func(&i->parser.ctx,
+ i->in.p + i->in.offs, i->in.size - i->in.offs,
+ i->mfc.out_buf_addr[n], i->mfc.out_buf_size,
+ &used, &fs, 0);
+
+ if (ret == 0) {
+ dbg("Parser has extracted all frames");
+ i->parser.finished = 1;
+ fs = 0;
+ }
+
+ dbg("Extracted frame of size %d", fs);
+
+ ret = mfc_dec_queue_buf_out(i, n, fs);
+ i->mfc.out_buf_flag[n] = 1;
+
+ i->in.offs += used;
+
+ } else {
+ ret = dequeue_output(i, &n);
+ i->mfc.out_buf_flag[n] = 0;
+ if (ret && !i->parser.finished) {
+ err("Failed to dequeue a buffer in parser_thread");
+ i->error = 1;
+ }
+ }
+ }
+ dbg("Parser thread finished");
+ return 0;
+}
+
+/* This thread handles the CAPTURE side of MFC. it receives
+ * decoded frames and queues empty buffers back to MFC.
+ * Also it passes the decoded frames to FIMC, so they
+ * can be processed and displayed. */
+void *mfc_thread_func(void *args)
+{
+ struct instance *i = (struct instance *)args;
+ int finished;
+ int n;
+
+ while (!i->error && !i->finish) {
+ if (i->mfc.cap_buf_queued < i->mfc.cap_buf_cnt_min) {
+ /* sem_wait - wait until there is a buffer returned from
+ * fimc */
+ dbg("Before fimc.done");
+ sem_wait(&i->fimc.done);
+ dbg("After fimc.done");
+
+ n = 0;
+ while (n < i->mfc.cap_buf_cnt &&
+ i->mfc.cap_buf_flag[n] != BUF_FREE)
+ n++;
+
+ if (n < i->mfc.cap_buf_cnt) {
+ /* Can queue a buffer */
+ mfc_dec_queue_buf_cap(i, n);
+ i->mfc.cap_buf_flag[n] = 1;
+ i->mfc.cap_buf_queued++;
+ } else {
+ err("Something went seriously wrong. There should be a buffer");
+ i->error = 1;
+ continue;
+ }
+
+ continue;
+ }
+
+ if (i->mfc.cap_buf_queued < i->mfc.cap_buf_cnt) {
+ n = 0;
+ while (n < i->mfc.cap_buf_cnt &&
+ i->mfc.cap_buf_flag[n] != BUF_FREE)
+ n++;
+
+ if (n < i->mfc.cap_buf_cnt) {
+ /* sem_wait - we already found a buffer to queue
+ * so no waiting */
+ dbg("Before fimc.done");
+ sem_wait(&i->fimc.done);
+ dbg("After fimc.done");
+
+ /* Can queue a buffer */
+ mfc_dec_queue_buf_cap(i, n);
+ i->mfc.cap_buf_flag[n] = BUF_MFC;
+ i->mfc.cap_buf_queued++;
+ continue;
+ }
+ }
+
+ if (i->mfc.cap_buf_queued >= i->mfc.cap_buf_cnt_min ||
+ i->parser.finished
+ ) {
+ /* Can dequeue a processed buffer */
+ if (dequeue_capture(i, &n, &finished)) {
+ err("Error when dequeueing CAPTURE buffer");
+ i->error = 1;
+ break;
+ }
+
+ if (finished) {
+ dbg("Finished extracting last frames");
+ i->finish = 1;
+ break;
+ }
+
+ /* Pass to the FIMC */
+ i->mfc.cap_buf_flag[n] = BUF_FIMC;
+ i->mfc.cap_buf_queued--;
+ queue_add(&i->fimc.queue, n);
+
+ sem_post(&i->fimc.todo);
+
+ continue;
+ }
+ }
+
+ dbg("MFC thread finished");
+ return 0;
+}
+
+/* This thread handles FIMC processing and optionally frame buffer
+ * switching and synchronisation to the vsync of frame buffer. */
+void *fimc_thread_func(void *args)
+{
+ static int first_run = 1;
+ struct instance *i = (struct instance *)args;
+ int n, tmp;
+
+ while (!i->error && !i->finish) {
+ dbg("Before fimc.todo");
+ sem_wait(&i->fimc.todo);
+ dbg("After fimc.todo");
+
+ dbg("Processing by FIMC");
+
+ n = queue_remove(&i->fimc.queue);
+
+ if (i->mfc.cap_buf_flag[n] != BUF_FIMC) {
+ err("Buffer chosen to be processed by FIMC in wrong");
+ i->error = 1;
+ break;
+ }
+
+ if (n >= i->mfc.cap_buf_cnt) {
+ err("Strange. Could not find the buffer to process.");
+ i->error = 1;
+ break;
+ }
+
+ if (fimc_dec_queue_buf_out_from_mfc(i, n)) {
+ i->error = 1;
+ break;
+ }
+
+ i->fb.cur_buf = 0;
+
+ if (i->fb.double_buf) {
+ i->fb.cur_buf++;
+ i->fb.cur_buf %= i->fb.buffers;
+ }
+
+ if (fimc_dec_queue_buf_cap_from_fb(i, i->fb.cur_buf)) {
+ i->error = 1;
+ break;
+ }
+
+ if (first_run) {
+ /* Since our fabulous V4L2 framework enforces that at
+ * least one buffer is queued before switching streaming
+ * on then we need to add the following code. Otherwise
+ * it could be ommited and it all would be handled by
+ * the setup sequence in main.*/
+ first_run = 0;
+
+ if (fimc_stream(i, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ VIDIOC_STREAMON)) {
+ i->error = 1;
+ break;
+ }
+ if (fimc_stream(i, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ VIDIOC_STREAMON)) {
+ i->error = 1;
+ break;
+ }
+ }
+
+ if (fimc_dec_dequeue_buf_cap(i, &tmp)) {
+ i->error = 1;
+ break;
+ }
+ if (fimc_dec_dequeue_buf_out(i, &tmp)) {
+ i->error = 1;
+ break;
+ }
+
+ if (i->fb.double_buf) {
+ fb_set_virt_y_offset(i, i->fb.height);
+ fb_wait_for_vsync(i);
+ }
+
+ i->mfc.cap_buf_flag[n] = BUF_FREE;
+
+ sem_post(&i->fimc.done);
+ }
+
+ dbg("FIMC thread finished");
+ return 0;
+}
+
+int main(int argc, char **argv)
+{
+ struct instance inst;
+ pthread_t fimc_thread;
+ pthread_t mfc_thread;
+ pthread_t parser_thread;
+ int n;
+
+ printf("V4L2 Codec decoding example application\n");
+ printf("Kamil Debski <k.debski@samsung.com>\n");
+ printf("Copyright 2012 Samsung Electronics Co., Ltd.\n\n");
+
+ if (parse_args(&inst, argc, argv)) {
+ print_usage(argv[0]);
+ return 1;
+ }
+
+ if (queue_init(&inst.fimc.queue, MFC_MAX_CAP_BUF))
+ return 1;
+
+ if (input_open(&inst, inst.in.name)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (fb_open(&inst, inst.fb.name)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (fimc_open(&inst, inst.fimc.name)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (mfc_open(&inst, inst.mfc.name)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ dbg("Successfully opened all necessary files and devices");
+
+ if (mfc_dec_setup_output(&inst, inst.parser.codec,
+ STREAM_BUUFER_SIZE, STREAM_BUFFER_CNT)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ parse_stream_init(&inst.parser.ctx);
+
+ if (extract_and_process_header(&inst)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (mfc_dec_setup_capture(&inst, RESULT_EXTRA_BUFFER_CNT)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (dequeue_output(&inst, &n)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (fimc_setup_output_from_mfc(&inst)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (fimc_setup_capture_from_fb(&inst)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (fimc_set_crop(&inst, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ inst.mfc.cap_crop_w, inst.mfc.cap_crop_h,
+ inst.mfc.cap_crop_left, inst.mfc.cap_crop_top)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ dbg("I for one welcome our succesfully setup environment.");
+
+ /* Since our fabulous V4L2 framework enforces that at least one buffer
+ * is queued before switching streaming on then we need to add the
+ * following code. Otherwise it could be ommited and it all would be
+ * handled by the mfc_thread.*/
+
+ for (n = 0 ; n < inst.mfc.cap_buf_cnt; n++) {
+
+ if (mfc_dec_queue_buf_cap(&inst, n)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ inst.mfc.cap_buf_flag[n] = BUF_MFC;
+ inst.mfc.cap_buf_queued++;
+ }
+
+ if (mfc_stream(&inst, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ VIDIOC_STREAMON)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ sem_init(&inst.fimc.todo, 0, 0);
+ sem_init(&inst.fimc.done, 0, 0);
+
+ /* Now we're safe to run the threads */
+ dbg("Launching threads");
+
+ if (pthread_create(&parser_thread, NULL, parser_thread_func, &inst)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (pthread_create(&mfc_thread, NULL, mfc_thread_func, &inst)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+ if (pthread_create(&fimc_thread, NULL, fimc_thread_func, &inst)) {
+ cleanup(&inst);
+ return 1;
+ }
+
+
+ pthread_join(parser_thread, 0);
+ pthread_join(mfc_thread, 0);
+ pthread_join(fimc_thread, 0);
+
+ dbg("Threads have finished");
+
+ cleanup(&inst);
+ return 0;
+}
+
diff --git a/v4l2-mfc-example/mfc.c b/v4l2-mfc-example/mfc.c
new file mode 100644
index 0000000..b883f3f
--- /dev/null
+++ b/v4l2-mfc-example/mfc.c
@@ -0,0 +1,346 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * MFC operations
+ *
+ * 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 <fcntl.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "common.h"
+#include "mfc.h"
+
+static char *dbg_type[2] = {"OUTPUT", "CAPTURE"};
+static char *dbg_status[2] = {"ON", "OFF"};
+
+int mfc_open(struct instance *i, char *name)
+{
+ struct v4l2_capability cap;
+ int ret;
+
+ i->mfc.fd = open(name, O_RDWR, 0);
+ if (i->mfc.fd < 0) {
+ err("Failed to open MFC: %s", name);
+ return -1;
+ }
+
+ memzero(cap);
+ ret = ioctl(i->mfc.fd, VIDIOC_QUERYCAP, &cap);
+ if (ret != 0) {
+ err("Failed to verify capabilities");
+ return -1;
+ }
+
+ dbg("MFC Info (%s): driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x",
+ name, cap.driver, cap.bus_info, cap.card, i->mfc.fd);
+
+ if ( !(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) ||
+ !(cap.capabilities & V4L2_CAP_VIDEO_OUTPUT_MPLANE) ||
+ !(cap.capabilities & V4L2_CAP_STREAMING)) {
+ err("Insufficient capabilities of MFC device (is %s correct?)",
+ name);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+void mfc_close(struct instance *i)
+{
+ close(i->mfc.fd);
+}
+
+
+int mfc_dec_setup_output(struct instance *i, unsigned long codec,
+ unsigned int size, int count)
+{
+ struct v4l2_format fmt;
+ struct v4l2_requestbuffers reqbuf;
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[MFC_OUT_PLANES];
+ int ret;
+ int n;
+
+ memzero(fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ fmt.fmt.pix_mp.pixelformat = codec;
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage = size;
+ fmt.fmt.pix_mp.num_planes = MFC_OUT_PLANES;
+
+ ret = ioctl(i->mfc.fd, VIDIOC_S_FMT, &fmt);
+ if (ret != 0) {
+ err("Failed to setup OUTPUT for MFC decoding");
+ return -1;
+ }
+
+ dbg("Setup MFC decoding OUTPUT buffer size=%u (requested=%u)",
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage, size);
+
+ i->mfc.out_buf_size = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ memzero(reqbuf);
+ reqbuf.count = count;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ ret = ioctl(i->mfc.fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret != 0) {
+ err("REQBUFS failed on OUTPUT queue of MFC");
+ return -1;
+ }
+ i->mfc.out_buf_cnt = reqbuf.count;
+
+ dbg("Number of MFC OUTPUT buffers is %d (requested %d)",
+ i->mfc.out_buf_cnt, count);
+
+ for (n = 0; n < i->mfc.out_buf_cnt; n++) {
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = n;
+ buf.m.planes = planes;
+ buf.length = MFC_OUT_PLANES;
+
+ ret = ioctl(i->mfc.fd, VIDIOC_QUERYBUF, &buf);
+ if (ret != 0) {
+ err("QUERYBUF failed on OUTPUT buffer of MFC");
+ return -1;
+ }
+
+ i->mfc.out_buf_off[n] = buf.m.planes[0].m.mem_offset;
+
+ i->mfc.out_buf_addr[n] = mmap(NULL, buf.m.planes[0].length,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ i->mfc.fd, buf.m.planes[0].m.mem_offset);
+
+ if (i->mfc.out_buf_addr[n] == MAP_FAILED) {
+ err("Failed to MMAP MFC OUTPUT buffer");
+ return -1;
+ }
+
+ i->mfc.out_buf_flag[n] = 0;
+ }
+
+ dbg("Succesfully mmapped %d MFC OUTPUT buffers", n);
+
+ return 0;
+}
+
+int mfc_dec_queue_buf(struct instance *i, int n, int l1, int l2, int type,
+ int nplanes)
+{
+ struct v4l2_buffer qbuf;
+ struct v4l2_plane planes[MFC_MAX_PLANES];
+ int ret;
+
+ memzero(qbuf);
+ qbuf.type = type;
+ qbuf.memory = V4L2_MEMORY_MMAP;
+ qbuf.index = n;
+ qbuf.m.planes = planes;
+ qbuf.length = nplanes;
+ qbuf.m.planes[0].bytesused = l1;
+ qbuf.m.planes[1].bytesused = l2;
+
+ ret = ioctl(i->mfc.fd, VIDIOC_QBUF, &qbuf);
+
+ if (ret) {
+ err("Failed to queue buffer (index=%d) on %s", n,
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE]);
+ return -1;
+ }
+
+ dbg("Queued buffer on %s queue with index %d",
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE], n);
+
+ return 0;
+}
+
+int mfc_dec_queue_buf_out(struct instance *i, int n, int length)
+{
+ if (n >= i->mfc.out_buf_cnt) {
+ err("Tried to queue a non exisiting buffer");
+ return -1;
+ }
+
+ return mfc_dec_queue_buf(i, n, length, 0,
+ V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, MFC_OUT_PLANES);
+}
+
+int mfc_dec_queue_buf_cap(struct instance *i, int n)
+{
+ if (n >= i->mfc.cap_buf_cnt) {
+ err("Tried to queue a non exisiting buffer");
+ return -1;
+ }
+
+ return mfc_dec_queue_buf(i, n, i->mfc.cap_buf_size[0],
+ i->mfc.cap_buf_size[1], V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ MFC_CAP_PLANES);
+}
+
+int mfc_dec_dequeue_buf(struct instance *i, struct v4l2_buffer *qbuf)
+{
+ int ret;
+
+ ret = ioctl(i->mfc.fd, VIDIOC_DQBUF, qbuf);
+
+ if (ret) {
+ err("Failed to dequeue buffer");
+ return -1;
+ }
+
+ dbg("Dequeued buffer with index %d on %s queue", qbuf->index,
+ dbg_type[qbuf->type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE]);
+
+ return 0;
+}
+
+int mfc_stream(struct instance *i, enum v4l2_buf_type type, int status)
+{
+ int ret;
+
+ ret = ioctl(i->mfc.fd, status, &type);
+ if (ret) {
+ err("Failed to change streaming on MFC (type=%s, status=%s)",
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE],
+ dbg_status[status==VIDIOC_STREAMOFF]);
+ return -1;
+ }
+
+ dbg("Stream %s on %s queue", dbg_status[status==VIDIOC_STREAMOFF],
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE]);
+
+ return 0;
+}
+
+int mfc_dec_setup_capture(struct instance *i, int extra_buf)
+{
+ struct v4l2_format fmt;
+ struct v4l2_requestbuffers reqbuf;
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[MFC_CAP_PLANES];
+ struct v4l2_control ctrl;
+ struct v4l2_crop crop;
+ int ret;
+ int n;
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ ret = ioctl(i->mfc.fd, VIDIOC_G_FMT, &fmt);
+ if (ret) {
+ err("Failed to read format (after parsing header)");
+ return -1;
+ }
+ i->mfc.cap_w = fmt.fmt.pix_mp.width;
+ i->mfc.cap_h = fmt.fmt.pix_mp.height;
+
+ i->mfc.cap_buf_size[0] = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+ i->mfc.cap_buf_size[1] = fmt.fmt.pix_mp.plane_fmt[1].sizeimage;
+
+ ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
+ ret = ioctl(i->mfc.fd, VIDIOC_G_CTRL, &ctrl);
+ if (ret) {
+ err("Failed to get the number of buffers required by MFC");
+ return -1;
+ }
+
+ i->mfc.cap_buf_cnt = ctrl.value + extra_buf;
+ i->mfc.cap_buf_cnt_min = ctrl.value;
+ i->mfc.cap_buf_queued = 0;
+
+ dbg("MFC buffer parameters: %dx%d plane[0]=%d plane[1]=%d",
+ fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
+ i->mfc.cap_buf_size[0], i->mfc.cap_buf_size[1]);
+
+ memzero(crop);
+ crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ ret = ioctl(i->mfc.fd, VIDIOC_G_CROP, &crop);
+ if (ret) {
+ err("Failed to get crop information");
+ return -1;
+ }
+
+ i->mfc.cap_crop_w = crop.c.width;
+ i->mfc.cap_crop_h = crop.c.height;
+ i->mfc.cap_crop_left = crop.c.left;
+ i->mfc.cap_crop_top = crop.c.top;
+
+ dbg("Crop parameters w=%d h=%d l=%d t=%d", crop.c.width, crop.c.height,
+ crop.c.left, crop.c.top);
+
+ memzero(reqbuf);
+ reqbuf.count = i->mfc.cap_buf_cnt;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ ret = ioctl(i->mfc.fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret != 0) {
+ err("REQBUFS failed on CAPTURE queue of MFC");
+ return -1;
+ }
+
+ dbg("Number of MFC CAPTURE buffers is %d (requested %d, extra %d)",
+ reqbuf.count, i->mfc.cap_buf_cnt, extra_buf);
+
+ i->mfc.cap_buf_cnt = reqbuf.count;
+
+ for (n = 0; n < i->mfc.cap_buf_cnt; n++) {
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = n;
+ buf.m.planes = planes;
+ buf.length = MFC_CAP_PLANES;
+
+ ret = ioctl(i->mfc.fd, VIDIOC_QUERYBUF, &buf);
+ if (ret != 0) {
+ err("QUERYBUF failed on CAPTURE buffer of MFC");
+ return -1;
+ }
+
+ i->mfc.cap_buf_off[n][0] = buf.m.planes[0].m.mem_offset;
+ i->mfc.cap_buf_off[n][1] = buf.m.planes[1].m.mem_offset;
+
+ i->mfc.cap_buf_addr[n][0] = mmap(NULL, buf.m.planes[0].length,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ i->mfc.fd, buf.m.planes[0].m.mem_offset);
+ i->mfc.cap_buf_addr[n][1] = mmap(NULL, buf.m.planes[1].length,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ i->mfc.fd, buf.m.planes[1].m.mem_offset);
+
+ if (i->mfc.cap_buf_addr[n][0] == MAP_FAILED ||
+ i->mfc.cap_buf_addr[n][1] == MAP_FAILED) {
+ err("Failed to MMAP MFC CAPTURE buffer");
+ return -1;
+ }
+ }
+
+ dbg("Succesfully mmapped %d MFC CAPTURE buffers", n);
+
+ return 0;
+}
+
diff --git a/v4l2-mfc-example/mfc.h b/v4l2-mfc-example/mfc.h
new file mode 100644
index 0000000..a051f2a
--- /dev/null
+++ b/v4l2-mfc-example/mfc.h
@@ -0,0 +1,53 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * MFC operations 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 INCLUDE_MFC_H
+#define INCLUDE_MFC_H
+
+#include "common.h"
+
+/* Open the MFC device */
+int mfc_open(struct instance *i, char *name);
+/* Close the MFC devices */
+void mfc_close(struct instance *i);
+/* Setup the OUTPUT queue. The size determines the size for the stream
+ * buffer. This is the maximum size a single compressed frame can have.
+ * The count is the number of the stream buffers to allocate. */
+int mfc_dec_setup_output(struct instance *i, unsigned long codec,
+ unsigned int size, int count);
+/* Queue OUTPUT buffer */
+int mfc_dec_queue_buf_out(struct instance *i, int n, int length);
+/* Queue CAPTURE buffer */
+int mfc_dec_queue_buf_cap(struct instance *i, int n);
+/* Control MFC streaming */
+int mfc_stream(struct instance *i, enum v4l2_buf_type type, int status);
+/* Setup CAPTURE queue. The argument extra_buf means the number of extra
+ * buffers that should added to the minimum number of buffers required
+ * by MFC. The final number of buffers allocated is stored in the instance
+ * structure. */
+int mfc_dec_setup_capture(struct instance *i, int extra_buf);
+/* Dequeue a buffer, the structure *buf is used to return the parameters of the
+ * dequeued buffer. */
+int mfc_dec_dequeue_buf(struct instance *i, struct v4l2_buffer *buf);
+
+#endif /* INCLUDE_MFC_H */
+
diff --git a/v4l2-mfc-example/parser.c b/v4l2-mfc-example/parser.c
new file mode 100644
index 0000000..6691dd1
--- /dev/null
+++ b/v4l2-mfc-example/parser.c
@@ -0,0 +1,349 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Really simple stream parser 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.
+ *
+ */
+
+#include "common.h"
+#include "parser.h"
+#include <string.h>
+
+int parse_stream_init(struct mfc_parser_context *ctx)
+{
+ if (!ctx) {
+ err("ctx is NULL");
+ return -1;
+ }
+ memzero(*ctx);
+ return 0;
+}
+
+int parse_mpeg4_stream(
+ struct mfc_parser_context *ctx,
+ char* in, int in_size, char* out, int out_size,
+ int *consumed, int *frame_size, char get_head)
+{
+ char *in_orig;
+ char tmp;
+ char frame_finished;
+ int frame_length;
+
+ in_orig = in;
+
+ *consumed = 0;
+
+ frame_finished = 0;
+
+ while (in_size-- > 0) {
+ switch (ctx->state) {
+ case MPEG4_PARSER_NO_CODE:
+ if (*in == 0x0) {
+ ctx->state = MPEG4_PARSER_CODE_0x1;
+ ctx->tmp_code_start = *consumed;
+ }
+ break;
+ case MPEG4_PARSER_CODE_0x1:
+ if (*in == 0x0)
+ ctx->state = MPEG4_PARSER_CODE_0x2;
+ else
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ break;
+ case MPEG4_PARSER_CODE_0x2:
+ if (*in == 0x1) {
+ ctx->state = MPEG4_PARSER_CODE_1x1;
+ } else if ((*in & 0xFC) == 0x80) {
+ // Short header
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ ctx->last_tag = MPEG4_TAG_HEAD;
+ ctx->main_count++;
+ } else {
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ }
+ break;
+ case MPEG4_PARSER_CODE_1x1:
+ tmp = *in & 0xF0;
+ if (tmp == 0x00 || tmp == 0x01 || tmp == 0x20 ||
+ *in == 0xb0 || *in == 0xb2 || *in == 0xb3 ||
+ *in == 0xb5) {
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ ctx->last_tag = MPEG4_TAG_HEAD;
+ ctx->headers_count++;
+ } else if (*in == 0xb6) {
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ ctx->last_tag = MPEG4_TAG_VOP;
+ ctx->main_count++;
+ } else
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ break;
+ }
+
+ if (get_head == 1 && ctx->headers_count > 1 && ctx->main_count == 1) {
+ ctx->code_end = ctx->tmp_code_start;
+ ctx->got_end = 1;
+ break;
+ }
+
+ if (ctx->got_start == 0 && ctx->headers_count == 1 && ctx->main_count == 0) {
+ ctx->code_start = ctx->tmp_code_start;
+ ctx->got_start = 1;
+ }
+
+ if (ctx->got_start == 0 && ctx->headers_count == 0 && ctx->main_count == 1) {
+ ctx->code_start = ctx->tmp_code_start;
+ ctx->got_start = 1;
+ ctx->seek_end = 1;
+ ctx->headers_count = 0;
+ ctx->main_count = 0;
+ }
+
+ if (ctx->seek_end == 0 && ctx->headers_count > 0 && ctx->main_count == 1) {
+ ctx->seek_end = 1;
+ ctx->headers_count = 0;
+ ctx->main_count = 0;
+ }
+
+ if (ctx->seek_end == 1 && (ctx->headers_count > 0 || ctx->main_count > 0)) {
+ ctx->code_end = ctx->tmp_code_start;
+ ctx->got_end = 1;
+ if (ctx->headers_count == 0)
+ ctx->seek_end = 1;
+ else
+ ctx->seek_end = 0;
+ break;
+ }
+
+ in++;
+ (*consumed)++;
+ }
+
+
+ *frame_size = 0;
+
+ if (ctx->got_end == 1) {
+ frame_length = ctx->code_end;
+ } else
+ frame_length = *consumed;
+
+
+ if (ctx->code_start >= 0) {
+ frame_length -= ctx->code_start;
+ in = in_orig + ctx->code_start;
+ } else {
+ memcpy(out, ctx->bytes, -ctx->code_start);
+ *frame_size += -ctx->code_start;
+ out += -ctx->code_start;
+ in_size -= -ctx->code_start;
+ in = in_orig;
+ }
+
+ if (ctx->got_start) {
+ if (out_size < frame_length) {
+ err("Output buffer too small for current frame");
+ return 0;
+ }
+
+ memcpy(out, in, frame_length);
+ *frame_size += frame_length;
+
+ if (ctx->got_end) {
+ ctx->code_start = ctx->code_end - *consumed;
+ ctx->got_start = 1;
+ ctx->got_end = 0;
+ frame_finished = 1;
+ if (ctx->last_tag == MPEG4_TAG_VOP) {
+ ctx->seek_end = 1;
+ ctx->main_count = 0;
+ ctx->headers_count = 0;
+ } else {
+ ctx->seek_end = 0;
+ ctx->main_count = 0;
+ ctx->headers_count = 1;
+ }
+ memcpy(ctx->bytes, in_orig + ctx->code_end, *consumed - ctx->code_end);
+ } else {
+ ctx->code_start = 0;
+ frame_finished = 0;
+ }
+ }
+
+ ctx->tmp_code_start -= *consumed;
+
+ return frame_finished;
+}
+
+int parse_h264_stream(
+ struct mfc_parser_context *ctx,
+ char* in, int in_size, char* out, int out_size,
+ int *consumed, int *frame_size, char get_head)
+{
+ char *in_orig;
+ char tmp;
+ char frame_finished;
+ int frame_length;
+
+ in_orig = in;
+
+ *consumed = 0;
+
+ frame_finished = 0;
+
+ while (in_size-- > 0) {
+ switch (ctx->state) {
+ case H264_PARSER_NO_CODE:
+ if (*in == 0x0) {
+ ctx->state = H264_PARSER_CODE_0x1;
+ ctx->tmp_code_start = *consumed;
+ }
+ break;
+ case H264_PARSER_CODE_0x1:
+ if (*in == 0x0)
+ ctx->state = H264_PARSER_CODE_0x2;
+ else
+ ctx->state = H264_PARSER_NO_CODE;
+ break;
+ case H264_PARSER_CODE_0x2:
+ if (*in == 0x1) {
+ ctx->state = H264_PARSER_CODE_1x1;
+ } else if (*in == 0x0) {
+ ctx->state = H264_PARSER_CODE_0x3;
+ } else {
+ ctx->state = H264_PARSER_NO_CODE;
+ }
+ break;
+ case H264_PARSER_CODE_0x3:
+ if (*in == 0x1)
+ ctx->state = H264_PARSER_CODE_1x1;
+ else
+ ctx->state = H264_PARSER_NO_CODE;
+ break;
+ case H264_PARSER_CODE_1x1:
+ tmp = *in & 0x1F;
+
+ if (tmp == 1 || tmp == 5) {
+ ctx->state = H264_PARSER_CODE_SLICE;
+ } else if (tmp == 6 || tmp == 7 || tmp == 8) {
+ ctx->state = H264_PARSER_NO_CODE;
+ ctx->last_tag = H264_TAG_HEAD;
+ ctx->headers_count++;
+ }
+ else
+ ctx->state = H264_PARSER_NO_CODE;
+ break;
+ case H264_PARSER_CODE_SLICE:
+ if ((*in & 0x80) == 0x80) {
+ ctx->main_count++;
+ ctx->last_tag = H264_TAG_SLICE;
+ }
+ ctx->state = H264_PARSER_NO_CODE;
+ break;
+ }
+
+ if (get_head == 1 && ctx->headers_count > 1 && ctx->main_count == 1) {
+ ctx->code_end = ctx->tmp_code_start;
+ ctx->got_end = 1;
+ break;
+ }
+
+ if (ctx->got_start == 0 && ctx->headers_count == 1 && ctx->main_count == 0) {
+ ctx->code_start = ctx->tmp_code_start;
+ ctx->got_start = 1;
+ }
+
+ if (ctx->got_start == 0 && ctx->headers_count == 0 && ctx->main_count == 1) {
+ ctx->code_start = ctx->tmp_code_start;
+ ctx->got_start = 1;
+ ctx->seek_end = 1;
+ ctx->headers_count = 0;
+ ctx->main_count = 0;
+ }
+
+ if (ctx->seek_end == 0 && ctx->headers_count > 0 && ctx->main_count == 1) {
+ ctx->seek_end = 1;
+ ctx->headers_count = 0;
+ ctx->main_count = 0;
+ }
+
+ if (ctx->seek_end == 1 && (ctx->headers_count > 0 || ctx->main_count > 0)) {
+ ctx->code_end = ctx->tmp_code_start;
+ ctx->got_end = 1;
+ if (ctx->headers_count == 0)
+ ctx->seek_end = 1;
+ else
+ ctx->seek_end = 0;
+ break;
+ }
+
+ in++;
+ (*consumed)++;
+ }
+
+
+ *frame_size = 0;
+
+ if (ctx->got_end == 1) {
+ frame_length = ctx->code_end;
+ } else
+ frame_length = *consumed;
+
+
+ if (ctx->code_start >= 0) {
+ frame_length -= ctx->code_start;
+ in = in_orig + ctx->code_start;
+ } else {
+ memcpy(out, ctx->bytes, -ctx->code_start);
+ *frame_size += -ctx->code_start;
+ out += -ctx->code_start;
+ in_size -= -ctx->code_start;
+ in = in_orig;
+ }
+
+ if (ctx->got_start) {
+ if (out_size < frame_length) {
+ err("Output buffer too small for current frame");
+ return 0;
+ }
+ memcpy(out, in, frame_length);
+ *frame_size += frame_length;
+
+ if (ctx->got_end) {
+ ctx->code_start = ctx->code_end - *consumed;
+ ctx->got_start = 1;
+ ctx->got_end = 0;
+ frame_finished = 1;
+ if (ctx->last_tag == H264_TAG_SLICE) {
+ ctx->seek_end = 1;
+ ctx->main_count = 0;
+ ctx->headers_count = 0;
+ } else {
+ ctx->seek_end = 0;
+ ctx->main_count = 0;
+ ctx->headers_count = 1;
+ }
+ memcpy(ctx->bytes, in_orig + ctx->code_end, *consumed - ctx->code_end);
+ } else {
+ ctx->code_start = 0;
+ frame_finished = 0;
+ }
+ }
+
+ ctx->tmp_code_start -= *consumed;
+
+ return frame_finished;
+}
+
diff --git a/v4l2-mfc-example/parser.h b/v4l2-mfc-example/parser.h
new file mode 100644
index 0000000..3887a89
--- /dev/null
+++ b/v4l2-mfc-example/parser.h
@@ -0,0 +1,90 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Really simple stream 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 INCLUDE_PARSER_H
+#define INCLUDE_PARSER_H
+
+/* H264 parser states */
+enum mfc_h264_parser_state {
+ H264_PARSER_NO_CODE,
+ H264_PARSER_CODE_0x1,
+ H264_PARSER_CODE_0x2,
+ H264_PARSER_CODE_0x3,
+ H264_PARSER_CODE_1x1,
+ H264_PARSER_CODE_SLICE,
+};
+
+/* H264 recent tag type */
+enum mfc_h264_tag_type {
+ H264_TAG_HEAD,
+ H264_TAG_SLICE,
+};
+
+/* MPEG4 parser states */
+enum mfc_mpeg4_parser_state {
+ MPEG4_PARSER_NO_CODE,
+ MPEG4_PARSER_CODE_0x1,
+ MPEG4_PARSER_CODE_0x2,
+ MPEG4_PARSER_CODE_1x1,
+};
+
+/* MPEG4 recent tag type */
+enum mfc_mpeg4_tag_type {
+ MPEG4_TAG_HEAD,
+ MPEG4_TAG_VOP,
+};
+
+/* Parser context */
+struct mfc_parser_context {
+ int state;
+ int last_tag;
+ char bytes[6];
+ int main_count;
+ int headers_count;
+ int tmp_code_start;
+ int code_start;
+ int code_end;
+ char got_start;
+ char got_end;
+ char seek_end;
+};
+
+/* Initialize the stream parser */
+int parse_stream_init(struct mfc_parser_context *ctx);
+
+/* Parser the stream:
+ * - consumed is used to return the number of bytes consumed from the output
+ * - frame_size is used to return the size of the frame that has been extracted
+ * - get_head - when equal to 1 it is used to extract the stream header wehn
+ * setting up MFC
+ * Return value: 1 - if a complete frame has been extracted, 0 otherwise
+ */
+int parse_mpeg4_stream(struct mfc_parser_context *ctx,
+ char* in, int in_size, char* out, int out_size,
+ int *consumed, int *frame_size, char get_head);
+
+int parse_h264_stream(struct mfc_parser_context *ctx,
+ char* in, int in_size, char* out, int out_size,
+ int *consumed, int *frame_size, char get_head);
+
+#endif /* PARSER_H_ */
+
diff --git a/v4l2-mfc-example/queue.c b/v4l2-mfc-example/queue.c
new file mode 100644
index 0000000..62b7dfd
--- /dev/null
+++ b/v4l2-mfc-example/queue.c
@@ -0,0 +1,80 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Queue handling
+ *
+ * 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 "common.h"
+#include "queue.h"
+
+#include <pthread.h>
+#include <stdlib.h>
+
+int queue_init(struct queue *q, int size)
+{
+ q->q = (int*)malloc(size * sizeof(int));
+ if (!q->q) {
+ err("Failed to init queue (malloc failed)");
+ return -1;
+ }
+ q->size = size;
+ q->head = 0;
+ q->tail = 0;
+ q->n = 0;
+ pthread_mutex_init(&q->mutex, NULL);
+ return 0;
+}
+
+int queue_add(struct queue *q, int e)
+{
+ pthread_mutex_lock(&q->mutex);
+ if (q->n >= q->size) {
+ pthread_mutex_unlock(&q->mutex);
+ return -1;
+ }
+ q->q[q->head] = e;
+ q->head++;
+ q->head %= q->size;
+ q->n++;
+ pthread_mutex_unlock(&q->mutex);
+ return 0;
+}
+
+int queue_remove(struct queue *q)
+{
+ int x;
+ pthread_mutex_lock(&q->mutex);
+ if (q->n == 0) {
+ pthread_mutex_unlock(&q->mutex);
+ return -1;
+ }
+ x = q->q[q->tail];
+ q->tail++;
+ q->tail %= q->size;
+ q->n--;
+ pthread_mutex_unlock(&q->mutex);
+ return x;
+}
+
+void queue_free(struct queue *q)
+{
+ free(q->q);
+ pthread_mutex_destroy(&q->mutex);
+}
+
diff --git a/v4l2-mfc-example/queue.h b/v4l2-mfc-example/queue.h
new file mode 100644
index 0000000..963cf90
--- /dev/null
+++ b/v4l2-mfc-example/queue.h
@@ -0,0 +1,47 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Queue handling 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 INCLUDE_QUEUE_H
+#define INCLUDE_QUEUE_H
+
+#include <pthread.h>
+
+struct queue {
+ int size;
+ int head;
+ int tail;
+ int n;
+ int *q;
+ pthread_mutex_t mutex;
+};
+
+/* Initialize queue and allocate memory */
+int queue_init(struct queue *q, int size);
+/* Add an element to the queue */
+int queue_add(struct queue *q, int e);
+/* Remove the element form queue */
+int queue_remove(struct queue *q);
+/* Free the internal queue memory */
+void queue_free(struct queue *q);
+
+#endif /* INCLUDE_QUEUE_H */
+