/* ******************************************************************************* * Copyright (c) 2010-2022 VATICS(KNERON) Inc. All rights reserved. * * +-----------------------------------------------------------------+ * | THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED | * | AND COPIED IN ACCORDANCE WITH THE TERMS AND CONDITIONS OF SUCH | * | A LICENSE AND WITH THE INCLUSION OF THE THIS COPY RIGHT NOTICE. | * | THIS SOFTWARE OR ANY OTHER COPIES OF THIS SOFTWARE MAY NOT BE | * | PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. THE | * | OWNERSHIP AND TITLE OF THIS SOFTWARE IS NOT TRANSFERRED. | * | | * | THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT | * | ANY PRIOR NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY | * | VATICS(KNERON) INC. | * +-----------------------------------------------------------------+ * ******************************************************************************* */ #include #include #include #include #include #include #include #include #include "fec_api.h" #include "lvgl_example.h" #include "lvgl_example_msg_sender.h" #include "MemBroker/mem_broker.h" #include "MsgBroker/msg_broker.h" #include "vmf/video_source.h" #include "vmf/video_bind.h" #include "vmf/video_encoder.h" #include "vmf/video_display_mechanism.h" #include "vmf/source_connect.h" #include "vmf/sync_shared_memory.h" #include "vmf/config_fec.h" #include "vmf/vector_dma.h" #include "comm/video_buf.h" #include "comm/frame_info.h" #include "iniparser/iniparser.h" #include "lvgl/lvgl.h" #include "lvgl/custom/lv_custom.h" #include "lv_drivers/display/v4l2dev.h" #define VENC_VSRC_PIN "vsrc_ssm" #define VENC_RESOURCE_DIR "./Resource/" #define release_string(X) do { if (X) free(X); } while (0) typedef enum { HIDE = 0, SHOW } TEXT_MODE; VMF_LAYOUT_T g_tLayout; VMF_VSRC_HANDLE_T* g_ptVsrcHandle = NULL; static char *g_szConfig = NULL; static char *g_szAutoSceneConfig = NULL; static char *g_szSensorConfig = NULL; static uint32_t g_dwWidth, g_dwHeight; static uint32_t g_dwFontSize, g_dwSubIdx; static VMF_VENC_CODEC_TYPE g_eCodecType = VMF_VENC_CODEC_TYPE_H265; static int g_bTerminate = 0; static char text_string[32]; /*gui setting */ static uint32_t pool_size; static uint32_t gui_width; static uint32_t gui_height; static uint32_t gui_x; static uint32_t gui_y; static bool yuv_device; /*Indev button data*/ static const char *btn_txt[2][4] = { { "page1", "show_def", "show_ghi", "show_jkl" }, { "show_lmn", "show_opq", "show_rst", "return" } }; static const char *ovl_txt[2][4] = { { "", "def", "ghi", "jkl" }, {"lmn", "opq", "rst", "" } }; static lv_point_t positons[4]; static int btn_index = 0; static bool btn_status = 0; static int page_index = 0; static VMF_BIND_CONTEXT_T* g_ptBind = NULL; extern void dump_yuv(uint8_t *, uint8_t *, uint8_t *, uint32_t, const char *); static void load_config(void); static void sig_handler(int); static void exit_handler(void); static int init_video_source(void); static int init_fec_mode(void); static int init_dma_handle(VMF_DMA_DESCRIPTOR_T **, VMF_DMA_HANDLE_T **); static int dma_upadate_mem(uint8_t *, uint8_t *, uint32_t, VMF_DMA_DESCRIPTOR_T *, VMF_DMA_HANDLE_T *); static void vsrc_init_callback(unsigned int, unsigned int); static void setup_fec(VMF_FEC_ORIG_CONFIG_T *); static void setup_spec(VMF_VSRC_SPEC_CONFIG_T *, VMF_VENC_CODEC_TYPE); static void release_video_source(void); static int init_bind(void); static int init_video_display(VMF_VDISP_HANDLE_T **); static void *video_loop(void *); static void *gui_loop(void *); static void draw_screen(void); static bool button_read(lv_indev_drv_t *, lv_indev_data_t *); static int get_button_index(void); static bool get_button_status(void); static void reset_button_status(void); static void button_event(lv_obj_t *, lv_event_t); static void release_bind(void); static void msg_callback(MsgContext *, void *); static int init_text_overlay(void); static int set_text_overlay(TEXT_MODE); void dump_yuv(uint8_t *y, uint8_t *u, uint8_t *v, uint32_t y_size, const char *fmt) { FILE *out = fopen(fmt, "wb"); fwrite(y, 1, y_size, out); fwrite(u, 1, y_size >> 2, out); fwrite(v, 1, y_size >> 2, out); fclose(out); } void load_config(void) { const char *tmp; dictionary *ini; if((ini = iniparser_load(g_szConfig)) == NULL) { print_msg("Unable to parse file: %s\n", g_szConfig); exit(EXIT_FAILURE); } tmp = iniparser_getstring(ini, "sensor:sensor_config", NULL); g_szSensorConfig = strdup(tmp); tmp = iniparser_getstring(ini, "sensor:autoscene_config", NULL); g_szAutoSceneConfig = strdup(tmp); g_dwWidth = iniparser_getint(ini, "stream:width", 1920); g_dwHeight = iniparser_getint(ini, "stream:height", 1080); pool_size = iniparser_getint(ini, "gui:pool_size", 0); gui_width = iniparser_getint(ini, "gui:width", 480); gui_height = iniparser_getint(ini, "gui:height", 320); gui_x = iniparser_getint(ini, "gui:pos_x", 0); gui_y = iniparser_getint(ini, "gui:pos_y", 0); yuv_device = iniparser_getboolean(ini, "gui:yuv_device", 0); g_dwFontSize = iniparser_getint(ini, "text_overlay:font_size", 5); iniparser_freedict(ini); } void sig_handler(int sig) { print_msg("[%s] receive SIGNAL: %d\n",__func__, sig); g_bTerminate = 1; } void exit_handler(void) { release_string(g_szConfig); release_string(g_szSensorConfig); release_string(g_szAutoSceneConfig); } int init_video_source(void) { VMF_VSRC_INITOPT_T tInitOpt; VMF_VSRC_FRONTEND_CONFIG_T tFrontCfg; VMF_FEC_ORIG_CONFIG_T tFecOrgCfg; memset(&tInitOpt, 0, sizeof tInitOpt); memset(&tFrontCfg, 0, sizeof tFrontCfg); memset(&tFecOrgCfg, 0, sizeof tFecOrgCfg); setup_fec(&tFecOrgCfg); tFrontCfg.tFecInitConfig.ptFecConfig = &tFecOrgCfg; tFrontCfg.tFecInitConfig.eCoeffMode = VMF_FEC_COEF_MODE_ORIG; tFrontCfg.tFecInitConfig.eFecMethod = VMF_FEC_METHOD_GTR; tFrontCfg.tFecInitConfig.eGridSize = VMF_FEC_GRID_8X8; tFrontCfg.dwSensorConfigCount = 1; tFrontCfg.apszSensorConfig[0] = g_szSensorConfig; tInitOpt.dwFrontConfigCount = 1; tInitOpt.ptFrontConfig = &tFrontCfg; tInitOpt.pszAutoSceneConfig = g_szAutoSceneConfig; tInitOpt.pszOutPinPrefix = VENC_VSRC_PIN; tInitOpt.bShared = 1; tInitOpt.fnInitCallback = vsrc_init_callback; tInitOpt.pszResourceDir = VENC_RESOURCE_DIR; setup_spec(&tInitOpt.tSpecConfig, g_eCodecType); g_ptVsrcHandle = VMF_VSRC_Init(&tInitOpt); if (!g_ptVsrcHandle) { print_msg("[%s] VMF_VSRC_Init failed!\n", __func__); return -1; } if (VMF_VSRC_Start(g_ptVsrcHandle, NULL)) { print_msg("[%s] VMF_VSRC_Start failed!\n", __func__); return -1; } return 0; } int init_fec_mode(void) { if (setup_fec_mode(0, FEC_MODE_1R, false, g_eCodecType)) { print_msg("[%s] setup_fec_mode failed!\n", __func__); return -1; } return 0; } int init_dma_handle(VMF_DMA_DESCRIPTOR_T **pptDmaDesc, VMF_DMA_HANDLE_T **pptDmaHandle) { VMF_DMA_1D_INIT_T tDmaInit; memset(&tDmaInit, 0, sizeof tDmaInit); tDmaInit.dwFormatFlag = 0; *pptDmaDesc = VMF_DMA_Descriptor_Create(DMA_1D, &tDmaInit); if (!*pptDmaDesc) { print_msg("[%s] VMF_DMA_Descriptor_Create failed!\n", __func__); return -1; } *pptDmaHandle = VMF_DMA_Init(1, 128); if (!*pptDmaHandle) { print_msg("[%s] VMF_DMA_Init failed!\n", __func__); return -1; } return 0; } int dma_upadate_mem(uint8_t *pbyDst, uint8_t *pbySrc, uint32_t dwSize, VMF_DMA_DESCRIPTOR_T *ptDmaDesc, VMF_DMA_HANDLE_T *ptDmaHandle) { VMF_DMA_ADDR_T tDmaAddr; tDmaAddr.pbySrcYPhysAddr = pbySrc; tDmaAddr.pbyDstYPhysAddr = pbyDst; tDmaAddr.dwTransSize = dwSize; if (VMF_DMA_Descriptor_Update_Addr(ptDmaDesc, &tDmaAddr)) { print_msg("[%s] VMF_DMA_Descriptor_Update_Addr failed!\n", __func__); return -1; } if (VMF_DMA_Setup(ptDmaHandle, &ptDmaDesc, 1)) { print_msg("[%s] VMF_DMA_Setup failed!\n", __func__); return -1; } if (VMF_DMA_Process(ptDmaHandle)) { print_msg("[%s] VMF_DMA_Process failed!\n", __func__); return -1; } return 0; } void vsrc_init_callback(unsigned int dwWidth, unsigned int dwHeight) { memset(&g_tLayout, 0, sizeof g_tLayout); g_tLayout.dwCanvasWidth = dwWidth; g_tLayout.dwCanvasHeight = dwHeight; g_tLayout.dwVideoPosX = 0; g_tLayout.dwVideoPosY = 0; g_tLayout.dwVideoWidth = dwWidth; g_tLayout.dwVideoHeight = dwHeight; print_msg("[%s]: width:%u, height:%u\n", __func__, dwHeight, dwHeight); } void setup_spec(VMF_VSRC_SPEC_CONFIG_T *ptSpec, VMF_VENC_CODEC_TYPE eCodecType) { ptSpec->bEnableSpec = 1; ptSpec->dwIspMode = VMF_ISP_MODE_FEC; switch (eCodecType) { case VMF_VENC_CODEC_TYPE_H264: ptSpec->tIfpEncSpec.bEncH264 = 1; ptSpec->tIspEncSpec.bEncH264 = 1; break; case VMF_VENC_CODEC_TYPE_H265: ptSpec->tIfpEncSpec.bEncH265 = 1; ptSpec->tIspEncSpec.bEncH265 = 1; break; case VMF_VENC_CODEC_TYPE_MJPG: ptSpec->tIfpEncSpec.bEncJPEG = 1; ptSpec->tIspEncSpec.bEncJPEG = 1; break; default: exit(EXIT_FAILURE); } } void setup_fec(VMF_FEC_ORIG_CONFIG_T *ptFecOrgCfg) { ptFecOrgCfg->eAppType = VMF_FEC_APP_TABL; ptFecOrgCfg->fZoom = 1.0; } void release_video_source(void) { if (g_ptVsrcHandle) { VMF_VSRC_Stop(g_ptVsrcHandle); VMF_VSRC_Release(g_ptVsrcHandle); } } int init_bind(void) { VMF_BIND_INITOPT_T tInitOpt; memset(&tInitOpt, 0, sizeof tInitOpt); tInitOpt.dwSrcOutputIndex = 0; tInitOpt.ptSrcHandle = g_ptVsrcHandle; tInitOpt.pfnQueryFunc = (VMF_BIND_QUERY_FUNC) VMF_VSRC_GetInfo; tInitOpt.pfnIspFunc = (VMF_BIND_CONFIG_ISP_FUNC) VMF_VSRC_ConfigISP; g_ptBind = VMF_BIND_Init(&tInitOpt); if (!g_ptBind){ print_msg("[%s] VMF_BIND_Init failed!!\n", __func__); return -1; } return 0; } void release_bind(void) { if (g_ptBind) VMF_BIND_Release(g_ptBind); } int init_video_display(VMF_VDISP_HANDLE_T **pptDispHandle) { VMF_VDISP_INITOPT_T tDisplayOpt; memset(&tDisplayOpt, 0, sizeof tDisplayOpt); tDisplayOpt.dwInPixFormat = VMF_MAKEFOURCC('Y', 'M', '1', '2');; tDisplayOpt.dwMaxInWidth = VMF_32_ALIGN(g_dwWidth); tDisplayOpt.dwMaxInHeight = VMF_16_ALIGN(g_dwHeight); if ((*pptDispHandle = VMF_VDISP_Init(&tDisplayOpt)) == NULL) { printf("%s VMF_VDISP_Init failed!\n", __func__); return -1; } return 0; } void *video_loop(void *data __attribute__((unused))) { VMF_SRC_CONNECT_INFO_T tSrcCntInfo; SSM_HANDLE_T *ptSsmHandle = NULL; SSM_BUFFER_T tSsmBuf; VMF_FRAME_BUF_T atDispBuf[VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE]; uint8_t *atPhysAddr[VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE]; unsigned int dwDispQIdx = 0; pthread_t thrd_g; unsigned int dwYSize = VMF_32_ALIGN(g_dwWidth)*VMF_16_ALIGN(g_dwHeight), dwYUVSize = dwYSize * 3 >> 1; VMF_DMA_DESCRIPTOR_T *ptDmaDesc = NULL; VMF_DMA_HANDLE_T *ptDmaHandle = NULL; VMF_VDISP_HANDLE_T *ptDispHandle = NULL; if (init_video_display(&ptDispHandle)) { print_msg("[%s] init_video_display failed!\n", __func__); goto RELEASE; } if (init_dma_handle(&ptDmaDesc, &ptDmaHandle)) { print_msg("[%s] init_dma_handle failed!\n", __func__); goto RELEASE; } memset(&tSrcCntInfo, 0, sizeof tSrcCntInfo); memset(&tSsmBuf, 0, sizeof tSsmBuf); memset(atDispBuf, 0, sizeof atDispBuf); for (int i = 0; i < VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE; i++) { atDispBuf[i].apdwData[0] = MemBroker_GetMemory(dwYUVSize, VMF_ALIGN_TYPE_8_BYTE); if (!atDispBuf[i].apdwData[0]) { print_msg("[%s] allocate display buffer failed!\n", __func__); goto RELEASE; } atDispBuf[i].apdwData[1] = atDispBuf[i].apdwData[0] + dwYSize; atDispBuf[i].apdwData[2] = atDispBuf[i].apdwData[1] + (dwYSize >> 2); atPhysAddr[i] = MemBroker_GetPhysAddr(atDispBuf[i].apdwData[0]); } tSrcCntInfo.dwCodecType = g_eCodecType; if (VMF_BIND_Request(g_ptBind, g_dwWidth, g_dwHeight, 0, 0, &tSrcCntInfo)) { print_msg("[%s] VMF_BIND_Request failed!\n", __func__); exit(EXIT_FAILURE); } g_dwSubIdx = tSrcCntInfo.bUseResizedSrc ? 1 : 0; ptSsmHandle = SSM_Reader_Init(tSrcCntInfo.szSrcPin, tSrcCntInfo.bIsSsmShared); if (!ptSsmHandle) { print_msg("[%s] SSM_Reader_Init failed!\n", __func__); goto RELEASE; } if (init_text_overlay()) { print_msg("[%s] init_text_overlay failed\n", __func__); goto RELEASE; } pthread_create(&thrd_g, NULL, gui_loop, ptDispHandle); pthread_setname_np(thrd_g, "gui_loop"); while(!g_bTerminate) { uint8_t *pbySrc, *pbyDst; SSM_Reader_ReturnReceiveBuff(ptSsmHandle, &tSsmBuf); pbySrc = MemBroker_GetPhysAddr(tSsmBuf.buffer + VMF_MAX_SSM_HEADER_SIZE); pbyDst = atPhysAddr[dwDispQIdx]; if (dma_upadate_mem(pbyDst, pbySrc, dwYUVSize, ptDmaDesc, ptDmaHandle)) { print_msg("[%s] dma_upadate_mem failed!\n", __func__); goto RELEASE; } VMF_VDISP_ProcessOneFrame(ptDispHandle, &atDispBuf[dwDispQIdx], &dwDispQIdx); } RELEASE: if (thrd_g) { VMF_VDISP_PIP_Stop(ptDispHandle); pthread_join(thrd_g, NULL); } if (ptSsmHandle) { SSM_Reader_ReturnBuff(ptSsmHandle, &tSsmBuf); SSM_Release(ptSsmHandle); } if (ptDispHandle) VMF_VDISP_Release(ptDispHandle); for (int i = 0; i < VMF_VIDEO_DISPLAY_MIN_QUEUE_SIZE; i++) { if (atDispBuf[i].apdwData[0]) MemBroker_FreeMemory(atDispBuf[i].apdwData[0]); } if (ptDmaDesc) VMF_DMA_Descriptor_Destroy(ptDmaDesc); if (ptDmaHandle) VMF_DMA_Release(ptDmaHandle); return NULL; } void *gui_loop(void *data) { VMF_VDISP_HANDLE_T *disp_handle = data; unsigned int disp_buf_size; lv_color_t *buf; lv_disp_buf_t disp_buf; lv_disp_drv_t disp_drv; lv_indev_drv_t indev_drv; lv_indev_t *my_indev; /*LittlevGL init, allocate memory space as pool*/ lv_init(pool_size); /*Setup screen parameters*/ lv_scr_setup(disp_handle, gui_width, gui_height, gui_x, gui_y, yuv_device); /*Linux v4l2 dev init*/ v4l2dev_init(); /*Init display buffer, allocate buffer for screen-flushing*/ disp_buf_size = 10 * lv_scr_get_hor_res(); buf = malloc(disp_buf_size * sizeof *buf); /*Initialize a descriptor for the buffer*/ lv_disp_buf_init(&disp_buf, buf, NULL, disp_buf_size); /*Initialize and register a display driver*/ lv_disp_drv_init(&disp_drv); disp_drv.buffer = &disp_buf; disp_drv.flush_cb = v4l2dev_flush; lv_disp_drv_register(&disp_drv); /*Draw the menu on screen*/ draw_screen(); /*Initialize and register a input driver*/ lv_indev_drv_init(&indev_drv); indev_drv.type = LV_INDEV_TYPE_BUTTON; indev_drv.read_cb = button_read; my_indev = lv_indev_drv_register(&indev_drv); lv_indev_set_button_points(my_indev, positons); /*Flush screen*/ while (!g_bTerminate) { lv_task_handler(); usleep(5000); } /*Release display buffer*/ free(buf); /*Linux v4l2 dev release*/ v4l2dev_release(); /*LittlevGL release*/ lv_release(); return NULL; } void draw_screen(void) { lv_obj_t *scr = NULL; static lv_style_t button_style_rel; static lv_style_t button_style_pre; uint32_t btn_w = lv_scr_get_hor_res() / 2; uint32_t btn_h = lv_scr_get_ver_res() / 8; /*Init screen*/ scr = lv_disp_get_scr_act(NULL); lv_obj_clean(scr); lv_obj_set_style(scr, &lv_style_transp); /*Init button styles*/ lv_style_copy(&button_style_rel, &lv_style_btn_rel); lv_style_copy(&button_style_pre, &lv_style_btn_pr); button_style_rel.body.opa = LV_OPA_TRANSP; button_style_rel.body.border.opa = LV_OPA_TRANSP; button_style_rel.text.color = LV_COLOR_WHITE; button_style_pre.body.opa = LV_OPA_TRANSP; button_style_pre.body.border.opa = LV_OPA_TRANSP; button_style_pre.text.color = LV_COLOR_YELLOW; // button_style_pre.text.font = &lv_font_roboto_16; for (int i = 0; i < 4; i++) { lv_obj_t *btn; char name[32]; lv_obj_t *label; uint32_t offset = btn_h * 2 + i * btn_h; lv_area_t coords; /*Init buttons*/ btn = lv_btn_create(scr, NULL); lv_btn_set_style(btn, LV_BTN_STYLE_REL, &button_style_rel); lv_btn_set_style(btn, LV_BTN_STYLE_PR, &button_style_pre); lv_obj_set_size(btn, btn_w, btn_h); lv_obj_set_event_cb(btn, button_event); lv_obj_align(btn, NULL, LV_ALIGN_IN_TOP_MID, 0, offset); lv_obj_get_coords(btn, &coords); positons[i].x = (coords.x1 + coords.x2) >> 1; positons[i].y = (coords.y1 + coords.y2) >> 1; /*Init labels*/ strncpy(name, btn_txt[page_index][i], 32); label = lv_label_create(btn, NULL); lv_label_set_text(label, name); } } bool button_read(lv_indev_drv_t *drv __attribute__((unused)), lv_indev_data_t *data) { static uint32_t last_btn = 0; /*Store the last indicated button*/ int curr_btn = get_button_index(); if (curr_btn >= 0) { /*Is there a button indicated? (-1 means no button was indicated)*/ last_btn = curr_btn; data->state = LV_INDEV_STATE_PR; } else { data->state = LV_INDEV_STATE_REL; } data->btn_id = last_btn; return false; } /* * This fuction reads external hardware button ID (0, 1, 2, ...) * * msg_callback is used to simulate external buttons*/ int get_button_index(void) { return btn_index; } /* * This fuction reads external hardware button status (pressed/unpressed). * * msg_callback is used to simulate external buttons*/ bool get_button_status(void) { return btn_status; } /* * This fuction resets external hardware button to unpressed. */ void reset_button_status(void) { btn_status = false; } void button_event(lv_obj_t *obj __attribute__((unused)), lv_event_t event) { if (get_button_status() && event == LV_EVENT_PRESSING){ int ret = 0; int curr_idx = get_button_index(); int swap_idx = (page_index == 0) ? 0 : 3; /*Do something*/ if (curr_idx == swap_idx) { page_index = (page_index == 0) ? 1 : 0; draw_screen(); ret |= set_text_overlay(HIDE); // release_text_overlay(); } else { strncpy(text_string, ovl_txt[page_index][curr_idx], 32); ret |= set_text_overlay(SHOW); } print_msg("Button %d is selected and pressed.\n", curr_idx); reset_button_status(); } } void msg_callback(MsgContext *msg_context, void *user_data __attribute__((unused))) { print_msg("msg_context->pszHost=%s, msg_context->pszCmd=%s \n", msg_context->pszHost, msg_context->pszCmd); if (!strcasecmp(msg_context->pszHost, MODULE_NAME)) { if (!strcasecmp(msg_context->pszCmd, MSG_LVGL_BTN)) { btn_index = *(int *) msg_context->pbyData; btn_status = false; } else if (!strcasecmp(msg_context->pszCmd, MSG_LVGL_ENT)) { btn_status = true; } else if (!strcasecmp(msg_context->pszCmd, MSG_LVGL_RST)) { btn_index = -1; } } if (msg_context->bHasResponse) { msg_context->dwDataSize = 0; } } int set_text_overlay(TEXT_MODE eMode) { VMF_OVERLAY_TEXT_CONFIG_T tTxtConfig; VMF_OVERLAY_CONFIG_T tOvlConfig; memset(&tTxtConfig, 0, sizeof tTxtConfig); memset(&tOvlConfig, 0, sizeof tOvlConfig); tTxtConfig.dwPosX = 0; tTxtConfig.dwPosY = 0; tTxtConfig.dwAlign = VMF_TEXT_ALIGN_TOP | VMF_TEXT_ALIGN_RIGHT; tTxtConfig.pszText = text_string; //tOvlConfig->eMode = VMF_OVERLAY_MONO; tOvlConfig.eMode = VMF_OVERLAY_MONO; tOvlConfig.pszDatetimeFormat = NULL; tOvlConfig.dwDatetimePosX = 0; tOvlConfig.dwDatetimePosY = 0; tOvlConfig.dwDateTimeAlign = VMF_TEXT_ALIGN_TOP | VMF_TEXT_ALIGN_LEFT; tOvlConfig.dwTextCount = eMode; tOvlConfig.ptTextArray = &tTxtConfig; return VMF_VSRC_ConfigOverlay(g_ptVsrcHandle, 0, g_dwSubIdx, &tOvlConfig); } int init_text_overlay(void) { FONT_INFO_T tInfo; memset(&tInfo, 0, sizeof tInfo); memset(text_string, 0, sizeof text_string); tInfo.pszFontPath = "DejaVuSans-Bold.ttf"; tInfo.nFontSize = g_dwFontSize; tInfo.fOutlineWidth = 2; // set 0, if outline is not needed tInfo.tColorInfo.dwFontColor = 255 | (128<<8) | (128<<16);// Byte 0:y, 1:u, 2:v tInfo.tColorInfo.dwBorderColor = 0 | (128<<8) | (128<<16); // Byte 0:y, 1:u, 2:v tInfo.tColorInfo.dwBackColor = 0; // Byte 0:y, 1:u, 2:v, 3:alpha, set 0 as transparent background return VMF_VSRC_SetFont(g_ptVsrcHandle, 0, g_dwSubIdx, &tInfo); } int main(int argc, char **argv) { int ch; pthread_t thrd_v; atexit(exit_handler); signal(SIGTERM, sig_handler); signal(SIGINT, sig_handler); while ((ch = getopt(argc, argv, "c:h")) != -1) { switch(ch) { case 'c': g_szConfig = strdup(optarg); break; case 'h': default: print_msg("Usage:%s [-c