ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

从官方例程深度学习海思SDK及API(3)

2021-11-30 21:33:23  阅读:169  来源: 互联网

标签:编码 码率 例程 SAMPLE API HI VENC 图像 海思


目录

一、图像编码压缩基本原理

由于网上已有大量优秀的文章讲了这个,这里我就不写了,内容重复无意义!
参考:http://blog.csdn.net/newchenxf/article/details/51693753

压缩和解压缩就是数学运算的过程。

二、MPP手册中图像编码部分解读

在这里插入图片描述
在这里插入图片描述

	码流(DataRate)是指视频文件在单位时间内使用的数据流量,也叫码率,是视频编码中
画面质量控制中最重要的部分。同样分辨率下,视频文件的码流越大,压缩比就越小,画面质
量就越高。
   上行带宽就是本地上传信息到网络上的带宽。上行速率是指用户电脑向网络发送信息时的
数据传输速率,比如用FTP上传文件到网上去,影响上传速度的就是“上行速率”。

   下行带宽就是从网络上下载信息的带宽。下行速率是指用户电脑从网络下载信息时的数据
传输速率,比如从FTP服务器上文件下载到用户电脑,影响下传速度的就是“下行速率”。

	注:监控点的带宽是要求上行的最小限度带宽(监控点将视频信息上传到监控中心);监控
中心的带宽是要求下行的最小限度带宽(将监控点的视频信息下载到监控中心);例:电信2Mbps
的ADSL宽带,理论上其上行带宽是512kbps=64kb/s,其下行带宽是2Mbps=256kb/s  

BP:最基本的,传输速率高,但视频质量较差
MP:主流的,传输速度、视频直流相对都可以

详解:https://blog.csdn.net/u014470361/article/details/88713266
在这里插入图片描述
  典型的编码流程包括了输入图像的接收、图像内容的遮挡和覆盖、图像的编码、以及码流的输出等过程。

  VENC 模块由编码通道子模块( VENC)和编码协议子模块( H.264/H.265/JPEG/MJPEG)组成。通道支持接收 YUV 格式图像输入,支持格式为 Semi-planar YUV 4:2:0 或 Semi-planarYUV 4:2:2,其中 H.264/H.265 只支持 Semi-planar YUV 4:2:0, JPEG/MJPEG 支持 Semiplanar YUV 4:2:0 或 Semi-planar YUV 4:2:2。另外, Hi3518EV200 能够支持单分量输入(只存在 Y 分量,即黑白图像)。通道模块接收外部原始图像数据,而不关心图像数据是来自哪个外部模块。

通道接收到图像之后,比较图像尺寸和编码通道尺寸:
	如果输入图像比编码通道尺寸大, VENC 将按照编码通道尺寸大小,调用 VGS 对
源图像进行缩小,然后对缩小之后的图像进行编码。
	如果输入图像比编码通道尺寸小, VENC 丢弃源图像。 VENC 不支持放大输入图
像编码。
	如果输入图像与编码通道尺寸相当, VENC 直接接受源图像,进行编码。

说明:
1、 通道的帧率控制默认不打开,需要用户调用接口设置。RC(码率控制器) 中也具有帧率控
制功能。推荐使用 RC 的帧率控制,这样不会对码率控制造成过大的冲击。

2、 对于 Hi3518EV200 的H.264 编码,输入图像格式由非单分量切换为单分量时,由于存
在帧间预测量化误差,在编码出下一个I 帧之前图像会存有色度残留。建议客户在切换单分
量时调用接口 HI_MPI_VENC_ResetChn 进行通道复位。

  REGION 模块支持对图像内容的遮挡和叠加。完成视频区域管理之后,图像被送入具体协议类型编码通道,完成视频编码,输出码流。
在这里插入图片描述
  码率控制器实现对编码码率进行控制。从信息学的角度分析,图像的压缩比越低,压缩图像的质量越高;图像压缩比例越高,压缩图像的质量越低。对于场景变化的真实场景,图像质量稳定,编码码率会波动;编码码率稳定,图像质量会波动。以 H.264 编码为例,通常图像 Qp 越低,图像的质量越好,码率越高;图像 Qp 越高,图像质量越差,码率越低。码率控制是针对连续的编码码流而言,所以, JPEG 协议编码通道不包括码率控制功能。

Qp参考:http://blog.csdn.net/u013354805/article/details/51988171

  码率控制器分别提供了对 H.264\H.265\MJPEG 协议编码通道 CBR、 VBR、 FIXQP 等三种码率控制模式,对图像质量和码率进行调节。Hi3518EV200 不支持 H.265 编码,所以,也不支持 H.265 类型的码率控制。

CBR( Constant Bit Rate)固定比特率。

VBR( Variable Bit Rate)可变比特率,即允许在码率统计时间内编码码率波动,从而保证编码图像质量平稳。

Fix Qp 固定 Qp 值。在码率统计时间内,编码图像所有宏块 Qp 值相同,采用用户设定的图像 Qp 值, I 帧和 P 帧的 QP 值可以分别设置。

更多细节,请阅读文档:
Hi3518E V200R001C01SPC030\01.software\board\document_cn\HiMPP IPC V2.0 媒体处理软件开发参考.pdf

