海思3516A区域管理操作汇总及位图填充示例

按照手册中的说明,使用区域管理功能需要做如下几步
1.用户填充区域属性并创建区域
2.将该区域指定到具体通道中(如VENC)。在指定到具体通道时,需要输入通道的显示属性
    之后用户可以通过以下操作来控制区域属性,以及在某通道的通道显示属性
    2.1通过 HI_MPI_RGN_GetAttr、 HI_MPI_RGN_SetAttr 获取和设置区域属性
    2.2通过 HI_MPI_RGN_SetBitMap(仅针对 Overlay)设置区域的位图信息
    2.3通过 HI_MPI_RGN_GetDisplayAttr 和 HI_MPI_RGN_SetDisplayAttr 获取和设置区域在某通道(如 VENC 通道)的通道显示属性。
    2.4最后用户可以将该区域从通道中撤出(非必须操作),再销毁区域。

 

接下来我们看看每一步应该怎么做

第一步:用户填充区域属性并创建区域

想要创建区域,可以调用函数

HI_MPI_RGN_Create

来创建一个region区域,函数原型如下

HI_S32 HI_MPI_RGN_Create(
    RGN_HANDLE Handle,                  //区域句柄号
    const RGN_ATTR_S *pstRegion         //区域属性指针
);


每一个区域对应一个句柄,和一个具体的属性RGN_ATTR_S,属性结构体定义如下
 

typedef struct hiRGN_ATTR_S
{
    RGN_TYPE_E enType;                  //区域类型
    RGN_ATTR_U unAttr;                  //区域属性
} RGN_ATTR_S;

区域类型有以下几种

  • OVERLAY_RGN                           //VENC通道视频叠加区域
  • COVER_RGN                               //VI通道视频叠加区域
  • COVEREX_RGN                          //扩展视频遮挡区域
  • OVERLAYEX_RGN                      //扩展视频叠加区域

对应的区域属性就有两种

  • OVERLAY_ATTR_S      stOverlay                //VENC通道叠加区域属性
  • OVERLAYEX_ATTR_S    stOverlayEx         //扩展叠加区域属性

这两个属性的结构体其实是一样的,以OVERLAY_ATTR_S举例,结构体定义如下
 

typedef struct hiOVERLAY_ATTR_S
{
    PIXEL_FORMAT_E enPixelFmt;          //OSD的像素格式,目前只支持ARGB1555,ARGB4444,ARGB8888这三种
    HI_U32 u32BgColor;                  //区域的背景色
    SIZE_S stSize;                      //区域的高宽
}OVERLAY_ATTR_S;

将这些属性填充好,调用HI_MPI_RGN_Create函数后,区域就创建好了,之后我们就可以通过区域句柄号进行管理

 

第二步:将该区域指定到具体通道中

想要指定到具体通道,可以调用函数

HI_MPI_RGN_AttachToChn

将区域叠加到通道上,该函数原型为

HI_S32 HI_MPI_RGN_AttachToChn(
    RGN_HANDLE Handle,                  //区域句柄号
    const MPP_CHN_S *pstChn,            //通道结构体指针
    const RGN_CHN_ATTR_S *pstChnAttr    //区域通道显示属性指针
);


区域句柄我们已经有了,通道结构体指针定义如下

typedef struct hiMPP_CHN_S
{
    MOD_ID_E    enModId;                //模块号
    HI_S32      s32DevId;               //设备号
    HI_S32      s32ChnId;               //通道号
} MPP_CHN_S;


其实意思就是告诉系统,这个区域是叠加在哪个模块的哪个设备的哪个通道上面的
区域通道显示属性指针就是用来设定区域显示属性的,结构体定义如下
 

typedef struct hiRGN_CHN_ATTR_S
{
    HI_BOOL           bShow;            //区域是否显示
    RGN_TYPE_E        enType;           //区域类型(OVERLAY_RGN,COVER_RGN,COVEREX_RGN,OVERLAYEX_RGN)
    RGN_CHN_ATTR_U    unChnAttr;        //区域通道显示属性(对应上面的四种类型,这个属性也有四种,具体请查阅手册)
} RGN_CHN_ATTR_S;

