1 /*
2  * Copyright (c) 2024 Charles Dias <charlesdias.cd@outlook.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/kernel.h>
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/display.h>
10 #include <zephyr/drivers/video.h>
11 #include <zephyr/drivers/video-controls.h>
12 #include <lvgl.h>
13 
14 #define LOG_LEVEL CONFIG_LOG_DEFAULT_LEVEL
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(main);
17 
18 #define VIDEO_DEV_SW "VIDEO_SW_GENERATOR"
19 
main(void)20 int main(void)
21 {
22 	struct video_buffer *buffers[2], *vbuf;
23 	const struct device *display_dev;
24 	struct video_format fmt;
25 	struct video_caps caps;
26 	const struct device *video_dev;
27 	size_t bsize;
28 	int i = 0;
29 
30 	display_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_display));
31 	if (!device_is_ready(display_dev)) {
32 		LOG_ERR("Device not ready, aborting test");
33 		return 0;
34 	}
35 
36 #if DT_HAS_CHOSEN(zephyr_camera)
37 	video_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_camera));
38 	if (!device_is_ready(video_dev)) {
39 		LOG_ERR("%s device is not ready", video_dev->name);
40 		return 0;
41 	}
42 #else
43 	video_dev = device_get_binding(VIDEO_DEV_SW);
44 	if (video_dev == NULL) {
45 		LOG_ERR("%s device not found", VIDEO_DEV_SW);
46 		return 0;
47 	}
48 #endif
49 
50 	LOG_INF("- Device name: %s", video_dev->name);
51 
52 	/* Get capabilities */
53 	if (video_get_caps(video_dev, VIDEO_EP_OUT, &caps)) {
54 		LOG_ERR("Unable to retrieve video capabilities");
55 		return 0;
56 	}
57 
58 	LOG_INF("- Capabilities:");
59 	while (caps.format_caps[i].pixelformat) {
60 		const struct video_format_cap *fcap = &caps.format_caps[i];
61 		/* four %c to string */
62 		LOG_INF("  %c%c%c%c width [%u; %u; %u] height [%u; %u; %u]",
63 			(char)fcap->pixelformat, (char)(fcap->pixelformat >> 8),
64 			(char)(fcap->pixelformat >> 16), (char)(fcap->pixelformat >> 24),
65 			fcap->width_min, fcap->width_max, fcap->width_step, fcap->height_min,
66 			fcap->height_max, fcap->height_step);
67 		i++;
68 	}
69 
70 	/* Get default/native format */
71 	if (video_get_format(video_dev, VIDEO_EP_OUT, &fmt)) {
72 		LOG_ERR("Unable to retrieve video format");
73 		return 0;
74 	}
75 
76 	/* Set format */
77 	fmt.width = CONFIG_VIDEO_WIDTH;
78 	fmt.height = CONFIG_VIDEO_HEIGHT;
79 	fmt.pitch = fmt.width * 2;
80 	fmt.pixelformat = VIDEO_PIX_FMT_RGB565;
81 
82 	if (video_set_format(video_dev, VIDEO_EP_OUT, &fmt)) {
83 		LOG_ERR("Unable to set up video format");
84 		return 0;
85 	}
86 
87 	LOG_INF("- Format: %c%c%c%c %ux%u %u", (char)fmt.pixelformat, (char)(fmt.pixelformat >> 8),
88 		(char)(fmt.pixelformat >> 16), (char)(fmt.pixelformat >> 24), fmt.width, fmt.height,
89 		fmt.pitch);
90 
91 	if (caps.min_line_count != LINE_COUNT_HEIGHT) {
92 		LOG_ERR("Partial framebuffers not supported by this sample");
93 		return 0;
94 	}
95 	/* Size to allocate for each buffer */
96 	bsize = fmt.pitch * fmt.height;
97 
98 	/* Alloc video buffers and enqueue for capture */
99 	for (i = 0; i < ARRAY_SIZE(buffers); i++) {
100 		buffers[i] = video_buffer_alloc(bsize, K_FOREVER);
101 		if (buffers[i] == NULL) {
102 			LOG_ERR("Unable to alloc video buffer");
103 			return 0;
104 		}
105 
106 		video_enqueue(video_dev, VIDEO_EP_OUT, buffers[i]);
107 	}
108 
109 #ifdef CONFIG_VIDEO_HFLIP
110 	/* Video flip image horizontally */
111 	if (video_set_ctrl(video_dev, VIDEO_CID_HFLIP, (void *)1)) {
112 		LOG_ERR("Unable to set video control (HFLIP)");
113 		return 0;
114 	}
115 #endif
116 
117 #ifdef CONFIG_VIDEO_VFLIP
118 	/* Video flip image vertically */
119 	if (video_set_ctrl(video_dev, VIDEO_CID_VFLIP, (void *)1)) {
120 		LOG_ERR("Unable to set video control (VFLIP)");
121 		return 0;
122 	}
123 #endif
124 
125 	/* Start video capture */
126 	if (video_stream_start(video_dev)) {
127 		LOG_ERR("Unable to start capture (interface)");
128 		return 0;
129 	}
130 
131 	display_blanking_off(display_dev);
132 
133 	const lv_img_dsc_t video_img = {
134 		.header.w = CONFIG_VIDEO_WIDTH,
135 		.header.h = CONFIG_VIDEO_HEIGHT,
136 		.data_size = CONFIG_VIDEO_WIDTH * CONFIG_VIDEO_HEIGHT * sizeof(lv_color_t),
137 		.header.cf = LV_COLOR_FORMAT_NATIVE,
138 		.data = (const uint8_t *)buffers[0]->buffer,
139 	};
140 
141 	lv_obj_t *screen = lv_img_create(lv_scr_act());
142 
143 	LOG_INF("- Capture started");
144 
145 	/* Grab video frames */
146 	while (1) {
147 		int err;
148 
149 		err = video_dequeue(video_dev, VIDEO_EP_OUT, &vbuf, K_FOREVER);
150 		if (err) {
151 			LOG_ERR("Unable to dequeue video buf");
152 			return 0;
153 		}
154 
155 		lv_img_set_src(screen, &video_img);
156 		lv_obj_align(screen, LV_ALIGN_BOTTOM_LEFT, 0, 0);
157 
158 		lv_task_handler();
159 
160 		err = video_enqueue(video_dev, VIDEO_EP_OUT, vbuf);
161 		if (err) {
162 			LOG_ERR("Unable to requeue video buf");
163 			return 0;
164 		}
165 	}
166 }
167