前言:
功能测试的OSD使用还是比较简单的,随便找个位置做个时间戳,背景还是黑色,且只能显示一行,很明显效果并不是那么理想,这里做一个升级,对海思区域叠加的配置以及osd窗口的创建等都在本文一并写了。
OSD多行文字
我们的位图需要20ms之内作一次更新,有了性能要求之后,就不能像之前一样随意的把创建位图并叠加笼统的放进循环,频繁的打开创建销毁了,虽然文件很小也不必每次都进行保存了。一个合适的做法是在最开始做个初始化,根据需要随时获得待更新的数据,最后退出的时候销毁
那么多行文字怎么解决呢?最直接的想法肯定是生成好几个位图,拼成一整个,把整个的数据copy到region进行更新(更简单粗暴的就是多个region但是很明显没必要),那么怎么拼呢?是生成一个个位图后分别粘贴到全局的bitmap拼成大的,还是根源处解决呢…bitmap处需要大量计算,而且写起来也比较复杂,而根源处没有相应的库函数啊(SDL2更新了可以在字符串中加空格进行换行)
SDL_ConvertSurface和SDL_CreateRGBSurface
我们之前一直在刚好生成大小合适的图像,因为SDL_ConvertSurface是库函数也没有深究以为没有暴露对应的函数接口给用户,实际上并不是的,我们可以参考该函数的更底层是怎么进行转换的
/* * Convert a surface into the specified pixel format. */ SDL_Surface * SDL_ConvertSurface (SDL_Surface *surface, SDL_PixelFormat *format, Uint32 flags) { SDL_Surface *convert; Uint32 colorkey = 0; Uint8 alpha = 0; Uint32 surface_flags; SDL_Rect bounds; /* Check for empty destination palette! (results in empty image) */ if ( format->palette != NULL ) { int i; for ( i=0; i<format->palette->ncolors; ++i ) { if ( (format->palette->colors[i].r != 0) || (format->palette->colors[i].g != 0) || (format->palette->colors[i].b != 0) ) break; } if ( i == format->palette->ncolors ) { SDL_SetError("Empty destination palette"); return(NULL); } } /* Only create hw surfaces with alpha channel if hw alpha blits are supported */ if(format->Amask != 0 && (flags & SDL_HWSURFACE)) { const SDL_VideoInfo *vi = SDL_GetVideoInfo(); if(!vi || !vi->blit_hw_A) flags &= ~SDL_HWSURFACE; } /* Create a new surface with the desired format */ convert = SDL_CreateRGBSurface(flags, surface->w, surface->h, format->BitsPerPixel, format->Rmask, format->Gmask, format->Bmask, format->Amask); if ( convert == NULL ) { return(NULL); } /* Copy the palette if any */ if ( format->palette && convert->format->palette ) { SDL_memcpy(convert->format->palette->colors, format->palette->colors, format->palette->ncolors*sizeof(SDL_Color)); convert->format->palette->ncolors = format->palette->ncolors; } /* Save the original surface color key and alpha */ surface_flags = surface->flags; if ( (surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) { /* Convert colourkeyed surfaces to RGBA if requested */ if((flags & SDL_SRCCOLORKEY) != SDL_SRCCOLORKEY && format->Amask) { surface_flags &= ~SDL_SRCCOLORKEY; } else { colorkey = surface->format->colorkey; SDL_SetColorKey(surface, 0, 0); } } if ( (surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) { /* Copy over the alpha channel to RGBA if requested */ if ( format->Amask ) { surface->flags &= ~SDL_SRCALPHA; } else { alpha = surface->format->alpha; SDL_SetAlpha(surface, 0, 0); } } /* Copy over the image data */ bounds.x = 0; bounds.y = 0; bounds.w = surface->w; bounds.h = surface->h; SDL_LowerBlit(surface, &bounds, convert, &bounds); /* Clean up the original surface, and update converted surface */ if ( convert != NULL ) { SDL_SetClipRect(convert, &surface->clip_rect); } if ( (surface_flags & SDL_SRCCOLORKEY) == SDL_SRCCOLORKEY ) { Uint32 cflags = surface_flags&(SDL_SRCCOLORKEY|SDL_RLEACCELOK); if ( convert != NULL ) { Uint8 keyR, keyG, keyB; SDL_GetRGB(colorkey,surface->format,&keyR,&keyG,&keyB); SDL_SetColorKey(convert, cflags|(flags&SDL_RLEACCELOK), SDL_MapRGB(convert->format, keyR, keyG, keyB)); } SDL_SetColorKey(surface, cflags, colorkey); } if ( (surface_flags & SDL_SRCALPHA) == SDL_SRCALPHA ) { Uint32 aflags = surface_flags&(SDL_SRCALPHA|SDL_RLEACCELOK); if ( convert != NULL ) { SDL_SetAlpha(convert, aflags|(flags&SDL_RLEACCELOK), alpha); } if ( format->Amask ) { surface->flags |= SDL_SRCALPHA; } else { SDL_SetAlpha(surface, aflags, alpha); } } /* We're ready to go! */ return(convert); }
不难看到,除去了大量的判断后关键的操作是SDL_CreateRGBSurface创建窗口(位图)和叠加SDL_LowerBlit,SDL_LowerBlit是可以指定位置的啊,
/* * Set up a blit between two surfaces -- split into three parts: * The upper part, SDL_UpperBlit(), performs clipping and rectangle * verification. The lower part is a pointer to a low level * accelerated blitting function. * * These parts are separated out and each used internally by this * library in the optimimum places. They are exported so that if * you know exactly what you are doing, you can optimize your code * by calling the one(s) you need. */ int SDL_LowerBlit (SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect) { SDL_blit do_blit; SDL_Rect hw_srcrect; SDL_Rect hw_dstrect; /* Check to make sure the blit mapping is valid */ if ( (src->map->dst != dst) || (src->map->dst->format_version != src->map->format_version) ) { if ( SDL_MapSurface(src, dst) < 0 ) { return(-1); } } /* Figure out which blitter to use */ if ( (src->flags & SDL_HWACCEL) == SDL_HWACCEL ) { if ( src == SDL_VideoSurface ) { hw_srcrect = *srcrect; hw_srcrect.x += current_video->offset_x; hw_srcrect.y += current_video->offset_y; srcrect = &hw_srcrect; } if ( dst == SDL_VideoSurface ) { hw_dstrect = *dstrect; hw_dstrect.x += current_video->offset_x; hw_dstrect.y += current_video->offset_y; dstrect = &hw_dstrect; } do_blit = src->map->hw_blit; } else { do_blit = src->map->sw_blit; } return(do_blit(src, srcrect, dst, dstrect)); }
那么我们就可以将TTF_RenderUTF8_Solid转位图格式后的surface按照不同的宽高叠进来,把时间信息osd_time_text,脱靶信息osd_losttarget_info_text,其他信息osd_other_info_text经过TTF_RenderUTF8_Solid转位图后设置不同的宽高SDL_LowerBlit到整个osd_bottom_left 中,在hi_memcpy到海思的结构体bitmap中用于更新
/* *描述 :用于将想填写的内容生成位图左下角 用于pthread osd_create_task *参数 :pu8Str_losttargrt_inf 传字符串脱靶信息 * pu8Str_other_info 传字符串视场角信息和焦距 * pu8Str_time u8指针,传字符串时间 *返回值:成功返回0 *注意 :无 */ HI_S32 string_to_bmp_bottom_left(char *pu8Str_losttargrt_info,char *pu8Str_other_info,char *pu8Str_time) { SDL_Rect bounddst,boundsrc; // printf("before TTF_RenderUTF8_Solid\n"); osd_time_text = TTF_RenderUTF8_Solid(font, pu8Str_time, forecol); osd_losttarget_info_text = TTF_RenderUTF8_Solid(font, pu8Str_losttargrt_info, forecol); osd_other_info_text = TTF_RenderUTF8_Solid(font, pu8Str_other_info, forecol); // osd_bottom_left = SDL_ConvertSurface(osd_time_text,osd_fmt,0); osd_bottom_left = SDL_CreateRGBSurface(SDL_SWSURFACE, osd_time_text->w, osd_time_text->h*3, osd_fmt->BitsPerPixel,osd_fmt->Rmask, osd_fmt->Gmask, osd_fmt->Bmask, osd_fmt->Amask); // printf ("w is %d ,h is %d\n",osd_time_text->w,osd_time_text->h); // hi_memset(osd_bottom_left, sizeof(SDL_Surface),0, sizeof(SDL_Surface)); boundsrc.x = 0; boundsrc.y = 0; boundsrc.w = osd_losttarget_info_text->w; boundsrc.h = osd_losttarget_info_text->h; bounddst.x = 0; bounddst.y = 0; bounddst.w = osd_losttarget_info_text->w; bounddst.h = osd_losttarget_info_text->h; SDL_LowerBlit(osd_losttarget_info_text, &boundsrc, osd_bottom_left, &bounddst); boundsrc.x = 0; boundsrc.y = 0; boundsrc.w = osd_other_info_text->w; boundsrc.h = osd_other_info_text->h; bounddst.x = 0; bounddst.y = osd_losttarget_info_text->h; bounddst.w = osd_other_info_text->w; bounddst.h = osd_other_info_text->h; SDL_LowerBlit(osd_other_info_text, &boundsrc, osd_bottom_left, &bounddst); boundsrc.x = 0; boundsrc.y = 0; boundsrc.w = osd_time_text->w; boundsrc.h = osd_time_text->h; bounddst.x = 0; bounddst.y = osd_losttarget_info_text->h+osd_other_info_text->h; bounddst.w = osd_time_text->w; bounddst.h = osd_time_text->h; SDL_LowerBlit(osd_time_text, &boundsrc, osd_bottom_left, &bounddst); // printf ("w is %d ,h is %d\n",osd_bottom_left->w,osd_bottom_left->h); // stBitmap_bottom_left.pData = malloc(2*(osd_bottom_left->w)*(osd_bottom_left->h)); // if(stBitmap_bottom_left.pData == NULL) // { // printf("stBitmap.pData faided\r\n"); // } // pthread_mutex_lock(&mutex); hi_memset(stBitmap_bottom_left.pData, (2*(osd_bottom_left->w)*(osd_bottom_left->h)),0, (2*(osd_bottom_left->w)*(osd_bottom_left->h))); hi_memcpy(stBitmap_bottom_left.pData, (2*(osd_bottom_left->w)*(osd_bottom_left->h)),osd_bottom_left->pixels, (2*(osd_bottom_left->w)*(osd_bottom_left->h))); // pthread_mutex_unlock(&mutex); stBitmap_bottom_left.u32Width = osd_bottom_left->w; stBitmap_bottom_left.u32Height = osd_bottom_left->h; // printf ("stBitmap_bottom_left is %d ,h is %d\n",osd_bottom_left->w,osd_bottom_left->h);//446,90 // char savename[20] = {0}; // snprintf(savename,20,"./osd/now_time.bmp"); // printf("before SDL_SaveBMP\n"); // SDL_SaveBMP(osd_bottom_left, savename); // memset(stBitmap.pData, 0, sizeof(BITMAP_S)); SDL_FreeSurface(osd_time_text); SDL_FreeSurface(osd_losttarget_info_text); SDL_FreeSurface(osd_other_info_text); SDL_FreeSurface(osd_bottom_left); // TTF_CloseFont(font); // TTF_Quit(); return 0; }
/* *描述 :用于将想填写的内容生成位图左上角 用于pthread osd_create_task *参数 :pu8Str_angle_info 跟踪器状态(检测/识别/跟踪) * pu8Str_other_info 帧号 *返回值:成功返回0 *注意 :无 */ HI_S32 string_to_bmp_top_left(char *pu8Str_trk_info,char *pu8Str_frame_info) { // SDL_Color forecol = { 0xff, 0xff, 0xff, 0xff }; SDL_Rect bounddst,boundsrc; // printf("before TTF_RenderUTF8_Solid\n"); osd_trk_info_text = TTF_RenderUTF8_Solid(font, pu8Str_trk_info, forecol); osd_frame_info_text = TTF_RenderUTF8_Solid(font, pu8Str_frame_info, forecol); // osd_top_left = SDL_ConvertSurface(osd_time_text,osd_fmt,0); osd_top_left = SDL_CreateRGBSurface(SDL_SWSURFACE, osd_trk_info_text->w, osd_trk_info_text->h*2, osd_fmt->BitsPerPixel,osd_fmt->Rmask, osd_fmt->Gmask, osd_fmt->Bmask, osd_fmt->Amask);// // hi_memset(osd_top_left, sizeof(SDL_Surface),0, sizeof(SDL_Surface));//也可以选择初始化的时候建一次,每次memset,最后释放,但是直接memset无法调用SDL_LowerBlit boundsrc.x = 0; boundsrc.y = 0; boundsrc.w = osd_trk_info_text->w; boundsrc.h = osd_trk_info_text->h; bounddst.x = 0; bounddst.y = 0; bounddst.w = osd_trk_info_text->w; bounddst.h = osd_trk_info_text->h; SDL_LowerBlit(osd_trk_info_text, &boundsrc, osd_top_left, &bounddst); boundsrc.x = 0; boundsrc.y = 0; boundsrc.w = osd_frame_info_text->w; boundsrc.h = osd_frame_info_text->h; bounddst.x = 0; bounddst.y = osd_trk_info_text->h; bounddst.w = osd_frame_info_text->w; bounddst.h = osd_frame_info_text->h; SDL_LowerBlit(osd_frame_info_text, &boundsrc, osd_top_left, &bounddst); // stBitmap_top_left.pData = malloc(2*(osd_top_left->w)*(osd_top_left->h)); // if(stBitmap_top_left.pData == NULL) // { // printf("stBitmap_top_left.pData faided\r\n"); // } // printf ("osd_top_left is %d ,h is %d\n",osd_top_left->w,osd_top_left->h); // pthread_mutex_lock(&mutex ); hi_memset(stBitmap_top_left.pData, (2*(osd_top_left->w)*(osd_top_left->h)),0, (2*(osd_top_left->w)*(osd_top_left->h))); hi_memcpy(stBitmap_top_left.pData, (2*(osd_top_left->w)*(osd_top_left->h)),osd_top_left->pixels, (2*(osd_top_left->w)*(osd_top_left->h))); // pthread_mutex_unlock(&mutex ); // memcpy(stBitmap.pData+(2*(osd_top_left->w)*(osd_top_left->h)), osd_top_left->pixels, (2*(osd_top_left->w)*(osd_top_left->h))); stBitmap_top_left.u32Width = osd_top_left->w; stBitmap_top_left.u32Height = osd_top_left->h; // char savename[20] = {0}; // snprintf(savename,20,"./osd/now_time.bmp"); // printf("before SDL_SaveBMP\n"); // SDL_SaveBMP(osd_top_left, savename); // memset(stBitmap_top_left.pData, 0, sizeof(BITMAP_S)); SDL_FreeSurface(osd_trk_info_text); SDL_FreeSurface(osd_frame_info_text); SDL_FreeSurface(osd_top_left); // TTF_CloseFont(font); // TTF_Quit(); return 0; }
有需要的话可以吧保存放开
补充
循环内我们每次都创建了新的SDL_CreateRGBSurface,必须对应的SDL_FreeSurface,不然过一会内存就会占满,卡在这里不往下进行,之后段错误啊,buserror(可能是memset bitmap数据时填错了长度,出现概率低,修改后暂未出现)out of memory之类的都出现过,不易排查。至于这个为什么不创建一次,最后free的原因是没有往下深入研究,不知道memset这个surface的结构体那部分,在长时间系统测试稳定和性能完全达标(不到5ms)后,暂时先这样处理
区域叠加的多行
在哪部分叠加
之前的RGN叠加在venc通道,且只能叠加一个通道,一旦需要多路码流叠加,来回切通道会造成不必要的麻烦。mentor建议直接叠加到vpss上,之前没有选择叠加在vpss因为 stRgnAttr.enType选择了OVERLAY_RGN,这个模式不能在vpss叠加,选择COVEREX_RGN就可以解决
透明度
选择了COVEREX_RGN后发现还没有做改动我们的背景色就由纯黑变成了浅黑,说明alpha值在这里生效了,那就更简单了,stChnAttr.unChnAttr.stOverlayChn.u32BgAlpha值改为0,我们的背景就变成透明的啦!
多个区域叠加
这个也比较简单OverlayHandle选择不同值即可取值范围[0, RGN_HANDLE_MAX),创建不同区域分别叠加即可
叠加部分代码
/* *描述 :用于将视频文件添加时间水印左下角 *参数 :无 *返回值:OverlayHandle_bottom_leftle *注意 :参数在HI_MPI_RGN_Create并不做检查,只有在HI_MPI_RGN_AttachToChn的时候才会报出相应的错 */ HI_S32 RGN_AddOsdToVpss_bottom_left(HI_S32 s32ChnId) { HI_S32 s32Ret; RGN_ATTR_S stRgnAttr; RGN_CHN_ATTR_S stChnAttr; MPP_CHN_S stChn; // RGN_CANVAS_INFO_S stCanvasInfo; stChn.enModId = HI_ID_VPSS; /**模块号**///HI_ID_VPSS HI_ID_VENC stChn.s32DevId = 0; /**设备号**/ stChn.s32ChnId = s32ChnId; /**通道号**/ /**创建区域**/ sleep(2);//等待位图生成 stRgnAttr.unAttr.stOverlay.u32CanvasNum = 1; stRgnAttr.enType = OVERLAYEX_RGN; /**区域类型:叠加**/ stRgnAttr.unAttr.stOverlay.enPixelFmt = PIXEL_FORMAT_ARGB_1555; /**像素格式**///PIXEL_FORMAT_BGR_565 PIXEL_FORMAT_ARGB_1555 if (stBitmap_bottom_left.u32Width % 2 != 0) { stBitmap_bottom_left.u32Width += 1; } if (stBitmap_bottom_left.u32Height % 2 != 0) { stBitmap_bottom_left.u32Height += 1; } printf ("stBitmap_bottom_left.u32Width is %d ,stBitmap_bottom_left.u32Height is %d\n",stBitmap_bottom_left.u32Width,stBitmap_bottom_left.u32Height); stRgnAttr.unAttr.stOverlay.stSize.u32Width = stBitmap_bottom_left.u32Width;//240; /**区域宽**/ stRgnAttr.unAttr.stOverlay.stSize.u32Height = stBitmap_bottom_left.u32Height;//192; /**区域高**/ stRgnAttr.unAttr.stOverlay.u32BgColor = 0xffffff00;//0x00007c00; /**区域背景颜色**/ s32Ret = HI_MPI_RGN_Create(OverlayHandle_bottom_left, &stRgnAttr); if(s32Ret != HI_SUCCESS) { SAMPLE_PRT("RGN create failed: %#x\n", s32Ret); } /**将区域叠加到通道**/ /**设置叠加区域的通道显示属性**/ stChnAttr.bShow = HI_TRUE; stChnAttr.enType = OVERLAYEX_RGN; stChnAttr.unChnAttr.stOverlayChn.stPoint.s32X = 20;//240; stChnAttr.unChnAttr.stOverlayChn.stPoint.s32Y = 980;//192; stChnAttr.unChnAttr.stOverlayChn.u32BgAlpha = 0;//128; stChnAttr.unChnAttr.stOverlayChn.u32FgAlpha = 128;//80; stChnAttr.unChnAttr.stOverlayChn.u32Layer = OverlayHandle_bottom_left; /**设置QP属性Qp(量化参数,反应压缩效果)以 H.264 编码为例,通常图像 Qp(量化参数,反应压缩效果) 越低,图像的质量越好,码率越高;图像 Qp 越高,图像质量越差,码率越低。**/ stChnAttr.unChnAttr.stOverlayChn.stQpInfo.bAbsQp = HI_FALSE; stChnAttr.unChnAttr.stOverlayChn.stQpInfo.s32Qp = 0; stChnAttr.unChnAttr.stOverlayChn.stQpInfo.bQpDisable = HI_FALSE; /**定义 OSD 反色相关属性**/ /**单元反色区域,反色处理的基本单元,[16, 64],需 16 对齐**/ stChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Height = 16; stChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Width = 16; /**亮度阈值,取值范围:[0, 255]**/ stChnAttr.unChnAttr.stOverlayChn.stInvertColor.u32LumThresh = 128;//128 /**OSD 反色触发模式**/ stChnAttr.unChnAttr.stOverlayChn.stInvertColor.enChgMod = LESSTHAN_LUM_THRESH; /**OSD 反色开关。overlay不支持反色**/ stChnAttr.unChnAttr.stOverlayChn.stInvertColor.bInvColEn = HI_TRUE; stChnAttr.unChnAttr.stOverlayChn.enAttachDest =ATTACH_JPEG_MAIN; // OverlayHandle =0; s32Ret = HI_MPI_RGN_AttachToChn(OverlayHandle_bottom_left, &stChn, &stChnAttr); if(s32Ret != HI_SUCCESS) { SAMPLE_PRT("HI_MPI_RGN_AttachToChn: %#x\n", s32Ret); } stBitmap_bottom_left.enPixelFormat = PIXEL_FORMAT_ARGB_1555; // stBitmap.u32Height = OVERLAY_H; // stBitmap.u32Width = OVERLAY_W; s32Ret = HI_MPI_RGN_SetBitMap(OverlayHandle_bottom_left,&stBitmap_bottom_left); if(s32Ret != HI_SUCCESS) { SAMPLE_PRT("HI_MPI_RGN_SetBitMap failed with %#x!\n", s32Ret); } // hi_memset(stBitmap_bottom_left.pData, sizeof(BITMAP_S),0, sizeof(BITMAP_S)); // s32Ret = HI_MPI_RGN_GetCanvasInfo(OverlayHandle,&stCanvasInfo); // s32Ret = HI_MPI_RGN_DetachFromChn(OverlayHandle, &stChn);//最后用户可以将该区域从通道中撤出(非必须操作),再销毁区域。 // if(s32Ret != HI_SUCCESS) // { // SAMPLE_PRT("HI_MPI_RGN_DetachFromChn: %#x\n", s32Ret); // } // s32Ret = HI_MPI_RGN_Destroy(OverlayHandle); // if(s32Ret != HI_SUCCESS) // { // SAMPLE_PRT("RGN create failed: %#x\n", s32Ret); // } return 0; }
HI_S32 RGN_AddOsdToVpss_top_left(HI_S32 s32ChnId) { HI_S32 s32Ret; RGN_ATTR_S stRgnAttr; RGN_CHN_ATTR_S stChnAttr; MPP_CHN_S stChn; // OverlayHandle_top_left =1; stChn.enModId = HI_ID_VPSS; /**模块号**///HI_ID_VPSS HI_ID_VENC stChn.s32DevId = 0; /**设备号**/ stChn.s32ChnId = s32ChnId; /**通道号**/ /**创建区域**/ sleep(2);//等待位图生成 stRgnAttr.unAttr.stOverlay.u32CanvasNum = 2; stRgnAttr.enType = OVERLAYEX_RGN; /**区域类型:叠加**/ stRgnAttr.unAttr.stOverlay.enPixelFmt = PIXEL_FORMAT_ARGB_1555; /**像素格式**///PIXEL_FORMAT_BGR_565 PIXEL_FORMAT_ARGB_1555 if (stBitmap_top_left.u32Width % 2 != 0) { stBitmap_top_left.u32Width += 1; } if (stBitmap_top_left.u32Height % 2 != 0) { stBitmap_top_left.u32Height += 1; } // printf ("stBitmap_top_left.u32Width is %d ,stBitmap_top_left.u32Height is %d\n",stBitmap_top_left.u32Width,stBitmap_top_left.u32Height);394,60 stRgnAttr.unAttr.stOverlay.stSize.u32Width = stBitmap_top_left.u32Width;//240; /**区域宽**/ stRgnAttr.unAttr.stOverlay.stSize.u32Height = stBitmap_top_left.u32Height;//192; /**区域高**/ stRgnAttr.unAttr.stOverlay.u32BgColor = 0xffffff00;//0x00007c00; /**区域背景颜色**/ s32Ret = HI_MPI_RGN_Create(OverlayHandle_top_left, &stRgnAttr); if(s32Ret != HI_SUCCESS) { SAMPLE_PRT("RGN create failed: %#x\n", s32Ret); } /**将区域叠加到通道**/ /**设置叠加区域的通道显示属性**/ stChnAttr.bShow = HI_TRUE; stChnAttr.enType = OVERLAYEX_RGN; stChnAttr.unChnAttr.stOverlayChn.stPoint.s32X = 20;//240; stChnAttr.unChnAttr.stOverlayChn.stPoint.s32Y = 20;//192; stChnAttr.unChnAttr.stOverlayChn.u32BgAlpha = 0;//128; stChnAttr.unChnAttr.stOverlayChn.u32FgAlpha = 128; stChnAttr.unChnAttr.stOverlayChn.u32Layer = OverlayHandle_top_left; /**设置QP属性Qp(量化参数,反应压缩效果)以 H.264 编码为例,通常图像 Qp(量化参数,反应压缩效果) 越低,图像的质量越好,码率越高;图像 Qp 越高,图像质量越差,码率越低。**/ stChnAttr.unChnAttr.stOverlayChn.stQpInfo.bAbsQp = HI_FALSE; stChnAttr.unChnAttr.stOverlayChn.stQpInfo.s32Qp = 0; stChnAttr.unChnAttr.stOverlayChn.stQpInfo.bQpDisable = HI_FALSE; /**定义 OSD 反色相关属性**/ /**单元反色区域,反色处理的基本单元,[16, 64],需 16 对齐**/ stChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Height = 16; stChnAttr.unChnAttr.stOverlayChn.stInvertColor.stInvColArea.u32Width = 16; /**亮度阈值,取值范围:[0, 255]**/ stChnAttr.unChnAttr.stOverlayChn.stInvertColor.u32LumThresh = 128;//128 /**OSD 反色触发模式**/ stChnAttr.unChnAttr.stOverlayChn.stInvertColor.enChgMod = LESSTHAN_LUM_THRESH; /**OSD 反色开关。overlay不支持反色**/ stChnAttr.unChnAttr.stOverlayChn.stInvertColor.bInvColEn = HI_FALSE; stChnAttr.unChnAttr.stOverlayChn.enAttachDest =ATTACH_JPEG_MAIN; // OverlayHandle =0; s32Ret = HI_MPI_RGN_AttachToChn(OverlayHandle_top_left, &stChn, &stChnAttr); if(s32Ret != HI_SUCCESS) { SAMPLE_PRT("HI_MPI_RGN_AttachToChn: %#x\n", s32Ret); } stBitmap_top_left.enPixelFormat = PIXEL_FORMAT_ARGB_1555; s32Ret = HI_MPI_RGN_SetBitMap(OverlayHandle_top_left,&stBitmap_top_left); if(s32Ret != HI_SUCCESS) { SAMPLE_PRT("HI_MPI_RGN_SetBitMap failed with %#x!\n", s32Ret); } // hi_memset(stBitmap_top_left.pData, sizeof(BITMAP_S),0, sizeof(BITMAP_S)); return 0; }
初始化和退出
都配置完了就可以初始化了,初始化打开osd字体,在创建叠加通道前生成位图,区域初始化就是我们整个osd功能初始化的内容了,退出相应的关闭字体,释放打开的结构体就好
/* *描述 :用于OSD 初始化,打开字体,生成位图后区域初始化 *参数 :arg 为自定义结构video_process_s,VPSS_GRP和VPSS_CHN用于传参给HI_MPI_VPSS_GetChnFrame *返回值: *注意 : */ HI_VOID osd_init(HI_VOID *arg) { if (TTF_Init() < 0 ) { fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError()); SDL_Quit(); } font = TTF_OpenFont(FONT_PATH, 29); if ( font == NULL ) { fprintf(stderr, "Couldn't load %d pt font from %s: %s\n",18,"ptsize", SDL_GetError()); // return -1; } video_process_s* pstPara; pstPara = (video_process_s*)arg; time_t now; struct tm *ptm; char timestr[OSD_LENGTH] = {0}; char lost_target_info_str[OSD_LENGTH] = {0}; char other_info_str[OSD_LENGTH] = {0}; char trk_str[OSD_LENGTH] = {0}; char frame_str[OSD_LENGTH] = {0}; osd_fmt = (SDL_PixelFormat*)malloc(sizeof(SDL_PixelFormat)); hi_memset(osd_fmt,sizeof(SDL_PixelFormat),0,sizeof(SDL_PixelFormat)); osd_fmt->BitsPerPixel = 16; osd_fmt->BytesPerPixel = 2; osd_fmt->colorkey = 0xffffffff; osd_fmt->alpha = 0xff; // osd_bottom_left = SDL_CreateRGBSurface(SDL_SWSURFACE, OSD_BOTTOM_LEFT_W, OSD_BOTTOM_LEFT_H, // osd_fmt->BitsPerPixel,osd_fmt->Rmask, osd_fmt->Gmask, osd_fmt->Bmask, osd_fmt->Amask); // osd_top_left = SDL_CreateRGBSurface(SDL_SWSURFACE, OSD_TOP_LEFT_W, OSD_TOP_LEFT_H, // osd_fmt->BitsPerPixel,osd_fmt->Rmask, osd_fmt->Gmask, osd_fmt->Bmask, osd_fmt->Amask); time(&now); ptm = localtime(&now); snprintf(timestr,100,"当前时间:%d-%02d-%02d %02d:%02d:%02d ",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec); snprintf(lost_target_info_str,100,"脱靶信息:X:%02d,Y:%02d",osd_fmt->BitsPerPixel,osd_fmt->BytesPerPixel); snprintf(other_info_str,100,"视场角信息:X:%02d,Y:%02d,焦距:%02d",osd_fmt->BitsPerPixel,osd_fmt->BytesPerPixel,osd_fmt->BytesPerPixel); snprintf(trk_str,100,"跟踪器状态:检测?识别?跟踪? "); snprintf(frame_str,100,"帧率:%02d",osd_fmt->BytesPerPixel); string_to_bmp_bottom_left(lost_target_info_str,other_info_str,timestr); string_to_bmp_top_left(trk_str,frame_str); RGN_AddOsdToVpss_bottom_left(pstPara->VpssChn); RGN_AddOsdToVpss_top_left(pstPara->VpssChn); } /* *描述 :用于OSD 退出,关闭字体,释放配置结构体 *参数 :无 *注意 : */ HI_VOID osd_exit() { // SDL_FreeSurface(osd_time_text); // SDL_FreeSurface(osd_losttarget_info_text); // SDL_FreeSurface(osd_other_info_text); // SDL_FreeSurface(osd_bottom_left); // SDL_FreeSurface(osd_trk_info_text); // SDL_FreeSurface(osd_frame_info_text); // SDL_FreeSurface(osd_top_left); free(osd_fmt); TTF_CloseFont(font); TTF_Quit(); }
字符叠加线程
当然,这里除了时间戳,其他要更新的消息都是随便填的哈哈
* *描述 :线程里用于字符叠加 *参数 :arg 为自定义结构video_process_s,VPSS_GRP和VPSS_CHN用于传参给HI_MPI_VPSS_GetChnFrame *返回值:无 *注意 : */ HI_VOID *frame_osd_task(HI_VOID *arg) { cpu_set_t mask;//cpu核的集合 cpu_set_t get;//获取在集合中的cpu int num = sysconf(_SC_NPROCESSORS_CONF); printf("frame_osd_task:system has %d processor(s)\n", num); CPU_ZERO(&mask);//置空 CPU_SET(0, &mask);//设置亲和力值 if (pthread_setaffinity_np(pthread_self(), sizeof(mask), &mask) < 0)//设置线程CPU亲和力 { fprintf(stderr, "set thread affinity failed\n"); } if (pthread_getaffinity_np(pthread_self(), sizeof(get), &get) < 0)//获取线程CPU亲和力 { fprintf(stderr, "get thread affinity failed\n"); } VIDEO_FRAME_INFO_S stVideoFrame_process_osd; video_process_s* pstPara; pstPara = (video_process_s*)arg; // HI_S32 s32Ret; time_t now; struct tm *ptm; char timestr[OSD_LENGTH] = {0}; char lost_target_info_str[OSD_LENGTH] = {0}; char other_info_str[OSD_LENGTH] = {0}; char trk_str[OSD_LENGTH] = {0}; char frame_str[OSD_LENGTH] = {0}; stBitmap_bottom_left.pData = malloc(2*OSD_BOTTOM_LEFT_W*OSD_BOTTOM_LEFT_H); if(stBitmap_bottom_left.pData == NULL) { printf("stBitmap.pData faided\r\n"); } stBitmap_top_left.pData = malloc(2*OSD_TOP_LEFT_W*OSD_TOP_LEFT_H); if(stBitmap_top_left.pData == NULL) { printf("stBitmap_top_left.pData faided\r\n"); } osd_init(arg); // int timeOffset = 0; // int timeOffset1 = 0; while(1) { // sleep(1); // sem_wait(&sem_frm_process); if(q_stVideoFrame_process_osd.empty() != true) { // tmp = clockGetTime(CLOCK_MONOTONIC); //单位us // timeOffset = tmp.tv_sec*1000*1000 + tmp.tv_usec;/* 测试代码执行时间 */ // printf("osd queue not empty\n"); stVideoFrame_process_osd = q_stVideoFrame_process_osd.front(); q_stVideoFrame_process_osd.pop(); // printf("osd%d\n", q_stVideoFrame_process_osd.size()); time(&now); ptm = localtime(&now); snprintf(timestr,100,"当前时间:%d-%02d-%02d %02d:%02d:%02d ",ptm->tm_year+1900,ptm->tm_mon+1,ptm->tm_mday,ptm->tm_hour,ptm->tm_min,ptm->tm_sec); snprintf(lost_target_info_str,100,"脱靶信息:X:%02d,Y:%02d",ptm->tm_year,ptm->tm_year); snprintf(other_info_str,100,"视场角信息:X:%02d,Y:%02d,焦距:%02d",ptm->tm_year,ptm->tm_year,ptm->tm_year); snprintf(trk_str,100,"跟踪器状态:检测?识别?跟踪? "); snprintf(frame_str,100,"帧率:%02d",ptm->tm_year); string_to_bmp_bottom_left(lost_target_info_str,other_info_str,timestr); string_to_bmp_top_left(trk_str,frame_str); HI_MPI_RGN_UpdateCanvas(OverlayHandle_top_left); HI_MPI_RGN_UpdateCanvas(OverlayHandle_bottom_left); // pthread_mutex_lock(&mutex); HI_MPI_RGN_SetBitMap(OverlayHandle_bottom_left,&stBitmap_bottom_left);//s32Ret 为RGN_HANDLE OverlayHandle HI_MPI_RGN_SetBitMap(OverlayHandle_top_left,&stBitmap_top_left);//s32Ret 为RGN_HANDLE OverlayHandle // pthread_mutex_unlock(&mutex); // usleep(15000); // memset(&tmp,0,sizeof(tmp)); // tmp = clockGetTime(CLOCK_MONOTONIC); // timeOffset1 = tmp.tv_sec*1000*1000 + tmp.tv_usec; // printf("timeOffset:%d\n",(timeOffset1 - timeOffset));/* 测试代码执行时间 */ // hi_memset(stBitmap_bottom_left.pData, sizeof(BITMAP_S),0, sizeof(BITMAP_S)); // hi_memset(stBitmap_top_left.pData, sizeof(BITMAP_S),0, sizeof(BITMAP_S)); // hi_memset(timestr,OSD_LENGTH,0,OSD_LENGTH); // q_stVideoFrame_venc.push(stVideoFrame_process_osd); // printf("osd ok\n"); HI_MPI_VENC_SendFrame(pstPara->VpssChn, &stVideoFrame_process_osd,1000); HI_MPI_VPSS_ReleaseChnFrame(pstPara->VpssGrp, pstPara->VpssChn, &stVideoFrame_process_osd); } else { usleep(1000); } } return NULL; }
效果
摆出最后叠加后的效果图
后续改进
这样的OSD已经满足大多数需求了,但是还是没有考虑到如果过亮或者过暗的情况,对于亮度的采集和反色处理将在下一篇更新