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