diff --git a/arch/sim/Kconfig b/arch/sim/Kconfig index 86a83eea207..3b1791a49e3 100644 --- a/arch/sim/Kconfig +++ b/arch/sim/Kconfig @@ -331,6 +331,20 @@ config SIM_CAMERA_DEV_PATH endif +menu "Simulated v4l2m2m support" + +config SIM_VIDEO_DECODER + bool "Video decoder support on sim" + depends on VIDEO + default n + +config SIM_VIDEO_DECODER_DEV_PATH + string "Video decoder device path" + depends on SIM_VIDEO_DECODER + default "/dev/video1" + +endmenu + menu "Simulated Graphics/Input" config SIM_X11FB diff --git a/arch/sim/src/Makefile b/arch/sim/src/Makefile index 3001eb7cf17..2494b120a2d 100644 --- a/arch/sim/src/Makefile +++ b/arch/sim/src/Makefile @@ -250,6 +250,12 @@ ifeq ($(CONFIG_SIM_CAMERA_V4L2),y) STDLIBS += -lv4l2 endif +ifeq ($(CONFIG_SIM_VIDEO_DECODER),y) + HOSTSRCS += sim_hostdecoder.c + CSRCS += sim_decoder.c + STDLIBS += -lopenh264 +endif + COBJS = $(CSRCS:.c=$(OBJEXT)) NUTTXOBJS = $(AOBJS) $(COBJS) diff --git a/arch/sim/src/sim/CMakeLists.txt b/arch/sim/src/sim/CMakeLists.txt index ff926921825..7925700296c 100644 --- a/arch/sim/src/sim/CMakeLists.txt +++ b/arch/sim/src/sim/CMakeLists.txt @@ -155,6 +155,12 @@ if(CONFIG_SIM_CAMERA_V4L2) list(APPEND STDLIBS v4l2) endif() +if(CONFIG_SIM_VIDEO_DECODER) + list(APPEND HOSTSRCS sim_hostdecoder.c) + list(APPEND SRCS sim_decoder.c) + list(APPEND STDLIBS openh264) +endif() + if(CONFIG_SPINLOCK) list(APPEND HOSTSRCS sim_testset.c) endif() diff --git a/arch/sim/src/sim/sim_decoder.c b/arch/sim/src/sim/sim_decoder.c new file mode 100644 index 00000000000..c9f4f7592a0 --- /dev/null +++ b/arch/sim/src/sim/sim_decoder.c @@ -0,0 +1,478 @@ +/**************************************************************************** + * arch/sim/src/sim/sim_decoder.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include + +#include +#include + +#include "sim_hostdecoder.h" +#include "sim_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define SIM_DECODER_NAME "sim-h264" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct sim_decoder_s +{ + struct host_decoder_s *decoder; + struct v4l2_format output_fmt; + struct v4l2_format capture_fmt; + struct work_s work; + void *cookie; + bool capture_on; + bool flushing; +}; + +typedef struct sim_decoder_s sim_decoder_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +static int sim_decoder_open(void *cookie, void **priv); +static int sim_decoder_close(void *priv); +static int sim_decoder_capture_streamon(void *priv); +static int sim_decoder_output_streamon(void *priv); +static int sim_decoder_capture_streamoff(void *priv); +static int sim_decoder_output_streamoff(void *priv); +static int sim_decoder_output_available(void *priv); +static int sim_decoder_capture_available(void *priv); +static int sim_decoder_querycap(void *priv, + struct v4l2_capability *cap); +static int sim_decoder_capture_enum_fmt(void *priv, + struct v4l2_fmtdesc *fmt); +static int sim_decoder_output_enum_fmt(void *priv, + struct v4l2_fmtdesc *fmt); +static int sim_decoder_capture_g_fmt(void *priv, + struct v4l2_format *fmt); +static int sim_decoder_output_g_fmt(void *priv, + struct v4l2_format *fmt); +static int sim_decoder_capture_s_fmt(void *priv, + struct v4l2_format *fmt); +static int sim_decoder_output_s_fmt(void *priv, + struct v4l2_format *fmt); +static int sim_decoder_capture_try_fmt(void *priv, + struct v4l2_format *fmt); +static int sim_decoder_output_try_fmt(void *priv, + struct v4l2_format *fmt); +static int sim_decoder_subscribe_event(void *priv, + struct v4l2_event_subscription *sub); +static size_t sim_decoder_capture_g_bufsize(void *priv); +static size_t sim_decoder_output_g_bufsize(void *priv); +static int sim_decoder_process(sim_decoder_t *sim_decoder, + struct v4l2_buffer *dst_buf, + struct v4l2_buffer *src_buf); +static void sim_decoder_work(void *cookie); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct codec_ops_s g_sim_decoder_ops = +{ + .open = sim_decoder_open, + .close = sim_decoder_close, + .capture_streamon = sim_decoder_capture_streamon, + .output_streamon = sim_decoder_output_streamon, + .capture_streamoff = sim_decoder_capture_streamoff, + .output_streamoff = sim_decoder_output_streamoff, + .output_available = sim_decoder_output_available, + .capture_available = sim_decoder_capture_available, + .querycap = sim_decoder_querycap, + .capture_enum_fmt = sim_decoder_capture_enum_fmt, + .output_enum_fmt = sim_decoder_output_enum_fmt, + .capture_g_fmt = sim_decoder_capture_g_fmt, + .output_g_fmt = sim_decoder_output_g_fmt, + .capture_s_fmt = sim_decoder_capture_s_fmt, + .output_s_fmt = sim_decoder_output_s_fmt, + .capture_try_fmt = sim_decoder_capture_try_fmt, + .output_try_fmt = sim_decoder_output_try_fmt, + .subscribe_event = sim_decoder_subscribe_event, + .capture_g_bufsize = sim_decoder_capture_g_bufsize, + .output_g_bufsize = sim_decoder_output_g_bufsize, +}; + +static struct codec_s g_sim_codec_decoder = +{ + .ops = &g_sim_decoder_ops, +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static int sim_decoder_open(void *cookie, void **priv) +{ + sim_decoder_t *sim_decoder; + struct host_decoder_s *decoder; + + sim_decoder = kmm_zalloc(sizeof(sim_decoder_t)); + if (sim_decoder == NULL) + { + return -ENOMEM; + } + + decoder = host_decoder_open(); + if (decoder == NULL) + { + kmm_free(sim_decoder); + return -ENOMEM; + } + + sim_decoder->decoder = decoder; + sim_decoder->cookie = cookie; + *priv = sim_decoder; + + return 0; +} + +static int sim_decoder_close(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + host_decoder_close(sim_decoder->decoder); + kmm_free(sim_decoder); + + return 0; +} + +static int sim_decoder_capture_streamon(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + sim_decoder->capture_on = true; + work_queue(HPWORK, &sim_decoder->work, + sim_decoder_work, sim_decoder, 0); + + return 0; +} + +static int sim_decoder_output_streamon(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + return host_decoder_streamon(sim_decoder->decoder); +} + +static int sim_decoder_capture_streamoff(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + sim_decoder->capture_on = false; + return host_decoder_streamoff(sim_decoder->decoder); +} + +static int sim_decoder_output_streamoff(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + if (!sim_decoder->capture_on) + { + return 0; + } + + sim_decoder->flushing = true; + work_queue(HPWORK, &sim_decoder->work, + sim_decoder_work, sim_decoder, 0); + + return 0; +} + +static int sim_decoder_output_available(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + if (sim_decoder->capture_on == false) + { + return 0; + } + + work_queue(HPWORK, &sim_decoder->work, + sim_decoder_work, sim_decoder, 0); + + return 0; +} + +static int sim_decoder_capture_available(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + if (sim_decoder->capture_on == false) + { + return 0; + } + + work_queue(HPWORK, &sim_decoder->work, + sim_decoder_work, sim_decoder, 0); + + return 0; +} + +static int sim_decoder_querycap(void *priv, + struct v4l2_capability *cap) +{ + strlcpy((char *)cap->driver, SIM_DECODER_NAME, sizeof(cap->driver)); + strlcpy((char *)cap->card, SIM_DECODER_NAME, sizeof(cap->card)); + cap->capabilities = V4L2_CAP_VIDEO_M2M; + + return 0; +} + +static int sim_decoder_capture_enum_fmt(void *priv, + struct v4l2_fmtdesc *f) +{ + if (f->index >= 1) + { + return -EINVAL; + } + + f->pixelformat = V4L2_PIX_FMT_YUV420; + return 0; +} + +static int sim_decoder_output_enum_fmt(void *priv, + struct v4l2_fmtdesc *fmt) +{ + if (fmt->index >= 1) + { + return -EINVAL; + } + + fmt->pixelformat = V4L2_PIX_FMT_H264; + return 0; +} + +static int sim_decoder_capture_g_fmt(void *priv, + struct v4l2_format *fmt) +{ + sim_decoder_t *sim_decoder = priv; + + *fmt = sim_decoder->capture_fmt; + return 0; +} + +static int sim_decoder_output_g_fmt(void *priv, + struct v4l2_format *fmt) +{ + sim_decoder_t *sim_decoder = priv; + + *fmt = sim_decoder->output_fmt; + return 0; +} + +static int sim_decoder_capture_s_fmt(void *priv, + struct v4l2_format *fmt) +{ + sim_decoder_t *sim_decoder = priv; + size_t sizeimage; + + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + { + sim_decoder->capture_fmt = *fmt; + + sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height * 3 / 2; + sim_decoder->capture_fmt.fmt.pix.sizeimage = sizeimage; + sim_decoder->capture_fmt.fmt.pix.bytesperline = fmt->fmt.pix.width; + + return 0; + } + + return -EINVAL; +} + +static int sim_decoder_output_s_fmt(void *priv, + struct v4l2_format *fmt) +{ + sim_decoder_t *sim_decoder = priv; + size_t sizeimage; + + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_H264) + { + sim_decoder->output_fmt = *fmt; + + sizeimage = fmt->fmt.pix.width * fmt->fmt.pix.height; + sizeimage = (sizeimage * 3 / 2) / 2 + 128; + sim_decoder->output_fmt.fmt.pix.sizeimage = sizeimage; + + return 0; + } + + return -EINVAL; +} + +static int sim_decoder_capture_try_fmt(void *priv, + struct v4l2_format *fmt) +{ + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + { + return 0; + } + + return -EINVAL; +} + +static int sim_decoder_output_try_fmt(void *priv, + struct v4l2_format *fmt) +{ + if (fmt->fmt.pix.pixelformat == V4L2_PIX_FMT_H264) + { + return 0; + } + + return -EINVAL; +} + +static int sim_decoder_subscribe_event(void *priv, + struct v4l2_event_subscription *sub) +{ + switch (sub->type) + { + case V4L2_EVENT_EOS: + return OK; + + default: + return -EINVAL; + } +} + +static size_t sim_decoder_capture_g_bufsize(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + if (sim_decoder->capture_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) + { + return sim_decoder->capture_fmt.fmt.pix.sizeimage; + } + + return 0; +} + +static size_t sim_decoder_output_g_bufsize(void *priv) +{ + sim_decoder_t *sim_decoder = priv; + + if (sim_decoder->output_fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_H264) + { + return sim_decoder->output_fmt.fmt.pix.sizeimage; + } + + return 0; +} + +static int sim_decoder_process(sim_decoder_t *sim_decoder, + struct v4l2_buffer *dst_buf, + struct v4l2_buffer *src_buf) +{ + struct v4l2_event event; + uint8_t *src_data = NULL; + uint32_t src_size = 0; + int64_t src_pts = 0; + int64_t dst_pts = 0; + int ret; + + if (src_buf != NULL) + { + src_data = (uint8_t *)src_buf->m.userptr; + src_size = src_buf->bytesused; + src_pts = src_buf->timestamp.tv_sec * 1000000 + + src_buf->timestamp.tv_usec; + } + + ret = host_decoder_enqueue(sim_decoder->decoder, + src_data, src_pts, src_size); + if (ret >= 0 && src_buf != NULL) + { + codec_output_put_buf(sim_decoder->cookie, src_buf); + } + + if (ret < 1) + { + return ret; + } + + ret = host_decoder_dequeue(sim_decoder->decoder, + (uint8_t *)dst_buf->m.userptr, + &dst_pts, + &dst_buf->bytesused); + if (ret == 0 && src_buf == NULL) + { + sim_decoder->flushing = false; + dst_buf->flags |= V4L2_BUF_FLAG_LAST; + + memset(&event, 0, sizeof(event)); + event.type = V4L2_EVENT_EOS; + codec_queue_event(sim_decoder->cookie, &event); + } + + dst_buf->timestamp.tv_usec = dst_pts % 1000000; + dst_buf->timestamp.tv_sec = dst_pts / 1000000; + + codec_capture_put_buf(sim_decoder->cookie, dst_buf); + return ret; +} + +static void sim_decoder_work(void *decoder) +{ + sim_decoder_t *sim_decoder = decoder; + struct v4l2_buffer *src_buf; + struct v4l2_buffer *dst_buf; + int ret; + + src_buf = codec_output_get_buf(sim_decoder->cookie); + if (src_buf == NULL && !sim_decoder->flushing) + { + return; + } + + dst_buf = codec_capture_get_buf(sim_decoder->cookie); + if (dst_buf == NULL) + { + return; + } + + ret = sim_decoder_process(decoder, dst_buf, src_buf); + if (ret > 0) + { + work_queue(HPWORK, &sim_decoder->work, + sim_decoder_work, sim_decoder, 0); + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int sim_decoder_initialize() +{ + return codec_register(CONFIG_SIM_VIDEO_DECODER_DEV_PATH, + &g_sim_codec_decoder); +} diff --git a/arch/sim/src/sim/sim_hostdecoder.c b/arch/sim/src/sim/sim_hostdecoder.c new file mode 100644 index 00000000000..5d2c2fcec4a --- /dev/null +++ b/arch/sim/src/sim/sim_hostdecoder.c @@ -0,0 +1,205 @@ +/**************************************************************************** + * arch/sim/src/sim/sim_hostdecoder.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include +#include + +#include "sim_hostdecoder.h" + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct host_decoder_s +{ + ISVCDecoder *dec; + SBufferInfo bufinfo; + uint8_t *pdst[4]; + int remaining_frames; +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +struct host_decoder_s *host_decoder_open(void) +{ + struct host_decoder_s *decoder; + + decoder = calloc(1, sizeof(struct host_decoder_s)); + if (decoder == NULL) + { + syslog(LOG_ERR, "Init host decoder failed\n"); + } + + return decoder; +} + +int host_decoder_close(struct host_decoder_s *decoder) +{ + free(decoder); + return 0; +} + +int host_decoder_streamon(struct host_decoder_s *decoder) +{ + SDecodingParam param; + int level = WELS_LOG_RESV; + int ret; + + memset(¶m, 0, sizeof(SDecodingParam)); + param.eEcActiveIdc = ERROR_CON_DISABLE; + param.sVideoProperty.eVideoBsType = VIDEO_BITSTREAM_DEFAULT; + + ret = WelsCreateDecoder(&decoder->dec); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to create decoder\n"); + return ret; + } + + (*decoder->dec)->SetOption(decoder->dec, + DECODER_OPTION_TRACE_LEVEL, + &level); + + ret = (*decoder->dec)->Initialize(decoder->dec, ¶m); + if (ret < 0) + { + syslog(LOG_ERR, "Failed to initialize decoder\n"); + WelsDestroyDecoder(decoder->dec); + } + + return ret; +} + +int host_decoder_streamoff(struct host_decoder_s *decoder) +{ + (*decoder->dec)->Uninitialize(decoder->dec); + WelsDestroyDecoder(decoder->dec); + + decoder->remaining_frames = 0; + return 0; +} + +int host_decoder_enqueue(struct host_decoder_s *decoder, + void *data, int64_t pts, int size) +{ + DECODING_STATE state; + + memset(&decoder->bufinfo, 0, sizeof(SBufferInfo)); + memset(decoder->pdst, 0, sizeof(decoder->pdst)); + + if (decoder->remaining_frames == 0) + { + /* When data is NULL and remaining_frames has + * not been assigned, call DecodeFrame2 to obtain + * the pending buffer. + */ + + decoder->bufinfo.uiInBsTimeStamp = pts; + state = (*decoder->dec)->DecodeFrame2(decoder->dec, + data, + size, + decoder->pdst, + &decoder->bufinfo); + if (state != dsErrorFree) + { + syslog(LOG_ERR, "hostdec - decode failed 0x%04x\n", state); + return -EINVAL; + } + + if (data == NULL) + { + (*decoder->dec)->GetOption(decoder->dec, + DECODER_OPTION_NUM_OF_FRAMES_REMAINING_IN_BUFFER, + &decoder->remaining_frames); + } + } + else + { + /* When remaining_frames is assigned and greater than + * zero, call FlushFrame to get the last buffers. + */ + + state = (*decoder->dec)->FlushFrame(decoder->dec, + decoder->pdst, + &decoder->bufinfo); + if (state != dsErrorFree) + { + syslog(LOG_ERR, "hostdec - flush failed 0x%04x\n", state); + return -EINVAL; + } + + decoder->remaining_frames--; + } + + return decoder->bufinfo.iBufferStatus; +} + +int host_decoder_dequeue(struct host_decoder_s *decoder, + void *data, int64_t *pts, uint32_t *size) +{ + uint8_t *dst_addr = data; + int plane; + + for (plane = 0; plane < 3; plane++) + { + uint8_t *src_addr = decoder->pdst[plane]; + int width = decoder->bufinfo.UsrData.sSystemBuffer.iWidth; + int height = decoder->bufinfo.UsrData.sSystemBuffer.iHeight; + int stride = decoder->bufinfo.UsrData.sSystemBuffer.iStride[0]; + int row; + + if (plane > 0) + { + /* UV plane stride is iStride[1] */ + + width = width / 2; + height = height / 2; + stride = decoder->bufinfo.UsrData.sSystemBuffer.iStride[1]; + } + + for (row = 0; row < height; row++) + { + memcpy(dst_addr, src_addr, width); + src_addr += stride; + dst_addr += width; + } + } + + *pts = decoder->bufinfo.uiOutYuvTimeStamp; + *size = decoder->bufinfo.UsrData.sSystemBuffer.iWidth * + decoder->bufinfo.UsrData.sSystemBuffer.iHeight * 3 / 2; + + return decoder->remaining_frames; +} diff --git a/arch/sim/src/sim/sim_hostdecoder.h b/arch/sim/src/sim/sim_hostdecoder.h new file mode 100644 index 00000000000..a15412ad849 --- /dev/null +++ b/arch/sim/src/sim/sim_hostdecoder.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * arch/sim/src/sim/sim_hostdecoder.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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 __ARCH_SIM_SRC_SIM_SIM_HOSTDECODER_H +#define __ARCH_SIM_SRC_SIM_SIM_HOSTDECODER_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +struct host_decoder_s; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +struct host_decoder_s *host_decoder_open(void); +int host_decoder_close(struct host_decoder_s *decoder); +int host_decoder_streamon(struct host_decoder_s *decoder); +int host_decoder_streamoff(struct host_decoder_s *decoder); +int host_decoder_enqueue(struct host_decoder_s *decoder, + void *data, int64_t pts, int size); +int host_decoder_dequeue(struct host_decoder_s *decoder, + void *data, int64_t *pts, uint32_t *size); + +#endif /* __ARCH_SIM_SRC_SIM_SIM_HOSTDECODER_H */ + diff --git a/arch/sim/src/sim/sim_initialize.c b/arch/sim/src/sim/sim_initialize.c index d5b194cbff6..4c286c5b9c9 100644 --- a/arch/sim/src/sim/sim_initialize.c +++ b/arch/sim/src/sim/sim_initialize.c @@ -306,6 +306,10 @@ void up_initialize(void) sim_usbhost_initialize(); #endif +#ifdef CONFIG_SIM_VIDEO_DECODER + sim_decoder_initialize(); +#endif + kthread_create("loop_task", CONFIG_SIM_LOOPTASK_PRIORITY, CONFIG_DEFAULT_TASK_STACKSIZE, sim_loop_task, NULL); diff --git a/arch/sim/src/sim/sim_internal.h b/arch/sim/src/sim/sim_internal.h index 7b18ed4c32d..1c3b23c95c7 100644 --- a/arch/sim/src/sim/sim_internal.h +++ b/arch/sim/src/sim/sim_internal.h @@ -435,6 +435,10 @@ int sim_camera_initialize(void); void sim_camera_loop(void); #endif +#ifdef CONFIG_SIM_VIDEO_DECODER +int sim_decoder_initialize(void); +#endif + /* sim_usbdev.c *************************************************************/ #ifdef CONFIG_SIM_USB_DEV diff --git a/drivers/video/CMakeLists.txt b/drivers/video/CMakeLists.txt index d1db060dfa8..50716d37a8b 100644 --- a/drivers/video/CMakeLists.txt +++ b/drivers/video/CMakeLists.txt @@ -29,7 +29,7 @@ if(CONFIG_DRIVERS_VIDEO) endif() if(CONFIG_VIDEO_STREAM) - list(APPEND SRCS v4l2_core.c video_framebuff.c v4l2_cap.c) + list(APPEND SRCS v4l2_core.c video_framebuff.c v4l2_cap.c v4l2_m2m.c) endif() # These video drivers depend on I2C support diff --git a/drivers/video/Make.defs b/drivers/video/Make.defs index 2ba0082996c..b217a72e9c0 100644 --- a/drivers/video/Make.defs +++ b/drivers/video/Make.defs @@ -27,7 +27,7 @@ ifeq ($(CONFIG_VIDEO_FB),y) endif ifeq ($(CONFIG_VIDEO_STREAM),y) - CSRCS += v4l2_core.c video_framebuff.c v4l2_cap.c + CSRCS += v4l2_core.c video_framebuff.c v4l2_cap.c v4l2_m2m.c endif # These video drivers depend on I2C support diff --git a/drivers/video/v4l2_m2m.c b/drivers/video/v4l2_m2m.c new file mode 100644 index 00000000000..f08f61cadf8 --- /dev/null +++ b/drivers/video/v4l2_m2m.c @@ -0,0 +1,1100 @@ +/**************************************************************************** + * drivers/video/v4l2_m2m.c + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include +#include +#include +#include + +#include +#include + +#include "video_framebuff.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Offset base for buffers on the destination queue - used to distinguish + * between source and destination buffers when mmapping - they receive the + * same offsets but for different queues. + */ + +#define CAPTURE_BUF_OFFSET (1 << 30) +#define CODEC_EVENT_COUNT 6 + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct codec_event_s +{ + sq_entry_t entry; + struct v4l2_event event; +}; + +typedef struct codec_event_s codec_event_t; + +struct codec_type_inf_s +{ + video_framebuff_t bufinf; + FAR uint8_t *bufheap; /* for V4L2_MEMORY_MMAP buffers */ + bool buflast; +}; + +typedef struct codec_type_inf_s codec_type_inf_t; + +struct codec_file_s +{ + codec_type_inf_t capture_inf; + codec_type_inf_t output_inf; + sq_queue_t event_avail; + sq_queue_t event_free; + codec_event_t event_pool[CODEC_EVENT_COUNT]; + FAR struct pollfd *fds; + FAR void *priv; +}; + +typedef struct codec_file_s codec_file_t; + +struct codec_mng_s +{ + struct v4l2_s v4l2; + FAR struct codec_s *codec; +}; + +typedef struct codec_mng_s codec_mng_t; + +/**************************************************************************** + * Private Function Prototypes + ****************************************************************************/ + +/* Character driver methods. */ + +static int codec_open(FAR struct file *filep); +static int codec_close(FAR struct file *filep); +static int codec_mmap(FAR struct file *filep, + FAR struct mm_map_entry_s *map); +static int codec_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup); + +/* Common function */ + +static FAR codec_type_inf_t * +codec_get_type_inf(FAR struct codec_file_s *cfile, int type); + +/* ioctl function for each cmds of ioctl */ + +static int codec_querycap(FAR struct file *filep, + FAR struct v4l2_capability *cap); +static int codec_reqbufs(FAR struct file *filep, + FAR struct v4l2_requestbuffers *reqbufs); +static int codec_querybuf(FAR struct file *filep, + FAR struct v4l2_buffer *buf); +static int codec_qbuf(FAR struct file *filep, + FAR struct v4l2_buffer *buf); +static int codec_dqbuf(FAR struct file *filep, + FAR struct v4l2_buffer *buf); +static int codec_g_fmt(FAR struct file *filep, + FAR struct v4l2_format *fmt); +static int codec_s_fmt(FAR struct file *filep, + FAR struct v4l2_format *fmt); +static int codec_try_fmt(FAR struct file *filep, + FAR struct v4l2_format *fmt); +static int codec_g_parm(FAR struct file *filep, + FAR struct v4l2_streamparm *parm); +static int codec_s_parm(FAR struct file *filep, + FAR struct v4l2_streamparm *parm); +static int codec_streamon(FAR struct file *filep, + FAR enum v4l2_buf_type *type); +static int codec_streamoff(FAR struct file *filep, + FAR enum v4l2_buf_type *type); +static int codec_g_selection(FAR struct file *filep, + FAR struct v4l2_selection *clip); +static int codec_s_selection(FAR struct file *filep, + FAR struct v4l2_selection *clip); +static int codec_g_ext_ctrls(FAR struct file *filep, + FAR struct v4l2_ext_controls *ctrls); +static int codec_s_ext_ctrls(FAR struct file *filep, + FAR struct v4l2_ext_controls *ctrls); +static int codec_enum_fmt(FAR struct file *filep, + FAR struct v4l2_fmtdesc *fmt); +static int codec_cropcap(FAR struct file *filep, + FAR struct v4l2_cropcap *cropcap); +static int codec_dqevent(FAR struct file *filep, + FAR struct v4l2_event *event); +static int codec_subscribe_event(FAR struct file *filep, + FAR struct v4l2_event_subscription *sub); +static int codec_decoder_cmd(FAR struct file *filep, + FAR struct v4l2_decoder_cmd *cmd); +static int codec_encoder_cmd(FAR struct file *filep, + FAR struct v4l2_encoder_cmd *cmd); + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static const struct v4l2_ops_s g_codec_vops = +{ + codec_querycap, /* querycap */ + NULL, /* g_input */ + NULL, /* enum_input */ + codec_reqbufs, /* reqbufs */ + codec_querybuf, /* querybuf */ + codec_qbuf, /* qbuf */ + codec_dqbuf, /* dqbuf */ + NULL, /* cancel_dqbuf */ + codec_g_fmt, /* g_fmt */ + codec_s_fmt, /* s_fmt */ + codec_try_fmt, /* try_fmt */ + codec_g_parm, /* g_parm */ + codec_s_parm, /* s_parm */ + codec_streamon, /* streamon */ + codec_streamoff, /* streamoff */ + NULL, /* do_halfpush */ + NULL, /* takepict_start */ + NULL, /* takepict_stop */ + codec_s_selection, /* s_selection */ + codec_g_selection, /* g_selection */ + NULL, /* queryctrl */ + NULL, /* query_ext_ctrl */ + NULL, /* querymenu */ + NULL, /* g_ctrl */ + NULL, /* s_ctrl */ + codec_g_ext_ctrls, /* g_ext_ctrls */ + codec_s_ext_ctrls, /* s_ext_ctrls */ + NULL, /* query_ext_ctrl_scene */ + NULL, /* querymenu_scene */ + NULL, /* g_ext_ctrls_scene */ + NULL, /* s_ext_ctrls_scene */ + codec_enum_fmt, /* enum_fmt */ + NULL, /* enum_frminterval */ + NULL, /* enum_frmsize */ + codec_cropcap, /* cropcap */ + codec_dqevent, /* dqevent */ + codec_subscribe_event, /* subscribe_event */ + codec_decoder_cmd, /* decoder_cmd */ + codec_encoder_cmd /* encoder_cmd */ +}; + +static const struct file_operations g_codec_fops = +{ + codec_open, /* open */ + codec_close, /* close */ + NULL, /* read */ + NULL, /* write */ + NULL, /* seek */ + NULL, /* ioctl */ + codec_mmap, /* mmap */ + NULL, /* truncate */ + codec_poll, /* poll */ +}; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +static FAR codec_type_inf_t * +codec_get_type_inf(FAR struct codec_file_s *cfile, int type) +{ + if (V4L2_TYPE_IS_OUTPUT(type)) + { + return &cfile->output_inf; + } + else + { + return &cfile->capture_inf; + } +} + +static int codec_querycap(FAR struct file *filep, + FAR struct v4l2_capability *cap) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + return CODEC_QUERYCAP(cmng->codec, cfile->priv, cap); +} + +static int codec_enum_fmt(FAR struct file *filep, + FAR struct v4l2_fmtdesc *fmt) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (fmt == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(fmt->type)) + { + return CODEC_OUTPUT_ENUM_FMT(cmng->codec, cfile->priv, fmt); + } + else + { + return CODEC_CAPTURE_ENUM_FMT(cmng->codec, cfile->priv, fmt); + } +} + +static int codec_reqbufs(FAR struct file *filep, + FAR struct v4l2_requestbuffers *reqbufs) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + FAR codec_type_inf_t *type_inf; + irqstate_t flags; + size_t buf_size; + int ret = OK; + + if (reqbufs == NULL) + { + return -EINVAL; + } + + reqbufs->mode = V4L2_BUF_MODE_FIFO; + if (reqbufs->count > V4L2_REQBUFS_COUNT_MAX) + { + reqbufs->count = V4L2_REQBUFS_COUNT_MAX; + } + + if (V4L2_TYPE_IS_OUTPUT(reqbufs->type)) + { + buf_size = CODEC_OUTPUT_G_BUFSIZE(cmng->codec, cfile->priv); + } + else + { + buf_size = CODEC_CAPTURE_G_BUFSIZE(cmng->codec, cfile->priv); + } + + if (buf_size == 0) + { + return -EINVAL; + } + + flags = enter_critical_section(); + + type_inf = codec_get_type_inf(cfile, reqbufs->type); + video_framebuff_change_mode(&type_inf->bufinf, reqbufs->mode); + ret = video_framebuff_realloc_container(&type_inf->bufinf, + reqbufs->count); + if (ret == 0 && reqbufs->memory == V4L2_MEMORY_MMAP) + { + kumm_free(type_inf->bufheap); + type_inf->bufheap = kumm_memalign(32, reqbufs->count * buf_size); + if (type_inf->bufheap == NULL) + { + ret = -ENOMEM; + } + } + + leave_critical_section(flags); + return ret; +} + +static int codec_querybuf(FAR struct file *filep, + FAR struct v4l2_buffer *buf) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + FAR codec_type_inf_t *type_inf; + + if (buf == NULL || buf->memory != V4L2_MEMORY_MMAP) + { + return -EINVAL; + } + + type_inf = codec_get_type_inf(cfile, buf->type); + if (type_inf == NULL) + { + return -EINVAL; + } + + if (buf->index >= type_inf->bufinf.container_size) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(buf->type)) + { + buf->length = CODEC_OUTPUT_G_BUFSIZE(cmng->codec, cfile->priv); + buf->m.offset = buf->length * buf->index; + } + else + { + buf->length = CODEC_CAPTURE_G_BUFSIZE(cmng->codec, cfile->priv); + buf->m.offset = buf->length * buf->index + CAPTURE_BUF_OFFSET; + } + + if (buf->length == 0) + { + return -EINVAL; + } + + return OK; +} + +static int codec_qbuf(FAR struct file *filep, + FAR struct v4l2_buffer *buf) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + FAR codec_type_inf_t *type_inf; + FAR vbuf_container_t *container; + size_t buf_size; + + if (buf == NULL) + { + return -EINVAL; + } + + type_inf = codec_get_type_inf(cfile, buf->type); + if (type_inf == NULL) + { + return -EINVAL; + } + + container = video_framebuff_get_container(&type_inf->bufinf); + if (container == NULL) + { + vwarn("get container failed\n"); + return -EAGAIN; + } + + memcpy(&container->buf, buf, sizeof(struct v4l2_buffer)); + if (buf->memory == V4L2_MEMORY_MMAP) + { + /* only use userptr inside the container */ + + if (V4L2_TYPE_IS_OUTPUT(buf->type)) + { + buf_size = CODEC_OUTPUT_G_BUFSIZE(cmng->codec, cfile->priv); + } + else + { + buf_size = CODEC_CAPTURE_G_BUFSIZE(cmng->codec, cfile->priv); + } + + if (buf_size == 0) + { + return -EINVAL; + } + + container->buf.length = buf_size; + container->buf.m.userptr = (unsigned long)(type_inf->bufheap + + container->buf.length * buf->index); + } + + video_framebuff_queue_container(&type_inf->bufinf, container); + + if (V4L2_TYPE_IS_OUTPUT(buf->type)) + { + return CODEC_OUTPUT_AVAILABLE(cmng->codec, cfile->priv); + } + else + { + return CODEC_CAPTURE_AVAILABLE(cmng->codec, cfile->priv); + } +} + +static int codec_dqbuf(FAR struct file *filep, + FAR struct v4l2_buffer *buf) +{ + FAR codec_file_t *cfile = filep->f_priv; + FAR codec_type_inf_t *type_inf; + FAR vbuf_container_t *container; + irqstate_t flags; + + if (buf == NULL) + { + return -EINVAL; + } + + type_inf = codec_get_type_inf(cfile, buf->type); + if (type_inf == NULL) + { + return -EINVAL; + } + + flags = enter_critical_section(); + + if (video_framebuff_is_empty(&type_inf->bufinf)) + { + leave_critical_section(flags); + return -EAGAIN; + } + + container = video_framebuff_dq_valid_container(&type_inf->bufinf); + if (container == NULL) + { + leave_critical_section(flags); + return -EAGAIN; + } + + memcpy(buf, &container->buf, sizeof(struct v4l2_buffer)); + video_framebuff_free_container(&type_inf->bufinf, container); + + vinfo("%s dequeue done\n", V4L2_TYPE_IS_OUTPUT(buf->type) ? + "output" : "capture"); + + leave_critical_section(flags); + return OK; +} + +static int codec_s_selection(FAR struct file *filep, + FAR struct v4l2_selection *clip) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (clip == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(clip->type)) + { + return CODEC_OUTPUT_S_SELECTION(cmng->codec, cfile->priv, clip); + } + else + { + return CODEC_CAPTURE_S_SELECTION(cmng->codec, cfile->priv, clip); + } +} + +static int codec_g_selection(FAR struct file *filep, + FAR struct v4l2_selection *clip) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (clip == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(clip->type)) + { + return CODEC_OUTPUT_G_SELECTION(cmng->codec, cfile->priv, clip); + } + else + { + return CODEC_CAPTURE_G_SELECTION(cmng->codec, cfile->priv, clip); + } +} + +static int codec_g_ext_ctrls(FAR struct file *filep, + FAR struct v4l2_ext_controls *ctrls) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (ctrls == NULL) + { + return -EINVAL; + } + + return CODEC_G_EXT_CTRLS(cmng->codec, cfile->priv, ctrls); +} + +static int codec_s_ext_ctrls(FAR struct file *filep, + FAR struct v4l2_ext_controls *ctrls) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (ctrls == NULL) + { + return -EINVAL; + } + + return CODEC_S_EXT_CTRLS(cmng->codec, cfile->priv, ctrls); +} + +static int codec_try_fmt(FAR struct file *filep, + FAR struct v4l2_format *fmt) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (fmt == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(fmt->type)) + { + return CODEC_OUTPUT_TRY_FMT(cmng->codec, cfile->priv, fmt); + } + else + { + return CODEC_CAPTURE_TRY_FMT(cmng->codec, cfile->priv, fmt); + } +} + +static int codec_g_fmt(FAR struct file *filep, + FAR struct v4l2_format *fmt) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (fmt == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(fmt->type)) + { + return CODEC_OUTPUT_G_FMT(cmng->codec, cfile->priv, fmt); + } + else + { + return CODEC_CAPTURE_G_FMT(cmng->codec, cfile->priv, fmt); + } +} + +static int codec_s_fmt(FAR struct file *filep, + FAR struct v4l2_format *fmt) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (fmt == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(fmt->type)) + { + return CODEC_OUTPUT_S_FMT(cmng->codec, cfile->priv, fmt); + } + else + { + return CODEC_CAPTURE_S_FMT(cmng->codec, cfile->priv, fmt); + } +} + +static int codec_g_parm(FAR struct file *filep, + FAR struct v4l2_streamparm *parm) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (parm == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(parm->type)) + { + return CODEC_OUTPUT_G_PARM(cmng->codec, cfile->priv, parm); + } + else + { + return CODEC_CAPTURE_G_PARM(cmng->codec, cfile->priv, parm); + } +} + +static int codec_s_parm(FAR struct file *filep, + FAR struct v4l2_streamparm *parm) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (parm == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(parm->type)) + { + return CODEC_OUTPUT_S_PARM(cmng->codec, cfile->priv, parm); + } + else + { + return CODEC_CAPTURE_S_PARM(cmng->codec, cfile->priv, parm); + } +} + +static int codec_streamon(FAR struct file *filep, + FAR enum v4l2_buf_type *type) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + FAR codec_type_inf_t *type_inf; + + if (type == NULL) + { + return -EINVAL; + } + + type_inf = codec_get_type_inf(cfile, *type); + if (type_inf == NULL) + { + return -EINVAL; + } + + type_inf->buflast = false; + + if (V4L2_TYPE_IS_OUTPUT(*type)) + { + return CODEC_OUTPUT_STREAMON(cmng->codec, cfile->priv); + } + else + { + return CODEC_CAPTURE_STREAMON(cmng->codec, cfile->priv); + } +} + +static int codec_streamoff(FAR struct file *filep, + FAR enum v4l2_buf_type *type) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + FAR codec_type_inf_t *type_inf; + + if (type == NULL) + { + return -EINVAL; + } + + type_inf = codec_get_type_inf(cfile, *type); + if (type_inf == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(*type)) + { + return CODEC_OUTPUT_STREAMOFF(cmng->codec, cfile->priv); + } + else + { + return CODEC_CAPTURE_STREAMOFF(cmng->codec, cfile->priv); + } +} + +int codec_cropcap(FAR struct file *filep, + FAR struct v4l2_cropcap *cropcap) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (cropcap == NULL) + { + return -EINVAL; + } + + if (V4L2_TYPE_IS_OUTPUT(cropcap->type)) + { + return CODEC_OUTPUT_CROPCAP(cmng->codec, cfile->priv, cropcap); + } + else + { + return CODEC_CAPTURE_CROPCAP(cmng->codec, cfile->priv, cropcap); + } +} + +int codec_dqevent(FAR struct file *filep, + FAR struct v4l2_event *event) +{ + FAR codec_file_t *cfile = filep->f_priv; + FAR codec_event_t *cevt; + irqstate_t flags; + + if (event == NULL) + { + return -EINVAL; + } + + flags = enter_critical_section(); + + if (sq_empty(&cfile->event_avail)) + { + leave_critical_section(flags); + return -ENOENT; + } + + cevt = (FAR codec_event_t *)sq_remfirst(&cfile->event_avail); + memcpy(event, &cevt->event, sizeof(struct v4l2_event)); + sq_addlast((FAR sq_entry_t *)cevt, &cfile->event_free); + + leave_critical_section(flags); + return OK; +} + +int codec_subscribe_event(FAR struct file *filep, + FAR struct v4l2_event_subscription *sub) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (sub == NULL) + { + return -EINVAL; + } + + return CODEC_SUBSCRIBE_EVENT(cmng->codec, cfile->priv, sub); +} + +int codec_decoder_cmd(FAR struct file *filep, + FAR struct v4l2_decoder_cmd *cmd) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (cmd == NULL) + { + return -EINVAL; + } + + return CODEC_DECODER_CMD(cmng->codec, cfile->priv, cmd); +} + +int codec_encoder_cmd(FAR struct file *filep, + FAR struct v4l2_encoder_cmd *cmd) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + if (cmd == NULL) + { + return -EINVAL; + } + + return CODEC_ENCODER_CMD(cmng->codec, cfile->priv, cmd); +} + +/* file operations */ + +static int codec_open(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR struct codec_mng_s *cmng = inode->i_private; + FAR struct codec_file_s *cfile; + int ret; + int i; + + cfile = kmm_zalloc(sizeof(struct codec_file_s)); + if (cfile == NULL) + { + return -ENOMEM; + } + + filep->f_priv = cfile; + + ret = CODEC_OPEN(cmng->codec, cfile, &cfile->priv); + if (ret != OK) + { + kmm_free(cfile); + return ret; + } + + sq_init(&cfile->event_avail); + sq_init(&cfile->event_free); + + for (i = 0; i < CODEC_EVENT_COUNT; i++) + { + sq_addlast((FAR sq_entry_t *)&cfile->event_pool[i], + &cfile->event_free); + } + + video_framebuff_init(&cfile->capture_inf.bufinf); + video_framebuff_init(&cfile->output_inf.bufinf); + + return OK; +} + +static int codec_close(FAR struct file *filep) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + + CODEC_CLOSE(cmng->codec, cfile->priv); + + video_framebuff_uninit(&cfile->capture_inf.bufinf); + video_framebuff_uninit(&cfile->output_inf.bufinf); + kumm_free(cfile->capture_inf.bufheap); + kumm_free(cfile->output_inf.bufheap); + kmm_free(cfile); + + return OK; +} + +static int codec_munmap(FAR struct task_group_s *group, + FAR struct mm_map_entry_s *entry, + FAR void *start, size_t length) +{ + return mm_map_remove(get_group_mm(group), entry); +} + +static int codec_mmap(FAR struct file *filep, + FAR struct mm_map_entry_s *map) +{ + FAR struct inode *inode = filep->f_inode; + FAR codec_mng_t *cmng = inode->i_private; + FAR codec_file_t *cfile = filep->f_priv; + FAR codec_type_inf_t *type_inf; + int ret = -EINVAL; + size_t total_size; + size_t buf_size; + + if (map == NULL) + { + return -EINVAL; + } + + if (map->offset < CAPTURE_BUF_OFFSET) + { + type_inf = &cfile->output_inf; + buf_size = CODEC_OUTPUT_G_BUFSIZE(cmng->codec, cfile->priv); + } + else + { + type_inf = &cfile->capture_inf; + map->offset -= CAPTURE_BUF_OFFSET; + buf_size = CODEC_CAPTURE_G_BUFSIZE(cmng->codec, cfile->priv); + } + + if (buf_size == 0) + { + return -EINVAL; + } + + total_size = type_inf->bufinf.container_size * buf_size; + if (map->offset >= 0 && map->offset < total_size && + map->length && map->offset + map->length <= total_size) + { + map->vaddr = type_inf->bufheap + map->offset; + map->munmap = codec_munmap; + ret = mm_map_add(get_current_mm(), map); + } + + return ret; +} + +static int codec_poll(FAR struct file *filep, + FAR struct pollfd *fds, bool setup) +{ + FAR codec_file_t *cfile = filep->f_priv; + pollevent_t eventset = 0; + irqstate_t flags; + + flags = enter_critical_section(); + + if (setup) + { + if (cfile->fds == NULL) + { + cfile->fds = fds; + fds->priv = &cfile->fds; + + if (!video_framebuff_is_empty(&cfile->output_inf.bufinf)) + { + eventset |= POLLOUT; + } + + if (cfile->capture_inf.buflast || + !video_framebuff_is_empty(&cfile->capture_inf.bufinf)) + { + eventset |= POLLIN; + } + + if (!sq_empty(&cfile->event_avail)) + { + eventset |= POLLPRI; + } + + if (eventset > 0) + { + poll_notify(&cfile->fds, 1, eventset); + } + } + else + { + leave_critical_section(flags); + return -EBUSY; + } + } + else if (fds->priv) + { + cfile->fds = NULL; + fds->priv = NULL; + } + + leave_critical_section(flags); + return OK; +} + +static FAR struct v4l2_buffer *codec_get_buf(FAR codec_type_inf_t *type_inf) +{ + FAR vbuf_container_t *container; + + container = video_framebuff_get_vacant_container(&type_inf->bufinf); + if (container == NULL) + { + vinfo("No buffer available\n"); + return NULL; + } + + return &container->buf; +} + +static int codec_put_buf(FAR codec_file_t *cfile, + FAR codec_type_inf_t *type_inf, + FAR struct v4l2_buffer *buf) +{ + if (cfile == NULL || type_inf == NULL || buf == NULL) + { + return -EINVAL; + } + + if (buf->flags & V4L2_BUF_FLAG_LAST) + { + type_inf->buflast = true; + } + + video_framebuff_capture_done(&type_inf->bufinf); + poll_notify(&cfile->fds, 1, + V4L2_TYPE_IS_OUTPUT(buf->type) ? POLLOUT : POLLIN); + + return OK; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +int codec_register(FAR const char *devpath, FAR struct codec_s *codec) +{ + FAR struct codec_mng_s *cmng; + int ret; + + if (devpath == NULL || codec == NULL) + { + return -EINVAL; + } + + cmng = kmm_zalloc(sizeof(struct codec_mng_s)); + if (cmng == NULL) + { + verr("Failed to allocate codec instance\n"); + return -ENOMEM; + } + + cmng->v4l2.vops = &g_codec_vops; + cmng->v4l2.fops = &g_codec_fops; + cmng->codec = codec; + + /* Register the character driver */ + + ret = video_register(devpath, (FAR struct v4l2_s *)cmng); + if (ret < 0) + { + verr("Failed to register driver: %d\n", ret); + kmm_free(cmng); + return ret; + } + + return OK; +} + +int codec_unregister(FAR const char *devpath) +{ + return unregister_driver(devpath); +} + +FAR struct v4l2_buffer *codec_output_get_buf(void *cookie) +{ + FAR codec_file_t *cfile = cookie; + + return codec_get_buf(&cfile->output_inf); +} + +FAR struct v4l2_buffer *codec_capture_get_buf(void *cookie) +{ + FAR codec_file_t *cfile = cookie; + + return codec_get_buf(&cfile->capture_inf); +} + +int codec_output_put_buf(FAR void *cookie, FAR struct v4l2_buffer *buf) +{ + FAR codec_file_t *cfile = cookie; + + return codec_put_buf(cfile, &cfile->output_inf, buf); +} + +int codec_capture_put_buf(FAR void *cookie, FAR struct v4l2_buffer *buf) +{ + FAR codec_file_t *cfile = cookie; + + return codec_put_buf(cfile, &cfile->capture_inf, buf); +} + +int codec_queue_event(FAR void *cookie, FAR struct v4l2_event *evt) +{ + FAR codec_file_t *cfile = cookie; + FAR codec_event_t *cevt; + irqstate_t flags; + + flags = enter_critical_section(); + + cevt = (FAR codec_event_t *)sq_remfirst(&cfile->event_free); + if (cevt == NULL) + { + leave_critical_section(flags); + return -EINVAL; + } + + memcpy(&cevt->event, evt, sizeof(struct v4l2_event)); + sq_addlast((FAR sq_entry_t *)cevt, &cfile->event_avail); + + poll_notify(&cfile->fds, 1, POLLPRI); + leave_critical_section(flags); + + return OK; +} + diff --git a/include/nuttx/video/v4l2_m2m.h b/include/nuttx/video/v4l2_m2m.h new file mode 100644 index 00000000000..2b3f4767d50 --- /dev/null +++ b/include/nuttx/video/v4l2_m2m.h @@ -0,0 +1,307 @@ +/**************************************************************************** + * include/nuttx/video/v4l2_m2m.h + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. The + * ASF licenses this file to you 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_NUTTX_VIDEO_V4L2_M2M_H +#define __INCLUDE_NUTTX_VIDEO_V4L2_M2M_H + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/* Method access helper macros */ + +#define CODEC_OPEN(codec, cookie, priv) \ + ((codec)->ops->open ? \ + (codec)->ops->open(cookie, priv) : -ENOTTY) + +#define CODEC_CLOSE(codec, priv) \ + ((codec)->ops->close ? \ + (codec)->ops->close(priv) : -ENOTTY) + +#define CODEC_CAPTURE_STREAMON(codec, priv) \ + ((codec)->ops->capture_streamon ? \ + (codec)->ops->capture_streamon(priv) : -ENOTTY) + +#define CODEC_OUTPUT_STREAMON(codec, priv) \ + ((codec)->ops->output_streamon ? \ + (codec)->ops->output_streamon(priv) : -ENOTTY) + +#define CODEC_CAPTURE_STREAMOFF(codec, priv) \ + ((codec)->ops->capture_streamoff ? \ + (codec)->ops->capture_streamoff(priv) : -ENOTTY) + +#define CODEC_OUTPUT_STREAMOFF(codec, priv) \ + ((codec)->ops->output_streamoff ? \ + (codec)->ops->output_streamoff(priv) : -ENOTTY) + +#define CODEC_CAPTURE_AVAILABLE(codec, priv) \ + ((codec)->ops->capture_available ? \ + (codec)->ops->capture_available(priv) : 0) + +#define CODEC_OUTPUT_AVAILABLE(codec, priv) \ + ((codec)->ops->output_available ? \ + (codec)->ops->output_available(priv) : -ENOTTY) + +#define CODEC_QUERYCAP(codec, priv, cap) \ + ((codec)->ops->querycap ? \ + (codec)->ops->querycap(priv, cap) : -ENOTTY) + +#define CODEC_CAPTURE_ENUM_FMT(codec, priv, fmt) \ + ((codec)->ops->capture_enum_fmt ? \ + (codec)->ops->capture_enum_fmt(priv, fmt) : -ENOTTY) + +#define CODEC_OUTPUT_ENUM_FMT(codec, priv, fmt) \ + ((codec)->ops->output_enum_fmt ? \ + (codec)->ops->output_enum_fmt(priv, fmt) : -ENOTTY) + +#define CODEC_CAPTURE_G_FMT(codec, priv, fmt) \ + ((codec)->ops->capture_g_fmt ? \ + (codec)->ops->capture_g_fmt(priv, fmt) : -ENOTTY) + +#define CODEC_OUTPUT_G_FMT(codec, priv, fmt) \ + ((codec)->ops->output_g_fmt ? \ + (codec)->ops->output_g_fmt(priv, fmt) : -ENOTTY) + +#define CODEC_CAPTURE_S_FMT(codec, priv, fmt) \ + ((codec)->ops->capture_s_fmt ? \ + (codec)->ops->capture_s_fmt(priv, fmt) : -ENOTTY) + +#define CODEC_OUTPUT_S_FMT(codec, priv, fmt) \ + ((codec)->ops->output_s_fmt ? \ + (codec)->ops->output_s_fmt(priv, fmt) : -ENOTTY) + +#define CODEC_CAPTURE_TRY_FMT(codec, priv, fmt) \ + ((codec)->ops->capture_try_fmt ? \ + (codec)->ops->capture_try_fmt(priv, fmt) : -ENOTTY) + +#define CODEC_OUTPUT_TRY_FMT(codec, priv, fmt) \ + ((codec)->ops->output_try_fmt ? \ + (codec)->ops->output_try_fmt(priv, fmt) : -ENOTTY) + +#define CODEC_CAPTURE_G_PARM(codec, priv, parm) \ + ((codec)->ops->capture_g_parm ? \ + (codec)->ops->capture_g_parm(priv, parm) : -ENOTTY) + +#define CODEC_OUTPUT_G_PARM(codec, priv, parm) \ + ((codec)->ops->output_g_parm ? \ + (codec)->ops->output_g_parm(priv, parm) : -ENOTTY) + +#define CODEC_CAPTURE_S_PARM(codec, priv, parm) \ + ((codec)->ops->capture_s_parm ? \ + (codec)->ops->capture_s_parm(priv, parm) : -ENOTTY) + +#define CODEC_OUTPUT_S_PARM(codec, priv, parm) \ + ((codec)->ops->output_s_parm ? \ + (codec)->ops->output_s_parm(priv, parm) : -ENOTTY) + +#define CODEC_G_EXT_CTRLS(codec, priv, ctrls) \ + ((codec)->ops->g_ext_ctrls ? \ + (codec)->ops->g_ext_ctrls(priv, ctrls) : -ENOTTY) + +#define CODEC_S_EXT_CTRLS(codec, priv, ctrls) \ + ((codec)->ops->s_ext_ctrls ? \ + (codec)->ops->s_ext_ctrls(priv, ctrls) : -ENOTTY) + +#define CODEC_CAPTURE_G_SELECTION(codec, priv, clip) \ + ((codec)->ops->capture_g_selection ? \ + (codec)->ops->capture_g_selection(priv, clip) : -ENOTTY) + +#define CODEC_OUTPUT_G_SELECTION(codec, priv, clip) \ + ((codec)->ops->output_g_selection ? \ + (codec)->ops->output_g_selection(priv, clip) : -ENOTTY) + +#define CODEC_CAPTURE_S_SELECTION(codec, priv, clip) \ + ((codec)->ops->capture_s_selection ? \ + (codec)->ops->capture_s_selection(priv, clip) : -ENOTTY) + +#define CODEC_OUTPUT_S_SELECTION(codec, priv, clip) \ + ((codec)->ops->output_s_selection ? \ + (codec)->ops->output_s_selection(priv, clip) : -ENOTTY) + +#define CODEC_CAPTURE_CROPCAP(codec, priv, cropcap) \ + ((codec)->ops->capture_cropcap ? \ + (codec)->ops->capture_cropcap(priv, cropcap) : -ENOTTY) + +#define CODEC_OUTPUT_CROPCAP(codec, priv, cropcap) \ + ((codec)->ops->output_cropcap ? \ + (codec)->ops->output_cropcap(priv, cropcap) : -ENOTTY) + +#define CODEC_DQEVENT(codec, priv, event) \ + ((codec)->ops->dqevent ? \ + (codec)->ops->dqevent(priv, event) : -ENOTTY) + +#define CODEC_SUBSCRIBE_EVENT(codec, priv, sub) \ + ((codec)->ops->subscribe_event ? \ + (codec)->ops->subscribe_event(priv, sub) : -ENOTTY) + +#define CODEC_DECODER_CMD(codec, priv, cmd) \ + ((codec)->ops->decoder_cmd ? \ + (codec)->ops->decoder_cmd(priv, cmd) : -ENOTTY) + +#define CODEC_ENCODER_CMD(codec, priv, cmd) \ + ((codec)->ops->encoder_cmd ? \ + (codec)->ops->encoder_cmd(priv, cmd) : -ENOTTY) + +#define CODEC_CAPTURE_G_BUFSIZE(codec, priv) \ + ((codec)->ops->capture_g_bufsize ? \ + (codec)->ops->capture_g_bufsize(priv) : -ENOTTY) + +#define CODEC_OUTPUT_G_BUFSIZE(codec, priv) \ + ((codec)->ops->output_g_bufsize ? \ + (codec)->ops->output_g_bufsize(priv) : -ENOTTY) + +/**************************************************************************** + * Public Types + ****************************************************************************/ + +#ifdef __cplusplus +#define EXTERN extern "C" +extern "C" +{ +#else +#define EXTERN extern +#endif + +struct codec_s; +struct codec_ops_s +{ + CODE int (*open)(FAR void *cookie, FAR void **priv); + CODE int (*close)(FAR void *priv); + + CODE int (*capture_streamon)(FAR void *priv); + CODE int (*output_streamon)(FAR void *priv); + CODE int (*capture_streamoff)(FAR void *priv); + CODE int (*output_streamoff)(FAR void *priv); + + CODE int (*capture_available)(FAR void *priv); + CODE int (*output_available)(FAR void *priv); + + /* VIDIOC_QUERYCAP handler */ + + CODE int (*querycap)(FAR void *priv, + FAR struct v4l2_capability *cap); + + /* VIDIOC_ENUM_FMT handlers */ + + CODE int (*capture_enum_fmt)(FAR void *priv, + FAR struct v4l2_fmtdesc *fmt); + + CODE int (*output_enum_fmt)(FAR void *priv, + FAR struct v4l2_fmtdesc *fmt); + + /* VIDIOC_G_FMT handlers */ + + CODE int (*capture_g_fmt)(FAR void *priv, + FAR struct v4l2_format *fmt); + CODE int (*output_g_fmt)(FAR void *priv, + FAR struct v4l2_format *fmt); + + /* VIDIOC_S_FMT handlers */ + + CODE int (*capture_s_fmt)(FAR void *priv, + FAR struct v4l2_format *fmt); + CODE int (*output_s_fmt)(FAR void *priv, + FAR struct v4l2_format *fmt); + + /* VIDIOC_TRY_FMT handlers */ + + CODE int (*capture_try_fmt)(FAR void *priv, + FAR struct v4l2_format *fmt); + CODE int (*output_try_fmt)(FAR void *priv, + FAR struct v4l2_format *fmt); + + /* Buffer handlers */ + + CODE size_t (*capture_g_bufsize)(FAR void *priv); + CODE size_t (*output_g_bufsize)(FAR void *priv); + + /* Stream type-dependent parameter ioctls */ + + CODE int (*capture_g_parm)(FAR void *priv, + FAR struct v4l2_streamparm *parm); + CODE int (*output_g_parm)(FAR void *priv, + FAR struct v4l2_streamparm *parm); + CODE int (*capture_s_parm)(FAR void *priv, + FAR struct v4l2_streamparm *parm); + CODE int (*output_s_parm)(FAR void *priv, + FAR struct v4l2_streamparm *parm); + + /* Control handlers */ + + CODE int (*g_ext_ctrls)(FAR void *priv, + FAR struct v4l2_ext_controls *ctrls); + CODE int (*s_ext_ctrls)(FAR void *priv, + FAR struct v4l2_ext_controls *ctrls); + + /* Crop ioctls */ + + CODE int (*capture_g_selection)(FAR void *priv, + FAR struct v4l2_selection *clip); + CODE int (*output_g_selection)(FAR void *priv, + FAR struct v4l2_selection *clip); + CODE int (*capture_s_selection)(FAR void *priv, + FAR struct v4l2_selection *clip); + CODE int (*output_s_selection)(FAR void *priv, + FAR struct v4l2_selection *clip); + CODE int (*capture_cropcap)(FAR void *priv, + FAR struct v4l2_cropcap *cropcap); + CODE int (*output_cropcap)(FAR void *priv, + FAR struct v4l2_cropcap *cropcap); + + /* Event handlers */ + + CODE int (*subscribe_event)(FAR void *priv, + FAR struct v4l2_event_subscription *sub); + + /* Command handlers */ + + CODE int (*decoder_cmd)(FAR void *priv, + FAR struct v4l2_decoder_cmd *cmd); + CODE int (*encoder_cmd)(FAR void *priv, + FAR struct v4l2_encoder_cmd *cmd); +}; + +struct codec_s +{ + FAR const struct codec_ops_s *ops; +}; + +/**************************************************************************** + * Public Function Prototypes + ****************************************************************************/ + +int codec_register(FAR const char *devpath, FAR struct codec_s *codec); +int codec_unregister(FAR const char *devpath); +FAR struct v4l2_buffer *codec_output_get_buf(FAR void *cookie); +FAR struct v4l2_buffer *codec_capture_get_buf(FAR void *cookie); +int codec_output_put_buf(FAR void *cookie, FAR struct v4l2_buffer *buf); +int codec_capture_put_buf(FAR void *cookie, FAR struct v4l2_buffer *buf); +int codec_queue_event(FAR void *cookie, FAR struct v4l2_event *evt); + +#ifdef __cplusplus +} +#endif + +#endif /* __INCLUDE_NUTTX_VIDEO_V4L2_M2M_H */ diff --git a/tools/nxstyle.c b/tools/nxstyle.c index 31e1ed96ac3..8dab2a29afe 100644 --- a/tools/nxstyle.c +++ b/tools/nxstyle.c @@ -451,6 +451,35 @@ static const char *g_white_content_list[] = "CurrentTime", "XUnmapWindow", + /* Ref: + * nuttx/arch/sim/src/sim_hostdecoder.* + */ + + "ISVCDecoder", + "SBufferInfo", + "SDecodingParam", + "eEcActiveIdc", + "sVideoProperty", + "eVideoBsType", + "cmResultSuccess", + "uiInBsTimeStamp", + "dsErrorFree", + "iBufferStatus", + "UsrData", + "sSystemBuffer", + "iWidth", + "iHeight", + "iStride", + "uiOutYuvTimeStamp", + "WelsCreateDecoder", + "WelsDestroyDecoder", + "Initialize", + "Uninitialize", + "DecodeFrame2", + "FlushFrame", + "SetOption", + "GetOption", + /* Ref: * sim/posix/sim_deviceimage.c */