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