三、sample中venc模块源码解读

    /******************************************
     step 5: start stream venc
    ******************************************/
    /*** HD1080P **/
    printf("\t c) cbr.\n");
    printf("\t v) vbr.\n");
    printf("\t f) fixQp\n");
    printf("please input choose rc mode!\n");
    c = (char)getchar();
    switch(c)
    {
        case 'c':
            enRcMode = SAMPLE_RC_CBR;
            break;
        case 'v':
            enRcMode = SAMPLE_RC_VBR;
            break;
        case 'f':
            enRcMode = SAMPLE_RC_FIXQP;
            break;
        default:
            printf("rc mode! is invaild!\n");
            goto END_VENC_1080P_CLASSIC_4;
    }

	/*** enSize[0] **/
	if(s32ChnNum >= 1)
	{
		VpssGrp = 0;
	    VpssChn = 0;
	    VencChn = 0;
	    s32Ret = SAMPLE_COMM_VENC_Start(VencChn, enPayLoad[0],\
	                                   gs_enNorm, enSize[0], enRcMode,u32Profile);
	    if (HI_SUCCESS != s32Ret)
	    {
	        SAMPLE_PRT("Start Venc failed!\n");
	        goto END_VENC_1080P_CLASSIC_5;
	    }

	    s32Ret = SAMPLE_COMM_VENC_BindVpss(VencChn, VpssGrp, VpssChn);
	    if (HI_SUCCESS != s32Ret)
	    {
	        SAMPLE_PRT("Start Venc failed!\n");
	        goto END_VENC_1080P_CLASSIC_5;
	    }
	}
SAMPLE_COMM_VENC_Start
	SAMPLE_COMM_SYS_GetPicSize //获取图片的参数、
	HI_MPI_VENC_CreateChn     创建编码通道
	HI_MPI_VENC_StartRecvPic 开启编码通道接收输入图像。
SAMPLE_COMM_VENC_BindVpss
	HI_MPI_SYS_Bind 数据源到数据接收者绑定接口

四、编码后的流文件输出和专栏总结

    /******************************************
     step 6: stream venc process -- get stream, then save it to file. 
    ******************************************/
    s32Ret = SAMPLE_COMM_VENC_StartGetStream(s32ChnNum);
    if (HI_SUCCESS != s32Ret)
    {
        SAMPLE_PRT("Start Venc failed!\n");
        goto END_VENC_1080P_CLASSIC_5;
    }

    printf("please press twice ENTER to exit this sample\n");
    getchar();
    getchar();

    /******************************************
     step 7: exit process
    ******************************************/
    SAMPLE_COMM_VENC_StopGetStream();    
