海思多媒体(MPP)开发(5)——区域管理(REGION&OSD字符显示)

(一)基本概念理解

(1)Overlay叠加

    视频叠加区域,其中区域支持位图的加载、背景色更新等功能,简单理解就是可以设置透明度,也就是下面的Alpha值

(2)Cover遮挡

    视频遮挡区域,其中区域支持纯色块遮挡,与Overlay叠加不同的是它不能加载图片,不能设置透明度

(3)Alpha通道

 如果图形卡具有32位总线,附加的8位信号就被用来保存不可见的透明度信号以方便处理用,这就是Alpha通道。白色的alpha象素用以定义不透明的彩色象素,而黑色的alpha象素用以定义透明象素,黑白之间的灰阶用来定义半透明象素。

  •     VPSS OVERLAY时,Alpha取值范围为[0, 255]。取值越小,越透明。
  •     VPSS VENC 时,Alpha取值范围为[0, 127]。取值越小,越透明。

(4)Stride图像跨距 

Image Stride(内存图像行跨度) 当视频图像存储在内存时,图像的每一行末尾也许包含一些扩展的内容,这些扩展的内容只影响图像如何存储在内存中,但是不影响图像如何显示出来;Stride 就是这些扩展内容的名称,Stride 也被称作 Pitch,如果图像的每一行像素末尾拥有扩展内容,Stride 的值一定大于图像的宽度值,就像下图所示:

海思多媒体(MPP)开发(5)——区域管理(REGION&OSD字符显示)_第1张图片


两个缓冲区包含同样大小(宽度和高度)的视频帧,却不一定拥有同样的 Stride 值,如果你处理一个视频帧,你必须在计算的时候把 Stride 考虑进去;

在做OSD水印的时候,叠加图片的stride值大于region画布的宽度时,该图像添加到画布会失败。

(二)像素格式:

  • OVERLAY VENC类型支持:Argb1555,Argb4444
  • OVERLAY VPSS类型支持:Argb1555,Argb4444,Argb8888

ARGB---Alpha,Red,Green,Blue.一种色彩模式,也就是RGB色彩模式附加上Alpha(透明度)通道,常见于32位位图的存储结构。

(a)格式类型

ALPHA_8:数字为8,图形参数应该由一个字节来表示,应该是一种8位的位图,常见的颜色格式:

  •     ARGB_4444:4+4+4+4=16,图形的参数应该由两个字节来表示,应该是一种16位的位图.
  •     ARGB_8888:8+8+8+8=32,图形的参数应该由四个字节来表示,应该是一种32位的位图.
  •     RGB_565:5+6+5=16,图形的参数应该由两个字节来表示,应该是一种16位的位图.

    Argb1555 也就是15位表示透明度和分别使用5位表示R,G,B,构成一个32位的位图,其中有两个位没有使用到。(b)颜色格式:
    颜色对照表可以查看:https://tool.oschina.net/commons?type=3
    同样是RGB颜色,但是颜色格式却有很多种,所以查表得到的颜色与显示的颜色是不能对应的,需要转换或是直接对应格式查找。

(c)颜色转换

三种RGB格式表示方式:

  • RGB555: R-5bit,G-5bit,B-5bit
  • RGB565: R-5bit,G-6bit,B-5bit
  • RGB888: R-8bit,G-8bit,B-8bit

RGB888转RGB555
RGB888 : R7 R6 R5 R4 R3 R2 R1 R0 G7 G6 G5 G4 G3 G2 G1 G0 B7 B6 B5 B4 B3 B2 B1 B0
RGB555 :0 R7 R6 R5 R4 R3 G7 G6 G5 G4 G3 B7 B6 B5 B4 B3

其它格式于此类似。

(三)实时刷新OSD图像

    在海思官方提供的region sample中,它们使用的是现成的图片,也就是直接拿现成的图片加载到视频流中去,这样有一个问题,就是如果我需要实时改变OSD的内容,这个就不好处理了。 比如在视频中添加时间水印。

    以时间水印为例,要实现将时间水印添加到视频流流中去,大的流程只有两个:

  • 生成带带时间的图像
  • 将时间图像加载到预设置的region画布中去

(1)生成时间位图:

   这里需要使用到freetype、SDL、SDl_ttf这三个库。

  • FreeType2是一个简单的跨平台的字体绘制引擎
  • SDL(Simple DirectMedia Layer)是一套开放源代码的跨平台多媒体开发库,使用C语言写成。SDL提供了数种控制图像、声音、输出入的函数,让开发者只要用相同或是相似的代码就可以开发出跨多个平台(Linux、Windows、Mac OS X等)的应用软件。
  • SDL_ttf是TrueType字体渲染库,使用SDL库,几乎一样的便携。这取决于FreeType2处理TrueType字体数据。它允许程序员使用多个TrueType字体无需代码的字体渲染程序本身。随着轮廓字体和反走样的力量,高质量的文本输出可以毫不费力的获得。

    需要将这三个库移植到海思设备中去,交叉编译移植过程这里不介绍,网上有很多介绍。
    这里提供一个简单的测试程序:

