海思RTSP推流第二篇——海思平台和H264编码Demo

前言:
记得行内一个老师说过,基础不牢,地动山摇,对于海思的编码过程还是不是很熟悉所以回头把这个分析一遍。
海思平台:
官方手册:HiMPP IPC V2.0 媒体处理软件开发参考,里面有介绍海思IPC平台的架构和框架;
这里贴几个图具体还得查看文档。
Hi35xx 典型的系统层次图:
在这里插入图片描述
海思媒体处理平台架构:分为好几个模块,视频输入( VI)、视频
处理( VPSS)、视频编码( VENC)、视频解码( VDEC)、视频输出(VO)、视频侦测分
析(VDA)、音频输入(AI)、音频输出(AO)、音频编码( AENC)、音频解码( ADEC)、
区域管理( REGION)等模块
在这里插入图片描述
各模块的简介,具体还是得查看手册可知道其细节
在这里插入图片描述
正文:海思平台H264编码的过程

一、会有相关的音视频的参数和概念

  1. 比如VPSS视频处理子系统,通道、码率控制模式等…对这些概念需要理解其作用;
    //编码协议类型
PAYLOAD_TYPE_E enPayLoad[1]= {PT_H264};
PIC_SIZE_E enSize[3] = {PIC_HD1080};
HI_U32 u32Profile = 0;

//定义视频缓存池属性结构体。
VB_CONF_S stVbConf;
//vi config
SAMPLE_VI_CONFIG_S stViConfig = {0};

//VPSS
VPSS_GRP VpssGrp;
VPSS_CHN VpssChn;
VPSS_GRP_ATTR_S stVpssGrpAttr;
VPSS_CHN_ATTR_S stVpssChnAttr;
VPSS_CHN_MODE_S stVpssChnMode;

//编码通道
VENC_CHN VencChn;
//定义编码通道码率控制器 模式 
SAMPLE_RC_E enRcMode= SAMPLE_RC_CBR;
//通道数
HI_S32 s32ChnNum=0;

HI_S32 s32Ret = HI_SUCCESS;
HI_U32 u32BlkSize;
SIZE_S stSize;
char c;

PAYLOAD_TYPE_E :定义编码协议类型枚举;
VB_CONF_S :定义视频缓存池属性结构体;
SAMPLE_RC_E :定义编码通道码率控制器 模式 ;
什么是码率? 码率就是数据传输时单位时间传送的数据位数。
重点提下码率控制模式:包含了vbr和cbr 、avbr、 qvbr 、cvbr、fixqp等多种

typedef enum sample_rc_e
{
    SAMPLE_RC_CBR = 0,      //CBR:(Constant Bit Rate)恒定码率
    SAMPLE_RC_VBR,          //VBR:(Variable Bit Rate)可变码率
	SAMPLE_RC_AVBR,         //AVBR(Adaptive Variable Bit rate)变码流:AVBR是通过对VBR的进一步改进,增加了自适应动态码率控制功能
    SAMPLE_RC_FIXQP         //FIXQP(固定质量)是一种视频编码方式
}SAMPLE_RC_E;

详细参考博客:https://blog.youkuaiyun.com/qq_28258885/article/details/118891810

  1. 对海思平台图形处理器的一些个人理解
    海思平台框架按照上面已经给出,分为多个模块VI、VPSS、VENC等模块,模块之间连接要通过绑定来实现数据传输,海思有一种数据交换模式叫在线和离线模式,在线模式下可以不经过内存直接把数据传输给另一个模块,VI和VPSS之间,图像采集编码这个过程需要申请一个大的内存——内存池,用于存放图像数据 stVbConf 设置相关参数,图像采集过程申请一个内存缓冲块存放数据,内存块在完成编码后需要释放如下图所示。
    在这里插入图片描述

二、海思平台视频编码过程

视频编码流程:申请内存池 -> 初始化sys -> 启动VI -> 启动视频处理子系统 -> 绑定VI和VPSS -> 启动VENC -> 绑定VENC和VPSS -> 获取和保存视频裸流;
过程中有相关的参数设置需要根据需要查阅手册配置合理的参数。

/******************************************
 step  1: init sys variable 
******************************************/

SAMPLE_COMM_VI_GetSizeBySensor(&enSize[0]);

/*video buffer*/
u32BlkSize = SAMPLE_COMM_SYS_CalcPicVbBlkSize(gs_enNorm,\
            enSize[0], SAMPLE_PIXEL_FORMAT, SAMPLE_SYS_ALIGN_WIDTH);