创建区域并指定到具体的通道中之后,我们的区域就已经完成了
接下来我们就可以通过其他操作在区域里面进行显示了
比如叠加层次,位图填充,叠加透明度等等(表8-2 Hi3516A/Hi3518EV200 region 支持的功能)

 

这里给出一个示例Demo如下

HI_S32 regions_Create( RGN_HANDLE RgnHandle, int width, int height, int position_x, int position_y )
{
    HI_S32 s32Ret = HI_FAILURE;
    RGN_ATTR_S stRgnAttr;
    
    MPP_CHN_S stChn;
    RGN_CHN_ATTR_S stChnAttr;
    
    /****************************************
    step 1: create overlay regions
    ****************************************/
    stRgnAttr.enType = OVERLAYEX_RGN;
    stRgnAttr.unAttr.stOverlay.enPixelFmt = PIXEL_FORMAT_RGB_1555;
    stRgnAttr.unAttr.stOverlay.stSize.u32Width = width;
    stRgnAttr.unAttr.stOverlay.stSize.u32Height = height;
    stRgnAttr.unAttr.stOverlay.u32BgColor = 0x1f;
    RgnHandle = 0;///*域操作句柄*/

    s32Ret = HI_MPI_RGN_Create(RgnHandle, &stRgnAttr);
    if (HI_SUCCESS != s32Ret)
    {
	SAMPLE_PRT("HI_MPI_RGN_Create (%d) failed with %#x!\n", RgnHandle, s32Ret);
	return HI_FAILURE;
    }
    
    /****************************************
    step 2: attach to VPSS
    ****************************************/    
    
    stChn.enModId = HI_ID_VPSS;//HI_ID_VENC;//HI_ID_GROUP;
    stChn.s32DevId = 0;
    stChn.s32ChnId = 0;
    
    
    memset(&stChnAttr, 0, sizeof(stChnAttr));
    stChnAttr.bShow = HI_TRUE;
    stChnAttr.enType = OVERLAYEX_RGN;
    stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32X = position_x;	//通道画面中显示的位置(x)
    stChnAttr.unChnAttr.stOverlayExChn.stPoint.s32Y = position_y;	//通道画面中显示的位置(Y)
    stChnAttr.unChnAttr.stOverlayExChn.u32BgAlpha = 0;  	        //OSD Alpha
    stChnAttr.unChnAttr.stOverlayExChn.u32FgAlpha = 128;
    stChnAttr.unChnAttr.stOverlayExChn.u32Layer = 0;

    s32Ret = HI_MPI_RGN_AttachToChn(RgnHandle, &stChn, &stChnAttr);
    if (HI_SUCCESS != s32Ret) {
	SAMPLE_PRT("HI_MPI_RGN_AttachToChn (RgnHandle:%d, chnid:%d) failed with %#x!\n", RgnHandle, stChn.s32ChnId , s32Ret);
	return HI_FAILURE;
    }
    
    return HI_TRUE;
}

 

事实上我们创建了区域以后,需要给我们的区域填充一些内容,常用的就是位图填充了,下面我们看看如何进行位图填充

 

功能一:bmp图片进行位图填充

 

Hi3516A的OSD只能使用位图来进行填充,如果你有一张图片,比如logo之类的,把它做成bmp格式的图片
然后调用

HI_S32 SAMPLE_RGN_LoadBmp

函数先读取位图文件信息,函数原型如下

HI_S32 SAMPLE_RGN_LoadBmp(
    const char *filename,               //文件路径
    BITMAP_S *pstBitmap,                //位图属性指针,打开的位图属性将会存放在这里
    HI_BOOL bFil,                       //暂时不关注
    HI_U32 u16FilColor                  //暂时不关注
)


位图文件的属性有了,我们可以调用

HI_MPI_RGN_SetBitMap

函数在区域里面进行位图填充,函数原型如下

HI_S32 HI_MPI_RGN_SetBitMap(
    RGN_HANDLE Handle,                  //区域句柄号
    const BITMAP_S *pstBitmap           //位图属性指针,就是我们要进行填充的位图信息
);


调用之后,系统就会在根据我们之前设置的区域属性,在对应模块对应设备对应通道上面进行填充

示例Demo如下

