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