stVbConf.astCommPool[0].u32BlkSize = u32BlkSize;
stVbConf.astCommPool[0].u32BlkCnt = g_u32BlkCnt;


/******************************************
 step 2: mpp system init. 
******************************************/
s32Ret = SAMPLE_COMM_SYS_Init(&stVbConf);

/******************************************
 step 3: start vi dev & chn to capture
******************************************/
stViConfig.enViMode   = SENSOR_TYPE;
stViConfig.enRotate   = ROTATE_NONE;
stViConfig.enNorm     = VIDEO_ENCODING_MODE_AUTO;
stViConfig.enViChnSet = VI_CHN_SET_NORMAL;
stViConfig.enWDRMode  = WDR_MODE_NONE;
//开始视频输入
s32Ret = SAMPLE_COMM_VI_StartVi(&stViConfig);

/******************************************
 step 4: start vpss and vi bind vpss
******************************************/
s32Ret = SAMPLE_COMM_SYS_GetPicSize(gs_enNorm, enSize[0], &stSize);

VpssGrp = 0;
stVpssGrpAttr.u32MaxW = stSize.u32Width;
stVpssGrpAttr.u32MaxH = stSize.u32Height;
stVpssGrpAttr.bIeEn = HI_FALSE;
stVpssGrpAttr.bNrEn = HI_TRUE;
stVpssGrpAttr.bHistEn = HI_FALSE;
stVpssGrpAttr.bDciEn = HI_FALSE;
stVpssGrpAttr.enDieMode = VPSS_DIE_MODE_NODIE;
stVpssGrpAttr.enPixFmt = PIXEL_FORMAT_YUV_SEMIPLANAR_420;	
s32Ret = SAMPLE_COMM_VPSS_StartGroup(VpssGrp, &stVpssGrpAttr);

s32Ret = SAMPLE_COMM_VI_BindVpss(stViConfig.enViMode);

VpssChn = 0;
stVpssChnMode.enChnMode      = VPSS_CHN_MODE_USER;
stVpssChnMode.bDouble        = HI_FALSE;
stVpssChnMode.enPixelFormat  = PIXEL_FORMAT_YUV_SEMIPLANAR_420;
stVpssChnMode.u32Width       = stSize.u32Width;
stVpssChnMode.u32Height      = stSize.u32Height;
stVpssChnMode.enCompressMode = COMPRESS_MODE_SEG;
memset(&stVpssChnAttr, 0, sizeof(stVpssChnAttr));
stVpssChnAttr.s32SrcFrameRate = -1;
stVpssChnAttr.s32DstFrameRate = -1;
s32Ret = SAMPLE_COMM_VPSS_EnableChn(VpssGrp, VpssChn, &stVpssChnAttr, &stVpssChnMode, HI_NULL);


/******************************************
 step 5: start stream venc
******************************************/
printf("\t c) cbr.\n");
printf("\t v) vbr.\n");
printf("\t a) Avbr.\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 'a':
        enRcMode = SAMPLE_RC_AVBR;
        break;
    case 'f':
        enRcMode = SAMPLE_RC_FIXQP;
        break;
    default:
        printf("rc mode! is invaild!\n");
        goto END_VENC_1080P_CLASSIC_4;
}

s32Ret = SAMPLE_COMM_VENC_Start(VencChn, enPayLoad[0],\
                                gs_enNorm, enSize[0], enRcMode,u32Profile);

s32Ret = SAMPLE_COMM_VENC_BindVpss(VencChn, VpssGrp, VpssChn);
    
/******************************************
 step 6: stream venc process -- get stream, then save it to file. 
******************************************/
s32Ret = SAMPLE_COMM_VENC_StartGetStream(s32ChnNum);

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);
}

这里面涉及到一个比较重要的变量是视频流结构体

typedef struct hiVENC_STREAM_S
{
    VENC_PACK_S *pstPack;                           /*stream pack attribute*/
    HI_U32      u32PackCount;                       /*the pack number of one frame stream*/
    HI_U32      u32Seq;                             /*the list number of stream*/

    union
    {
        VENC_STREAM_INFO_H264_S  stH264Info;         /*the stream info of h264*/
        VENC_STREAM_INFO_JPEG_S  stJpegInfo;         /*the stream info of jpeg*/
        VENC_STREAM_INFO_MPEG4_S stMpeg4Info;       /*the stream info of mpeg4*/
        VENC_STREAM_INFO_H265_S  stH265Info;        /*the stream info of h265*/
    };
}VENC_STREAM_S;