/******************************************************************************
* funciton : start get venc stream process thread
******************************************************************************/
HI_S32 SAMPLE_COMM_VENC_StartGetStream(HI_S32 s32Cnt)
{
    gs_stPara.bThreadStart = HI_TRUE;
    gs_stPara.s32Cnt = s32Cnt;

    return pthread_create(&gs_VencPid, 0, SAMPLE_COMM_VENC_GetVencStreamProc, (HI_VOID*)&gs_stPara);
}
/******************************************************************************
* funciton : get stream from each channels and save them
******************************************************************************/
HI_VOID* SAMPLE_COMM_VENC_GetVencStreamProc(HI_VOID *p)
{
//该函数的内容被我删除一部分,只留了主要部分,用于梳理思路

    /******************************************
     step 1:  check & prepare save-file & venc-fd
    ******************************************/
    for (i = 0; i < s32ChnTotal; i++)
    {
        /* decide the stream file name, and open file to save stream */
        VencChn = i;
        s32Ret = HI_MPI_VENC_GetChnAttr(VencChn, &stVencChnAttr);
        enPayLoadType[i] = stVencChnAttr.stVeAttr.enType;

        s32Ret = SAMPLE_COMM_VENC_GetFilePostfix(enPayLoadType[i], szFilePostfix);

        /* Set Venc Fd. */
        VencFd[i] = HI_MPI_VENC_GetFd(i);//打开一个空白文件,得到文件描述符,之后
        if (VencFd[i] < 0)//进行写操作,即写视频流文件
        {
            SAMPLE_PRT("HI_MPI_VENC_GetFd failed with %#x!\n", 
                   VencFd[i]);
            return NULL;
        }
        if (maxfd <= VencFd[i])
        {
            maxfd = VencFd[i];
        }
    }

    /******************************************
     step 2:  Start to get streams of each channel.
    ******************************************/
    while (HI_TRUE == pstPara->bThreadStart)//开始从每个通道得到视频流
    {//当连按两下enter键,这个变量pstPara->bThreadStart被赋值为false
        FD_ZERO(&read_fds);
        for (i = 0; i < s32ChnTotal; i++)
        {
            FD_SET(VencFd[i], &read_fds);
        }

        TimeoutVal.tv_sec  = 2;
        TimeoutVal.tv_usec = 0;
        s32Ret = select(maxfd + 1, &read_fds, NULL, NULL, &TimeoutVal);
        if (s32Ret < 0)
        {
            SAMPLE_PRT("select failed!\n");
            break;
        }
        else if (s32Ret == 0)
        {
            SAMPLE_PRT("get venc stream time out, exit thread\n");
            continue;
        }
        else
        {
            for (i = 0; i < s32ChnTotal; i++)
            {
                if (FD_ISSET(VencFd[i], &read_fds))
                {
                    /*******************************************************
                     step 2.1 : query how many packs in one-frame stream.
                    *******************************************************/
                    memset(&stStream, 0, sizeof(stStream));
                    s32Ret = HI_MPI_VENC_Query(i, &stStat);//查询编码通道状态
					
					/*******************************************************
					step 2.2 :suggest to check both u32CurPacks and u32LeftStreamFrames at the same time,for example:
					 if(0 == stStat.u32CurPacks || 0 == stStat.u32LeftStreamFrames)
					 {
						SAMPLE_PRT("NOTE: Current  frame is NULL!\n");
						continue;
					 }
					 在编码通道状态结构体中, u32CurPacks 表示当前帧的码流包个数。
					在调用HI_MPI_VENC_GetStream 之前应确保 u32CurPacks 大于 0

					在编码通道状态结构体中, u32LeftStreamFrames 表示码流buffer 
					中剩余的帧数目。在复位通道前,可以通过查询是否还有图像的码流没
					有被取走来决定复位时机,防止复位时将可能需要的码流清理出去
					*******************************************************/
					if(0 == stStat.u32CurPacks)
					{
						  SAMPLE_PRT("NOTE: Current  frame is NULL!\n");
						  continue;
					}
                    /*******************************************************
                     step 2.3 : malloc corresponding number of pack nodes.
码流结构体 VENC_STREAM_S 包含 4 个部分:
− 码流包信息指针 pstPack
指向一组 VENC_PACK_S 的内存空间,该空间由调用者分配。如果是按包获
取,则此空间不小于 sizeof( VENC_PACK_S)的大小;如果按帧获取,则此
空间不小于 N × sizeof( VENC_PACK_S)的大小,其中 N 代表当前帧之中的
包的个数,可以在 select 之后通过查询接口获得。
− 码流包个数 u32PackCount
在输入时,此值指定 pstPack 中 VENC_PACK_S 的个数。按包获取时,
u32PackCount 必须不小于 1;按帧获取时, u32PackCount 必须不小于当前帧的
包个数。在函数调用成功后, u32PackCount 返回实际填充 pstPack 的包的个
数。
− 序列号 u32Seq
按帧获取时是帧序列号;按包获取时为包序列号。
− 码流特征信息,数据类型为联合体,包含了不同编码协议对应的码流特征信息
stH264Info/ stJpegInfo/ stMpeg                
                    *******************************************************/
                    stStream.pstPack = (VENC_PACK_S*)malloc(sizeof(VENC_PACK_S) * stStat.u32CurPacks);

                    /*******************************************************
                     step 2.4 : call mpi to get one-frame stream
                    *******************************************************/
                    stStream.u32PackCount = stStat.u32CurPacks;
                    //获取编码码流
                    s32Ret = HI_MPI_VENC_GetStream(i, &stStream, HI_TRUE);
                    /*******************************************************
                     step 2.5 : save frame to file,
                    *******************************************************/
                    s32Ret = SAMPLE_COMM_VENC_SaveStream(enPayLoadType[i], pFile[i], &stStream);
                    /*******************************************************
                     step 2.6 : release stream
                    *******************************************************/
                    //释放码流缓存。
                    s32Ret = HI_MPI_VENC_ReleaseStream(i, &stStream);
                    /*******************************************************
                     step 2.7 : free pack nodes
                    *******************************************************/
                    free(stStream.pstPack);
                    stStream.pstPack = NULL;
                }
            }
        }
    }

    /*******************************************************
    * step 3 : close save-file
    *******************************************************/
    for (i = 0; i < s32ChnTotal; i++)
    {
        fclose(pFile[i]);//关闭打开的文件
    }

    return NULL;
}
HI_S32 SAMPLE_COMM_VENC_StopGetStream()
{
    if (HI_TRUE == gs_stPara.bThreadStart)
    {
        gs_stPara.bThreadStart = HI_FALSE;//用于停止上边这个函数中的那个While循环
        pthread_join(gs_VencPid, 0);
    }
    return HI_SUCCESS;
}
SAMPLE_COMM_VENC_SaveStream
	SAMPLE_COMM_VENC_SaveH264
		        fwrite
       			fflush //flush()会强迫将缓冲区内的数据写回参数stream 指定的文件中

如果你想理解更多的函数细节和视频编解码的一些流程及参数设置,请仔细阅读我们本季前几篇文章提到的数据手册。

注:本资料大部分由朱老师物联网大讲堂课程笔记整理而来、参考了华为海思SDK中提供的开发手册,并且引用了部分他人博客的内容,如有侵权,联系删除!水平有限,如有错误,欢迎各位在评论区交流。

标签:编码,码率,例程,SAMPLE,API,HI,VENC,图像,海思
来源: https://blog.csdn.net/weixin_45842280/article/details/121619781

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有