1 /**************************************************************************//**
2 *
3 * @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 *
7 * Change Logs:
8 * Date Author Notes
9 * 2022-8-16 Wayne First version
10 *
11 ******************************************************************************/
12
13 #include <rtthread.h>
14
15 #if defined(BSP_USING_CCAP)
16
17 #include "drv_ccap.h"
18 #include <dfs_posix.h>
19
20 #define DBG_ENABLE
21 #define DBG_LEVEL DBG_LOG
22 #define DBG_SECTION_NAME "ccap.saver"
23 #define DBG_COLOR
24 #include <rtdbg.h>
25
26 #define THREAD_PRIORITY 5
27 #define THREAD_STACK_SIZE 4096
28 #define THREAD_TIMESLICE 5
29
30 #define DEF_FRAME_WIDTH 640
31 #define DEF_FRAME_HEIGHT 480
32
33 typedef struct
34 {
35 char *thread_name;
36 char *devname_ccap;
37 char *devname_sensor;
38 } ccap_grabber_param;
39 typedef ccap_grabber_param *ccap_grabber_param_t;
40
41 typedef struct
42 {
43 ccap_config sCcapConfig;
44 uint32_t u32CurFBPointer;
45 uint32_t u32FrameEnd;
46 rt_sem_t semFrameEnd;
47 } ccap_grabber_context;
48 typedef ccap_grabber_context *ccap_grabber_context_t;
49
nu_ccap_event_hook(void * pvData,uint32_t u32EvtMask)50 static void nu_ccap_event_hook(void *pvData, uint32_t u32EvtMask)
51 {
52 ccap_grabber_context_t psGrabberContext = (ccap_grabber_context_t)pvData;
53
54 if (u32EvtMask & NU_CCAP_FRAME_END)
55 {
56 rt_sem_release(psGrabberContext->semFrameEnd);
57 }
58
59 if (u32EvtMask & NU_CCAP_ADDRESS_MATCH)
60 {
61 LOG_I("Address matched");
62 }
63
64 if (u32EvtMask & NU_CCAP_MEMORY_ERROR)
65 {
66 LOG_E("Access memory error");
67 }
68 }
69
ccap_sensor_init(ccap_grabber_context_t psGrabberContext,ccap_grabber_param_t psGrabberParam)70 static rt_device_t ccap_sensor_init(ccap_grabber_context_t psGrabberContext, ccap_grabber_param_t psGrabberParam)
71 {
72 rt_err_t ret;
73 ccap_view_info_t psViewInfo;
74 sensor_mode_info *psSensorModeInfo;
75 rt_device_t psDevSensor = RT_NULL;
76 rt_device_t psDevCcap = RT_NULL;
77 ccap_config_t psCcapConfig = &psGrabberContext->sCcapConfig;
78
79 psDevCcap = rt_device_find(psGrabberParam->devname_ccap);
80 if (psDevCcap == RT_NULL)
81 {
82 LOG_E("Can't find %s", psGrabberParam->devname_ccap);
83 goto exit_ccap_sensor_init;
84 }
85
86 psDevSensor = rt_device_find(psGrabberParam->devname_sensor);
87 if (psDevSensor == RT_NULL)
88 {
89 LOG_E("Can't find %s", psGrabberParam->devname_sensor);
90 goto exit_ccap_sensor_init;
91 }
92
93 /* Packet pipe for preview */
94 psCcapConfig->sPipeInfo_Packet.u32Width = DEF_FRAME_WIDTH;
95 psCcapConfig->sPipeInfo_Packet.u32Height = DEF_FRAME_HEIGHT;
96 psCcapConfig->sPipeInfo_Packet.pu8FarmAddr = rt_malloc_align(psCcapConfig->sPipeInfo_Packet.u32Height * psCcapConfig->sPipeInfo_Packet.u32Width * 2, 32);
97 if (psCcapConfig->sPipeInfo_Packet.pu8FarmAddr == RT_NULL)
98 {
99 LOG_E("Can't malloc");
100 goto exit_ccap_sensor_init;
101 }
102
103 psCcapConfig->sPipeInfo_Packet.u32PixFmt = CCAP_PAR_OUTFMT_RGB565;
104 psCcapConfig->u32Stride_Packet = psCcapConfig->sPipeInfo_Packet.u32Width;
105
106 /* Planar pipe for encoding */
107 psCcapConfig->sPipeInfo_Planar.u32Width = psCcapConfig->sPipeInfo_Packet.u32Width;
108 psCcapConfig->sPipeInfo_Planar.u32Height = psCcapConfig->sPipeInfo_Packet.u32Height;
109 psCcapConfig->sPipeInfo_Planar.pu8FarmAddr = rt_malloc_align(psCcapConfig->sPipeInfo_Planar.u32Height * psCcapConfig->sPipeInfo_Planar.u32Width * 2, 32);
110 if (psCcapConfig->sPipeInfo_Planar.pu8FarmAddr == RT_NULL)
111 {
112 LOG_E("Can't malloc");
113 goto exit_ccap_sensor_init;
114 }
115
116 psCcapConfig->sPipeInfo_Planar.u32PixFmt = CCAP_PAR_PLNFMT_YUV422;
117 psCcapConfig->u32Stride_Planar = psCcapConfig->sPipeInfo_Planar.u32Width;
118
119 LOG_I("Packet.FarmAddr@0x%08X", psCcapConfig->sPipeInfo_Packet.pu8FarmAddr);
120 LOG_I("Packet.FarmWidth: %d", psCcapConfig->sPipeInfo_Packet.u32Width);
121 LOG_I("Packet.FarmHeight: %d", psCcapConfig->sPipeInfo_Packet.u32Height);
122
123 LOG_I("Planar.FarmAddr@0x%08X", psCcapConfig->sPipeInfo_Planar.pu8FarmAddr);
124 LOG_I("Planar.FarmWidth: %d", psCcapConfig->sPipeInfo_Planar.u32Width);
125 LOG_I("Planar.FarmHeight: %d", psCcapConfig->sPipeInfo_Planar.u32Height);
126
127 /* open CCAP */
128 ret = rt_device_open(psDevCcap, 0);
129 if (ret != RT_EOK)
130 {
131 LOG_E("Can't open %s", psGrabberParam->devname_ccap);
132 goto exit_ccap_sensor_init;
133 }
134
135 /* Find suit mode for packet pipe */
136 if (psCcapConfig->sPipeInfo_Packet.pu8FarmAddr != RT_NULL)
137 {
138 /* Check view window of packet pipe */
139 psViewInfo = &psCcapConfig->sPipeInfo_Packet;
140
141 if ((rt_device_control(psDevSensor, CCAP_SENSOR_CMD_GET_SUIT_MODE, (void *)&psViewInfo) != RT_EOK)
142 || (psViewInfo == RT_NULL))
143 {
144 LOG_E("Can't get suit mode for packet.");
145 goto fail_ccap_init;
146 }
147 }
148
149 /* Find suit mode for planner pipe */
150 if (psCcapConfig->sPipeInfo_Planar.pu8FarmAddr != RT_NULL)
151 {
152 int recheck = 1;
153
154 if (psViewInfo != RT_NULL)
155 {
156 if ((psCcapConfig->sPipeInfo_Planar.u32Width <= psViewInfo->u32Width) ||
157 (psCcapConfig->sPipeInfo_Planar.u32Height <= psViewInfo->u32Height))
158 recheck = 0;
159 }
160
161 if (recheck)
162 {
163 /* Check view window of planner pipe */
164 psViewInfo = &psCcapConfig->sPipeInfo_Planar;
165
166 /* Find suit mode */
167 if ((rt_device_control(psDevSensor, CCAP_SENSOR_CMD_GET_SUIT_MODE, (void *)&psViewInfo) != RT_EOK)
168 || (psViewInfo == RT_NULL))
169 {
170 LOG_E("Can't get suit mode for planner.");
171 goto exit_ccap_sensor_init;
172 }
173 }
174 }
175
176 /* Set cropping rectangle */
177 psCcapConfig->sRectCropping.x = 0;
178 psCcapConfig->sRectCropping.y = 0;
179 psCcapConfig->sRectCropping.width = psViewInfo->u32Width;
180 psCcapConfig->sRectCropping.height = psViewInfo->u32Height;
181
182 /* ISR Hook */
183 psCcapConfig->pfnEvHndler = nu_ccap_event_hook;
184 psCcapConfig->pvData = (void *)psGrabberContext;
185
186 /* Get Suitable mode. */
187 psSensorModeInfo = (sensor_mode_info *)psViewInfo;
188
189 /* Feed CCAP configuration */
190 ret = rt_device_control(psDevCcap, CCAP_CMD_CONFIG, (void *)psCcapConfig);
191 if (ret != RT_EOK)
192 {
193 LOG_E("Can't feed configuration %s", psGrabberParam->devname_ccap);
194 goto fail_ccap_init;
195 }
196
197 {
198 int i32SenClk = psSensorModeInfo->u32SenClk;
199
200 /* speed up pixel clock */
201 if (rt_device_control(psDevCcap, CCAP_CMD_SET_SENCLK, (void *)&i32SenClk) != RT_EOK)
202 {
203 LOG_E("Can't feed setting.");
204 goto fail_ccap_init;
205 }
206 }
207
208 /* Initial CCAP sensor */
209 if (rt_device_open(psDevSensor, 0) != RT_EOK)
210 {
211 LOG_E("Can't open sensor.");
212 goto fail_sensor_init;
213 }
214
215 /* Feed settings to sensor */
216 if (rt_device_control(psDevSensor, CCAP_SENSOR_CMD_SET_MODE, (void *)psSensorModeInfo) != RT_EOK)
217 {
218 LOG_E("Can't feed setting.");
219 goto fail_sensor_init;
220 }
221
222 ret = rt_device_control(psDevCcap, CCAP_CMD_SET_PIPES, (void *)psViewInfo);
223 if (ret != RT_EOK)
224 {
225 LOG_E("Can't set pipes %s", psGrabberParam->devname_ccap);
226 goto fail_ccap_init;
227 }
228
229 return psDevCcap;
230
231 fail_sensor_init:
232
233 if (psDevSensor)
234 rt_device_close(psDevSensor);
235
236 fail_ccap_init:
237
238 if (psDevCcap)
239 rt_device_close(psDevCcap);
240
241 exit_ccap_sensor_init:
242
243 psDevCcap = psDevSensor = RT_NULL;
244
245 return psDevCcap;
246 }
247
ccap_sensor_fini(rt_device_t psDevCcap,rt_device_t psDevSensor)248 static void ccap_sensor_fini(rt_device_t psDevCcap, rt_device_t psDevSensor)
249 {
250 if (psDevSensor)
251 rt_device_close(psDevSensor);
252
253 if (psDevCcap)
254 rt_device_close(psDevCcap);
255 }
256
ccap_save_frame(char * szFilename,const void * data,size_t size)257 static int ccap_save_frame(char *szFilename, const void *data, size_t size)
258 {
259 int fd;
260 int wrote_size = 0;
261
262 fd = open(szFilename, O_WRONLY | O_CREAT);
263 if (fd < 0)
264 {
265 LOG_E("Could not open %s for writing.", szFilename);
266 goto exit_ccap_save_planar_frame;
267 }
268
269 if ((wrote_size = write(fd, data, size)) != size)
270 {
271 LOG_E("Could not write to %s (%d != %d).", szFilename, wrote_size, size);
272 goto exit_ccap_save_planar_frame;
273 }
274
275 wrote_size = size;
276
277 LOG_I("Output %s", szFilename);
278
279 exit_ccap_save_planar_frame:
280
281 if (fd >= 0)
282 close(fd);
283
284 return wrote_size;
285 }
286
ccap_grabber(void * parameter)287 static void ccap_grabber(void *parameter)
288 {
289 ccap_grabber_param_t psGrabberParam = (ccap_grabber_param_t)parameter;
290 ccap_grabber_context sGrabberContext;
291
292 rt_device_t psDevCcap = RT_NULL;
293
294 rt_memset((void *)&sGrabberContext, 0, sizeof(ccap_grabber_context));
295
296 sGrabberContext.semFrameEnd = rt_sem_create(psGrabberParam->devname_ccap, 0, RT_IPC_FLAG_FIFO);
297 if (sGrabberContext.semFrameEnd == RT_NULL)
298 {
299 LOG_E("Can't allocate sem resource %s", psGrabberParam->devname_ccap);
300 goto exit_ccap_grabber;
301 }
302
303 /* initial ccap & sensor*/
304 psDevCcap = ccap_sensor_init(&sGrabberContext, psGrabberParam);
305 if (psDevCcap == RT_NULL)
306 {
307 LOG_E("Can't init %s and %s", psGrabberParam->devname_ccap, psGrabberParam->devname_sensor);
308 goto exit_ccap_grabber;
309 }
310
311 /* Start to capture */
312 if (rt_device_control(psDevCcap, CCAP_CMD_START_CAPTURE, RT_NULL) != RT_EOK)
313 {
314 LOG_E("Can't start %s", psGrabberParam->devname_ccap);
315 goto exit_ccap_grabber;
316 }
317
318 while (1)
319 {
320 if (sGrabberContext.semFrameEnd)
321 {
322 rt_sem_take(sGrabberContext.semFrameEnd, RT_WAITING_FOREVER);
323 }
324
325 sGrabberContext.u32FrameEnd++;
326 LOG_I("%s Grabbed %d", psGrabberParam->devname_ccap, sGrabberContext.u32FrameEnd);
327
328 if (sGrabberContext.u32FrameEnd == 30)
329 {
330 char szFilename[64];
331 uint32_t u32Factor = 0;
332
333 LOG_I("%s Capturing %d", psGrabberParam->devname_ccap, sGrabberContext.u32FrameEnd);
334
335 if (sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32PixFmt == CCAP_PAR_PLNFMT_YUV420)
336 {
337 u32Factor = 3;
338 rt_snprintf(szFilename, sizeof(szFilename), "/%08d_%dx%d.yuv420p",
339 rt_tick_get(),
340 sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Width,
341 sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Height);
342 }
343 else if (sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32PixFmt == CCAP_PAR_PLNFMT_YUV422)
344 {
345 u32Factor = 4;
346 rt_snprintf(szFilename, sizeof(szFilename), "/%08d_%s_%dx%d.yuv422p",
347 rt_tick_get(),
348 psGrabberParam->devname_ccap,
349 sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Width,
350 sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Height);
351 }
352
353 if (u32Factor > 0)
354 {
355 /* Save YUV422 or YUV420 frame from packet pipe*/
356 ccap_save_frame(szFilename, (const void *)sGrabberContext.sCcapConfig.sPipeInfo_Planar.pu8FarmAddr, sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Width * sGrabberContext.sCcapConfig.sPipeInfo_Planar.u32Height * u32Factor / 2);
357 }
358
359 /* Save RGB565 frame from packet pipe*/
360 rt_snprintf(szFilename, sizeof(szFilename), "/%08d_%s_%dx%d.rgb565",
361 rt_tick_get(),
362 psGrabberParam->devname_ccap,
363 sGrabberContext.sCcapConfig.sPipeInfo_Packet.u32Width,
364 sGrabberContext.sCcapConfig.sPipeInfo_Packet.u32Height);
365
366 ccap_save_frame(szFilename, (const void *)sGrabberContext.sCcapConfig.sPipeInfo_Packet.pu8FarmAddr, sGrabberContext.sCcapConfig.sPipeInfo_Packet.u32Width * sGrabberContext.sCcapConfig.sPipeInfo_Packet.u32Height * 2);
367
368 break;
369 }
370 }
371
372 exit_ccap_grabber:
373
374 ccap_sensor_fini(rt_device_find(psGrabberParam->devname_ccap), rt_device_find(psGrabberParam->devname_sensor));
375
376 return;
377 }
378
ccap_grabber_create(ccap_grabber_param_t psGrabberParam)379 static void ccap_grabber_create(ccap_grabber_param_t psGrabberParam)
380 {
381 rt_thread_t ccap_thread = rt_thread_find(psGrabberParam->thread_name);
382 if (ccap_thread == RT_NULL)
383 {
384 ccap_thread = rt_thread_create(psGrabberParam->thread_name,
385 ccap_grabber,
386 psGrabberParam,
387 THREAD_STACK_SIZE,
388 THREAD_PRIORITY,
389 THREAD_TIMESLICE);
390
391 if (ccap_thread != RT_NULL)
392 rt_thread_startup(ccap_thread);
393 }
394 }
395
ccap_saver(void)396 int ccap_saver(void)
397 {
398 #if defined(BSP_USING_CCAP0)
399 static ccap_grabber_param ccap0_grabber_param = {"grab0", "ccap0", "sensor0"};
400 ccap_grabber_create(&ccap0_grabber_param);
401 #endif
402 #if defined(BSP_USING_CCAP1)
403 static ccap_grabber_param ccap1_grabber_param = {"grab1", "ccap1", "sensor1"};
404 ccap_grabber_create(&ccap1_grabber_param);
405 #endif
406 return 0;
407 }
408 MSH_CMD_EXPORT(ccap_saver, camera saver demo);
409 #endif
410