diff options
| -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 | 
