• 海思3559万能平台搭建:OSD功能的优化


    前言:

      功能测试的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);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111

      不难看到,除去了大量的判断后关键的操作是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));
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48

      那么我们就可以将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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    /* 
     *描述  :用于将想填写的内容生成位图左上角 用于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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67

      有需要的话可以吧保存放开

    补充

      循环内我们每次都创建了新的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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    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;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83

    初始化和退出

      都配置完了就可以初始化了,初始化打开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();
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74

    字符叠加线程

      当然,这里除了时间戳,其他要更新的消息都是随便填的哈哈

    * 
     *描述  :线程里用于字符叠加
     *参数  :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;   
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107

    效果

      摆出最后叠加后的效果图
    在这里插入图片描述

    后续改进

      这样的OSD已经满足大多数需求了,但是还是没有考虑到如果过亮或者过暗的情况,对于亮度的采集和反色处理将在下一篇更新

  • 相关阅读:
    新手小白怎样开始学做自媒体呢?
    mysql 是不是要创建一个用户 然后给这个用户远程权限 操作msg数据库 而不是给root用户远程权限?
    产品需求分析师的基本职责(合集)
    MMDetection 使用示例:从入门到出门
    百万军中取上将首级如探囊取物, 千万行里改关键源码在弹指瞬间。 功能超强的程序编辑器!
    推荐一款可以识别m3u8格式ts流批量下载并且合成mp4视频的chrome插件——猫抓
    5.0 Vue基础入门
    python模块之aioHttp 异步请求
    multiset 用法简说
    kafkaStream实时流式计算
  • 原文地址:https://blog.csdn.net/qq_42330920/article/details/127066531