HI_S32 update_regions(const char *filename, RGN_HANDLE RgnHandle, const BITMAP_S *stBitmap)
{
    HI_S32 s32Ret = HI_FAILURE;
    
    if( HI_SUCCESS != SAMPLE_RGN_LoadBmp(filename, stBitmap, HI_FALSE, 0))
    {
	SAMPLE_PRT("load bmp failed\n");
        return HI_FAILURE;
    }
	    

    s32Ret = HI_MPI_RGN_SetBitMap(RgnHandle, stBitmap);
    if (s32Ret != HI_SUCCESS) {
	SAMPLE_PRT("HI_MPI_RGN_SetBitMap (RgnHandle:%d) failed with %#x!\n", RgnHandle, s32Ret);
	return HI_FAILURE;
    }


    return HI_TRUE;
}

 

功能二:字库文件进行位图填充

 

有时候我们需要在图像上动态更新时间,码率等等信息,这时候如果都使用bmp图片,肯定不太合适
那就需要把字库的点阵信息转化更位图信息,以英文字符举例


1.打开文件

FILE *asc16;

asc16 = fopen(asc16File, "rb");
if (asc16 == NULL )
{
    dpf("open asc16 error!\n");
    return HI_FAILURE;
}


2.分配内存空间

以8 * 16的字库举例,字库里面的8 * 16代表有8 * 16个点,但是我们要转化成位图信息就意味着,
我们要把字库中的每个点转化为RGB格式的像素点,如果使用RGB1555格式的像素点,
那点阵中的每一个需要占用1 + 5 + 5 + 5 = 16Bit共2个字节的大小,所以每一个字需要分配
2 * 8 * 16大小的空间,一共有ASCNum个字就需要2 * 8 * 16 * ASCNum大小的空间

char *FontDatabase = NULL;

FontDatabase = malloc((2 * 8 * 16) * ASCNum);
if (FontDatabase == NULL )
{
    dpf("do malloc error!\n");
    return HI_FAILURE;
}


    
3.读取所有可以显示的字符,并转化为像素信息

我们知道ASCII码可以显示的字符范围是‘ ’ ~ ‘~’,其他的是特殊字符
所以我们需要从‘ ’开始的位置一直读取到‘~’位置为止,并将每一个点阵点转化为像素点,使用RGB1555格式
    

HI_U16 *memp;
    
fseek(asc16, ' ' * 16, SEEK_SET);                   //找到‘ ’开始的位置,8 * 16点阵中每一个字占用16字节的空间
memp = (HI_U16*) FontDatabase;
for (i = 0; i < ASCNum * 16; i++)                   //遍历所有字
{
    ch = fgetc(asc16);                              //每次读取一行
    //显示一个字节
    for (j = 0; j < 8; j++, shortp++)               //遍历一行里面的所有点,如果是1,则转化为黑色((HI_U16)0x8000),是0则转化为白色((HI_U16)0x7fff)
    {
        if (ch & (0x01 << (7 - j)))         
            *memp = BlackPot;
        else
            *memp = WhitePot;
    }
}


    
4.构建位图数据

由之前的信息我们知道,想要叠加位图需要调用函数

HI_MPI_RGN_SetBitMap

这个函数原型如下

HI_S32 HI_MPI_RGN_SetBitMap(
    RGN_HANDLE Handle,                              //区域句柄号
    const BITMAP_S *pstBitmap                       //位图属性指针
);


区域句柄号我们创建的时候已经有了,现在需要的就是填充这个位图信息了,这个结构体原型如下

typedef struct hiBITMAP_S
{
    PIXEL_FORMAT_E enPixelFormat;                   //像素格式,比如555,565,1555等         
    HI_U32 u32Width;                                //图像宽度
    HI_U32 u32Height;                               //图像高度
    HI_VOID* pData;                                 //位图数据
} BITMAP_S;

假设我们需要显示count个字,代码应该大体是这样的
    

