1/* 2 * V4L mem2mem 3 * 4 * Copyright (C) 2017 Alexis Ballier <aballier@gentoo.org> 5 * Copyright (C) 2017 Jorge Ramirez <jorge.ramirez-ortiz@linaro.org> 6 * 7 * This file is part of FFmpeg. 8 * 9 * FFmpeg is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Lesser General Public 11 * License as published by the Free Software Foundation; either 12 * version 2.1 of the License, or (at your option) any later version. 13 * 14 * FFmpeg is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Lesser General Public License for more details. 18 * 19 * You should have received a copy of the GNU Lesser General Public 20 * License along with FFmpeg; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 22 */ 23 24#include <linux/videodev2.h> 25#include <sys/ioctl.h> 26#include <sys/mman.h> 27#include <unistd.h> 28#include <dirent.h> 29#include <fcntl.h> 30#include "libavcodec/avcodec.h" 31#include "libavutil/pixdesc.h" 32#include "libavutil/imgutils.h" 33#include "libavutil/pixfmt.h" 34#include "v4l2_context.h" 35#include "v4l2_fmt.h" 36#include "v4l2_m2m.h" 37 38static inline int v4l2_splane_video(struct v4l2_capability *cap) 39{ 40 if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) && 41 cap->capabilities & V4L2_CAP_STREAMING) 42 return 1; 43 44 if (cap->capabilities & V4L2_CAP_VIDEO_M2M) 45 return 1; 46 47 return 0; 48} 49 50static inline int v4l2_mplane_video(struct v4l2_capability *cap) 51{ 52 if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) && 53 cap->capabilities & V4L2_CAP_STREAMING) 54 return 1; 55 56 if (cap->capabilities & V4L2_CAP_VIDEO_M2M_MPLANE) 57 return 1; 58 59 return 0; 60} 61 62static int v4l2_prepare_contexts(V4L2m2mContext *s, int probe) 63{ 64 struct v4l2_capability cap; 65 void *log_ctx = s->avctx; 66 int ret; 67 68 s->capture.done = s->output.done = 0; 69 s->capture.name = "capture"; 70 s->output.name = "output"; 71 atomic_init(&s->refcount, 0); 72 sem_init(&s->refsync, 0, 0); 73 74 memset(&cap, 0, sizeof(cap)); 75 ret = ioctl(s->fd, VIDIOC_QUERYCAP, &cap); 76 if (ret < 0) 77 return ret; 78 79 av_log(log_ctx, probe ? AV_LOG_DEBUG : AV_LOG_INFO, 80 "driver '%s' on card '%s' in %s mode\n", cap.driver, cap.card, 81 v4l2_mplane_video(&cap) ? "mplane" : 82 v4l2_splane_video(&cap) ? "splane" : "unknown"); 83 84 if (v4l2_mplane_video(&cap)) { 85 s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; 86 s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; 87 return 0; 88 } 89 90 if (v4l2_splane_video(&cap)) { 91 s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 92 s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT; 93 return 0; 94 } 95 96 return AVERROR(EINVAL); 97} 98 99static int v4l2_probe_driver(V4L2m2mContext *s) 100{ 101 void *log_ctx = s->avctx; 102 int ret; 103 104 s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0); 105 if (s->fd < 0) 106 return AVERROR(errno); 107 108 ret = v4l2_prepare_contexts(s, 1); 109 if (ret < 0) 110 goto done; 111 112 ret = ff_v4l2_context_get_format(&s->output, 1); 113 if (ret) { 114 av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n"); 115 goto done; 116 } 117 118 ret = ff_v4l2_context_get_format(&s->capture, 1); 119 if (ret) { 120 av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n"); 121 goto done; 122 } 123 124done: 125 if (close(s->fd) < 0) { 126 ret = AVERROR(errno); 127 av_log(log_ctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno))); 128 } 129 130 s->fd = -1; 131 132 return ret; 133} 134 135static int v4l2_configure_contexts(V4L2m2mContext *s) 136{ 137 void *log_ctx = s->avctx; 138 int ret; 139 struct v4l2_format ofmt, cfmt; 140 141 s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0); 142 if (s->fd < 0) 143 return AVERROR(errno); 144 145 ret = v4l2_prepare_contexts(s, 0); 146 if (ret < 0) 147 goto error; 148 149 ofmt = s->output.format; 150 cfmt = s->capture.format; 151 av_log(log_ctx, AV_LOG_INFO, "requesting formats: output=%s capture=%s\n", 152 av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(ofmt.type) ? 153 ofmt.fmt.pix_mp.pixelformat : 154 ofmt.fmt.pix.pixelformat), 155 av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(cfmt.type) ? 156 cfmt.fmt.pix_mp.pixelformat : 157 cfmt.fmt.pix.pixelformat)); 158 159 ret = ff_v4l2_context_set_format(&s->output); 160 if (ret) { 161 av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n"); 162 goto error; 163 } 164 165 ret = ff_v4l2_context_set_format(&s->capture); 166 if (ret) { 167 av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n"); 168 goto error; 169 } 170 171 ret = ff_v4l2_context_init(&s->output); 172 if (ret) { 173 av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n"); 174 goto error; 175 } 176 177 /* decoder's buffers need to be updated at a later stage */ 178 if (s->avctx && !av_codec_is_decoder(s->avctx->codec)) { 179 ret = ff_v4l2_context_init(&s->capture); 180 if (ret) { 181 av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n"); 182 goto error; 183 } 184 } 185 186 return 0; 187 188error: 189 if (close(s->fd) < 0) { 190 ret = AVERROR(errno); 191 av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n", 192 s->devname, av_err2str(AVERROR(errno))); 193 } 194 s->fd = -1; 195 196 return ret; 197} 198 199/****************************************************************************** 200 * 201 * V4L2 M2M Interface 202 * 203 ******************************************************************************/ 204int ff_v4l2_m2m_codec_reinit(V4L2m2mContext *s) 205{ 206 void *log_ctx = s->avctx; 207 int ret; 208 209 av_log(log_ctx, AV_LOG_DEBUG, "reinit context\n"); 210 211 /* 1. streamoff */ 212 ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF); 213 if (ret) 214 av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n"); 215 216 /* 2. unmap the capture buffers (v4l2 and ffmpeg): 217 * we must wait for all references to be released before being allowed 218 * to queue new buffers. 219 */ 220 av_log(log_ctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n"); 221 if (atomic_load(&s->refcount)) 222 while(sem_wait(&s->refsync) == -1 && errno == EINTR); 223 224 ff_v4l2_context_release(&s->capture); 225 226 /* 3. get the new capture format */ 227 ret = ff_v4l2_context_get_format(&s->capture, 0); 228 if (ret) { 229 av_log(log_ctx, AV_LOG_ERROR, "query the new capture format\n"); 230 return ret; 231 } 232 233 /* 4. set the capture format */ 234 ret = ff_v4l2_context_set_format(&s->capture); 235 if (ret) { 236 av_log(log_ctx, AV_LOG_ERROR, "setting capture format\n"); 237 return ret; 238 } 239 240 /* 5. complete reinit */ 241 s->draining = 0; 242 s->reinit = 0; 243 244 return 0; 245} 246 247static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context) 248{ 249 V4L2m2mContext *s = (V4L2m2mContext*)context; 250 251 ff_v4l2_context_release(&s->capture); 252 sem_destroy(&s->refsync); 253 254 if (s->fd >= 0) 255 close(s->fd); 256 av_frame_unref(s->frame); 257 av_frame_free(&s->frame); 258 av_packet_unref(&s->buf_pkt); 259 260 av_free(s); 261} 262 263int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv) 264{ 265 V4L2m2mContext *s = priv->context; 266 int ret; 267 268 if (!s) 269 return 0; 270 271 if (s->fd >= 0) { 272 ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF); 273 if (ret) 274 av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name); 275 276 ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF); 277 if (ret) 278 av_log(s->avctx, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name); 279 } 280 281 ff_v4l2_context_release(&s->output); 282 283 s->self_ref = NULL; 284 av_buffer_unref(&priv->context_ref); 285 286 return 0; 287} 288 289int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv) 290{ 291 int ret = AVERROR(EINVAL); 292 struct dirent *entry; 293 DIR *dirp; 294 295 V4L2m2mContext *s = priv->context; 296 297 dirp = opendir("/dev"); 298 if (!dirp) 299 return AVERROR(errno); 300 301 for (entry = readdir(dirp); entry; entry = readdir(dirp)) { 302 303 if (strncmp(entry->d_name, "video", 5)) 304 continue; 305 306 snprintf(s->devname, sizeof(s->devname), "/dev/%s", entry->d_name); 307 av_log(s->avctx, AV_LOG_DEBUG, "probing device %s\n", s->devname); 308 ret = v4l2_probe_driver(s); 309 if (!ret) 310 break; 311 } 312 313 closedir(dirp); 314 315 if (ret) { 316 av_log(s->avctx, AV_LOG_ERROR, "Could not find a valid device\n"); 317 memset(s->devname, 0, sizeof(s->devname)); 318 319 return ret; 320 } 321 322 av_log(s->avctx, AV_LOG_INFO, "Using device %s\n", s->devname); 323 324 return v4l2_configure_contexts(s); 325} 326 327int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s) 328{ 329 *s = av_mallocz(sizeof(V4L2m2mContext)); 330 if (!*s) 331 return AVERROR(ENOMEM); 332 333 priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext), 334 &v4l2_m2m_destroy_context, NULL, 0); 335 if (!priv->context_ref) { 336 av_freep(s); 337 return AVERROR(ENOMEM); 338 } 339 340 /* assign the context */ 341 priv->context = *s; 342 (*s)->priv = priv; 343 344 /* populate it */ 345 priv->context->capture.num_buffers = priv->num_capture_buffers; 346 priv->context->output.num_buffers = priv->num_output_buffers; 347 priv->context->self_ref = priv->context_ref; 348 priv->context->fd = -1; 349 350 priv->context->frame = av_frame_alloc(); 351 if (!priv->context->frame) { 352 av_buffer_unref(&priv->context_ref); 353 *s = NULL; /* freed when unreferencing context_ref */ 354 return AVERROR(ENOMEM); 355 } 356 357 return 0; 358} 359