/* * 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 #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) { struct v4l2_capability cap; int ret; i->mfc.fd = open(i->mfc.name, O_RDWR, 0); if (i->mfc.fd < 0) { err("Failed to open MFC: %s", i->mfc.name); free(i->mfc.name); return -1; } memzero(cap); ret = ioctl(i->mfc.fd, VIDIOC_QUERYCAP, &cap); if (ret != 0) { err("Failed to verify capabilities"); free(i->mfc.name); return -1; } dbg("MFC Info (%s): driver=\"%s\" bus_info=\"%s\" card=\"%s\" fd=0x%x", i->mfc.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_VIDEO_M2M_MPLANE)) || !(cap.capabilities & V4L2_CAP_STREAMING)) { err("Insufficient capabilities of MFC device (is %s correct?)", i->mfc.name); free(i->mfc.name); return -1; } free(i->mfc.name); 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); memzero(planes); 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); memzero(planes); qbuf.type = type; qbuf.memory = V4L2_MEMORY_MMAP; qbuf.index = n; qbuf.m.planes = planes; qbuf.length = nplanes; qbuf.m.planes[0].data_offset = 0; qbuf.m.planes[1].data_offset = 0; 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; dbg("Preparing ioctl VIDIOC_DQBUF"); ret = ioctl(i->mfc.fd, VIDIOC_DQBUF, qbuf); dbg("Done ioctl VIDIOC_DQBUF"); 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; struct v4l2_exportbuffer expbuf; int ret; int n,d; memzero(fmt); 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_pixfmt = fmt.fmt.pix_mp.pixelformat; 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; memzero(ctrl); 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); memzero(planes); 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; } for(d=0; d<2;d++) { if (i->fimc.dmabuf) { memset(&expbuf, 0, sizeof(expbuf)); expbuf.index = n; expbuf.plane = d; expbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; ret = ioctl(i->mfc.fd, VIDIOC_EXPBUF, &expbuf); if (ret != 0) { err("Failed to export buffer\n"); return -1; } i->mfc.dbuf[n][d] = expbuf.fd; } else { i->mfc.cap_buf_off[n][d] = buf.m.planes[d].m.mem_offset; i->mfc.cap_buf_addr[n][d] = mmap(NULL, buf.m.planes[d].length, PROT_READ | PROT_WRITE, MAP_SHARED, i->mfc.fd, buf.m.planes[d].m.mem_offset); if (i->mfc.cap_buf_addr[n][d] == MAP_FAILED) { err("Failed to MMAP MFC CAPTURE buffer"); return -1; } } } } dbg("Succesfully mmapped %d MFC CAPTURE buffers", n); return 0; } int mfc_dec_stop(struct instance *i) { int ret; struct v4l2_decoder_cmd cmd; memzero(cmd); cmd.cmd = V4L2_ENC_CMD_STOP; ret = ioctl(i->mfc.fd, VIDIOC_DECODER_CMD, &cmd); if (ret != 0) { err("Failed ioctl vidioc_decoder_cmd"); return -1; } return 0; }