void make_bitmap(const char *osdString, BITMAP_S *pstBitmap)
{
    int locations[128] = {0};                       //存储字模的位置
    int count = 0;                                 //需要显示count个字
    int i = 0;
    int j = 0;
        
    pstBitmap->enPixelFormat = PIXEL_FORMAT_RGB_1555;    //像素格式为RGB1555
    pstBitmap->u32Width = 8 * count                     //每个字的宽度依然是8个像素点
    pstBitmap->u32Height = 16                           //每行的高度依然是16个像素点
    pstBitmap->pData = malloc(2 * 8 * 16 * count);      //点阵点转化为像素点需要的空间
        
    memset(pstBitmap->pData, 0, 2 * 8 * 16 * count);
        
    //获取要显示的字的个数和在字模中的位置
    for (i = 0; osdString[i] != '\0'; i++)
    {
        if (osdString[i] >= ' ' && osdString[i] <= '~')
        {
            locations[count] = osdString[i] - ' ';
            count++;
        }
    }
        
    //遍历所有要显示的字
    for (i = 0; i < count; i++)
    {
        //遍历每一行,一次拷贝一行即8 * 2个字节(每一个点阵点转化为像素点后占用2个字节存储)
        for (j = 0; j < 16; j++)
        {
            memcpy(
                //展开后对应存储像素点的第j行的起始位置
                pstBitmap->pData + j * pstBitmap->u32Width * 2,
                //展开后对应存储点阵点的第i个字符第j行的起始位置
                memp + (locations[i] * 2 * 8 * 16) + (16 * j),
                16
            );
        }
    }
}

填充好位图信息后,我们调用HI_MPI_RGN_SetBitMap函数进行设置即可。

 

汉字字库的方式其实是一样的,只不过汉字的位置是由区码加位码的形式来查找的,字库相关的知识可以参考HZK16汉字16*16点阵字库的使用及示例程序

而一个16 * 16汉字点阵转化为像素点需要16 * 16 * 2的空间

所以如果想把一个hzk16中的汉字点阵转化为像素,代码应该是这样的

HI_U16 *memp;
    
fseek(hzk16, location * 32, SEEK_SET);     //location代表汉字的区码+位码,16 * 16点阵中每一个字占用32字节的空间
memp = (HI_U16*) FontDatabase;
for (i = 0; i < 32; i++)                   //遍历这32个字节
{
    ch = fgetc(hzk16);                              //每次读取一个字节
    //显示一个字节
    for (j = 0; j < 8; j++)               //遍历字节,如果是1,则转化为黑色((HI_U16)0x8000),是0则转化为白色((HI_U16)0x7fff)
    {
        if (ch & (0x01 << (7 - j)))         
            *memp = BlackPot;
        else
            *memp = WhitePot;
    }
}

如果要显示count个汉字,代码大体应该是这样的

void make_bitmap(const char *osdString, BITMAP_S *pstBitmap)
{
    int locations[128] = {0};                       //存储字模的位置
    int count = 0;                                 //需要显示count个字
    int i = 0;
    int j = 0;
        
    pstBitmap->enPixelFormat = PIXEL_FORMAT_RGB_1555;    //像素格式为RGB1555
    pstBitmap->u32Width = 16 * count                     //每个字的宽度依然是16个像素点
    pstBitmap->u32Height = 16                            //每行的高度依然是16个像素点
    pstBitmap->pData = malloc(2 * 16 * 16 * count);       //点阵点转化为像素点需要的空间
        
    memset(pstBitmap->pData, 0, 2 * 16 * 16 * count);
        
    //获取要显示的字的个数和在字模中的位置
    for (i = 0; osdString[i] != '\0'; i++)
    {
        if (osdString[i] >= 0xa1 && osdString[i + 1] >= 0xa1)
        {
            locations[osdLen] = (osdString[i] - 0xa1) * 94 + (osdString[i + 1] - 0xa1);
            count++;
            i++;
        }
    }
        
    //遍历所有要显示的字
    for (i = 0; i < count; i++)
    {
        //遍历每一行,一次拷贝一行即16 * 2个字节(每一个点阵点转化为像素点后占用2个字节存储)
        for (j = 0; j < 16; j++)
        {
            memcpy(
                //展开后对应存储像素点的第j行的起始位置
                pstBitmap->pData + j * pstBitmap->u32Width * 2,
                //展开后对应存储点阵点的第i个字符第j行的起始位置
                memp + (locations[i] * 2 * 16 * 16) + (32 * j),
                32
            );
        }
    }
}

同样的,填充好位图信息后,我们调用HI_MPI_RGN_SetBitMap函数进行设置即可。

 

 

 

 

你可能感兴趣的:(音视频学习,海思,OSD,点阵传位图)