/************************************************************
*Copyright (C),lcb0281at163.com lcb0281atgmail.com
*BlogAddr: caibiao-lee.blog.csdn.net
*FileName: debug_font_osd.c
*Description:测试生成带时间字符的图像
*Date:     2020-02-03
*Author:   Caibiao Lee
*Version:  V1.0
*Others:
*History:
***********************************************************/
#include 
#include 
#include 
#include "SDL/SDL.h"
#include "SDL/SDL_ttf.h"

#define FONT_PATH       "./font/hisi_osd.ttf"

int string_to_bmp(char *pu8Str)
{
    SDL_PixelFormat *fmt;
    TTF_Font *font;  
    SDL_Surface *text, *temp;  

    if (TTF_Init() < 0 ) 
    {  
        fprintf(stderr, "Couldn't initialize TTF: %s\n",SDL_GetError());  
        SDL_Quit();
    }  

    font = TTF_OpenFont(FONT_PATH, 80); 
    if ( font == NULL ) 
    {  
        fprintf(stderr, "Couldn't load %d pt font from %s: %s\n",18,"ptsize", SDL_GetError());  
    }  

    SDL_Color forecol = { 0xff, 0xff, 0xff, 0xff };  
    text = TTF_RenderUTF8_Solid(font, pu8Str, forecol);

    fmt = (SDL_PixelFormat*)malloc(sizeof(SDL_PixelFormat));
    memset(fmt,0,sizeof(SDL_PixelFormat));
    fmt->BitsPerPixel = 16;
    fmt->BytesPerPixel = 2;
    fmt->colorkey = 0xffffffff;
    fmt->alpha = 0xff;

    temp = SDL_ConvertSurface(text,fmt,0);
    SDL_SaveBMP(temp, "save.bmp"); 

    SDL_FreeSurface(text);  
    SDL_FreeSurface(temp);
    TTF_CloseFont(font);  
    TTF_Quit();  

    return 0;
}

工程中包含下面些内容:

biao@ubuntu:~/test/github/hisi_sdk_develop/freetype_SDL_Dl_ttf_debug$ tree -L 2
.
├── bin
│   └── objs
├── debug_font_osd.c
├── debug_font_osd.h
├── font
│   ├── hisi_osd.ttf
│   └── hisi_osd.ttf_df
├── inc
│   ├── freetype2
│   ├── ft2build.h
│   └── SDL
├── lib
│   ├── libfreetype.a
│   ├── libfreetype.so
│   ├── libfreetype.so.6
│   ├── libSDL-1.2.so.0
│   ├── libSDL.a
│   ├── libSDLmain.a
│   ├── libSDL.so
│   ├── libSDL_ttf-2.0.so.0
│   ├── libSDL_ttf.a
│   ├── libSDL_ttf.so
│   └── pkgconfig
├── Makefile
├── save.bmp
└── test

8 directories, 18 files
biao@ubuntu:~/test/github/hisi_sdk_develop/freetype_SDL_Dl_ttf_debug$ 

生成保存的save.bmp图像如下:

(2)将字符水印添加到视频流中:

    这里是根据官方sample修改而来,主要流程是:

  • 将解码器与编码器绑定,区域通道与编码通道绑定,从h264文件中读取数据流,输入到解码器中,由解码器中流向编码器,最后将编码器产生的数据存成文件。

    编码之后的图像带有区域图像的水印,这里可以根据实际的分辨率设置VENC和VDEC。

