/**************************************************************************** * apps/system/nxcodec/nxcodec_context.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 #include #include "nxcodec_context.h" #include "nxcodec.h" /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define NXCODEC_CONTEXT_BUFNUMBER 3 /**************************************************************************** * Private Functions ****************************************************************************/ static inline FAR nxcodec_t * nxcodec_context_to_nxcodec(FAR nxcodec_context_t *ctx) { return V4L2_TYPE_IS_OUTPUT(ctx->type) ? container_of(ctx, nxcodec_t, output) : container_of(ctx, nxcodec_t, capture); } static FAR nxcodec_context_buf_t * nxcodec_context_dequeue_buf(FAR nxcodec_context_t *ctx) { FAR nxcodec_t *codec = nxcodec_context_to_nxcodec(ctx); struct v4l2_buffer buf; int ret; memset(&buf, 0, sizeof(buf)); buf.memory = V4L2_MEMORY_MMAP; buf.type = ctx->type; ret = ioctl(codec->fd, VIDIOC_DQBUF, &buf); if (ret < 0) { printf("%s: VIDIOC_DQBUF - %s\n", V4L2_TYPE_IS_OUTPUT(ctx->type) ? "output" : "capture", strerror(errno)); return NULL; } ctx->buf[buf.index].free = true; ctx->buf[buf.index].buf = buf; return &ctx->buf[buf.index]; } static FAR nxcodec_context_buf_t * nxcodec_context_get_freebuf(FAR nxcodec_context_t *ctx) { int i; if (V4L2_TYPE_IS_OUTPUT(ctx->type)) { while (nxcodec_context_dequeue_buf(ctx)); } for (i = 0; i < ctx->nbuffers; i++) { if (ctx->buf[i].free) { return &ctx->buf[i]; } } return NULL; } static int nxcodec_context_write_data(FAR nxcodec_context_t *ctx, FAR const char *buf, int size) { return write(ctx->fd, buf, size) < 0 ? -errno : 0; } static int nxcodec_context_read_yuv_data(FAR nxcodec_context_t *ctx, FAR char *buf, FAR uint32_t *bytesused) { size_t buflen = ctx->format.fmt.pix.width * ctx->format.fmt.pix.height * 3 / 2; ssize_t ret; ret = read(ctx->fd, buf, buflen); if (ret <= 0) { return -errno; } *bytesused = ret; return 0; } static int nxcodec_context_read_h264_data(FAR nxcodec_context_t *ctx, FAR char *buf, FAR uint32_t *bytesused) { char start_code[4]; ssize_t ret; int size; memset(start_code, 0, 4); ret = read(ctx->fd, buf, 4); if (ret <= 0) { return -errno; } if (buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x00 && buf[3] == 0x01) { size = 4; while (1) { ret = read(ctx->fd, buf + size, 1); if (ret < 0) { return -errno; } else if (ret == 0) { break; } start_code[0] = start_code[1]; start_code[1] = start_code[2]; start_code[2] = start_code[3]; start_code[3] = *(buf + size); size++; if (start_code[0] == 0x00 && start_code[1] == 0x00 && start_code[2] == 0x00 && start_code[3] == 0x01) { size -= 4; lseek(ctx->fd, -4, SEEK_CUR); break; } } } else { return -EINVAL; } *bytesused = size; return 0; } /**************************************************************************** * Public Functions ****************************************************************************/ int nxcodec_context_set_status(FAR nxcodec_context_t *ctx, uint32_t cmd) { FAR nxcodec_t *codec = nxcodec_context_to_nxcodec(ctx); return ioctl(codec->fd, cmd, &ctx->type) < 0 ? -errno : 0; } int nxcodec_context_enqueue_frame(FAR nxcodec_context_t *ctx) { FAR nxcodec_t *codec = nxcodec_context_to_nxcodec(ctx); FAR nxcodec_context_buf_t *buf; int ret; buf = nxcodec_context_get_freebuf(ctx); if (!buf) { return -EAGAIN; } if (ctx->format.fmt.pix.pixelformat == V4L2_PIX_FMT_H264) { ret = nxcodec_context_read_h264_data(ctx, buf->addr, &buf->buf.bytesused); if (ret < 0) { return ret; } } else if (ctx->format.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) { ret = nxcodec_context_read_yuv_data(ctx, buf->addr, &buf->buf.bytesused); if (ret < 0) { return ret; } } ret = ioctl(codec->fd, VIDIOC_QBUF, &buf->buf); if (ret < 0) { return -errno; } buf->free = false; return 0; } int nxcodec_context_dequeue_frame(FAR nxcodec_context_t *ctx) { FAR nxcodec_t *codec = nxcodec_context_to_nxcodec(ctx); FAR nxcodec_context_buf_t *buf; int ret; buf = nxcodec_context_dequeue_buf(ctx); if (!buf) { return -EAGAIN; } if (buf->buf.length > 0) { nxcodec_context_write_data(ctx, buf->addr, buf->buf.bytesused); } ret = ioctl(codec->fd, VIDIOC_QBUF, &buf->buf); if (ret < 0) { return -errno; } buf->free = false; return 0; } int nxcodec_context_get_format(FAR nxcodec_context_t *ctx) { FAR nxcodec_t *codec = nxcodec_context_to_nxcodec(ctx); struct v4l2_fmtdesc fdesc; int ret; fdesc.type = ctx->type; while (true) { ret = ioctl(codec->fd, VIDIOC_ENUM_FMT, &fdesc); if (ret < 0) { return -errno; } if (fdesc.pixelformat == ctx->format.fmt.pix.pixelformat) { break; } fdesc.index++; } ctx->format.type = ctx->type; return ioctl(codec->fd, VIDIOC_TRY_FMT, &ctx->format) < 0 ? -errno : 0; } int nxcodec_context_set_format(FAR nxcodec_context_t *ctx) { FAR nxcodec_t *codec = nxcodec_context_to_nxcodec(ctx); return ioctl(codec->fd, VIDIOC_S_FMT, &ctx->format) < 0 ? -errno : 0; } int nxcodec_context_init(FAR nxcodec_context_t *ctx) { FAR nxcodec_t *codec = nxcodec_context_to_nxcodec(ctx); struct v4l2_requestbuffers req; int ret; int i; memset(&req, 0, sizeof(req)); req.count = NXCODEC_CONTEXT_BUFNUMBER; req.memory = V4L2_MEMORY_MMAP; req.type = ctx->type; ret = ioctl(codec->fd, VIDIOC_REQBUFS, &req); if (ret < 0) { printf("type: %d VIDIOC_REQBUFS failed: %s\n", ctx->type, strerror(errno)); return -errno; } ctx->nbuffers = req.count; ctx->buf = calloc(ctx->nbuffers, sizeof(nxcodec_context_buf_t)); if (!ctx->buf) { printf("type: %d malloc enomem\n", ctx->type); return -ENOMEM; } for (i = 0; i < ctx->nbuffers; i++) { FAR nxcodec_context_buf_t *buf = &ctx->buf[i]; buf->buf.memory = V4L2_MEMORY_MMAP; buf->buf.type = ctx->type; buf->buf.index = i; ret = ioctl(codec->fd, VIDIOC_QUERYBUF, &buf->buf); if (ret < 0) { goto error; } buf->length = buf->buf.length; buf->addr = mmap(NULL, buf->buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, codec->fd, buf->buf.m.offset); if (buf->addr == MAP_FAILED) { goto error; } buf->free = true; if (V4L2_TYPE_IS_OUTPUT(ctx->type)) { continue; } ret = ioctl(codec->fd, VIDIOC_QBUF, &buf->buf); if (ret < 0) { munmap(buf->addr, buf->length); goto error; } buf->free = false; } return 0; error: free(ctx->buf); return -errno; } void nxcodec_context_uninit(FAR nxcodec_context_t *ctx) { int i; if (!ctx->buf) { return; } for (i = 0; i < ctx->nbuffers; i++) { FAR nxcodec_context_buf_t *buf = &ctx->buf[i]; if (buf->addr && buf->length) { if (munmap(buf->addr, buf->length) < 0) { printf("type: %d unmap plane (%s))\n", ctx->type, strerror(errno)); } } } free(ctx->buf); }