SDL_camera_v4l2.c 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2024 Sam Lantinga <[email protected]>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_CAMERA_DRIVER_V4L2
  20. #include <dirent.h>
  21. #include <errno.h>
  22. #include <fcntl.h> // low-level i/o
  23. #include <stddef.h>
  24. #include <sys/ioctl.h>
  25. #include <sys/mman.h>
  26. #include <sys/stat.h>
  27. #include <unistd.h>
  28. #include <linux/videodev2.h>
  29. #ifndef V4L2_CAP_DEVICE_CAPS
  30. // device_caps was added to struct v4l2_capability as of kernel 3.4.
  31. #define device_caps reserved[0]
  32. SDL_COMPILE_TIME_ASSERT(v4l2devicecaps, offsetof(struct v4l2_capability,device_caps) == offsetof(struct v4l2_capability,capabilities) + 4);
  33. #endif
  34. #include "../SDL_syscamera.h"
  35. #include "../SDL_camera_c.h"
  36. #include "../../video/SDL_pixels_c.h"
  37. #include "../../thread/SDL_systhread.h"
  38. #include "../../core/linux/SDL_evdev_capabilities.h"
  39. #include "../../core/linux/SDL_udev.h"
  40. #ifndef SDL_USE_LIBUDEV
  41. #include <dirent.h>
  42. #endif
  43. typedef struct V4L2DeviceHandle
  44. {
  45. char *bus_info;
  46. char *path;
  47. } V4L2DeviceHandle;
  48. typedef enum io_method {
  49. IO_METHOD_INVALID,
  50. IO_METHOD_READ,
  51. IO_METHOD_MMAP,
  52. IO_METHOD_USERPTR
  53. } io_method;
  54. struct buffer {
  55. void *start;
  56. size_t length;
  57. int available; // Is available in userspace
  58. };
  59. struct SDL_PrivateCameraData
  60. {
  61. int fd;
  62. io_method io;
  63. int nb_buffers;
  64. struct buffer *buffers;
  65. int driver_pitch;
  66. };
  67. static int xioctl(int fh, int request, void *arg)
  68. {
  69. int r;
  70. do {
  71. r = ioctl(fh, request, arg);
  72. } while ((r == -1) && (errno == EINTR));
  73. return r;
  74. }
  75. static bool V4L2_WaitDevice(SDL_Camera *device)
  76. {
  77. const int fd = device->hidden->fd;
  78. int rc;
  79. do {
  80. fd_set fds;
  81. FD_ZERO(&fds);
  82. FD_SET(fd, &fds);
  83. struct timeval tv;
  84. tv.tv_sec = 0;
  85. tv.tv_usec = 100 * 1000;
  86. rc = select(fd + 1, &fds, NULL, NULL, &tv);
  87. if ((rc == -1) && (errno == EINTR)) {
  88. rc = 0; // pretend it was a timeout, keep looping.
  89. } else if (rc > 0) {
  90. return true;
  91. }
  92. // Thread is requested to shut down
  93. if (SDL_AtomicGet(&device->shutdown)) {
  94. return true;
  95. }
  96. } while (rc == 0);
  97. return false;
  98. }
  99. static SDL_CameraFrameResult V4L2_AcquireFrame(SDL_Camera *device, SDL_Surface *frame, Uint64 *timestampNS)
  100. {
  101. const int fd = device->hidden->fd;
  102. const io_method io = device->hidden->io;
  103. size_t size = device->hidden->buffers[0].length;
  104. struct v4l2_buffer buf;
  105. switch (io) {
  106. case IO_METHOD_READ:
  107. if (read(fd, device->hidden->buffers[0].start, size) == -1) {
  108. switch (errno) {
  109. case EAGAIN:
  110. return SDL_CAMERA_FRAME_SKIP;
  111. case EIO:
  112. // Could ignore EIO, see spec.
  113. // fall through
  114. default:
  115. SDL_SetError("read");
  116. return SDL_CAMERA_FRAME_ERROR;
  117. }
  118. }
  119. *timestampNS = SDL_GetTicksNS(); // oh well, close enough.
  120. frame->pixels = device->hidden->buffers[0].start;
  121. frame->pitch = device->hidden->driver_pitch;
  122. break;
  123. case IO_METHOD_MMAP:
  124. SDL_zero(buf);
  125. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  126. buf.memory = V4L2_MEMORY_MMAP;
  127. if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
  128. switch (errno) {
  129. case EAGAIN:
  130. return SDL_CAMERA_FRAME_SKIP;
  131. case EIO:
  132. // Could ignore EIO, see spec.
  133. // fall through
  134. default:
  135. SDL_SetError("VIDIOC_DQBUF: %d", errno);
  136. return SDL_CAMERA_FRAME_ERROR;
  137. }
  138. }
  139. if ((int)buf.index < 0 || (int)buf.index >= device->hidden->nb_buffers) {
  140. SDL_SetError("invalid buffer index");
  141. return SDL_CAMERA_FRAME_ERROR;
  142. }
  143. frame->pixels = device->hidden->buffers[buf.index].start;
  144. frame->pitch = device->hidden->driver_pitch;
  145. device->hidden->buffers[buf.index].available = 1;
  146. *timestampNS = (((Uint64) buf.timestamp.tv_sec) * SDL_NS_PER_SECOND) + SDL_US_TO_NS(buf.timestamp.tv_usec);
  147. #if DEBUG_CAMERA
  148. SDL_Log("CAMERA: debug mmap: image %d/%d data[0]=%p", buf.index, device->hidden->nb_buffers, (void*)frame->pixels);
  149. #endif
  150. break;
  151. case IO_METHOD_USERPTR:
  152. SDL_zero(buf);
  153. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  154. buf.memory = V4L2_MEMORY_USERPTR;
  155. if (xioctl(fd, VIDIOC_DQBUF, &buf) == -1) {
  156. switch (errno) {
  157. case EAGAIN:
  158. return SDL_CAMERA_FRAME_SKIP;
  159. case EIO:
  160. // Could ignore EIO, see spec.
  161. // fall through
  162. default:
  163. SDL_SetError("VIDIOC_DQBUF");
  164. return SDL_CAMERA_FRAME_ERROR;
  165. }
  166. }
  167. int i;
  168. for (i = 0; i < device->hidden->nb_buffers; ++i) {
  169. if (buf.m.userptr == (unsigned long)device->hidden->buffers[i].start && buf.length == size) {
  170. break;
  171. }
  172. }
  173. if (i >= device->hidden->nb_buffers) {
  174. SDL_SetError("invalid buffer index");
  175. return SDL_CAMERA_FRAME_ERROR;
  176. }
  177. frame->pixels = (void*)buf.m.userptr;
  178. frame->pitch = device->hidden->driver_pitch;
  179. device->hidden->buffers[i].available = 1;
  180. *timestampNS = (((Uint64) buf.timestamp.tv_sec) * SDL_NS_PER_SECOND) + SDL_US_TO_NS(buf.timestamp.tv_usec);
  181. #if DEBUG_CAMERA
  182. SDL_Log("CAMERA: debug userptr: image %d/%d data[0]=%p", buf.index, device->hidden->nb_buffers, (void*)frame->pixels);
  183. #endif
  184. break;
  185. case IO_METHOD_INVALID:
  186. SDL_assert(!"Shouldn't have hit this");
  187. break;
  188. }
  189. return SDL_CAMERA_FRAME_READY;
  190. }
  191. static void V4L2_ReleaseFrame(SDL_Camera *device, SDL_Surface *frame)
  192. {
  193. struct v4l2_buffer buf;
  194. const int fd = device->hidden->fd;
  195. const io_method io = device->hidden->io;
  196. int i;
  197. for (i = 0; i < device->hidden->nb_buffers; ++i) {
  198. if (frame->pixels == device->hidden->buffers[i].start) {
  199. break;
  200. }
  201. }
  202. if (i >= device->hidden->nb_buffers) {
  203. return; // oh well, we didn't own this.
  204. }
  205. switch (io) {
  206. case IO_METHOD_READ:
  207. break;
  208. case IO_METHOD_MMAP:
  209. SDL_zero(buf);
  210. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  211. buf.memory = V4L2_MEMORY_MMAP;
  212. buf.index = i;
  213. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  214. // !!! FIXME: disconnect the device.
  215. return; //SDL_SetError("VIDIOC_QBUF");
  216. }
  217. device->hidden->buffers[i].available = 0;
  218. break;
  219. case IO_METHOD_USERPTR:
  220. SDL_zero(buf);
  221. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  222. buf.memory = V4L2_MEMORY_USERPTR;
  223. buf.index = i;
  224. buf.m.userptr = (unsigned long)frame->pixels;
  225. buf.length = (int) device->hidden->buffers[i].length;
  226. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  227. // !!! FIXME: disconnect the device.
  228. return; //SDL_SetError("VIDIOC_QBUF");
  229. }
  230. device->hidden->buffers[i].available = 0;
  231. break;
  232. case IO_METHOD_INVALID:
  233. SDL_assert(!"Shouldn't have hit this");
  234. break;
  235. }
  236. }
  237. static bool EnqueueBuffers(SDL_Camera *device)
  238. {
  239. const int fd = device->hidden->fd;
  240. const io_method io = device->hidden->io;
  241. switch (io) {
  242. case IO_METHOD_READ:
  243. break;
  244. case IO_METHOD_MMAP:
  245. for (int i = 0; i < device->hidden->nb_buffers; ++i) {
  246. if (device->hidden->buffers[i].available == 0) {
  247. struct v4l2_buffer buf;
  248. SDL_zero(buf);
  249. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  250. buf.memory = V4L2_MEMORY_MMAP;
  251. buf.index = i;
  252. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  253. return SDL_SetError("VIDIOC_QBUF");
  254. }
  255. }
  256. }
  257. break;
  258. case IO_METHOD_USERPTR:
  259. for (int i = 0; i < device->hidden->nb_buffers; ++i) {
  260. if (device->hidden->buffers[i].available == 0) {
  261. struct v4l2_buffer buf;
  262. SDL_zero(buf);
  263. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  264. buf.memory = V4L2_MEMORY_USERPTR;
  265. buf.index = i;
  266. buf.m.userptr = (unsigned long)device->hidden->buffers[i].start;
  267. buf.length = (int) device->hidden->buffers[i].length;
  268. if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  269. return SDL_SetError("VIDIOC_QBUF");
  270. }
  271. }
  272. }
  273. break;
  274. case IO_METHOD_INVALID: SDL_assert(!"Shouldn't have hit this"); break;
  275. }
  276. return true;
  277. }
  278. static bool AllocBufferRead(SDL_Camera *device, size_t buffer_size)
  279. {
  280. device->hidden->buffers[0].length = buffer_size;
  281. device->hidden->buffers[0].start = SDL_calloc(1, buffer_size);
  282. return (device->hidden->buffers[0].start != NULL);
  283. }
  284. static bool AllocBufferMmap(SDL_Camera *device)
  285. {
  286. const int fd = device->hidden->fd;
  287. int i;
  288. for (i = 0; i < device->hidden->nb_buffers; ++i) {
  289. struct v4l2_buffer buf;
  290. SDL_zero(buf);
  291. buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  292. buf.memory = V4L2_MEMORY_MMAP;
  293. buf.index = i;
  294. if (xioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
  295. return SDL_SetError("VIDIOC_QUERYBUF");
  296. }
  297. device->hidden->buffers[i].length = buf.length;
  298. device->hidden->buffers[i].start =
  299. mmap(NULL /* start anywhere */,
  300. buf.length,
  301. PROT_READ | PROT_WRITE /* required */,
  302. MAP_SHARED /* recommended */,
  303. fd, buf.m.offset);
  304. if (MAP_FAILED == device->hidden->buffers[i].start) {
  305. return SDL_SetError("mmap");
  306. }
  307. }
  308. return true;
  309. }
  310. static bool AllocBufferUserPtr(SDL_Camera *device, size_t buffer_size)
  311. {
  312. int i;
  313. for (i = 0; i < device->hidden->nb_buffers; ++i) {
  314. device->hidden->buffers[i].length = buffer_size;
  315. device->hidden->buffers[i].start = SDL_calloc(1, buffer_size);
  316. if (!device->hidden->buffers[i].start) {
  317. return false;
  318. }
  319. }
  320. return true;
  321. }
  322. static void format_v4l2_to_sdl(Uint32 fmt, SDL_PixelFormat *format, SDL_Colorspace *colorspace)
  323. {
  324. switch (fmt) {
  325. #define CASE(x, y, z) case x: *format = y; *colorspace = z; return
  326. CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2, SDL_COLORSPACE_BT709_LIMITED);
  327. #undef CASE
  328. default:
  329. #if DEBUG_CAMERA
  330. SDL_Log("CAMERA: Unknown format V4L2_PIX_FORMAT '%d'", fmt);
  331. #endif
  332. break;
  333. }
  334. *format = SDL_PIXELFORMAT_UNKNOWN;
  335. *colorspace = SDL_COLORSPACE_UNKNOWN;
  336. }
  337. static Uint32 format_sdl_to_v4l2(SDL_PixelFormat fmt)
  338. {
  339. switch (fmt) {
  340. #define CASE(y, x) case x: return y
  341. CASE(V4L2_PIX_FMT_YUYV, SDL_PIXELFORMAT_YUY2);
  342. CASE(V4L2_PIX_FMT_MJPEG, SDL_PIXELFORMAT_UNKNOWN);
  343. #undef CASE
  344. default:
  345. return true;
  346. }
  347. }
  348. static void V4L2_CloseDevice(SDL_Camera *device)
  349. {
  350. if (!device) {
  351. return;
  352. }
  353. if (device->hidden) {
  354. const io_method io = device->hidden->io;
  355. const int fd = device->hidden->fd;
  356. if ((io == IO_METHOD_MMAP) || (io == IO_METHOD_USERPTR)) {
  357. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  358. xioctl(fd, VIDIOC_STREAMOFF, &type);
  359. }
  360. if (device->hidden->buffers) {
  361. switch (io) {
  362. case IO_METHOD_INVALID:
  363. break;
  364. case IO_METHOD_READ:
  365. SDL_free(device->hidden->buffers[0].start);
  366. break;
  367. case IO_METHOD_MMAP:
  368. for (int i = 0; i < device->hidden->nb_buffers; ++i) {
  369. if (munmap(device->hidden->buffers[i].start, device->hidden->buffers[i].length) == -1) {
  370. SDL_SetError("munmap");
  371. }
  372. }
  373. break;
  374. case IO_METHOD_USERPTR:
  375. for (int i = 0; i < device->hidden->nb_buffers; ++i) {
  376. SDL_free(device->hidden->buffers[i].start);
  377. }
  378. break;
  379. }
  380. SDL_free(device->hidden->buffers);
  381. }
  382. if (fd != -1) {
  383. close(fd);
  384. }
  385. SDL_free(device->hidden);
  386. device->hidden = NULL;
  387. }
  388. }
  389. static bool V4L2_OpenDevice(SDL_Camera *device, const SDL_CameraSpec *spec)
  390. {
  391. const V4L2DeviceHandle *handle = (const V4L2DeviceHandle *) device->handle;
  392. struct stat st;
  393. struct v4l2_capability cap;
  394. const int fd = open(handle->path, O_RDWR /* required */ | O_NONBLOCK, 0);
  395. // most of this probably shouldn't fail unless the filesystem node changed out from under us since MaybeAddDevice().
  396. if (fd == -1) {
  397. return SDL_SetError("Cannot open '%s': %d, %s", handle->path, errno, strerror(errno));
  398. } else if (fstat(fd, &st) == -1) {
  399. close(fd);
  400. return SDL_SetError("Cannot identify '%s': %d, %s", handle->path, errno, strerror(errno));
  401. } else if (!S_ISCHR(st.st_mode)) {
  402. close(fd);
  403. return SDL_SetError("%s is not a character device", handle->path);
  404. } else if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
  405. const int err = errno;
  406. close(fd);
  407. if (err == EINVAL) {
  408. return SDL_SetError("%s is unexpectedly not a V4L2 device", handle->path);
  409. }
  410. return SDL_SetError("Error VIDIOC_QUERYCAP errno=%d device%s is no V4L2 device", err, handle->path);
  411. } else if ((cap.device_caps & V4L2_CAP_VIDEO_CAPTURE) == 0) {
  412. close(fd);
  413. return SDL_SetError("%s is unexpectedly not a video capture device", handle->path);
  414. }
  415. device->hidden = (struct SDL_PrivateCameraData *) SDL_calloc(1, sizeof (struct SDL_PrivateCameraData));
  416. if (device->hidden == NULL) {
  417. close(fd);
  418. return false;
  419. }
  420. device->hidden->fd = fd;
  421. device->hidden->io = IO_METHOD_INVALID;
  422. // Select video input, video standard and tune here.
  423. // errors in the crop code are not fatal.
  424. struct v4l2_cropcap cropcap;
  425. SDL_zero(cropcap);
  426. cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  427. if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) {
  428. struct v4l2_crop crop;
  429. SDL_zero(crop);
  430. crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  431. crop.c = cropcap.defrect; // reset to default
  432. xioctl(fd, VIDIOC_S_CROP, &crop);
  433. }
  434. struct v4l2_format fmt;
  435. SDL_zero(fmt);
  436. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  437. fmt.fmt.pix.width = spec->width;
  438. fmt.fmt.pix.height = spec->height;
  439. fmt.fmt.pix.pixelformat = format_sdl_to_v4l2(spec->format);
  440. //fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
  441. fmt.fmt.pix.field = V4L2_FIELD_ANY;
  442. #if DEBUG_CAMERA
  443. SDL_Log("CAMERA: set SDL format %s", SDL_GetPixelFormatName(spec->format));
  444. { const Uint32 f = fmt.fmt.pix.pixelformat; SDL_Log("CAMERA: set format V4L2_format=%d %c%c%c%c", f, (f >> 0) & 0xff, (f >> 8) & 0xff, (f >> 16) & 0xff, (f >> 24) & 0xff); }
  445. #endif
  446. if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
  447. return SDL_SetError("Error VIDIOC_S_FMT");
  448. }
  449. if (spec->framerate_numerator && spec->framerate_denominator) {
  450. struct v4l2_streamparm setfps;
  451. SDL_zero(setfps);
  452. setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  453. if (xioctl(fd, VIDIOC_G_PARM, &setfps) == 0) {
  454. if ( (setfps.parm.capture.timeperframe.denominator != spec->framerate_numerator) ||
  455. (setfps.parm.capture.timeperframe.numerator = spec->framerate_denominator) ) {
  456. setfps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  457. setfps.parm.capture.timeperframe.numerator = spec->framerate_denominator;
  458. setfps.parm.capture.timeperframe.denominator = spec->framerate_numerator;
  459. if (xioctl(fd, VIDIOC_S_PARM, &setfps) == -1) {
  460. return SDL_SetError("Error VIDIOC_S_PARM");
  461. }
  462. }
  463. }
  464. }
  465. SDL_zero(fmt);
  466. fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  467. if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {
  468. return SDL_SetError("Error VIDIOC_G_FMT");
  469. }
  470. device->hidden->driver_pitch = fmt.fmt.pix.bytesperline;
  471. io_method io = IO_METHOD_INVALID;
  472. if ((io == IO_METHOD_INVALID) && (cap.device_caps & V4L2_CAP_STREAMING)) {
  473. struct v4l2_requestbuffers req;
  474. SDL_zero(req);
  475. req.count = 8;
  476. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  477. req.memory = V4L2_MEMORY_MMAP;
  478. if ((xioctl(fd, VIDIOC_REQBUFS, &req) == 0) && (req.count >= 2)) {
  479. io = IO_METHOD_MMAP;
  480. device->hidden->nb_buffers = req.count;
  481. } else { // mmap didn't work out? Try USERPTR.
  482. SDL_zero(req);
  483. req.count = 8;
  484. req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  485. req.memory = V4L2_MEMORY_USERPTR;
  486. if (xioctl(fd, VIDIOC_REQBUFS, &req) == 0) {
  487. io = IO_METHOD_USERPTR;
  488. device->hidden->nb_buffers = 8;
  489. }
  490. }
  491. }
  492. if ((io == IO_METHOD_INVALID) && (cap.device_caps & V4L2_CAP_READWRITE)) {
  493. io = IO_METHOD_READ;
  494. device->hidden->nb_buffers = 1;
  495. }
  496. if (io == IO_METHOD_INVALID) {
  497. return SDL_SetError("Don't have a way to talk to this device");
  498. }
  499. device->hidden->io = io;
  500. device->hidden->buffers = SDL_calloc(device->hidden->nb_buffers, sizeof(*device->hidden->buffers));
  501. if (!device->hidden->buffers) {
  502. return false;
  503. }
  504. size_t size, pitch;
  505. if (!SDL_CalculateSurfaceSize(device->spec.format, device->spec.width, device->spec.height, &size, &pitch, false)) {
  506. return false;
  507. }
  508. bool rc = true;
  509. switch (io) {
  510. case IO_METHOD_READ:
  511. rc = AllocBufferRead(device, size);
  512. break;
  513. case IO_METHOD_MMAP:
  514. rc = AllocBufferMmap(device);
  515. break;
  516. case IO_METHOD_USERPTR:
  517. rc = AllocBufferUserPtr(device, size);
  518. break;
  519. case IO_METHOD_INVALID:
  520. SDL_assert(!"Shouldn't have hit this");
  521. break;
  522. }
  523. if (!rc) {
  524. return false;
  525. } else if (!EnqueueBuffers(device)) {
  526. return false;
  527. } else if (io != IO_METHOD_READ) {
  528. enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  529. if (xioctl(fd, VIDIOC_STREAMON, &type) == -1) {
  530. return SDL_SetError("VIDIOC_STREAMON");
  531. }
  532. }
  533. // Currently there is no user permission prompt for camera access, but maybe there will be a D-Bus portal interface at some point.
  534. SDL_CameraPermissionOutcome(device, true);
  535. return true;
  536. }
  537. static bool FindV4L2CameraByBusInfoCallback(SDL_Camera *device, void *userdata)
  538. {
  539. const V4L2DeviceHandle *handle = (const V4L2DeviceHandle *) device->handle;
  540. return (SDL_strcmp(handle->bus_info, (const char *) userdata) == 0);
  541. }
  542. static bool AddCameraFormat(const int fd, CameraFormatAddData *data, SDL_PixelFormat sdlfmt, SDL_Colorspace colorspace, Uint32 v4l2fmt, int w, int h)
  543. {
  544. struct v4l2_frmivalenum frmivalenum;
  545. SDL_zero(frmivalenum);
  546. frmivalenum.pixel_format = v4l2fmt;
  547. frmivalenum.width = (Uint32) w;
  548. frmivalenum.height = (Uint32) h;
  549. while (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &frmivalenum) == 0) {
  550. if (frmivalenum.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
  551. const int numerator = (int) frmivalenum.discrete.numerator;
  552. const int denominator = (int) frmivalenum.discrete.denominator;
  553. #if DEBUG_CAMERA
  554. const float fps = (float) denominator / (float) numerator;
  555. SDL_Log("CAMERA: * Has discrete frame interval (%d / %d), fps=%f", numerator, denominator, fps);
  556. #endif
  557. if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, denominator, numerator)) {
  558. return false; // Probably out of memory; we'll go with what we have, if anything.
  559. }
  560. frmivalenum.index++; // set up for the next one.
  561. } else if ((frmivalenum.type == V4L2_FRMIVAL_TYPE_STEPWISE) || (frmivalenum.type == V4L2_FRMIVAL_TYPE_CONTINUOUS)) {
  562. int d = frmivalenum.stepwise.min.denominator;
  563. // !!! FIXME: should we step by the numerator...?
  564. for (int n = (int) frmivalenum.stepwise.min.numerator; n <= (int) frmivalenum.stepwise.max.numerator; n += (int) frmivalenum.stepwise.step.numerator) {
  565. #if DEBUG_CAMERA
  566. const float fps = (float) d / (float) n;
  567. SDL_Log("CAMERA: * Has %s frame interval (%d / %d), fps=%f", (frmivalenum.type == V4L2_FRMIVAL_TYPE_STEPWISE) ? "stepwise" : "continuous", n, d, fps);
  568. #endif
  569. // SDL expects framerate, V4L2 provides interval
  570. if (!SDL_AddCameraFormat(data, sdlfmt, colorspace, w, h, d, n)) {
  571. return false; // Probably out of memory; we'll go with what we have, if anything.
  572. }
  573. d += (int) frmivalenum.stepwise.step.denominator;
  574. }
  575. break;
  576. }
  577. }
  578. return true;
  579. }
  580. static void MaybeAddDevice(const char *path)
  581. {
  582. if (!path) {
  583. return;
  584. }
  585. struct stat st;
  586. const int fd = open(path, O_RDWR /* required */ | O_NONBLOCK, 0);
  587. if (fd == -1) {
  588. return; // can't open it? skip it.
  589. } else if (fstat(fd, &st) == -1) {
  590. close(fd);
  591. return; // can't stat it? skip it.
  592. } else if (!S_ISCHR(st.st_mode)) {
  593. close(fd);
  594. return; // not a character device.
  595. }
  596. struct v4l2_capability vcap;
  597. const int rc = ioctl(fd, VIDIOC_QUERYCAP, &vcap);
  598. if (rc != 0) {
  599. close(fd);
  600. return; // probably not a v4l2 device at all.
  601. } else if ((vcap.device_caps & V4L2_CAP_VIDEO_CAPTURE) == 0) {
  602. close(fd);
  603. return; // not a video capture device.
  604. } else if (SDL_FindPhysicalCameraByCallback(FindV4L2CameraByBusInfoCallback, vcap.bus_info)) {
  605. close(fd);
  606. return; // already have it.
  607. }
  608. #if DEBUG_CAMERA
  609. SDL_Log("CAMERA: V4L2 camera path='%s' bus_info='%s' name='%s'", path, (const char *) vcap.bus_info, vcap.card);
  610. #endif
  611. CameraFormatAddData add_data;
  612. SDL_zero(add_data);
  613. struct v4l2_fmtdesc fmtdesc;
  614. SDL_zero(fmtdesc);
  615. fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  616. while (ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc) == 0) {
  617. SDL_PixelFormat sdlfmt = SDL_PIXELFORMAT_UNKNOWN;
  618. SDL_Colorspace colorspace = SDL_COLORSPACE_UNKNOWN;
  619. format_v4l2_to_sdl(fmtdesc.pixelformat, &sdlfmt, &colorspace);
  620. #if DEBUG_CAMERA
  621. SDL_Log("CAMERA: - Has format '%s'%s%s", SDL_GetPixelFormatName(sdlfmt),
  622. (fmtdesc.flags & V4L2_FMT_FLAG_EMULATED) ? " [EMULATED]" : "",
  623. (fmtdesc.flags & V4L2_FMT_FLAG_COMPRESSED) ? " [COMPRESSED]" : "");
  624. #endif
  625. fmtdesc.index++; // prepare for next iteration.
  626. if (sdlfmt == SDL_PIXELFORMAT_UNKNOWN) {
  627. continue; // unsupported by SDL atm.
  628. }
  629. struct v4l2_frmsizeenum frmsizeenum;
  630. SDL_zero(frmsizeenum);
  631. frmsizeenum.pixel_format = fmtdesc.pixelformat;
  632. while (ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsizeenum) == 0) {
  633. if (frmsizeenum.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
  634. const int w = (int) frmsizeenum.discrete.width;
  635. const int h = (int) frmsizeenum.discrete.height;
  636. #if DEBUG_CAMERA
  637. SDL_Log("CAMERA: * Has discrete size %dx%d", w, h);
  638. #endif
  639. if (!AddCameraFormat(fd, &add_data, sdlfmt, colorspace, fmtdesc.pixelformat, w, h)) {
  640. break; // Probably out of memory; we'll go with what we have, if anything.
  641. }
  642. frmsizeenum.index++; // set up for the next one.
  643. } else if ((frmsizeenum.type == V4L2_FRMSIZE_TYPE_STEPWISE) || (frmsizeenum.type == V4L2_FRMSIZE_TYPE_CONTINUOUS)) {
  644. const int minw = (int) frmsizeenum.stepwise.min_width;
  645. const int minh = (int) frmsizeenum.stepwise.min_height;
  646. const int maxw = (int) frmsizeenum.stepwise.max_width;
  647. const int maxh = (int) frmsizeenum.stepwise.max_height;
  648. const int stepw = (int) frmsizeenum.stepwise.step_width;
  649. const int steph = (int) frmsizeenum.stepwise.step_height;
  650. for (int w = minw; w <= maxw; w += stepw) {
  651. for (int h = minh; w <= maxh; w += steph) {
  652. #if DEBUG_CAMERA
  653. SDL_Log("CAMERA: * Has %s size %dx%d", (frmsizeenum.type == V4L2_FRMSIZE_TYPE_STEPWISE) ? "stepwise" : "continuous", w, h);
  654. #endif
  655. if (!AddCameraFormat(fd, &add_data, sdlfmt, colorspace, fmtdesc.pixelformat, w, h)) {
  656. break; // Probably out of memory; we'll go with what we have, if anything.
  657. }
  658. }
  659. }
  660. break;
  661. }
  662. }
  663. }
  664. close(fd);
  665. #if DEBUG_CAMERA
  666. SDL_Log("CAMERA: (total specs: %d)", add_data.num_specs);
  667. #endif
  668. if (add_data.num_specs > 0) {
  669. V4L2DeviceHandle *handle = (V4L2DeviceHandle *) SDL_calloc(1, sizeof (V4L2DeviceHandle));
  670. if (handle) {
  671. handle->path = SDL_strdup(path);
  672. if (handle->path) {
  673. handle->bus_info = SDL_strdup((char *)vcap.bus_info);
  674. if (handle->bus_info) {
  675. if (SDL_AddCamera((const char *) vcap.card, SDL_CAMERA_POSITION_UNKNOWN, add_data.num_specs, add_data.specs, handle)) {
  676. SDL_free(add_data.specs);
  677. return; // good to go.
  678. }
  679. SDL_free(handle->bus_info);
  680. }
  681. SDL_free(handle->path);
  682. }
  683. SDL_free(handle);
  684. }
  685. }
  686. SDL_free(add_data.specs);
  687. }
  688. static void V4L2_FreeDeviceHandle(SDL_Camera *device)
  689. {
  690. if (device) {
  691. V4L2DeviceHandle *handle = (V4L2DeviceHandle *) device->handle;
  692. SDL_free(handle->path);
  693. SDL_free(handle->bus_info);
  694. SDL_free(handle);
  695. }
  696. }
  697. #ifdef SDL_USE_LIBUDEV
  698. static bool FindV4L2CameraByPathCallback(SDL_Camera *device, void *userdata)
  699. {
  700. const V4L2DeviceHandle *handle = (const V4L2DeviceHandle *) device->handle;
  701. return (SDL_strcmp(handle->path, (const char *) userdata) == 0);
  702. }
  703. static void MaybeRemoveDevice(const char *path)
  704. {
  705. if (path) {
  706. SDL_CameraDisconnected(SDL_FindPhysicalCameraByCallback(FindV4L2CameraByPathCallback, (void *) path));
  707. }
  708. }
  709. static void CameraUdevCallback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath)
  710. {
  711. if (devpath && (udev_class & SDL_UDEV_DEVICE_VIDEO_CAPTURE)) {
  712. if (udev_type == SDL_UDEV_DEVICEADDED) {
  713. MaybeAddDevice(devpath);
  714. } else if (udev_type == SDL_UDEV_DEVICEREMOVED) {
  715. MaybeRemoveDevice(devpath);
  716. }
  717. }
  718. }
  719. #endif // SDL_USE_LIBUDEV
  720. static void V4L2_Deinitialize(void)
  721. {
  722. #ifdef SDL_USE_LIBUDEV
  723. SDL_UDEV_DelCallback(CameraUdevCallback);
  724. SDL_UDEV_Quit();
  725. #endif // SDL_USE_LIBUDEV
  726. }
  727. static void V4L2_DetectDevices(void)
  728. {
  729. #ifdef SDL_USE_LIBUDEV
  730. if (SDL_UDEV_Init()) {
  731. if (SDL_UDEV_AddCallback(CameraUdevCallback)) {
  732. SDL_UDEV_Scan(); // Force a scan to build the initial device list
  733. }
  734. return;
  735. }
  736. #endif // SDL_USE_LIBUDEV
  737. DIR *dirp = opendir("/dev");
  738. if (dirp) {
  739. struct dirent *dent;
  740. while ((dent = readdir(dirp)) != NULL) {
  741. int num = 0;
  742. if (SDL_sscanf(dent->d_name, "video%d", &num) == 1) {
  743. char fullpath[64];
  744. SDL_snprintf(fullpath, sizeof (fullpath), "/dev/video%d", num);
  745. MaybeAddDevice(fullpath);
  746. }
  747. }
  748. closedir(dirp);
  749. }
  750. }
  751. static bool V4L2_Init(SDL_CameraDriverImpl *impl)
  752. {
  753. impl->DetectDevices = V4L2_DetectDevices;
  754. impl->OpenDevice = V4L2_OpenDevice;
  755. impl->CloseDevice = V4L2_CloseDevice;
  756. impl->WaitDevice = V4L2_WaitDevice;
  757. impl->AcquireFrame = V4L2_AcquireFrame;
  758. impl->ReleaseFrame = V4L2_ReleaseFrame;
  759. impl->FreeDeviceHandle = V4L2_FreeDeviceHandle;
  760. impl->Deinitialize = V4L2_Deinitialize;
  761. return true;
  762. }
  763. CameraBootStrap V4L2_bootstrap = {
  764. "v4l2", "SDL Video4Linux2 camera driver", V4L2_Init, false
  765. };
  766. #endif // SDL_CAMERA_DRIVER_V4L2