summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmit Pundir <amit.pundir@linaro.org>2020-06-16 13:56:16 -0700
committerAmit Pundir <amit.pundir@linaro.org>2020-12-07 14:07:27 +0530
commit1b8f2f58134e5fedb3780cc925eff27496515314 (patch)
tree3c45061cece78215df6077c2f45bd30b30781ed6
parent4a86774d36f1a3c109a7e2da05aca9f9ca33b0df (diff)
db845c: Add v4l2_codec2 support and qcomlt's v4l2_decode test appwip/aosp
Based on https: //android-review.googlesource.com/c/1424772 Signed-off-by: Amit Pundir <amit.pundir@linaro.org> Change-Id: I72f0f19145daa322a4cc9024a41dc38ad04cd429
-rw-r--r--db845c/device.mk1
-rw-r--r--device-common.mk15
-rw-r--r--etc/media_codecs.xml15
-rw-r--r--etc/media_codecs_c2.xml16
-rw-r--r--manifest.xml13
-rw-r--r--seccomp_policy/codec2.vendor.ext.policy17
-rw-r--r--sepolicy/file_contexts1
-rw-r--r--stan/v4l2-decode/.gitignore3
-rw-r--r--stan/v4l2-decode/Android.bp18
-rw-r--r--stan/v4l2-decode/Makefile46
-rw-r--r--stan/v4l2-decode/README7
-rw-r--r--stan/v4l2-decode/args.c113
-rw-r--r--stan/v4l2-decode/args.h32
-rw-r--r--stan/v4l2-decode/common.h161
-rw-r--r--stan/v4l2-decode/ctrls.c31
-rw-r--r--stan/v4l2-decode/ctrls.h7
-rw-r--r--stan/v4l2-decode/fileops.c59
-rw-r--r--stan/v4l2-decode/fileops.h34
-rw-r--r--stan/v4l2-decode/main.c487
-rw-r--r--stan/v4l2-decode/parser.c895
-rw-r--r--stan/v4l2-decode/parser.h108
-rw-r--r--stan/v4l2-decode/test-streams/FVDO_Freeway_720p.264bin0 -> 4510750 bytes
-rw-r--r--stan/v4l2-decode/tracer.c85
-rw-r--r--stan/v4l2-decode/tracer.h17
-rw-r--r--stan/v4l2-decode/video.c892
-rw-r--r--stan/v4l2-decode/video.h71
-rw-r--r--ueventd.common.rc1
27 files changed, 3142 insertions, 3 deletions
diff --git a/db845c/device.mk b/db845c/device.mk
index 2240981..0271de2 100644
--- a/db845c/device.mk
+++ b/db845c/device.mk
@@ -44,6 +44,7 @@ PRODUCT_PACKAGES += \
qrtr-cfg \
qrtr-lookup \
rmtfs \
+ v4l2_decode \
tqftpserv
PRODUCT_COPY_FILES += \
diff --git a/device-common.mk b/device-common.mk
index 6fffd07..ec124cb 100644
--- a/device-common.mk
+++ b/device-common.mk
@@ -131,11 +131,20 @@ PRODUCT_COPY_FILES += \
frameworks/av/services/audiopolicy/config/default_volume_tables.xml:$(TARGET_COPY_OUT_VENDOR)/etc/default_volume_tables.xml \
frameworks/av/services/audiopolicy/config/audio_policy_volumes.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_policy_volumes.xml
+# V4L2-Codec2
+PRODUCT_PACKAGES += \
+ android.hardware.media.c2@1.0-service-v4l2:64 \
+ libv4l2_codec2_components \
+ libv4l2_codec2_store
+
+PRODUCT_COPY_FILES += \
+ $(LOCAL_PATH)/seccomp_policy/codec2.vendor.ext.policy:$(TARGET_COPY_OUT_VENDOR)/etc/seccomp_policy/codec2.vendor.ext.policy
+
# Copy media codecs config file
PRODUCT_COPY_FILES += \
$(LOCAL_PATH)/etc/media_codecs.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs.xml \
frameworks/av/media/libeffects/data/audio_effects.xml:$(TARGET_COPY_OUT_VENDOR)/etc/audio_effects.xml \
- frameworks/av/media/libstagefright/data/media_codecs_google_video.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_video.xml \
+ $(LOCAL_PATH)/etc/media_codecs_c2.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_c2.xml \
frameworks/av/media/libstagefright/data/media_codecs_google_audio.xml:$(TARGET_COPY_OUT_VENDOR)/etc/media_codecs_google_audio.xml
PRODUCT_COPY_FILES += \
@@ -182,4 +191,6 @@ PRODUCT_COPY_FILES += \
frameworks/native/data/etc/android.hardware.usb.host.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.hardware.usb.host.xml \
frameworks/native/data/etc/android.software.device_admin.xml:$(TARGET_COPY_OUT_VENDOR)/etc/permissions/android.software.device_admin.xml
-PRODUCT_SOONG_NAMESPACES += external/mesa3d
+PRODUCT_SOONG_NAMESPACES += \
+ external/mesa3d \
+ external/v4l2_codec2
diff --git a/etc/media_codecs.xml b/etc/media_codecs.xml
index 2b00568..424914d 100644
--- a/etc/media_codecs.xml
+++ b/etc/media_codecs.xml
@@ -77,5 +77,18 @@ Only the three quirks included above are recognized at this point:
<MediaCodecs>
<Include href="media_codecs_google_audio.xml" />
- <Include href="media_codecs_google_video.xml" />
+ <Decoders>
+ <MediaCodec name="OMX.google.mpeg4.decoder" type="video/mp4v-es" />
+ <MediaCodec name="OMX.google.h263.decoder" type="video/3gpp" />
+ <MediaCodec name="OMX.google.h264.decoder" type="video/avc" />
+ <MediaCodec name="OMX.google.vp8.decoder" type="video/x-vnd.on2.vp8" />
+ <MediaCodec name="OMX.google.vp9.decoder" type="video/x-vnd.on2.vp9" />
+ </Decoders>
+
+ <Encoders>
+ <MediaCodec name="OMX.google.h263.encoder" type="video/3gpp" />
+ <MediaCodec name="OMX.google.h264.encoder" type="video/avc" />
+ <MediaCodec name="OMX.google.mpeg4.encoder" type="video/mp4v-es" />
+ <MediaCodec name="OMX.google.vp8.encoder" type="video/x-vnd.on2.vp8" />
+ </Encoders>
</MediaCodecs>
diff --git a/etc/media_codecs_c2.xml b/etc/media_codecs_c2.xml
new file mode 100644
index 0000000..0060d5d
--- /dev/null
+++ b/etc/media_codecs_c2.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<MediaCodecs>
+ <Decoders>
+ <!-- C2 decoders -->
+ <MediaCodec name="c2.v4l2.avc.decoder" type="video/avc" >
+ <Limit name="size" min="16x16" max="4096x4096" />
+ <Limit name="alignment" value="2x2" />
+ <Limit name="block-size" value="16x16" />
+ <Limit name="blocks-per-second" min="1" max="2586720" />
+ <Limit name="bitrate" range="1-62500000" />
+ <Limit name="frame-rate" range="1-60" />
+ <Limit name="concurrent-instances" max="8" />
+ <Feature name="adaptive-playback" />
+ </MediaCodec>
+ </Decoders>
+</MediaCodecs>
diff --git a/manifest.xml b/manifest.xml
index 0c7ae03..dc574a1 100644
--- a/manifest.xml
+++ b/manifest.xml
@@ -112,6 +112,19 @@
</interface>
</hal>
<hal format="hidl">
+ <name>android.hardware.media.c2</name>
+ <transport>hwbinder</transport>
+ <version>1.0</version>
+ <interface>
+ <name>IComponentStore</name>
+ <instance>default</instance>
+ </interface>
+ <interface>
+ <name>IConfigurable</name>
+ <instance>default</instance>
+ </interface>
+ </hal>
+ <hal format="hidl">
<name>android.hardware.media.omx</name>
<transport>hwbinder</transport>
<version>1.0</version>
diff --git a/seccomp_policy/codec2.vendor.ext.policy b/seccomp_policy/codec2.vendor.ext.policy
new file mode 100644
index 0000000..7d61649
--- /dev/null
+++ b/seccomp_policy/codec2.vendor.ext.policy
@@ -0,0 +1,17 @@
+# device specific syscalls
+pselect6: 1
+eventfd2: 1
+_llseek: 1
+sysinfo: 1
+getcwd: 1
+getdents64: 1
+epoll_create1: 1
+epoll_ctl: 1
+epoll_pwait: 1
+mmap2: 1
+getuid32: 1
+geteuid32: 1
+fstat64: 1
+fstatat64: 1
+fstatfs64: 1
+ugetrlimit: 1
diff --git a/sepolicy/file_contexts b/sepolicy/file_contexts
index f70cc5f..f1e7a95 100644
--- a/sepolicy/file_contexts
+++ b/sepolicy/file_contexts
@@ -32,6 +32,7 @@
/system/bin/tinymix u:object_r:tinymix_exec:s0
/vendor/bin/hw/android\.hardware\.gatekeeper@1\.0-service\.software u:object_r:hal_gatekeeper_default_exec:s0
+/vendor/bin/hw/android\.hardware\.media\.c2@1\.0-service-v4l2(.*)? u:object_r:mediacodec_exec:s0
/vendor/bin/pd-mapper u:object_r:pd_mapper_exec:s0
/vendor/bin/qrtr-cfg u:object_r:qrtr_exec:s0
/vendor/bin/qrtr-ns u:object_r:qrtr_exec:s0
diff --git a/stan/v4l2-decode/.gitignore b/stan/v4l2-decode/.gitignore
new file mode 100644
index 0000000..7aa774e
--- /dev/null
+++ b/stan/v4l2-decode/.gitignore
@@ -0,0 +1,3 @@
+
+*.o
+v4l2_decode
diff --git a/stan/v4l2-decode/Android.bp b/stan/v4l2-decode/Android.bp
new file mode 100644
index 0000000..971d48a
--- /dev/null
+++ b/stan/v4l2-decode/Android.bp
@@ -0,0 +1,18 @@
+cc_binary {
+ name: "v4l2_decode",
+ vendor: true,
+ srcs: [
+ "main.c",
+ "args.c",
+ "parser.c",
+ "video.c",
+ "ctrls.c",
+ "tracer.c",
+ ],
+ cflags: [
+ "-pthread",
+ "-Wall",
+ "-g",
+ "-std=gnu99",
+ ],
+}
diff --git a/stan/v4l2-decode/Makefile b/stan/v4l2-decode/Makefile
new file mode 100644
index 0000000..aac70fa
--- /dev/null
+++ b/stan/v4l2-decode/Makefile
@@ -0,0 +1,46 @@
+# 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.
+#
+
+TCPATH = aarch64-linux-gnu-
+
+CC = ${TCPATH}gcc
+
+INCLUDES =
+
+SOURCES = main.c args.c parser.c video.c ctrls.c tracer.c
+OBJECTS := $(SOURCES:.c=.o)
+EXEC = v4l2_decode
+CFLAGS = -Wall -g -std=gnu99
+LIBS = -lpthread
+LIBPATH = -L/usr/lib/aarch64-linux-gnu
+LDFLAGS = -o $(EXEC) $(LIBPATH) $(LIBS)
+
+all: $(EXEC)
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(INCLUDES) $<
+
+$(EXEC): $(OBJECTS)
+ $(CC) -o $(EXEC) $(OBJECTS) $(LIBS)
+
+clean:
+ rm -f *.o $(EXEC)
+
+install:
+
+.PHONY: clean all
diff --git a/stan/v4l2-decode/README b/stan/v4l2-decode/README
new file mode 100644
index 0000000..5f87759
--- /dev/null
+++ b/stan/v4l2-decode/README
@@ -0,0 +1,7 @@
+#Example command line
+
+ -D option enables usage of drm/kms for displaying
+./v4l2_decode -w 1280 -h 720 -c h264 -i FVDO_Freeway_720p.264 -m /dev/video32 -D
+
+ -f used for saving decoded (NV12 format) frames on disk
+./v4l2_decode -w 1280 -h 720 -c h264 -i FVDO_Freeway_720p.264 -m /dev/video32 -f
diff --git a/stan/v4l2-decode/args.c b/stan/v4l2-decode/args.c
new file mode 100644
index 0000000..d78f710
--- /dev/null
+++ b/stan/v4l2-decode/args.c
@@ -0,0 +1,113 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Argument parser
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2015 Linaro 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 <stdlib.h>
+#include <linux/videodev2.h>
+
+#include "common.h"
+#include "parser.h"
+
+
+void print_usage(char *name)
+{
+ printf("Usage:\n");
+ printf("\t%s\n", name);
+ printf("\t-c <codec> - The codec of the encoded stream, "
+ "available codecs: mpeg4, h264\n");
+ printf("\t-d use dmabuf instead of mmap\n");
+ printf("\t-i <file> - Input file name\n");
+ printf("\t-m <device> - video decoder device (e.g. /dev/video0)\n");
+ printf("\t-w video width\n");
+ printf("\t-h video height\n");
+ printf("\t-f save frames on disk\n");
+
+ printf("\n");
+}
+
+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;
+ } else if (strncasecmp("h263", str, 5) == 0) {
+ return V4L2_PIX_FMT_H263;
+ } else if (strncasecmp("xvid", str, 5) == 0) {
+ return V4L2_PIX_FMT_XVID;
+ } else if (strncasecmp("mpeg2", str, 5) == 0) {
+ return V4L2_PIX_FMT_MPEG2;
+ } else if (strncasecmp("mpeg1", str, 5) == 0) {
+ return V4L2_PIX_FMT_MPEG1;
+ }
+ return 0;
+}
+
+int parse_args(struct instance *i, int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "w:h:c:di:mf:")) != -1) {
+ switch (c) {
+ case 'c':
+ i->codec = get_codec(optarg);
+ break;
+ case 'd':
+ i->video.cap_use_dmabuf = 1;
+ break;
+ case 'i':
+ i->file_name = optarg;
+ break;
+ case 'm':
+ i->video.name = optarg;
+ break;
+ case 'w':
+ i->width = atoi(optarg);
+ break;
+ case 'h':
+ i->height = atoi(optarg);
+ break;
+ case 'f':
+ i->save_frames = 1;
+ i->save_path = optarg;
+ break;
+ default:
+ err("Bad argument");
+ return -1;
+ }
+ }
+
+ if (!i->file_name) {
+ err("The following arguments are required: -i -m -c");
+ return -1;
+ }
+
+ if (!i->codec) {
+ err("Unknown or not set codec (-c)");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/stan/v4l2-decode/args.h b/stan/v4l2-decode/args.h
new file mode 100644
index 0000000..7801985
--- /dev/null
+++ b/stan/v4l2-decode/args.h
@@ -0,0 +1,32 @@
+/*
+ * 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/stan/v4l2-decode/common.h b/stan/v4l2-decode/common.h
new file mode 100644
index 0000000..cb2ee07
--- /dev/null
+++ b/stan/v4l2-decode/common.h
@@ -0,0 +1,161 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Common stuff header file
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2015 Linaro 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 <sys/time.h>
+#include <stdint.h>
+
+#include "parser.h"
+
+#ifndef V4L2_PIX_FMT_HEVC
+#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C') /* HEVC */
+#endif
+
+#ifndef V4L2_PIX_FMT_VP9
+#define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0') /* VP9 */
+#endif
+
+/* 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 */
+
+#define info(msg, ...) \
+ fprintf(stderr, "Info : " msg "\n", ##__VA_ARGS__)
+
+#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 MAX_OUT_BUF 16
+
+/* Maximum number of capture buffers (32 is the limit imposed by MFC */
+#define MAX_CAP_BUF 32
+
+/* Number of output planes */
+#define OUT_PLANES 1
+
+/* Number of capture planes */
+#define CAP_PLANES 1
+
+/* Input file related parameters */
+struct input {
+ const char *file_name;
+ int fd;
+ char *p;
+ int size;
+ int offs;
+};
+
+/* video decoder related parameters */
+struct video {
+ const char *name;
+ int fd;
+
+ /* Output queue related */
+ unsigned int out_buf_cnt;
+ unsigned int out_buf_size;
+ unsigned int out_buf_off[MAX_OUT_BUF];
+ char *out_buf_addr[MAX_OUT_BUF];
+ unsigned int out_buf_flag[MAX_OUT_BUF];
+
+ /* Capture queue related */
+ unsigned int cap_w;
+ unsigned int cap_h;
+ unsigned int cap_crop_w;
+ unsigned int cap_crop_h;
+ unsigned int cap_crop_left;
+ unsigned int cap_crop_top;
+ unsigned int cap_buf_cnt;
+ unsigned int cap_buf_cnt_min;
+ unsigned int cap_buf_size[CAP_PLANES];
+ unsigned int cap_buf_off[MAX_CAP_BUF][CAP_PLANES];
+ char *cap_buf_addr[MAX_CAP_BUF][CAP_PLANES];
+ unsigned int cap_buf_flag[MAX_CAP_BUF];
+ unsigned int cap_min_bufs;
+ int cap_use_dmabuf;
+
+ unsigned long total_captured;
+};
+
+struct buf_stats {
+ unsigned int qbuf_counter;
+ unsigned int dqbuf_counter;
+ struct timeval qbuf_start;
+ struct timeval qbuf_end;
+ struct timeval dqbuf_start;
+ struct timeval dqbuf_end;
+ struct timeval start;
+ struct timeval end;
+};
+
+struct instance {
+ int width;
+ int height;
+ int save_frames;
+ char *save_path;
+
+ unsigned int codec;
+ const char *file_name;
+ struct video video;
+ struct parser_context *parser_ctx;
+ pthread_mutex_t lock;
+ pthread_condattr_t attr;
+ pthread_cond_t cond;
+ int error; /* The error flag */
+ unsigned int finish; /* Flag set when decoding has been completed
+ and all threads finish */
+ /* buffer statistics */
+ struct buf_stats *stats;
+};
+
+#endif /* INCLUDE_COMMON_H */
diff --git a/stan/v4l2-decode/ctrls.c b/stan/v4l2-decode/ctrls.c
new file mode 100644
index 0000000..7f885ed
--- /dev/null
+++ b/stan/v4l2-decode/ctrls.c
@@ -0,0 +1,31 @@
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <linux/videodev2.h>
+
+#include "ctrls.h"
+
+#define DBG_TAG " ctrls"
+
+int ctrl_get_min_bufs_for_capture(struct instance *i)
+{
+ struct v4l2_control ctrl = {0};
+ int ret;
+
+ ctrl.id = V4L2_CID_MIN_BUFFERS_FOR_CAPTURE;
+
+ ret = ioctl(i->video.fd, VIDIOC_G_CTRL, &ctrl);
+ if (ret) {
+ err("get min capture buffers fail (%s)", strerror(errno));
+ return ret;
+ }
+
+ i->video.cap_min_bufs = ctrl.value;
+
+ info("minimum capture buffers count: %u", i->video.cap_min_bufs);
+
+ return 0;
+}
+
diff --git a/stan/v4l2-decode/ctrls.h b/stan/v4l2-decode/ctrls.h
new file mode 100644
index 0000000..181ed77
--- /dev/null
+++ b/stan/v4l2-decode/ctrls.h
@@ -0,0 +1,7 @@
+#ifndef __CTRLS__
+
+#include "common.h"
+
+int ctrl_get_min_bufs_for_capture(struct instance *i);
+
+#endif
diff --git a/stan/v4l2-decode/fileops.c b/stan/v4l2-decode/fileops.c
new file mode 100644
index 0000000..e2becb9
--- /dev/null
+++ b/stan/v4l2-decode/fileops.c
@@ -0,0 +1,59 @@
+/*
+ * 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 *inst, char *name)
+{
+ struct stat in_stat;
+
+ inst->in.fd = open(name, O_RDONLY);
+ if (!inst->in.fd) {
+ err("Failed to open file: %s", inst->in.name);
+ return -1;
+ }
+
+ fstat(inst->in.fd, &in_stat);
+
+ inst->in.size = in_stat.st_size;
+ inst->in.offs = 0;
+ inst->in.p = mmap(0, inst->in.size, PROT_READ, MAP_SHARED, inst->in.fd, 0);
+ if (inst->in.p == MAP_FAILED) {
+ err("Failed to map input file");
+ return -1;
+ }
+
+ return 0;
+}
+
+void input_close(struct instance *inst)
+{
+ munmap(inst->in.p, inst->in.size);
+ close(inst->in.fd);
+}
diff --git a/stan/v4l2-decode/fileops.h b/stan/v4l2-decode/fileops.h
new file mode 100644
index 0000000..5e1bb7c
--- /dev/null
+++ b/stan/v4l2-decode/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/stan/v4l2-decode/main.c b/stan/v4l2-decode/main.c
new file mode 100644
index 0000000..ddb5b8d
--- /dev/null
+++ b/stan/v4l2-decode/main.c
@@ -0,0 +1,487 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ * Main file of the application
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2015 Linaro 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 <linux/videodev2.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <poll.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "args.h"
+#include "common.h"
+#include "fileops.h"
+#include "video.h"
+#include "parser.h"
+#include "tracer.h"
+#include "ctrls.h"
+
+#define STREAM_BUFFER_SIZE (4 * 1024 * 1024)
+
+static int event_recv = 0;
+
+static const unsigned int event_types[] = {
+ V4L2_EVENT_EOS,
+ V4L2_EVENT_SOURCE_CHANGE,
+};
+
+int events_unsubscribe(struct video *vid)
+{
+ int size_event = sizeof(event_types) / sizeof(event_types[0]);
+ int i, ret;
+
+ for (i = 0; i < size_event; i++) {
+ ret = video_event_unsubscribe(vid, event_types[i]);
+ if (ret < 0) {
+ err("cannot unsubscribe event type %d (%s)",
+ event_types[i], strerror(errno));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int events_subscribe(struct video *vid)
+{
+ int n_events = sizeof(event_types) / sizeof(event_types[0]);
+ int i, ret;
+
+ for (i = 0; i < n_events; i++) {
+ ret = video_event_subscribe(vid, event_types[i]);
+ if (ret < 0) {
+ err("cannot subscribe event type %d (%s)",
+ event_types[i], strerror(errno));
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static int events_handler(struct instance *inst)
+{
+ struct video *vid = &inst->video;
+ struct v4l2_event event;
+ unsigned int w, h;
+ int ret;
+
+ memset(&event, 0, sizeof(event));
+
+ ret = video_event_dequeue(vid, &event);
+ if (ret < 0) {
+ err("cannot dequeue events (%s)", strerror(errno));
+ return -errno;
+ }
+
+ switch (event.type) {
+ case V4L2_EVENT_EOS:
+ info("EOS reached");
+ break;
+ case V4L2_EVENT_SOURCE_CHANGE:
+ info("Source changed");
+ ret = video_gfmt_cap(vid, &w, &h, NULL);
+ if (!ret)
+ info("new resolution is %ux%u", w, h);
+
+ ctrl_get_min_bufs_for_capture(inst);
+ event_recv = 1;
+ break;
+ default:
+ dbg("unknown event type occurred %x", event.type);
+ break;
+ }
+
+ return 0;
+}
+
+static int save_frame(struct instance *i, const void *buf, unsigned int size)
+{
+ mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+ char filename[64];
+ int fd;
+ int ret;
+ static unsigned int frame_num = 0;
+
+ if (!i->save_frames)
+ return 0;
+
+ ret = sprintf(filename, "%s/frame%04d.nv12", i->save_path, frame_num);
+ if (ret < 0) {
+ err("sprintf fail (%s)", strerror(errno));
+ return -1;
+ }
+
+ fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, mode);
+ if (fd < 0) {
+ err("cannot open file (%s)", strerror(errno));
+ return -1;
+ }
+
+ ret = write(fd, buf, size);
+ if (ret < 0) {
+ err("cannot write to file (%s)", strerror(errno));
+ return -1;
+ }
+
+ close(fd);
+
+ frame_num++;
+
+ return 0;
+}
+
+static void *parser_thread_func(void *args)
+{
+ struct instance *inst = args;
+ struct video *vid = &inst->video;
+ struct parser_context *parser = inst->parser_ctx;
+ unsigned int idx, fs;
+ int ret, last_parser_pkt = 0;
+
+ dbg("Parser thread started");
+
+ while (!inst->error && !inst->finish) {
+ idx = 0;
+ pthread_mutex_lock(&inst->lock);
+ while (idx < vid->out_buf_cnt && vid->out_buf_flag[idx])
+ idx++;
+ pthread_mutex_unlock(&inst->lock);
+
+ if (idx < vid->out_buf_cnt) {
+
+ if (last_parser_pkt) {
+ inst->finish = 1;
+// fs = 0;
+// ret = video_qbuf_out(vid, idx, fs);
+// pthread_mutex_lock(&inst->lock);
+// vid->out_buf_flag[idx] = 1;
+// pthread_mutex_unlock(&inst->lock);
+ continue;
+ }
+
+ ret = parser_read_frame(parser,
+ vid->out_buf_addr[idx],
+ vid->out_buf_size,
+ &fs,
+ 0);
+
+ if (!ret || (parser->input.offs == parser->input.size)) {
+ dbg("Last parser frame (fs: %u)", fs);
+ last_parser_pkt = 1;
+ }
+
+ dbg("Extracted frame of size %d", fs);
+
+ if (fs > vid->out_buf_size)
+ err("consumed %u, out buf sz %u",
+ fs, vid->out_buf_size);
+
+ ret = video_qbuf_out(vid, idx, fs);
+
+ pthread_mutex_lock(&inst->lock);
+ vid->out_buf_flag[idx] = 1;
+ pthread_mutex_unlock(&inst->lock);
+ } else {
+ pthread_mutex_lock(&inst->lock);
+ pthread_cond_wait(&inst->cond, &inst->lock);
+ pthread_mutex_unlock(&inst->lock);
+ }
+ }
+
+ dbg("Parser thread finished");
+
+ return NULL;
+}
+
+static int handle_capture(struct instance *inst)
+{
+ struct video *vid = &inst->video;
+ unsigned int bytesused, idx, finished, seq = 0;
+ int ret;
+
+ dbg("dequeuing capture buffer");
+
+ tracer_buf_start(inst, TYPE_DQBUF);
+
+ ret = video_dqbuf_cap(vid, &idx, &finished, &bytesused, &seq);
+
+ tracer_buf_finish(inst, TYPE_DQBUF);
+
+ if (ret < 0)
+ goto done;
+
+ vid->cap_buf_flag[idx] = 0;
+
+ fprintf(stdout, "%08ld\b\b\b\b\b\b\b\b", vid->total_captured);
+ fflush(stdout);
+
+ if (finished)
+ goto done;
+
+ vid->total_captured++;
+
+ save_frame(inst, vid->cap_buf_addr[idx][0], bytesused);
+
+ tracer_buf_start(inst, TYPE_QBUF);
+
+ ret = video_qbuf_cap(vid, idx);
+
+ tracer_buf_finish(inst, TYPE_QBUF);
+
+ if (ret < 0)
+ err("qbuf capture error %d", ret);
+
+done:
+ if (!ret)
+ vid->cap_buf_flag[idx] = 1;
+
+ dbg("seq: %u, captured: %lu\n", seq, vid->total_captured);
+
+ return ret;
+}
+
+static int handle_output(struct instance *inst)
+{
+ struct video *vid = &inst->video;
+ unsigned int idx;
+ int ret;
+
+ ret = video_dqbuf_out(vid, &idx);
+ if (ret < 0) {
+ err("dequeue output buffer fail");
+ } else {
+ pthread_mutex_lock(&inst->lock);
+ vid->out_buf_flag[idx] = 0;
+ pthread_mutex_unlock(&inst->lock);
+ pthread_cond_signal(&inst->cond);
+ }
+
+ return ret;
+}
+
+static void *main_thread_func(void *args)
+{
+ struct instance *inst = args;
+ struct video *vid = &inst->video;
+ struct pollfd pfd;
+ short revents;
+ int ret;
+
+ dbg("main thread started");
+
+ pfd.fd = vid->fd;
+ pfd.events = POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM |
+ POLLRDBAND | POLLPRI;
+
+ fprintf(stdout, "decoded frame ");
+
+ while (1) {
+ if (inst->finish)
+ break;
+
+ ret = poll(&pfd, 1, 2000);
+ if (!ret) {
+ inst->error = 1;
+ err("poll timeout");
+ break;
+ } else if (ret < 0) {
+ inst->error = 1;
+ err("poll error");
+ break;
+ }
+
+ revents = pfd.revents;
+
+ if (revents & POLLPRI)
+ events_handler(inst);
+
+ if (revents & (POLLIN | POLLRDNORM)) {
+ ret = handle_capture(inst);
+ if (ret)
+ break;
+ }
+
+ if (revents & (POLLOUT | POLLWRNORM)) {
+ ret = handle_output(inst);
+ if (ret)
+ break;
+ }
+ }
+
+ if (inst->error)
+ pthread_cond_signal(&inst->cond);
+
+ dbg("main thread finished");
+
+ return NULL;
+}
+
+static void cleanup(struct instance *inst)
+{
+ video_close(&inst->video);
+ parser_destroy(inst->parser_ctx);
+}
+
+static int drain_loop(struct instance *inst)
+{
+ int ret;
+
+ ret = video_dec_stop(&inst->video);
+ if (ret)
+ err("decoder stop command fail %d", ret);
+
+ while (1) {
+ ret = handle_capture(inst);
+ if (ret == -EPIPE || ret == -EIO)
+ break;
+ }
+
+ /* Drain loop should exits with EPIPE */
+ if (ret != -EPIPE)
+ err("Drain loop exits with error %d", ret);
+
+ return ret;
+}
+
+int main(int argc, char **argv)
+{
+ struct instance inst;
+ struct video *vid = &inst.video;
+ pthread_t parser_thread;
+ pthread_t main_thread;
+ int ret, n;
+
+ memset(&inst, 0, sizeof(inst));
+
+ ret = parse_args(&inst, argc, argv);
+ if (ret) {
+ print_usage(argv[0]);
+ return -1;
+ }
+
+ info("decoding resolution %dx%d", inst.width, inst.height);
+
+ pthread_mutex_init(&inst.lock, 0);
+ pthread_condattr_init(&inst.attr);
+ pthread_cond_init(&inst.cond, &inst.attr);
+
+ ret = tracer_init(&inst);
+ if (ret)
+ goto err;
+
+ ret = parser_create(&inst.parser_ctx, inst.file_name, inst.codec, 0, NULL);
+ if (ret)
+ goto err;
+
+ ret = video_open(vid, inst.video.name);
+ if (ret)
+ goto err;
+
+ ret = events_subscribe(vid);
+ if (ret)
+ goto err;
+
+ ret = video_set_framerate(vid, 30);
+ if (ret)
+ goto err;
+
+ ret = video_sfmt_out(vid, inst.width, inst.height, inst.codec);
+ if (ret)
+ goto err;
+
+ ret = video_setup_output(vid, STREAM_BUFFER_SIZE, 4);
+ if (ret)
+ goto err;
+
+ ret = video_set_control(vid);
+ if (ret)
+ goto err;
+
+ ret = video_streamon_out(vid);
+ if (ret)
+ goto err;
+
+ if (pthread_create(&parser_thread, NULL, parser_thread_func, &inst))
+ goto err;
+
+ if (pthread_create(&main_thread, NULL, main_thread_func, &inst))
+ goto err;
+
+ while (!event_recv)
+ usleep(1000);
+ event_recv = 0;
+
+ info("event received");
+
+ ret = video_setup_capture(vid, 4, inst.width, inst.height);
+ if (ret)
+ goto err;
+
+ ret = video_streamon_cap(vid);
+ if (ret)
+ goto err;
+
+ for (n = 0; n < vid->cap_buf_cnt; n++) {
+ if (inst.video.cap_use_dmabuf)
+ ret = video_qbuf_cap_dmabuf(vid, n, 0);
+ else
+ ret = video_qbuf_cap(vid, n);
+
+ if (ret)
+ goto err;
+
+ vid->cap_buf_flag[n] = 1;
+ }
+
+ pthread_join(parser_thread, 0);
+ pthread_join(main_thread, 0);
+
+ dbg("Threads have finished");
+
+ if (!inst.error && inst.finish)
+ ret = drain_loop(&inst);
+
+ video_stop(vid);
+ events_unsubscribe(vid);
+ tracer_show(&inst);
+ tracer_deinit(&inst);
+ pthread_mutex_destroy(&inst.lock);
+ pthread_cond_destroy(&inst.cond);
+ pthread_condattr_destroy(&inst.attr);
+
+ cleanup(&inst);
+
+ info("Total frames captured %ld", vid->total_captured);
+
+ return 0;
+err:
+ cleanup(&inst);
+ return 1;
+}
diff --git a/stan/v4l2-decode/parser.c b/stan/v4l2-decode/parser.c
new file mode 100644
index 0000000..ca7b6bf
--- /dev/null
+++ b/stan/v4l2-decode/parser.c
@@ -0,0 +1,895 @@
+/*
+ * 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 <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <linux/videodev2.h>
+
+#include "common.h"
+#include "parser.h"
+
+#ifndef V4L2_PIX_FMT_VP9
+#define V4L2_PIX_FMT_VP9 v4l2_fourcc('V', 'P', '9', '0')
+#endif
+
+#ifndef V4L2_PIX_FMT_HEVC
+#define V4L2_PIX_FMT_HEVC v4l2_fourcc('H', 'E', 'V', 'C')
+#endif
+
+#define DBG_TAG "parser"
+
+static void *tmp_buffer = NULL;
+
+int parser_mpeg2(struct parser_context *ctx,
+ char *in, int in_size,
+ char *out, int out_size,
+ int *consumed, int *frame_size, int get_head)
+{
+ char *in_orig;
+ 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 == 0x0) {
+ /* We still have two zeroes */
+ ctx->tmp_code_start++;
+ // TODO XXX check in h264 and mpeg4
+ } else {
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ }
+ break;
+ case MPEG4_PARSER_CODE_1x1:
+ if (*in == 0xb3 || *in == 0xb8) {
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ ctx->last_tag = MPEG4_TAG_HEAD;
+ ctx->headers_count++;
+ dbg("Found header at %d (%x)", *consumed, *consumed);
+ } else if (*in == 0x00) {
+ ctx->state = MPEG4_PARSER_NO_CODE;
+ ctx->last_tag = MPEG4_TAG_VOP;
+ ctx->main_count++;
+ dbg("Found picture at %d (%x)", *consumed, *consumed);
+ } 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 (%d < %d)",
+ out_size, frame_length);
+ 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 parser_mpeg4(struct parser_context *ctx,
+ char *in, int in_size,
+ char *out, int out_size,
+ int *consumed, int *frame_size, int 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;
+ /* Ignore the short header if the current hasn't
+ * been started with a short header. */
+
+ if (get_head && !ctx->short_header) {
+ ctx->last_tag = MPEG4_TAG_HEAD;
+ ctx->headers_count++;
+ ctx->short_header = 1;
+ } else if (!ctx->seek_end ||
+ (ctx->seek_end && ctx->short_header)) {
+ ctx->last_tag = MPEG4_TAG_VOP;
+ ctx->main_count++;
+ ctx->short_header = 1;
+ }
+ } else if (*in == 0x0) {
+ ctx->tmp_code_start++;
+ } 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;
+ ctx->short_header = 0;
+ /* If the last frame used the short then
+ * we shall save this information, otherwise
+ * it is necessary to clear it */
+ }
+ 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 parser_h264(struct parser_context *ctx,
+ char *in, int in_size,
+ char *out, int out_size,
+ int *consumed, int *frame_size, int 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 if (*in == 0x0)
+ ctx->tmp_code_start++;
+ 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;
+}
+
+/* Evaluates to a mask with n bits set */
+#define BITS_MASK(n) ((1<<(n))-1)
+
+/* Returns len bits, with the LSB at position bit */
+#define BITS_GET(val, bit, len) (((val)>>(bit))&BITS_MASK(len))
+
+#define CHECK_FOR_UPDATE(lval,rval,update_flag) do {\
+ unsigned int old = lval; \
+ update_flag |= (old != (lval = rval)); \
+ } while(0)
+
+#define FRAME_HEADER_SZ 3
+#define KEYFRAME_HEADER_SZ 7
+
+struct vp8_frame_hdr {
+ unsigned int is_keyframe; /* Frame is a keyframe */
+ unsigned int is_experimental; /* Frame is a keyframe */
+ unsigned int version; /* Bitstream version */
+ unsigned int is_shown; /* Frame is to be displayed. */
+ unsigned int part0_sz; /* Partition 0 length, in bytes */
+
+ struct vp8_kf_hdr
+ {
+ unsigned int w; /* Width */
+ unsigned int h; /* Height */
+ unsigned int scale_w; /* Scaling factor, Width */
+ unsigned int scale_h; /* Scaling factor, Height */
+ } kf;
+
+ unsigned int frame_size_updated; /* Flag to indicate a resolution
+ * update.
+ */
+};
+
+#define IVF_FILE_HEADER_SIZE 32
+#define IVF_FRAME_HEADER_SIZE 12
+
+#define fourcc(a, b, c, d) \
+ ((uint32_t)(a) | ((uint32_t)(b) << 8) | ((uint32_t)(c) << 16) | ((uint32_t)(d) << 24))
+
+/*
+An IVF file begins with a 32-byte header:
+ bytes 0-3 signature: 'DKIF'
+ bytes 4-5 version (should be 0)
+ bytes 6-7 length of header in bytes
+ bytes 8-11 codec FourCC (e.g., 'VP80')
+ bytes 12-13 width in pixels
+ bytes 14-15 height in pixels
+ bytes 16-19 frame rate
+ bytes 20-23 time scale
+ bytes 24-27 number of frames in file
+ bytes 28-31 unused
+
+Each frame consists of a 12-byte header followed by data:
+ bytes 0-3 size of frame in bytes (not including the 12-byte header)
+ bytes 4-11 64-bit presentation timestamp
+ bytes 12.. frame data
+
+*/
+static int parse_vpx_ivf_header(struct parser_context *ctx, const char *in)
+{
+ uint32_t ivf_magic, codec, frate, ts, num_frames, unused;
+ uint16_t version, hdr_len, w, h;
+ int off = 0;
+
+ if (ctx->is_32Bhdr_found)
+ return 0;
+
+ memcpy(&ivf_magic, in + off, 4);
+ off += 4;
+ memcpy(&version, in + off, 2);
+ off += 2;
+ memcpy(&hdr_len, in + off, 2);
+ off += 2;
+ memcpy(&codec, in + off, 4);
+ off += 4;
+ memcpy(&w, in + off, 2);
+ off += 2;
+ memcpy(&h, in + off, 2);
+ off += 2;
+ memcpy(&frate, in + off, 4);
+ off += 4;
+ memcpy(&ts, in + off, 4);
+ off += 4;
+ memcpy(&num_frames, in + off, 4);
+ off += 4;
+ memcpy(&unused, in + off, 4);
+ off += 4;
+
+ if (ivf_magic == fourcc('D', 'K', 'I', 'F')) {
+ dbg("it's IVF file!");
+ } else {
+ return -1;
+ }
+
+ ctx->is_32Bhdr_found = 1;
+
+ dbg("version:%u, hdr_len:%u, %ux%u, frate:%u, ts:%u, num_frames:%u, off:%u",
+ version, hdr_len, w, h, frate, ts, num_frames, off);
+
+ return off;
+}
+
+static int parse_vpx_ivf_framesize(const char *in, uint32_t *fsize)
+{
+ uint32_t framesize;
+ uint64_t pts;
+ int off = 0;
+
+ memcpy(&framesize, in + off, 4);
+ off += 4;
+ memcpy(&pts, in + off, 8);
+ off += 8;
+
+ if (fsize)
+ *fsize = framesize;
+
+ dbg("framesize:%u, pts:%lu", framesize, pts);
+
+ return off;
+}
+
+int parser_vp8(struct parser_context *ctx,
+ char *in, int in_size,
+ char *out, int out_size,
+ int *consumed, int *frame_size, int get_head)
+{
+ unsigned long raw;
+ char *data = in, *frame_start = NULL;
+ struct vp8_frame_hdr hdr = {0};
+ int frame_finished = 0, ret;
+ uint32_t framesz;
+ uint32_t pos = 0;
+ int is_32B_hdr = 0;
+
+ if (in_size < 10) {
+ err("ivalid size %d", in_size);
+ return -1;
+ }
+
+ *consumed = 0;
+
+ ret = parse_vpx_ivf_header(ctx, data);
+ if (ret == -1)
+ return -1;
+
+ if (ret > 0)
+ is_32B_hdr = 1;
+
+ data += ret;
+ pos += ret;
+
+ ret = parse_vpx_ivf_framesize(data, &framesz);
+ if (ret == -1)
+ return -1;
+
+ data += ret;
+ frame_start = data;
+ pos += ret;
+ pos += framesz;
+
+ raw = data[0] | (data[1] << 8) | (data[2] << 16);
+
+ hdr.is_keyframe = !BITS_GET(raw, 0, 1);
+ hdr.version = BITS_GET(raw, 1, 2);
+ hdr.is_experimental = BITS_GET(raw, 3, 1);
+ hdr.is_shown = BITS_GET(raw, 4, 1);
+ hdr.part0_sz = BITS_GET(raw, 5, 19);
+
+ if (in_size <= hdr.part0_sz + (hdr.is_keyframe ? 10 : 3)) {
+ err("%d < %d", in_size,
+ hdr.part0_sz + (hdr.is_keyframe ? 10 : 3));
+ return -1;
+ }
+
+ hdr.frame_size_updated = 0;
+
+ if (hdr.is_keyframe) {
+ unsigned int update = 0;
+
+ /* Keyframe header consists of a three byte sync code followed
+ * by the width and height and associated scaling factors.
+ */
+ if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a) {
+ err("keyframe header is missing");
+ return -1;
+ }
+
+ raw = data[6] | (data[7] << 8) | (data[8] << 16) |
+ (data[9] << 24);
+ CHECK_FOR_UPDATE(hdr.kf.w, BITS_GET(raw, 0, 14), update);
+ CHECK_FOR_UPDATE(hdr.kf.scale_w, BITS_GET(raw, 14, 2), update);
+ CHECK_FOR_UPDATE(hdr.kf.h, BITS_GET(raw, 16, 14), update);
+ CHECK_FOR_UPDATE(hdr.kf.scale_h, BITS_GET(raw, 30, 2), update);
+
+ hdr.frame_size_updated = update;
+
+ if (!hdr.kf.w || !hdr.kf.h) {
+ err("keyframe width or height (%ux%u)",
+ hdr.kf.w, hdr.kf.h);
+ return -1;
+ }
+ }
+
+#if 0
+ data += FRAME_HEADER_SZ;
+ in_size -= FRAME_HEADER_SZ;
+ *consumed = FRAME_HEADER_SZ;
+
+ if (hdr.is_keyframe) {
+ data += KEYFRAME_HEADER_SZ;
+ in_size -= KEYFRAME_HEADER_SZ;
+ *consumed += KEYFRAME_HEADER_SZ;
+ }
+
+ *frame_size = hdr.is_keyframe ? 10 : 3;
+ *frame_size += hdr.part0_sz;
+ *consumed += hdr.part0_sz;
+#endif
+ frame_finished = 1;
+
+ memcpy(out, frame_start, framesz);
+
+ dbg("is_keyframe:%u, part0_sz:%u, WxH:%ux%u, consumed:%d, frame_size:%d",
+ hdr.is_keyframe, hdr.part0_sz, hdr.kf.w, hdr.kf.h,
+ *consumed, *frame_size);
+
+ *frame_size = framesz;
+ *consumed = framesz;
+ *consumed += 12;
+ *consumed += is_32B_hdr ? 32 : 0;
+
+ dbg("framesz: %u, consumed: %u, pos: %u (in:%p)", *frame_size,
+ *consumed, pos, in);
+
+ return frame_finished;
+}
+
+void parser_destroy(struct parser_context *ctx)
+{
+ struct input_file *input = &ctx->input;
+
+ if (!ctx)
+ return;
+
+ if (input->fd > 0) {
+ munmap(input->data, input->size);
+ close(input->fd);
+ }
+
+ free(ctx);
+
+ free(tmp_buffer);
+ tmp_buffer = NULL;
+
+ dbg("parser_destroy");
+}
+
+int parser_create(struct parser_context **ctx, const char *url,
+ unsigned int codec,
+ unsigned int len, char *data)
+{
+ struct parser_context *context;
+ struct input_file *input;
+ struct stat in_stat;
+
+ *ctx = NULL;
+
+ if (!url && !data)
+ return -1;
+
+ context = malloc(sizeof(*context));
+ if (!context)
+ return -1;
+
+ tmp_buffer = malloc(1024*1024);
+ if (!tmp_buffer) {
+ free(context);
+ return -1;
+ }
+
+ memset(context, 0, sizeof(*context));
+
+ switch (codec) {
+ case V4L2_PIX_FMT_MPEG1:
+ case V4L2_PIX_FMT_MPEG2:
+ context->func = parser_mpeg2;
+ break;
+ case V4L2_PIX_FMT_XVID:
+ case V4L2_PIX_FMT_H263:
+ case V4L2_PIX_FMT_MPEG4:
+ context->func = parser_mpeg4;
+ break;
+ case V4L2_PIX_FMT_H264:
+ context->func = parser_h264;
+ break;
+ case V4L2_PIX_FMT_VP8:
+ context->func = parser_vp8;
+ break;
+ case V4L2_PIX_FMT_VP9:
+ goto error;
+ case V4L2_PIX_FMT_HEVC:
+ goto error;
+ default:
+ goto error;
+ }
+
+ input = &context->input;
+ input->offs = 0;
+
+ if (url) {
+ input->fd = open(url, O_RDONLY);
+ if (input->fd <= 0) {
+ err("failed to open file: %s", url);
+ goto error;
+ }
+
+ fstat(input->fd, &in_stat);
+
+ input->size = in_stat.st_size;
+ input->data = mmap(0, input->size, PROT_READ, MAP_SHARED,
+ input->fd, 0);
+ if (input->data == MAP_FAILED) {
+ err("failed to map input file");
+ goto error;
+ }
+ } else if (data){
+ input->size = len;
+ input->data = data;
+ }
+
+ *ctx = context;
+
+ dbg("ctx:%p, input: size:%u, data:%p", ctx, input->size, input->data);
+
+ return 0;
+error:
+ free(context);
+ return -1;
+}
+
+int parser_read_frame(struct parser_context *ctx, char *data, unsigned int size,
+ unsigned int *framesize, int stream_header)
+{
+ struct input_file *input = &ctx->input;
+ int consumed, fs;
+ int ret;
+
+ if (!data)
+ data = tmp_buffer;
+
+ ret = ctx->func(ctx,
+ input->data + input->offs,
+ input->size - input->offs,
+ data, size,
+ &consumed, &fs, stream_header);
+
+ dbg("consumed: %d, framesize: %d, frame: %d, stream_header: %d, ret: %d",
+ consumed, fs, ret, stream_header, ret);
+
+ input->offs += consumed;
+
+ if (framesize)
+ *framesize = fs;
+
+ return ret;
+}
diff --git a/stan/v4l2-decode/parser.h b/stan/v4l2-decode/parser.h
new file mode 100644
index 0000000..17743b6
--- /dev/null
+++ b/stan/v4l2-decode/parser.h
@@ -0,0 +1,108 @@
+/*
+ * 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 _PARSER_H_
+#define _PARSER_H_
+
+/* H264 parser states */
+enum 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 h264_tag_type {
+ H264_TAG_HEAD,
+ H264_TAG_SLICE,
+};
+
+/* MPEG4 parser states */
+enum mpeg4_parser_state {
+ MPEG4_PARSER_NO_CODE,
+ MPEG4_PARSER_CODE_0x1,
+ MPEG4_PARSER_CODE_0x2,
+ MPEG4_PARSER_CODE_1x1,
+};
+
+/* MPEG4 recent tag type */
+enum mpeg4_tag_type {
+ MPEG4_TAG_HEAD,
+ MPEG4_TAG_VOP,
+};
+
+struct input_file {
+ int fd;
+ char *data;
+ int size;
+ int offs;
+};
+
+struct parser_context;
+
+/* Parse 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 when
+ * setting up v4l2 decoder
+ * Return value: 1 - if a complete frame has been extracted, 0 otherwise
+ */
+
+typedef int (*parser_func)(struct parser_context *ctx,
+ char* in, int in_size,
+ char* out, int out_size,
+ int *consumed, int *frame_size, int get_head);
+
+/* Parser context */
+struct 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;
+ int short_header;
+
+ /* VP8 */
+ int is_32Bhdr_found;
+ parser_func func;
+ struct input_file input;
+};
+
+int parser_create(struct parser_context **ctx, const char *url,
+ unsigned int codec,
+ unsigned int len, char *data);
+
+void parser_destroy(struct parser_context *ctx);
+
+int parser_read_frame(struct parser_context *ctx, char *data, unsigned int size,
+ unsigned int *framesize, int stream_header);
+
+#endif /* _PARSER_H_ */
diff --git a/stan/v4l2-decode/test-streams/FVDO_Freeway_720p.264 b/stan/v4l2-decode/test-streams/FVDO_Freeway_720p.264
new file mode 100644
index 0000000..f631faf
--- /dev/null
+++ b/stan/v4l2-decode/test-streams/FVDO_Freeway_720p.264
Binary files differ
diff --git a/stan/v4l2-decode/tracer.c b/stan/v4l2-decode/tracer.c
new file mode 100644
index 0000000..a320103
--- /dev/null
+++ b/stan/v4l2-decode/tracer.c
@@ -0,0 +1,85 @@
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "tracer.h"
+#include "common.h"
+
+int tracer_init(struct instance *i)
+{
+ i->stats = calloc(STAT_BUFS, sizeof(struct buf_stats));
+ if (!i->stats)
+ return -1;
+
+ return 0;
+}
+
+void tracer_deinit(struct instance *i)
+{
+ if (i->stats)
+ free(i->stats);
+}
+
+void tracer_buf_start(struct instance *i, unsigned int type)
+{
+ struct buf_stats *stats = i->stats;
+
+ if (stats->dqbuf_counter >= STAT_BUFS ||
+ stats->qbuf_counter >= STAT_BUFS)
+ return;
+
+ if (stats->dqbuf_counter == 0)
+ gettimeofday(&stats->start, NULL);
+
+ if (type == TYPE_DQBUF)
+ gettimeofday(&stats[stats->dqbuf_counter].dqbuf_start, NULL);
+ else
+ gettimeofday(&stats[stats->qbuf_counter].qbuf_start, NULL);
+}
+
+void tracer_buf_finish(struct instance *i, unsigned int type)
+{
+ struct buf_stats *stats = i->stats;
+
+ if (stats->dqbuf_counter >= STAT_BUFS ||
+ stats->qbuf_counter >= STAT_BUFS)
+ return;
+
+ if (type == TYPE_DQBUF) {
+ gettimeofday(&stats[stats->dqbuf_counter].dqbuf_end, NULL);
+ stats->dqbuf_counter++;
+ } else {
+ gettimeofday(&stats[stats->qbuf_counter].qbuf_end, NULL);
+ stats->qbuf_counter++;
+ }
+
+ gettimeofday(&stats->end, NULL);
+}
+
+void tracer_show(struct instance *inst)
+{
+ struct buf_stats *stats = inst->stats;
+ unsigned long long delta, time, fps;
+ unsigned int last = stats->dqbuf_counter - 1;
+
+ delta = (stats[last].dqbuf_end.tv_sec * 1000000 +
+ stats[last].dqbuf_end.tv_usec);
+
+ delta -=(stats[0].dqbuf_end.tv_sec * 1000000 +
+ stats[0].dqbuf_end.tv_usec);
+
+ time = delta / last; /* time per frame in us */
+ fps = 1000000 / time;
+
+ fprintf(stdout, "%lld fps (%lld ms)\n", fps, time / 1000);
+
+ time = stats->end.tv_sec * 1000000 + stats->end.tv_usec;
+ time -= stats->start.tv_sec * 1000000 + stats->start.tv_usec;
+
+ fprintf(stdout, "total time %lld ms (%lld)\n", time / 1000,
+ last * 1000000 / time);
+}
diff --git a/stan/v4l2-decode/tracer.h b/stan/v4l2-decode/tracer.h
new file mode 100644
index 0000000..fb679a5
--- /dev/null
+++ b/stan/v4l2-decode/tracer.h
@@ -0,0 +1,17 @@
+
+#ifndef __TRACER_H__
+#define __TRACER_H__
+
+#define TYPE_QBUF 1
+#define TYPE_DQBUF 2
+#define STAT_BUFS 1000
+
+struct instance;
+
+int tracer_init(struct instance *i);
+void tracer_deinit(struct instance *i);
+void tracer_buf_start(struct instance *i, unsigned int type);
+void tracer_buf_finish(struct instance *i, unsigned int type);
+void tracer_show(struct instance *inst);
+
+#endif
diff --git a/stan/v4l2-decode/video.c b/stan/v4l2-decode/video.c
new file mode 100644
index 0000000..fc6a4dc
--- /dev/null
+++ b/stan/v4l2-decode/video.c
@@ -0,0 +1,892 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2015 Linaro 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 <errno.h>
+
+#include "video.h"
+#include "common.h"
+
+#ifndef V4L2_BUF_FLAG_LAST
+#define V4L2_BUF_FLAG_LAST 0x00100000
+#endif
+
+static char *dbg_type[2] = {"OUTPUT", "CAPTURE"};
+static char *dbg_status[2] = {"ON", "OFF"};
+
+/* check is it a decoder video device */
+static int is_video_decoder(int fd, const char *name)
+{
+ struct v4l2_capability cap;
+ struct v4l2_fmtdesc fdesc;
+ int found = 0;
+
+ memzero(cap);
+ if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
+ err("Failed to verify capabilities: %m");
+ return -1;
+ }
+
+ dbg("caps (%s): driver=\"%s\" bus_info=\"%s\" card=\"%s\" "
+ "version=%u.%u.%u", name, cap.driver, cap.bus_info, cap.card,
+ (cap.version >> 16) & 0xff,
+ (cap.version >> 8) & 0xff,
+ cap.version & 0xff);
+
+ if (!(cap.capabilities & V4L2_CAP_STREAMING) ||
+ !(cap.capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)) {
+ err("Insufficient capabilities for video device (is %s correct?)",
+ name);
+ return -1;
+ }
+
+ memzero(fdesc);
+ fdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+
+ while (!ioctl(fd, VIDIOC_ENUM_FMT, &fdesc)) {
+ dbg(" %s", fdesc.description);
+
+ switch (fdesc.pixelformat) {
+ case V4L2_PIX_FMT_NV12:
+ case V4L2_PIX_FMT_NV21:
+ case V4L2_PIX_FMT_NV12_UBWC:
+ found = 1;
+ break;
+ default:
+ dbg("%s is not a decoder video device", name);
+ return -1;
+ }
+
+ if (found)
+ break;
+
+ fdesc.index++;
+ }
+
+ found = 0;
+ memzero(fdesc);
+ fdesc.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+
+ while (!ioctl(fd, VIDIOC_ENUM_FMT, &fdesc)) {
+ dbg(" %s", fdesc.description);
+
+ switch (fdesc.pixelformat) {
+ case V4L2_PIX_FMT_MPEG:
+ case V4L2_PIX_FMT_H264:
+ case V4L2_PIX_FMT_H263:
+ case V4L2_PIX_FMT_MPEG1:
+ case V4L2_PIX_FMT_MPEG2:
+ case V4L2_PIX_FMT_MPEG4:
+ case V4L2_PIX_FMT_XVID:
+ case V4L2_PIX_FMT_VC1_ANNEX_G:
+ case V4L2_PIX_FMT_VC1_ANNEX_L:
+ case V4L2_PIX_FMT_VP8:
+ found = 1;
+ break;
+ default:
+ err("%s is not a decoder video device", name);
+ return -1;
+ }
+
+ if (found)
+ break;
+
+ fdesc.index++;
+ }
+
+ return 0;
+}
+
+int video_open(struct video *vid, const char *name)
+{
+ char video_name[64];
+ unsigned idx = 0, v = 0;
+ int ret, fd = -1;
+
+ while (v++ < 10) {
+ memset(video_name, 0, sizeof(video_name));
+ ret = sprintf(video_name, "/dev/video%d", idx);
+ if (ret < 0)
+ return ret;
+
+ idx++;
+
+ dbg("open video device: %s", video_name);
+
+ fd = open(video_name, O_RDWR | O_NONBLOCK, 0);
+ if (fd < 0) {
+ err("Failed to open video device: %s (%s)", video_name,
+ strerror(errno));
+ continue;
+ }
+
+ ret = is_video_decoder(fd, video_name);
+ if (ret < 0) {
+ close(fd);
+ continue;
+ }
+
+ if (!ret)
+ break;
+ }
+
+ if (ret < 0)
+ fd = open(name, O_RDWR | O_NONBLOCK, 0);
+
+ if (fd < 0) {
+ err("Failed to open video device: %s (%s)", name, strerror(errno));
+ return -1;
+ }
+
+ vid->fd = fd;
+
+ return 0;
+}
+
+
+void video_close(struct video *vid)
+{
+ if (vid->fd > 0)
+ close(vid->fd);
+}
+
+int video_set_control(struct video *vid)
+{
+ return 0;
+}
+
+int video_set_framerate(struct video *vid, unsigned int framerate)
+{
+ struct v4l2_streamparm parm = {0};
+
+ parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ parm.parm.output.timeperframe.numerator = 1;
+ parm.parm.output.timeperframe.denominator = framerate;
+
+ return ioctl(vid->fd, VIDIOC_S_PARM, &parm);
+}
+
+int video_buf_exp(struct video *vid, unsigned int index)
+{
+ struct v4l2_exportbuffer expbuf;
+ unsigned int num_planes = CAP_PLANES;
+ unsigned int n;
+
+ for (n = 0; n < num_planes; n++) {
+ memset(&expbuf, 0, sizeof(expbuf));
+
+ expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ expbuf.index = index;
+ expbuf.flags = O_CLOEXEC | O_RDWR;
+ expbuf.plane = n;
+
+ if (ioctl(vid->fd, VIDIOC_EXPBUF, &expbuf) < 0) {
+ err("CAPTURE: Failed to export buffer index%u (%s)",
+ index, strerror(errno));
+ return -1;
+ }
+
+ info("CAPTURE: Exported buffer index%u (plane%u) with fd %d",
+ index, n, expbuf.fd);
+ }
+
+ return 0;
+}
+
+int video_buf_create(struct video *vid, unsigned int width,
+ unsigned int height,unsigned int *index,
+ unsigned int count)
+{
+ struct v4l2_create_buffers b;
+ int ret;
+
+ memzero(b);
+ b.count = count;
+ b.memory = V4L2_MEMORY_MMAP;
+ b.format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ b.format.fmt.pix_mp.width = width;
+ b.format.fmt.pix_mp.height = height;
+ b.format.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
+
+ ret = ioctl(vid->fd, VIDIOC_CREATE_BUFS, &b);
+ if (ret) {
+ err("Failed to create bufs index%u (%s)", b.index,
+ strerror(errno));
+ return -1;
+ }
+
+ *index = b.index;
+
+ info("create_bufs: index %u, count %u", b.index, b.count);
+ return 0;
+}
+
+static int video_qbuf(struct video *vid, unsigned int index,unsigned int length,
+ unsigned int type, unsigned int nplanes)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[2];
+ int ret;
+
+ memzero(buf);
+ memset(planes, 0, sizeof(planes));
+
+ buf.type = type;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = index;
+ buf.length = nplanes;
+ buf.m.planes = planes;
+ buf.m.planes[0].bytesused = length;
+ buf.m.planes[0].data_offset = 0;
+
+ if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
+ buf.m.planes[0].length = vid->cap_buf_size[0];
+ } else {
+ buf.m.planes[0].length = vid->out_buf_size;
+ if (length == 0)
+ buf.flags |= V4L2_BUF_FLAG_LAST;
+ }
+
+ ret = ioctl(vid->fd, VIDIOC_QBUF, &buf);
+ if (ret) {
+ err("QBUF: Failed to queue buffer (index=%u) on %s (%s)",
+ buf.index,
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE],
+ strerror(errno));
+ return -errno;
+ }
+
+ dbg("QBUF: buffer on %s queue with index %u",
+ dbg_type[type==V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE], buf.index);
+
+ return 0;
+}
+
+int video_qbuf_out(struct video *vid, unsigned int idx, unsigned int length)
+{
+ if (idx >= vid->out_buf_cnt) {
+ err("Tried to queue a non exisiting buffer");
+ return -1;
+ }
+
+ return video_qbuf(vid, idx, length, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ OUT_PLANES);
+}
+
+int video_qbuf_cap(struct video *vid, unsigned int idx)
+{
+ if (idx >= vid->cap_buf_cnt) {
+ err("Tried to queue a non exisiting buffer");
+ return -1;
+ }
+
+ return video_qbuf(vid, idx, vid->cap_buf_size[0],
+ V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ CAP_PLANES);
+}
+
+int video_qbuf_cap_dmabuf(struct video *vid, unsigned int idx, int dbuf_fd)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[2];
+ int ret;
+
+ if (idx >= vid->cap_buf_cnt) {
+ err("Tried to queue a non exisiting buffer");
+ return -1;
+ }
+
+ memzero(buf);
+ memset(planes, 0, sizeof(planes));
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buf.memory = V4L2_MEMORY_DMABUF;
+ buf.index = idx;
+ buf.length = CAP_PLANES;
+ buf.m.planes = planes;
+
+ if (dbuf_fd > 0)
+ buf.m.planes[0].m.fd = dbuf_fd;
+
+ buf.m.planes[0].bytesused = vid->cap_buf_size[0];
+ buf.m.planes[1].bytesused = vid->cap_buf_size[1];
+
+ buf.m.planes[0].data_offset = 0;
+ buf.m.planes[1].data_offset = 0;
+
+ buf.m.planes[0].length = vid->cap_buf_size[0];
+
+ ret = ioctl(vid->fd, VIDIOC_QBUF, &buf);
+ if (ret) {
+ err("QBUF: Failed to queue buffer (index=%u) on CAPTURE (%s)",
+ buf.index, strerror(errno));
+ return -1;
+ }
+
+ dbg(" QBUF: Queued buffer on %s queue with index %u",
+ dbg_type[1], buf.index);
+
+ return 0;
+}
+
+static int video_dqbuf(struct video *vid, struct v4l2_buffer *buf)
+{
+ int ret;
+
+ ret = ioctl(vid->fd, VIDIOC_DQBUF, buf);
+ if (ret < 0)
+ return -errno;
+
+ dbg("Dequeued buffer on %s queue with index %u (flags:%x, bytesused:%u)",
+ dbg_type[buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE],
+ buf->index, buf->flags, buf->m.planes[0].bytesused);
+
+ return 0;
+}
+
+int video_dqbuf_out(struct video *vid, unsigned int *idx)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[OUT_PLANES];
+ int ret;
+
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.m.planes = planes;
+ buf.length = OUT_PLANES;
+
+ ret = video_dqbuf(vid, &buf);
+ if (ret < 0)
+ return ret;
+
+ if (idx)
+ *idx = buf.index;
+
+ return 0;
+}
+
+int video_dqbuf_cap(struct video *vid, unsigned int *idx, unsigned int *finished,
+ unsigned int *bytesused, unsigned int *sequence)
+{
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[CAP_PLANES];
+ int ret;
+
+ memset(planes, 0, sizeof(planes));
+ memzero(buf);
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ if (vid->cap_use_dmabuf)
+ buf.memory = V4L2_MEMORY_DMABUF;
+ else
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.m.planes = planes;
+ buf.length = CAP_PLANES;
+
+ ret = video_dqbuf(vid, &buf);
+ if (ret < 0)
+ return ret;
+
+ if (finished)
+ *finished = 0;
+
+ if (buf.flags & V4L2_BUF_FLAG_LAST || buf.m.planes[0].bytesused == 0) {
+ if (finished)
+ *finished = 1;
+ }
+
+ if (sequence)
+ *sequence = buf.sequence;
+
+ if (bytesused)
+ *bytesused = buf.m.planes[0].bytesused;
+ if (idx)
+ *idx = buf.index;
+
+ return 0;
+}
+
+static int video_stream(struct video *vid, enum v4l2_buf_type type, unsigned int status)
+{
+ int ret;
+
+ ret = ioctl(vid->fd, status, &type);
+ if (ret < 0) {
+ err("Failed to change streaming (type=%s, status=%s)",
+ dbg_type[type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE],
+ dbg_status[status == VIDIOC_STREAMOFF]);
+ return -errno;
+ }
+
+ info("Stream %s on %s queue", dbg_status[status==VIDIOC_STREAMOFF],
+ dbg_type[type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE]);
+
+ return 0;
+}
+
+int video_streamon_cap(struct video *vid)
+{
+ return video_stream(vid, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ VIDIOC_STREAMON);
+}
+
+int video_streamon_out(struct video *vid)
+{
+ return video_stream(vid, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ VIDIOC_STREAMON);
+}
+
+int video_streamoff_cap(struct video *vid)
+{
+ return video_stream(vid, V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
+ VIDIOC_STREAMOFF);
+}
+
+int video_streamoff_out(struct video *vid)
+{
+ return video_stream(vid, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
+ VIDIOC_STREAMOFF);
+}
+
+int video_dec_stop(struct video *vid)
+{
+ struct v4l2_decoder_cmd dec;
+ int ret;
+
+ memzero(dec);
+ dec.cmd = V4L2_DEC_CMD_STOP;
+
+ ret = ioctl(vid->fd, VIDIOC_DECODER_CMD, &dec);
+ if (ret < 0) {
+ err("DECODER_CMD failed (%s)", strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+int video_dec_start(struct video *vid)
+{
+ struct v4l2_decoder_cmd dec;
+ int ret;
+
+ memzero(dec);
+ dec.cmd = V4L2_DEC_CMD_START;
+
+ ret = ioctl(vid->fd, VIDIOC_DECODER_CMD, &dec);
+ if (ret < 0) {
+ err("DECODER_CMD failed (%s)", strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+int video_stop(struct video *vid)
+{
+ struct v4l2_requestbuffers reqbuf;
+ unsigned int n;
+ int ret;
+
+ ret = video_streamoff_cap(vid);
+ if (ret < 0)
+ err("STREAMOFF CAPTURE queue failed (%s)", strerror(errno));
+
+ ret = video_streamoff_out(vid);
+ if (ret < 0)
+ err("STREAMOFF OUTPUT queue failed (%s)", strerror(errno));
+
+ if (!vid->cap_use_dmabuf) {
+ for (n = 0; n < vid->cap_buf_cnt; n++) {
+ ret = munmap(vid->cap_buf_addr[n][0],
+ vid->cap_buf_size[0]);
+ if (ret)
+ err("unmap failed %s", strerror(errno));
+ }
+ }
+
+ for (n = 0; n < vid->out_buf_cnt; n++) {
+ ret = munmap(vid->out_buf_addr[n], vid->out_buf_size);
+ if (ret)
+ err("unmap failed %s", strerror(errno));
+ }
+
+ memzero(reqbuf);
+ reqbuf.count = 0;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ if (vid->cap_use_dmabuf)
+ reqbuf.memory = V4L2_MEMORY_DMABUF;
+ else
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ info("calling reqbuf(0)");
+
+ ret = ioctl(vid->fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret < 0)
+ err("REQBUFS(0) on CAPTURE queue (%s)", strerror(errno));
+
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ ret = ioctl(vid->fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret < 0)
+ err("REQBUFS(0) on OUTPUT queue (%s)", strerror(errno));
+
+ return 0;
+}
+
+int video_gfmt_cap(struct video *vid, unsigned int *width, unsigned int *height,
+ unsigned int *sizeimage)
+{
+ struct v4l2_format fmt;
+ int ret;
+
+ memzero(fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
+
+ ret = ioctl(vid->fd, VIDIOC_G_FMT, &fmt);
+ if (ret) {
+ err("CAPTURE: Failed to get format (%s)", strerror(errno));
+ return -1;
+ }
+
+ *width = fmt.fmt.pix_mp.width;
+ *height = fmt.fmt.pix_mp.height;
+ if (sizeimage)
+ *sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+ return 0;
+}
+
+int video_sfmt_cap(struct video *vid, unsigned int width, unsigned int height,
+ unsigned int pixfmt, unsigned int *sizeimage)
+{
+ struct v4l2_format fmt;
+ int ret;
+
+ memzero(fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt.fmt.pix_mp.width = width;
+ fmt.fmt.pix_mp.height = height;
+ fmt.fmt.pix_mp.pixelformat = pixfmt;
+
+ ret = ioctl(vid->fd, VIDIOC_S_FMT, &fmt);
+ if (ret) {
+ err("CAPTURE: Failed to set format (%s)", strerror(errno));
+ return -1;
+ }
+
+ *sizeimage = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+ return 0;
+}
+
+int video_sfmt_out(struct video *vid, unsigned int width, unsigned int height,
+ unsigned int pixfmt)
+{
+ struct v4l2_format fmt;
+ int ret;
+
+ memzero(fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ fmt.fmt.pix_mp.width = width;
+ fmt.fmt.pix_mp.height = height;
+ fmt.fmt.pix_mp.pixelformat = pixfmt;
+
+ ret = ioctl(vid->fd, VIDIOC_S_FMT, &fmt);
+ if (ret) {
+ err("OUTPUT: Failed to set format (%s)", strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int video_setup_capture_mmap(struct video *vid, unsigned int count,
+ unsigned int w, unsigned int h)
+{
+ struct v4l2_plane planes[CAP_PLANES];
+ struct v4l2_requestbuffers reqbuf;
+ struct v4l2_format fmt;
+ struct v4l2_buffer buf;
+ int ret, n;
+
+ memzero(fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt.fmt.pix_mp.height = h;
+ fmt.fmt.pix_mp.width = w;
+ fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
+ ret = ioctl(vid->fd, VIDIOC_S_FMT, &fmt);
+ if (ret) {
+ err("CAPTURE: Failed to set format (%s)", strerror(errno));
+ return -1;
+ }
+
+ vid->cap_w = fmt.fmt.pix_mp.width;
+ vid->cap_h = fmt.fmt.pix_mp.height;
+ vid->cap_buf_size[0] = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+
+ info("CAPTURE: Set format %ux%d sizeimage %u, bpl %u",
+ fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage,
+ fmt.fmt.pix_mp.plane_fmt[0].bytesperline);
+
+#if 1
+ memzero(fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
+
+ ret = ioctl(vid->fd, VIDIOC_G_FMT, &fmt);
+ if (ret) {
+ err("CAPTURE: Failed to get format (%s)", strerror(errno));
+ return -1;
+ }
+
+ info("CAPTURE: Get format %ux%u sizeimage %u, bpl %u",
+ fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage,
+ fmt.fmt.pix_mp.plane_fmt[0].bytesperline);
+
+ vid->cap_w = fmt.fmt.pix_mp.width;
+ vid->cap_h = fmt.fmt.pix_mp.height;
+ vid->cap_buf_size[0] = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+#endif
+
+ memzero(reqbuf);
+ reqbuf.count = count;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ ret = ioctl(vid->fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret != 0) {
+ err("CAPTURE: REQBUFS failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ info("CAPTURE: Number of buffers is %u (requested %u)",
+ reqbuf.count, count);
+
+ vid->cap_buf_cnt = reqbuf.count;
+
+ for (n = 0; n < vid->cap_buf_cnt; n++) {
+ memzero(buf);
+ memset(planes, 0, sizeof(planes));
+ buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = n;
+ buf.m.planes = planes;
+ buf.length = CAP_PLANES;
+
+ ret = ioctl(vid->fd, VIDIOC_QUERYBUF, &buf);
+ if (ret != 0) {
+ err("CAPTURE: QUERYBUF failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ vid->cap_buf_off[n][0] = buf.m.planes[0].m.mem_offset;
+
+ vid->cap_buf_addr[n][0] = mmap(NULL, buf.m.planes[0].length,
+ PROT_READ | PROT_WRITE,
+ MAP_SHARED,
+ vid->fd,
+ buf.m.planes[0].m.mem_offset);
+
+ if (vid->cap_buf_addr[n][0] == MAP_FAILED) {
+ err("CAPTURE: Failed to MMAP buffer");
+ return -1;
+ }
+
+ vid->cap_buf_size[0] = buf.m.planes[0].length;
+ }
+
+ info("CAPTURE: querybuf: sizeimage %u", vid->cap_buf_size[0]);
+
+ return 0;
+}
+
+static int video_setup_capture_dmabuf(struct video *vid, unsigned int count,
+ unsigned int w, unsigned int h)
+{
+ struct v4l2_requestbuffers reqbuf;
+ struct v4l2_format fmt;
+ int ret;
+
+ memzero(fmt);
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ fmt.fmt.pix_mp.height = h;
+ fmt.fmt.pix_mp.width = w;
+ fmt.fmt.pix_mp.pixelformat = V4L2_PIX_FMT_NV12;
+ ret = ioctl(vid->fd, VIDIOC_S_FMT, &fmt);
+ if (ret) {
+ err("CAPTURE: Failed to set format (%s)", strerror(errno));
+ return -1;
+ }
+
+ vid->cap_w = fmt.fmt.pix_mp.width;
+ vid->cap_h = fmt.fmt.pix_mp.height;
+
+ vid->cap_buf_size[0] = fmt.fmt.pix_mp.plane_fmt[0].sizeimage;
+ vid->cap_buf_size[1] = fmt.fmt.pix_mp.plane_fmt[1].sizeimage;
+
+ info("CAPTURE: Set format %ux%d sizeimage %u, bpl %u",
+ fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
+ fmt.fmt.pix_mp.plane_fmt[0].sizeimage,
+ fmt.fmt.pix_mp.plane_fmt[0].bytesperline);
+
+ memzero(reqbuf);
+ reqbuf.count = count;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_DMABUF;
+
+ ret = ioctl(vid->fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret != 0) {
+ err("CAPTURE: REQBUFS failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ info("CAPTURE: Number of buffers is %u (requested %u)",
+ reqbuf.count, vid->cap_buf_cnt);
+
+ vid->cap_buf_cnt = reqbuf.count;
+
+ return 0;
+}
+
+int video_setup_capture(struct video *vid, unsigned int count,
+ unsigned int w, unsigned int h)
+{
+ int ret;
+
+ if (vid->cap_use_dmabuf)
+ ret = video_setup_capture_dmabuf(vid, count, w, h);
+ else
+ ret = video_setup_capture_mmap(vid, count, w, h);
+
+ return ret;
+}
+
+int video_setup_output(struct video *vid, unsigned int size, unsigned int count)
+{
+ struct v4l2_requestbuffers reqbuf;
+ struct v4l2_buffer buf;
+ struct v4l2_plane planes[OUT_PLANES];
+ int ret;
+ int n;
+
+ memzero(reqbuf);
+ reqbuf.count = count;
+ reqbuf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ reqbuf.memory = V4L2_MEMORY_MMAP;
+
+ ret = ioctl(vid->fd, VIDIOC_REQBUFS, &reqbuf);
+ if (ret) {
+ err("OUTPUT: REQBUFS failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ vid->out_buf_cnt = reqbuf.count;
+
+ info("OUTPUT: Number of buffers is %u (requested %u)",
+ vid->out_buf_cnt, count);
+
+ for (n = 0; n < vid->out_buf_cnt; n++) {
+ memzero(buf);
+ memset(planes, 0, sizeof(planes));
+ buf.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
+ buf.memory = V4L2_MEMORY_MMAP;
+ buf.index = n;
+ buf.m.planes = planes;
+ buf.length = OUT_PLANES;
+
+ ret = ioctl(vid->fd, VIDIOC_QUERYBUF, &buf);
+ if (ret != 0) {
+ err("OUTPUT: QUERYBUF failed (%s)", strerror(errno));
+ return -1;
+ }
+
+ vid->out_buf_off[n] = buf.m.planes[0].m.mem_offset;
+ vid->out_buf_size = buf.m.planes[0].length;
+
+ vid->out_buf_addr[n] = mmap(NULL, buf.m.planes[0].length,
+ PROT_READ | PROT_WRITE, MAP_SHARED,
+ vid->fd,
+ buf.m.planes[0].m.mem_offset);
+
+ if (vid->out_buf_addr[n] == MAP_FAILED) {
+ err("OUTPUT: Failed to MMAP buffer");
+ return -1;
+ }
+
+ vid->out_buf_flag[n] = 0;
+ }
+
+ info("OUTPUT: querybuf sizeimage %u", vid->out_buf_size);
+
+ return 0;
+}
+
+int video_event_subscribe(struct video *vid, unsigned int event_type)
+{
+ struct v4l2_event_subscription sub;
+
+ memset(&sub, 0, sizeof(sub));
+ sub.type = event_type;
+
+ if (ioctl(vid->fd, VIDIOC_SUBSCRIBE_EVENT, &sub) < 0) {
+ err("failed to subscribe to event type %u: %m", sub.type);
+ return -1;
+ }
+
+ return 0;
+}
+
+int video_event_unsubscribe(struct video *vid, unsigned int event_type)
+{
+ struct v4l2_event_subscription sub;
+
+ memset(&sub, 0, sizeof(sub));
+ sub.type = event_type;
+
+ if (ioctl(vid->fd, VIDIOC_UNSUBSCRIBE_EVENT, &sub) < 0) {
+ err("failed to unsubscribe to event type %u: %m", sub.type);
+ return -1;
+ }
+
+ return 0;
+}
+
+int video_event_dequeue(struct video *vid, struct v4l2_event *ev)
+{
+ memset(ev, 0, sizeof (*ev));
+
+ if (ioctl(vid->fd, VIDIOC_DQEVENT, ev) < 0) {
+ dbg("failed to dequeue event: %m");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/stan/v4l2-decode/video.h b/stan/v4l2-decode/video.h
new file mode 100644
index 0000000..db56a92
--- /dev/null
+++ b/stan/v4l2-decode/video.h
@@ -0,0 +1,71 @@
+/*
+ * V4L2 Codec decoding example application
+ * Kamil Debski <k.debski@samsung.com>
+ *
+ *
+ * Copyright 2012 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2015 Linaro 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_VIDEO_H
+#define INCLUDE_VIDEO_H
+
+#include "common.h"
+
+#ifndef V4L2_PIX_FMT_NV12_UBWC
+/* Qualcomm UBWC 8-bit Y/CbCr 4:2:0 */
+#define V4L2_PIX_FMT_NV12_UBWC v4l2_fourcc('Q', '1', '2', '8')
+#endif
+
+int video_open(struct video *vid, const char *name);
+void video_close(struct video *vid);
+int video_setup_output(struct video *vid, unsigned int size, unsigned int count);
+int video_gfmt_cap(struct video *vid, unsigned int *width, unsigned int *height,
+ unsigned int *sizeimage);
+int video_sfmt_cap(struct video *vid, unsigned int width, unsigned int height,
+ unsigned int pixfmt, unsigned int *sizeimage);
+int video_sfmt_out(struct video *vid, unsigned int width, unsigned int height,
+ unsigned int pixfmt);
+int video_setup_capture(struct video *vid, unsigned int extra_buf,
+ unsigned int w, unsigned int h);
+int video_qbuf_out(struct video *vid, unsigned int n, unsigned int length);
+int video_qbuf_cap(struct video *vid, unsigned int n);
+int video_qbuf_cap_dmabuf(struct video *vid, unsigned int index, int dbuf_fd);
+int video_dqbuf_out(struct video *vid, unsigned int *n);
+int video_dqbuf_cap(struct video *vid, unsigned int *n, unsigned int *finished,
+ unsigned int *bytesused, unsigned int *sequence);
+int video_dqbuf_cap_dmabuf(struct video *vid, unsigned int *n,
+ unsigned int *finished, unsigned int *bytesused);
+int video_streamon_cap(struct video *vid);
+int video_streamon_out(struct video *vid);
+int video_streamoff_cap(struct video *vid);
+int video_streamoff_out(struct video *vid);
+
+int video_buf_exp(struct video *vid, unsigned int index);
+int video_buf_create(struct video *vid, unsigned int width,
+ unsigned int height,unsigned int *index,
+ unsigned int count);
+int video_set_control(struct video *vid);
+int video_set_framerate(struct video *vid, unsigned int framerate);
+int video_stop(struct video *vid);
+int video_dec_stop(struct video *vid);
+int video_dec_start(struct video *vid);
+
+int video_event_subscribe(struct video *vid, unsigned int event_type);
+int video_event_unsubscribe(struct video *vid, unsigned int event_type);
+int video_event_dequeue(struct video *vid, struct v4l2_event *ev);
+
+#endif /* INCLUDE_VIDEO_H */
diff --git a/ueventd.common.rc b/ueventd.common.rc
index b87dccf..7dd6d81 100644
--- a/ueventd.common.rc
+++ b/ueventd.common.rc
@@ -8,3 +8,4 @@ modalias_handling enabled
# media.codec2
/dev/ion 0664 system system
+/dev/video0 0666 media media