From 1b8f2f58134e5fedb3780cc925eff27496515314 Mon Sep 17 00:00:00 2001 From: Amit Pundir Date: Tue, 16 Jun 2020 13:56:16 -0700 Subject: db845c: Add v4l2_codec2 support and qcomlt's v4l2_decode test app Based on https: //android-review.googlesource.com/c/1424772 Signed-off-by: Amit Pundir Change-Id: I72f0f19145daa322a4cc9024a41dc38ad04cd429 --- db845c/device.mk | 1 + device-common.mk | 15 +- etc/media_codecs.xml | 15 +- etc/media_codecs_c2.xml | 16 + manifest.xml | 13 + seccomp_policy/codec2.vendor.ext.policy | 17 + sepolicy/file_contexts | 1 + stan/v4l2-decode/.gitignore | 3 + stan/v4l2-decode/Android.bp | 18 + stan/v4l2-decode/Makefile | 46 ++ stan/v4l2-decode/README | 7 + stan/v4l2-decode/args.c | 113 +++ stan/v4l2-decode/args.h | 32 + stan/v4l2-decode/common.h | 161 ++++ stan/v4l2-decode/ctrls.c | 31 + stan/v4l2-decode/ctrls.h | 7 + stan/v4l2-decode/fileops.c | 59 ++ stan/v4l2-decode/fileops.h | 34 + stan/v4l2-decode/main.c | 487 +++++++++++ stan/v4l2-decode/parser.c | 895 +++++++++++++++++++++ stan/v4l2-decode/parser.h | 108 +++ .../v4l2-decode/test-streams/FVDO_Freeway_720p.264 | Bin 0 -> 4510750 bytes stan/v4l2-decode/tracer.c | 85 ++ stan/v4l2-decode/tracer.h | 17 + stan/v4l2-decode/video.c | 892 ++++++++++++++++++++ stan/v4l2-decode/video.h | 71 ++ ueventd.common.rc | 1 + 27 files changed, 3142 insertions(+), 3 deletions(-) create mode 100644 etc/media_codecs_c2.xml create mode 100644 seccomp_policy/codec2.vendor.ext.policy create mode 100644 stan/v4l2-decode/.gitignore create mode 100644 stan/v4l2-decode/Android.bp create mode 100644 stan/v4l2-decode/Makefile create mode 100644 stan/v4l2-decode/README create mode 100644 stan/v4l2-decode/args.c create mode 100644 stan/v4l2-decode/args.h create mode 100644 stan/v4l2-decode/common.h create mode 100644 stan/v4l2-decode/ctrls.c create mode 100644 stan/v4l2-decode/ctrls.h create mode 100644 stan/v4l2-decode/fileops.c create mode 100644 stan/v4l2-decode/fileops.h create mode 100644 stan/v4l2-decode/main.c create mode 100644 stan/v4l2-decode/parser.c create mode 100644 stan/v4l2-decode/parser.h create mode 100644 stan/v4l2-decode/test-streams/FVDO_Freeway_720p.264 create mode 100644 stan/v4l2-decode/tracer.c create mode 100644 stan/v4l2-decode/tracer.h create mode 100644 stan/v4l2-decode/video.c create mode 100644 stan/v4l2-decode/video.h 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: - + + + + + + + + + + + + + + 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 @@ + + + + + + + + + + + + + + + + diff --git a/manifest.xml b/manifest.xml index 0c7ae03..dc574a1 100644 --- a/manifest.xml +++ b/manifest.xml @@ -111,6 +111,19 @@ default + + android.hardware.media.c2 + hwbinder + 1.0 + + IComponentStore + default + + + IConfigurable + default + + android.hardware.media.omx hwbinder 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 +# +# 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 + * + * 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 +#include +#include +#include +#include + +#include "common.h" +#include "parser.h" + + +void print_usage(char *name) +{ + printf("Usage:\n"); + printf("\t%s\n", name); + printf("\t-c - The codec of the encoded stream, " + "available codecs: mpeg4, h264\n"); + printf("\t-d use dmabuf instead of mmap\n"); + printf("\t-i - Input file name\n"); + printf("\t-m - 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 + * + * 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 + * + * 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 +#include +#include +#include + +#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 +#include +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include + +#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 + * + * 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * 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 Binary files /dev/null and b/stan/v4l2-decode/test-streams/FVDO_Freeway_720p.264 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 +#include +#include +#include +#include +#include + +#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 + * + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 + * + * + * 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 -- cgit v1.2.3