保存H264流的函数

/******************************************************************************
* funciton : save H264 stream
******************************************************************************/
HI_S32 SAMPLE_COMM_VENC_SaveH264(FILE* fpH264File, VENC_STREAM_S *pstStream)
{
    HI_S32 i;

    
    for (i = 0; i < pstStream->u32PackCount; i++)
    {
        fwrite(pstStream->pstPack[i].pu8Addr+pstStream->pstPack[i].u32Offset,
               pstStream->pstPack[i].u32Len-pstStream->pstPack[i].u32Offset, 1, fpH264File);

        fflush(fpH264File);
    }
    

    return HI_SUCCESS;
}

细节过程还是需要结合源码和手册对照。

以下是一个简单的海思媒体MPP拉取RTSP的示例代码框架,该代码主要实现了从RTSP拉取数据,使用海思MPP进行解码等基本操作。需要注意的是,实际使用时需要根据海思SDK的具体版本硬件环境进行调整。 ```c #include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include "hi_comm_video.h" #include "hi_comm_sys.h" #include "hi_comm_vo.h" #include "hi_comm_vpss.h" #include "hi_comm_vdec.h" #include "hi_defines.h" #include "mpi_sys.h" #include "mpi_vdec.h" #include "mpi_vo.h" #include "mpi_vpss.h" #include "hi_unf_rtsp.h" #define RTSP_URL "rtsp://your_rtsp_stream_url" // RTSP回调函数 HI_VOID *RtspStreamCallback(HI_VOID *pArg) { HI_S32 s32Ret; HI_UNF_RTSP_SESSION_S stRtspSession; HI_UNF_RTSP_STREAM_S stRtspStream; memset(&stRtspSession, 0, sizeof(HI_UNF_RTSP_SESSION_S)); memset(&stRtspStream, 0, sizeof(HI_UNF_RTSP_STREAM_S)); // 打开RTSP会话 s32Ret = HI_UNF_RTSP_OpenSession(RTSP_URL, &stRtspSession); if (s32Ret != HI_SUCCESS) { printf("HI_UNF_RTSP_OpenSession failed, ret = 0x%x\n", s32Ret); return NULL; } // 开始接收 s32Ret = HI_UNF_RTSP_StartStream(&stRtspSession, &stRtspStream); if (s32Ret != HI_SUCCESS) { printf("HI_UNF_RTSP_StartStream failed, ret = 0x%x\n", s32Ret); HI_UNF_RTSP_CloseSession(&stRtspSession); return NULL; } // 这里可以添加对接收的数据进行处理,例如送入海思MPP解码 while (1) { // 模拟处理,实际需要根据海思MPP接口进行数据传递 // ... } // 停止接收 HI_UNF_RTSP_StopStream(&stRtspSession); // 关闭RTSP会话 HI_UNF_RTSP_CloseSession(&stRtspSession); return NULL; } int main() { HI_S32 s32Ret; pthread_t tid; // 初始化海思系统 s32Ret = HI_MPI_SYS_Init(); if (s32Ret != HI_SUCCESS) { printf("HI_MPI_SYS_Init failed, ret = 0x%x\n", s32Ret); return -1; } // 创建RTSP接收线程 if (pthread_create(&tid, NULL, RtspStreamCallback, NULL) != 0) { printf("pthread_create failed\n"); HI_MPI_SYS_Exit(); return -1; } // 等待线程结束 pthread_join(tid, NULL); // 退出海思系统 HI_MPI_SYS_Exit(); return 0; } ``` ### 代码说明 1. **RTSP_URL**:需要替换为实际的RTSP地址。 2. **RtspStreamCallback**:RTSP接收回调函数,负责打开RTSP会话、开始接收,并对接收的数据进行处理。 3. **main**:初始化海思系统,创建RTSP接收线程,等待线程结束后退出海思系统。 ### 编译运行 将上述代码保存为一个 `.c` 文件,例如 `rtsp_pull.c`,然后使用海思SDK提供的交叉编译工具链进行编译: ```sh # 假设使用arm-hisiv500-linux-gcc交叉编译工具链 arm-hisiv500-linux-gcc -o rtsp_pull rtsp_pull.c -I/path/to/hi_sdk/include -L/path/to/hi_sdk/lib -lhi_unf_rtsp -lhi_mpi -lhi_common -lpthread ``` 将生成的可执行文件 `rtsp_pull` 拷贝到海思开发板上运行: ```sh ./rtsp_pull ```
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值