前言:
功能测试的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已经满足大多数需求了,但是还是没有考虑到如果过亮或者过暗的情况,对于亮度的采集和反色处理将在下一篇更新