You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

422 lines
12KB

  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. #include <linux/videodev2.h>
  24. #include <sys/ioctl.h>
  25. #include <sys/mman.h>
  26. #include <unistd.h>
  27. #include <dirent.h>
  28. #include <fcntl.h>
  29. #include "libavcodec/avcodec.h"
  30. #include "libavcodec/internal.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. static inline int v4l2_splane_video(struct v4l2_capability *cap)
  38. {
  39. if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT) &&
  40. cap->capabilities & V4L2_CAP_STREAMING)
  41. return 1;
  42. if (cap->capabilities & V4L2_CAP_VIDEO_M2M)
  43. return 1;
  44. return 0;
  45. }
  46. static inline int v4l2_mplane_video(struct v4l2_capability *cap)
  47. {
  48. if (cap->capabilities & (V4L2_CAP_VIDEO_CAPTURE_MPLANE | V4L2_CAP_VIDEO_OUTPUT_MPLANE) &&
  49. cap->capabilities & V4L2_CAP_STREAMING)
  50. return 1;
  51. if (cap->capabilities & V4L2_CAP_VIDEO_M2M_MPLANE)
  52. return 1;
  53. return 0;
  54. }
  55. static int v4l2_prepare_contexts(V4L2m2mContext* s, int probe)
  56. {
  57. struct v4l2_capability cap;
  58. void *log_ctx = s->priv;
  59. int ret;
  60. s->capture.done = s->output.done = 0;
  61. s->capture.name = "capture";
  62. s->output.name = "output";
  63. atomic_init(&s->refcount, 0);
  64. sem_init(&s->refsync, 0, 0);
  65. memset(&cap, 0, sizeof(cap));
  66. ret = ioctl(s->fd, VIDIOC_QUERYCAP, &cap);
  67. if (ret < 0)
  68. return ret;
  69. av_log(log_ctx, probe ? AV_LOG_DEBUG : AV_LOG_INFO,
  70. "driver '%s' on card '%s' in %s mode\n", cap.driver, cap.card,
  71. v4l2_mplane_video(&cap) ? "mplane" :
  72. v4l2_splane_video(&cap) ? "splane" : "unknown");
  73. if (v4l2_mplane_video(&cap)) {
  74. s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
  75. s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
  76. return 0;
  77. }
  78. if (v4l2_splane_video(&cap)) {
  79. s->capture.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  80. s->output.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
  81. return 0;
  82. }
  83. return AVERROR(EINVAL);
  84. }
  85. static int v4l2_probe_driver(V4L2m2mContext* s)
  86. {
  87. void *log_ctx = s->priv;
  88. int ret;
  89. s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
  90. if (s->fd < 0)
  91. return AVERROR(errno);
  92. ret = v4l2_prepare_contexts(s, 1);
  93. if (ret < 0)
  94. goto done;
  95. ret = ff_v4l2_context_get_format(&s->output, 1);
  96. if (ret) {
  97. av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
  98. goto done;
  99. }
  100. ret = ff_v4l2_context_get_format(&s->capture, 1);
  101. if (ret) {
  102. av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
  103. goto done;
  104. }
  105. done:
  106. if (close(s->fd) < 0) {
  107. ret = AVERROR(errno);
  108. av_log(log_ctx, AV_LOG_ERROR, "failure closing %s (%s)\n", s->devname, av_err2str(AVERROR(errno)));
  109. }
  110. s->fd = -1;
  111. return ret;
  112. }
  113. static int v4l2_configure_contexts(V4L2m2mContext* s)
  114. {
  115. void *log_ctx = s->priv;
  116. int ret;
  117. struct v4l2_format ofmt, cfmt;
  118. s->fd = open(s->devname, O_RDWR | O_NONBLOCK, 0);
  119. if (s->fd < 0)
  120. return AVERROR(errno);
  121. ret = v4l2_prepare_contexts(s, 0);
  122. if (ret < 0)
  123. goto error;
  124. ofmt = s->output.format;
  125. cfmt = s->capture.format;
  126. av_log(log_ctx, AV_LOG_INFO, "requesting formats: output=%s capture=%s\n",
  127. av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(ofmt.type) ?
  128. ofmt.fmt.pix_mp.pixelformat :
  129. ofmt.fmt.pix.pixelformat),
  130. av_fourcc2str(V4L2_TYPE_IS_MULTIPLANAR(cfmt.type) ?
  131. cfmt.fmt.pix_mp.pixelformat :
  132. cfmt.fmt.pix.pixelformat));
  133. ret = ff_v4l2_context_set_format(&s->output);
  134. if (ret) {
  135. av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
  136. goto error;
  137. }
  138. ret = ff_v4l2_context_set_format(&s->capture);
  139. if (ret) {
  140. av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
  141. goto error;
  142. }
  143. ret = ff_v4l2_context_init(&s->output);
  144. if (ret) {
  145. av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
  146. goto error;
  147. }
  148. /* decoder's buffers need to be updated at a later stage */
  149. if (!s->avctx || !av_codec_is_decoder(s->avctx->codec)) {
  150. ret = ff_v4l2_context_init(&s->capture);
  151. if (ret) {
  152. av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
  153. goto error;
  154. }
  155. }
  156. return 0;
  157. error:
  158. if (close(s->fd) < 0) {
  159. ret = AVERROR(errno);
  160. av_log(log_ctx, AV_LOG_ERROR, "error closing %s (%s)\n",
  161. s->devname, av_err2str(AVERROR(errno)));
  162. }
  163. s->fd = -1;
  164. return ret;
  165. }
  166. /******************************************************************************
  167. *
  168. * V4L2 M2M Interface
  169. *
  170. ******************************************************************************/
  171. int ff_v4l2_m2m_codec_reinit(V4L2m2mContext* s)
  172. {
  173. void *log_ctx = s->priv;
  174. int ret;
  175. av_log(log_ctx, AV_LOG_DEBUG, "reinit context\n");
  176. /* 1. streamoff */
  177. ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
  178. if (ret)
  179. av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
  180. /* 2. unmap the capture buffers (v4l2 and ffmpeg):
  181. * we must wait for all references to be released before being allowed
  182. * to queue new buffers.
  183. */
  184. av_log(log_ctx, AV_LOG_DEBUG, "waiting for user to release AVBufferRefs\n");
  185. if (atomic_load(&s->refcount))
  186. while(sem_wait(&s->refsync) == -1 && errno == EINTR);
  187. ff_v4l2_context_release(&s->capture);
  188. /* 3. get the new capture format */
  189. ret = ff_v4l2_context_get_format(&s->capture, 0);
  190. if (ret) {
  191. av_log(log_ctx, AV_LOG_ERROR, "query the new capture format\n");
  192. return ret;
  193. }
  194. /* 4. set the capture format */
  195. ret = ff_v4l2_context_set_format(&s->capture);
  196. if (ret) {
  197. av_log(log_ctx, AV_LOG_ERROR, "setting capture format\n");
  198. return ret;
  199. }
  200. /* 5. complete reinit */
  201. s->draining = 0;
  202. s->reinit = 0;
  203. return 0;
  204. }
  205. int ff_v4l2_m2m_codec_full_reinit(V4L2m2mContext *s)
  206. {
  207. void *log_ctx = s->avctx;
  208. int ret;
  209. av_log(log_ctx, AV_LOG_DEBUG, "%s full reinit\n", s->devname);
  210. /* wait for pending buffer references */
  211. if (atomic_load(&s->refcount))
  212. while(sem_wait(&s->refsync) == -1 && errno == EINTR);
  213. ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
  214. if (ret) {
  215. av_log(log_ctx, AV_LOG_ERROR, "output VIDIOC_STREAMOFF\n");
  216. goto error;
  217. }
  218. ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
  219. if (ret) {
  220. av_log(log_ctx, AV_LOG_ERROR, "capture VIDIOC_STREAMOFF\n");
  221. goto error;
  222. }
  223. /* release and unmmap the buffers */
  224. ff_v4l2_context_release(&s->output);
  225. ff_v4l2_context_release(&s->capture);
  226. /* start again now that we know the stream dimensions */
  227. s->draining = 0;
  228. s->reinit = 0;
  229. ret = ff_v4l2_context_get_format(&s->output, 0);
  230. if (ret) {
  231. av_log(log_ctx, AV_LOG_DEBUG, "v4l2 output format not supported\n");
  232. goto error;
  233. }
  234. ret = ff_v4l2_context_get_format(&s->capture, 0);
  235. if (ret) {
  236. av_log(log_ctx, AV_LOG_DEBUG, "v4l2 capture format not supported\n");
  237. goto error;
  238. }
  239. ret = ff_v4l2_context_set_format(&s->output);
  240. if (ret) {
  241. av_log(log_ctx, AV_LOG_ERROR, "can't set v4l2 output format\n");
  242. goto error;
  243. }
  244. ret = ff_v4l2_context_set_format(&s->capture);
  245. if (ret) {
  246. av_log(log_ctx, AV_LOG_ERROR, "can't to set v4l2 capture format\n");
  247. goto error;
  248. }
  249. ret = ff_v4l2_context_init(&s->output);
  250. if (ret) {
  251. av_log(log_ctx, AV_LOG_ERROR, "no v4l2 output context's buffers\n");
  252. goto error;
  253. }
  254. /* decoder's buffers need to be updated at a later stage */
  255. if (!s->avctx || !av_codec_is_decoder(s->avctx->codec)) {
  256. ret = ff_v4l2_context_init(&s->capture);
  257. if (ret) {
  258. av_log(log_ctx, AV_LOG_ERROR, "no v4l2 capture context's buffers\n");
  259. goto error;
  260. }
  261. }
  262. return 0;
  263. error:
  264. return ret;
  265. }
  266. static void v4l2_m2m_destroy_context(void *opaque, uint8_t *context)
  267. {
  268. V4L2m2mContext *s = (V4L2m2mContext*)context;
  269. ff_v4l2_context_release(&s->capture);
  270. sem_destroy(&s->refsync);
  271. close(s->fd);
  272. av_free(s);
  273. }
  274. int ff_v4l2_m2m_codec_end(V4L2m2mPriv *priv)
  275. {
  276. V4L2m2mContext* s = priv->context;
  277. int ret;
  278. ret = ff_v4l2_context_set_status(&s->output, VIDIOC_STREAMOFF);
  279. if (ret)
  280. av_log(priv, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->output.name);
  281. ret = ff_v4l2_context_set_status(&s->capture, VIDIOC_STREAMOFF);
  282. if (ret)
  283. av_log(priv, AV_LOG_ERROR, "VIDIOC_STREAMOFF %s\n", s->capture.name);
  284. ff_v4l2_context_release(&s->output);
  285. s->self_ref = NULL;
  286. av_buffer_unref(&priv->context_ref);
  287. return 0;
  288. }
  289. int ff_v4l2_m2m_codec_init(V4L2m2mPriv *priv)
  290. {
  291. int ret = AVERROR(EINVAL);
  292. struct dirent *entry;
  293. char node[PATH_MAX];
  294. DIR *dirp;
  295. V4L2m2mContext *s = priv->context;
  296. dirp = opendir("/dev");
  297. if (!dirp)
  298. return AVERROR(errno);
  299. for (entry = readdir(dirp); entry; entry = readdir(dirp)) {
  300. if (strncmp(entry->d_name, "video", 5))
  301. continue;
  302. snprintf(node, sizeof(node), "/dev/%s", entry->d_name);
  303. av_log(priv, AV_LOG_DEBUG, "probing device %s\n", node);
  304. strncpy(s->devname, node, strlen(node) + 1);
  305. ret = v4l2_probe_driver(s);
  306. if (!ret)
  307. break;
  308. }
  309. closedir(dirp);
  310. if (ret) {
  311. av_log(priv, AV_LOG_ERROR, "Could not find a valid device\n");
  312. memset(s->devname, 0, sizeof(s->devname));
  313. return ret;
  314. }
  315. av_log(priv, AV_LOG_INFO, "Using device %s\n", node);
  316. return v4l2_configure_contexts(s);
  317. }
  318. int ff_v4l2_m2m_create_context(V4L2m2mPriv *priv, V4L2m2mContext **s)
  319. {
  320. *s = av_mallocz(sizeof(V4L2m2mContext));
  321. if (!*s)
  322. return AVERROR(ENOMEM);
  323. priv->context_ref = av_buffer_create((uint8_t *) *s, sizeof(V4L2m2mContext),
  324. &v4l2_m2m_destroy_context, NULL, 0);
  325. if (!priv->context_ref) {
  326. av_freep(s);
  327. return AVERROR(ENOMEM);
  328. }
  329. /* assign the context */
  330. priv->context = *s;
  331. (*s)->priv = priv;
  332. /* populate it */
  333. priv->context->capture.num_buffers = priv->num_capture_buffers;
  334. priv->context->output.num_buffers = priv->num_output_buffers;
  335. priv->context->self_ref = priv->context_ref;
  336. priv->context->fd = -1;
  337. return 0;
  338. }