v4l2-request-test.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. /*
  2. * Copyright (C) 2018 Paul Kocialkowski <paul.kocialkowski@bootlin.com>
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. */
  17. #define _GNU_SOURCE
  18. #include <errno.h>
  19. #include <fcntl.h>
  20. #include <getopt.h>
  21. #include <stdbool.h>
  22. #include <stdio.h>
  23. #include <stdlib.h>
  24. #include <string.h>
  25. #include <sys/ioctl.h>
  26. #include <sys/stat.h>
  27. #include <sys/types.h>
  28. #include <time.h>
  29. #include <unistd.h>
  30. #include <drm_fourcc.h>
  31. #include <linux/media.h>
  32. #include <linux/videodev2.h>
  33. #include <mpeg2-ctrls.h>
  34. #include <xf86drm.h>
  35. #include "v4l2-request-test.h"
  36. struct format_description formats[] = {
  37. {
  38. .description = "NV12 YUV",
  39. .v4l2_format = V4L2_PIX_FMT_NV12,
  40. .v4l2_buffers_count = 1,
  41. .v4l2_mplane = false,
  42. .drm_format = DRM_FORMAT_NV12,
  43. .drm_modifier = DRM_FORMAT_MOD_NONE,
  44. .planes_count = 2,
  45. .bpp = 16,
  46. },
  47. #ifdef DRM_FORMAT_MOD_ALLWINNER_TILED
  48. {
  49. .description = "Sunxi Tiled NV12 YUV",
  50. .v4l2_format = V4L2_PIX_FMT_SUNXI_TILED_NV12,
  51. .v4l2_buffers_count = 1,
  52. .v4l2_mplane = false,
  53. .drm_format = DRM_FORMAT_NV12,
  54. .drm_modifier = DRM_FORMAT_MOD_ALLWINNER_TILED,
  55. .planes_count = 2,
  56. .bpp = 16
  57. },
  58. #endif
  59. };
  60. static void print_help(void)
  61. {
  62. printf("Usage: v4l2-request-test [OPTIONS] [SLICES PATH]\n\n"
  63. "Options:\n"
  64. " -v [video path] path for the video node\n"
  65. " -m [media path] path for the media node\n"
  66. " -d [DRM path] path for the DRM node\n"
  67. " -D [DRM driver] DRM driver to use\n"
  68. " -s [slices filename format] format for filenames in the slices path\n"
  69. " -f [fps] number of frames to display per second\n"
  70. " -P [video preset] video preset to use\n"
  71. " -i enable interactive mode\n"
  72. " -l loop preset frames\n"
  73. " -q enable quiet mode\n"
  74. " -h help\n\n"
  75. "Video presets:\n");
  76. presets_usage();
  77. }
  78. static void print_summary(struct config *config, struct preset *preset)
  79. {
  80. printf("Config:\n");
  81. printf(" Video path: %s\n", config->video_path);
  82. printf(" Media path: %s\n", config->media_path);
  83. printf(" DRM path: %s\n", config->drm_path);
  84. printf(" DRM driver: %s\n", config->drm_driver);
  85. printf(" Slices path: %s\n", config->slices_path);
  86. printf(" Slices filename format: %s\n", config->slices_filename_format);
  87. printf(" FPS: %d\n\n", config->fps);
  88. printf("Preset:\n");
  89. printf(" Name: %s\n", preset->name);
  90. printf(" Description: %s\n", preset->description);
  91. printf(" License: %s\n", preset->license);
  92. printf(" Attribution: %s\n", preset->attribution);
  93. printf(" Width: %d\n", preset->width);
  94. printf(" Height: %d\n", preset->height);
  95. printf(" Frames count: %d\n", preset->frames_count);
  96. printf(" Format: ");
  97. switch (preset->type) {
  98. case CODEC_TYPE_MPEG2:
  99. printf("MPEG2");
  100. break;
  101. case CODEC_TYPE_H264:
  102. printf("H264");
  103. break;
  104. case CODEC_TYPE_H265:
  105. printf("H265");
  106. break;
  107. default:
  108. printf("Invalid");
  109. break;
  110. }
  111. printf("\n\n");
  112. }
  113. static long time_diff(struct timespec *before, struct timespec *after)
  114. {
  115. long before_time = before->tv_sec * 1000000 + before->tv_nsec / 1000;
  116. long after_time = after->tv_sec * 1000000 + after->tv_nsec / 1000;
  117. return (after_time - before_time);
  118. }
  119. static void print_time_diff(struct timespec *before, struct timespec *after,
  120. const char *prefix)
  121. {
  122. long diff = time_diff(before, after);
  123. printf("%s time: %ld us\n", prefix, diff);
  124. }
  125. static int load_data(const char *path, void **data, unsigned int *size)
  126. {
  127. void *buffer = NULL;
  128. unsigned int length;
  129. struct stat st;
  130. int fd;
  131. int rc;
  132. rc = stat(path, &st);
  133. if (rc < 0) {
  134. fprintf(stderr, "Stating file failed\n");
  135. goto error;
  136. }
  137. length = st.st_size;
  138. buffer = malloc(length);
  139. fd = open(path, O_RDONLY);
  140. if (fd < 0) {
  141. fprintf(stderr, "Unable to open file path: %s\n",
  142. strerror(errno));
  143. goto error;
  144. }
  145. rc = read(fd, buffer, length);
  146. if (rc < 0) {
  147. fprintf(stderr, "Unable to read file data: %s\n",
  148. strerror(errno));
  149. goto error;
  150. }
  151. close(fd);
  152. *data = buffer;
  153. *size = length;
  154. rc = 0;
  155. goto complete;
  156. error:
  157. if (buffer != NULL)
  158. free(buffer);
  159. rc = -1;
  160. complete:
  161. return rc;
  162. }
  163. static void setup_config(struct config *config)
  164. {
  165. memset(config, 0, sizeof(*config));
  166. config->video_path = strdup("/dev/video0");
  167. config->media_path = strdup("/dev/media0");
  168. config->drm_path = strdup("/dev/dri/card0");
  169. config->drm_driver = strdup("sun4i-drm");
  170. config->preset_name = strdup("bbb-mpeg2");
  171. config->slices_filename_format = strdup("slice-%d.dump");
  172. config->fps = 0;
  173. config->quiet = false;
  174. config->interactive = false;
  175. config->loop = false;
  176. }
  177. static void cleanup_config(struct config *config)
  178. {
  179. free(config->video_path);
  180. free(config->media_path);
  181. free(config->drm_path);
  182. free(config->drm_driver);
  183. free(config->preset_name);
  184. free(config->slices_path);
  185. free(config->slices_filename_format);
  186. }
  187. int main(int argc, char *argv[])
  188. {
  189. struct preset *preset;
  190. struct config config;
  191. struct video_buffer *video_buffers;
  192. struct video_setup video_setup;
  193. struct gem_buffer *gem_buffers;
  194. struct display_setup display_setup;
  195. struct media_device_info device_info;
  196. struct frame frame;
  197. struct timespec before, after;
  198. struct timespec video_before, video_after;
  199. struct timespec display_before, display_after;
  200. struct format_description *selected_format = NULL;
  201. bool before_taken = false;
  202. void *slice_data = NULL;
  203. char *slice_filename = NULL;
  204. char *slice_path = NULL;
  205. unsigned int slice_size;
  206. unsigned int width;
  207. unsigned int height;
  208. unsigned int v4l2_index;
  209. unsigned int index;
  210. unsigned int index_origin;
  211. unsigned int display_index;
  212. unsigned int display_count;
  213. unsigned int i;
  214. long frame_time;
  215. long frame_diff;
  216. int video_fd = -1;
  217. int media_fd = -1;
  218. int drm_fd = -1;
  219. uint64_t ts;
  220. bool test;
  221. int opt;
  222. int rc;
  223. setup_config(&config);
  224. while (1) {
  225. opt = getopt(argc, argv, "v:m:d:D:s:f:P:ilqh");
  226. if (opt == -1)
  227. break;
  228. switch (opt) {
  229. case 'v':
  230. free(config.video_path);
  231. config.video_path = strdup(optarg);
  232. break;
  233. case 'm':
  234. free(config.media_path);
  235. config.media_path = strdup(optarg);
  236. break;
  237. case 'd':
  238. free(config.drm_path);
  239. config.drm_path = strdup(optarg);
  240. break;
  241. case 'D':
  242. free(config.drm_driver);
  243. config.drm_driver = strdup(optarg);
  244. break;
  245. case 's':
  246. free(config.slices_filename_format);
  247. config.slices_filename_format = strdup(optarg);
  248. break;
  249. case 'f':
  250. config.fps = atoi(optarg);
  251. break;
  252. case 'P':
  253. free(config.preset_name);
  254. config.preset_name = strdup(optarg);
  255. break;
  256. case 'i':
  257. config.interactive = true;
  258. break;
  259. case 'l':
  260. config.loop = true;
  261. break;
  262. case 'q':
  263. config.quiet = true;
  264. break;
  265. case 'h':
  266. print_help();
  267. rc = 0;
  268. goto complete;
  269. case '?':
  270. print_help();
  271. goto error;
  272. }
  273. }
  274. preset = preset_find(config.preset_name);
  275. if (preset == NULL) {
  276. fprintf(stderr, "Unable to find preset for name: %s\n",
  277. config.preset_name);
  278. goto error;
  279. }
  280. config.buffers_count = preset->buffers_count;
  281. width = preset->width;
  282. height = preset->height;
  283. if (optind < argc)
  284. config.slices_path = strdup(argv[optind]);
  285. else
  286. asprintf(&config.slices_path, "data/%s", config.preset_name);
  287. print_summary(&config, preset);
  288. video_fd = open(config.video_path, O_RDWR | O_NONBLOCK, 0);
  289. if (video_fd < 0) {
  290. fprintf(stderr, "Unable to open video node: %s\n",
  291. strerror(errno));
  292. goto error;
  293. }
  294. media_fd = open(config.media_path, O_RDWR | O_NONBLOCK, 0);
  295. if (media_fd < 0) {
  296. fprintf(stderr, "Unable to open media node: %s\n",
  297. strerror(errno));
  298. goto error;
  299. }
  300. rc = ioctl(media_fd, MEDIA_IOC_DEVICE_INFO, &device_info);
  301. if (rc < 0) {
  302. fprintf(stderr, "Unable to get media device info: %s\n",
  303. strerror(errno));
  304. goto error;
  305. }
  306. printf("Media device driver: %s\n", device_info.driver);
  307. drm_fd = drmOpen(config.drm_driver, config.drm_path);
  308. if (drm_fd < 0) {
  309. fprintf(stderr, "Unable to open DRM node: %s\n",
  310. strerror(errno));
  311. goto error;
  312. }
  313. for (i = 0; i < ARRAY_SIZE(formats); i++) {
  314. test = video_engine_format_test(video_fd,
  315. formats[i].v4l2_mplane, width,
  316. height, formats[i].v4l2_format);
  317. if (test) {
  318. selected_format = &formats[i];
  319. break;
  320. }
  321. }
  322. if (selected_format == NULL) {
  323. fprintf(stderr,
  324. "Unable to find any supported destination format\n");
  325. goto error;
  326. }
  327. printf("Destination format: %s\n", selected_format->description);
  328. test = video_engine_capabilities_test(video_fd, V4L2_CAP_STREAMING);
  329. if (!test) {
  330. fprintf(stderr, "Missing required driver streaming capability\n");
  331. goto error;
  332. }
  333. if (selected_format->v4l2_mplane)
  334. test = video_engine_capabilities_test(video_fd,
  335. V4L2_CAP_VIDEO_M2M_MPLANE);
  336. else
  337. test = video_engine_capabilities_test(video_fd,
  338. V4L2_CAP_VIDEO_M2M);
  339. if (!test) {
  340. fprintf(stderr, "Missing required driver M2M capability\n");
  341. goto error;
  342. }
  343. rc = video_engine_start(video_fd, media_fd, width, height,
  344. selected_format, preset->type, &video_buffers,
  345. config.buffers_count, &video_setup);
  346. if (rc < 0) {
  347. fprintf(stderr, "Unable to start video engine\n");
  348. goto error;
  349. }
  350. rc = display_engine_start(drm_fd, width, height, selected_format,
  351. video_buffers, config.buffers_count,
  352. &gem_buffers, &display_setup);
  353. if (rc < 0) {
  354. fprintf(stderr, "Unable to start display engine\n");
  355. goto error;
  356. }
  357. if (config.fps > 0)
  358. frame_time = 1000000 / config.fps;
  359. display_count = 0;
  360. display_index = 0;
  361. index_origin = index = 0;
  362. /*
  363. * Display count might be lower than frames count due to potentially
  364. * missing predicted frames. Adapt at GOP scheduling time.
  365. */
  366. preset->display_count = preset->frames_count;
  367. while (display_count < preset->display_count) {
  368. if (!config.quiet)
  369. printf("\nProcessing frame %d/%d\n", index + 1,
  370. preset->frames_count);
  371. if ((index_origin != index && index < preset->frames_count) ||
  372. (index == 0 && index == index_origin)) {
  373. rc = frame_gop_schedule(preset, index);
  374. if (rc < 0) {
  375. fprintf(stderr, "Unable to schedule GOP frames order\n");
  376. goto error;
  377. }
  378. }
  379. index_origin = index;
  380. rc = frame_gop_next(&display_index);
  381. if (rc < 0) {
  382. fprintf(stderr, "Unable to get next GOP frame index for display\n");
  383. goto error;
  384. }
  385. if (!before_taken)
  386. clock_gettime(CLOCK_MONOTONIC, &before);
  387. else
  388. before_taken = false;
  389. /* Catch-up with already rendered frames. */
  390. if (display_index < index)
  391. goto frame_display;
  392. asprintf(&slice_filename, config.slices_filename_format, index);
  393. asprintf(&slice_path, "%s/%s", config.slices_path,
  394. slice_filename);
  395. free(slice_filename);
  396. slice_filename = NULL;
  397. rc = load_data(slice_path, &slice_data, &slice_size);
  398. if (rc < 0) {
  399. fprintf(stderr, "Unable to load slice data\n");
  400. goto error;
  401. }
  402. free(slice_path);
  403. slice_path = NULL;
  404. if (!config.quiet)
  405. printf("Loaded %d bytes of video slice data\n",
  406. slice_size);
  407. rc = frame_controls_fill(&frame, preset, config.buffers_count,
  408. index, slice_size);
  409. if (rc < 0) {
  410. fprintf(stderr, "Unable to fill frame controls\n");
  411. goto error;
  412. }
  413. v4l2_index = index % config.buffers_count;
  414. ts = TS_REF_INDEX(index);
  415. clock_gettime(CLOCK_MONOTONIC, &video_before);
  416. rc = video_engine_decode(video_fd, v4l2_index, &frame.frame,
  417. preset->type, ts, slice_data,
  418. slice_size, video_buffers,
  419. &video_setup);
  420. if (rc < 0) {
  421. fprintf(stderr, "Unable to decode video frame\n");
  422. goto error;
  423. }
  424. clock_gettime(CLOCK_MONOTONIC, &video_after);
  425. free(slice_data);
  426. slice_data = NULL;
  427. if (!config.quiet) {
  428. printf("Decoded video frame successfuly!\n");
  429. print_time_diff(&video_before, &video_after,
  430. "Frame decode");
  431. }
  432. /* Keep decoding until we can display a frame. */
  433. if (display_index > index) {
  434. before_taken = true;
  435. index++;
  436. continue;
  437. }
  438. frame_display:
  439. rc = frame_gop_dequeue();
  440. if (rc < 0) {
  441. fprintf(stderr,
  442. "Unable to dequeue next GOP frame index for display\n");
  443. goto error;
  444. }
  445. v4l2_index = display_index % config.buffers_count;
  446. clock_gettime(CLOCK_MONOTONIC, &display_before);
  447. rc = display_engine_show(drm_fd, v4l2_index, video_buffers,
  448. gem_buffers, &display_setup);
  449. if (rc < 0) {
  450. fprintf(stderr, "Unable to display video frame\n");
  451. goto error;
  452. }
  453. clock_gettime(CLOCK_MONOTONIC, &display_after);
  454. if (!config.quiet) {
  455. printf("Displayed video frame successfuly!\n");
  456. print_time_diff(&display_before, &display_after,
  457. "Frame display");
  458. }
  459. clock_gettime(CLOCK_MONOTONIC, &after);
  460. display_count++;
  461. if (config.interactive) {
  462. getchar();
  463. } else if (config.fps > 0) {
  464. frame_diff = time_diff(&before, &after);
  465. if (frame_diff > frame_time)
  466. fprintf(stderr,
  467. "Unable to meet %d fps target: %ld us late!\n",
  468. config.fps, frame_diff - frame_time);
  469. else
  470. usleep(frame_time - frame_diff);
  471. }
  472. if (display_index >= index)
  473. index++;
  474. if (config.loop && display_count == preset->display_count) {
  475. display_count = 0;
  476. display_index = 0;
  477. index_origin = index = 0;
  478. }
  479. }
  480. rc = video_engine_stop(video_fd, video_buffers, config.buffers_count,
  481. &video_setup);
  482. if (rc < 0) {
  483. fprintf(stderr, "Unable to stop video engine\n");
  484. goto error;
  485. }
  486. rc = display_engine_stop(drm_fd, gem_buffers, &display_setup);
  487. if (rc < 0) {
  488. fprintf(stderr, "Unable to stop display engine\n");
  489. goto error;
  490. }
  491. rc = 0;
  492. goto complete;
  493. error:
  494. rc = 1;
  495. complete:
  496. if (slice_data != NULL)
  497. free(slice_data);
  498. if (slice_path != NULL)
  499. free(slice_path);
  500. if (slice_filename != NULL)
  501. free(slice_filename);
  502. if (drm_fd >= 0)
  503. drmClose(drm_fd);
  504. if (media_fd >= 0)
  505. close(media_fd);
  506. if (video_fd >= 0)
  507. close(video_fd);
  508. cleanup_config(&config);
  509. return rc;
  510. }