/************************************************* 
Function:    BIAO_RGN_AddOsdToVenc  
Description: 将视频文件添加时间水印
Input:  none
OutPut: none
Return: 0: success,none 0:error
Others: 解码器输入的分辨率与编码器的输出分辨率可以不相同,
    比如将1080P图像解码后,可以再编码成720P图像。
Author: Caibiao Lee
Date:   2020-03-08
*************************************************/
HI_S32 BIAO_RGN_AddOsdToVenc(HI_VOID)
{
    HI_S32 s32Ret = HI_SUCCESS;
    RGN_HANDLE OverlayHandle;
    HI_S32 u32OverlayRgnNum;
    MPP_CHN_S stSrcChn, stDesChn;
    RGN_ATTR_S stRgnAttrSet;
    RGN_CANVAS_INFO_S stCanvasInfo;
    BITMAP_S stBitmap;
    VENC_CHN VencChn;
    VDEC_CHN VdecChn;
    VDEC_SENDPARAM_S stVdesSendPram;
    VENC_PTHREAD_INFO_S stVencGetPram;
    SIZE_S stSize;
    FILE * pastream = NULL;
    HI_U32 i;
    int l_s32CanvasHandle = 0;

    /**分配缓存**/
    s32Ret = BIAO_RGN_SYS_Init(); 
    if(HI_SUCCESS != s32Ret)
    {
        printf("SAMPLE_RGN_SYS_Init failed! s32Ret: 0x%x.\n", s32Ret);
        goto END_O_VENC0;
    }
    
    /**创建区域,并将它添加到编码通道**/
    OverlayHandle    = 0;
    u32OverlayRgnNum = 1;
    s32Ret = BIAO_RGN_CreateOverlayForVenc(OverlayHandle, u32OverlayRgnNum);
    if(HI_SUCCESS != s32Ret)
    {
        printf("SAMPLE_RGN_CreateOverlayForVenc failed! s32Ret: 0x%x.\n", s32Ret);
        goto END_O_VENC1;
    }
    
    /**开启解码通道**/
    VdecChn = 0;
    s32Ret = BIAO_RGN_StartVdec(VdecChn);
    if(HI_SUCCESS != s32Ret)
    {
        printf("SAMPLE_RGN_StartVdec failed! s32Ret: 0x%x.\n", s32Ret);
        goto END_O_VENC2;
    }
    
    /**开启编码通道**/
    VencChn = 0;
    s32Ret = BIAO_RGN_StartVenc(VencChn);
    if(HI_SUCCESS != s32Ret)
    {
        printf("SAMPLE_RGN_StartVenc failed! s32Ret: 0x%x.\n", s32Ret);
        goto END_O_VENC3;
    }
    
    /**将解码通道绑定到编码通道**/
    stSrcChn.enModId  = HI_ID_VDEC;
    stSrcChn.s32DevId = 0;
    stSrcChn.s32ChnId = 0;

    stDesChn.enModId  = HI_ID_VENC;
    stDesChn.s32DevId = 0;
    stDesChn.s32ChnId = 0;

    s32Ret = HI_MPI_SYS_Bind(&stSrcChn, &stDesChn);
    if(HI_SUCCESS != s32Ret)
    {
        printf("HI_MPI_SYS_Bind failed! s32Ret: 0x%x.\n", s32Ret);
        goto END_O_VENC4;
    }

    /**创建一个线程用来从h264文件中读取数据,模拟h264数据流**/   
    stSize.u32Width  = DECODE_VIDEO_W;
    stSize.u32Height = DECODE_VIDEO_H;
    
    stVdesSendPram.bRun          = HI_TRUE;
    stVdesSendPram.VdChn         = VdecChn;
    stVdesSendPram.enPayload     = PT_H264;
    stVdesSendPram.enVideoMode   = VIDEO_MODE_FRAME;
    stVdesSendPram.s32MinBufSize = stSize.u32Height * stSize.u32Width / 2;
    pthread_create(&g_stVdecThread, NULL, BIAO_RGN_VdecSendStream, (HI_VOID*)&stVdesSendPram);


    /**更新OSD内容**/
    l_s32CanvasHandle = 0;
    pthread_create(&g_stRgnOsdThread, NULL, BIAO_UpdateCanvas, (HI_VOID*)&l_s32CanvasHandle);


    /**创建一个线程,将编码器输出的数据存成文件**/
    char pfilename[64]; 
    sprintf(pfilename, ENCODE_H264_FILE);
    pastream = fopen(pfilename, "wb");  
    HI_ASSERT( NULL != pastream);

    stVencGetPram.pstream   = pastream;
    stVencGetPram.VeChnId   = VencChn;
    stVencGetPram.s32FrmCnt = 0;
    pthread_create(&g_stVencThread, 0, BIAO_RGN_VencGetStream, (HI_VOID *)&stVencGetPram);

    printf("\n#############Sample start ok! Press Enter to switch!#############\n");

    
    /*************************************************
    step 8: stop thread and release all the resource
    *************************************************/

    /**延时之后推出编解码**/
    sleep(10);
    bExit = HI_TRUE;
    pthread_join(g_stVdecThread, 0);

    pthread_join(g_stVencThread, 0);

    pthread_join(g_stRgnOsdThread, 0);
    
    bExit = HI_FALSE;
    
END_O_VENC4:
    HI_MPI_SYS_UnBind(&stSrcChn, &stDesChn);

END_O_VENC3:
    BIAO_RGN_StopVenc(VencChn);
    
END_O_VENC2:
    BIAO_RGN_StopVdec(VdecChn);

END_O_VENC1:    
    BIAO_RGN_DestroyRegion(OverlayHandle, u32OverlayRgnNum);       

END_O_VENC0:
    SAMPLE_COMM_SYS_Exit();
    
    return s32Ret;
}

将海思官方视频添加水印之后的视频效果如下:

海思多媒体(MPP)开发(5)——区域管理(REGION&OSD字符显示)_第2张图片

第一个生成时间图像的工程可以从下面获取:

GitHub: freetype_SDL_Dl_ttf_debug 

CSDN :  freetype_SDL_Dl_ttf_debug.tar.gz

第二个将水印叠加到视频测工程可以从「目录与序言」提供的地址去获取

 

本专栏第一篇文章「目录与序言」列出了专栏的完整目录,按目录顺序阅读,有助于你的理解。

 

 

 

你可能感兴趣的:(海思多媒体平台MPP)