MPP平台下使用RTSP实现摄像头数据推流源码解析
在MPP平台的中实现RTSP摄像头实时预览的数据流向图如下所示:
对于前面的VI组件和VENC组件,我们已经学习过,这里就重点学习:获取编码后的码流如何发送给RTSP去做推流处理。
1.MPP源码
1.1 MPP平台初始化
MPP_SYS_CONF_S stSysConf;
memset(&stSysConf, 0, sizeof(MPP_SYS_CONF_S));
stSysConf.nAlignWidth = 32;
AW_MPI_SYS_SetConf(&stSysConf);
ret = AW_MPI_SYS_Init();
1.2 配置主码流参数
configMainStream(pContext);
1.3 根据VIPP设备创建VI组件
AW_MPI_VI_CreateVipp(pContext->mMainStream.mVipp);
AW_MPI_VI_SetVippAttr(pContext->mMainStream.mVipp, &pContext->mMainStream.mViAttr);
if (0 == pContext->mMainIspRunFlag)
{
AW_MPI_ISP_Run(pContext->mMainStream.mIsp);
pContext->mMainIspRunFlag = 1;
}
AW_MPI_VI_EnableVipp(pContext->mMainStream.mVipp);
1.4 创建VI虚通道
AW_MPI_VI_CreateVirChn(pContext->mMainStream.mVipp, pContext->mMainStream.mViChn, NULL);
1.5 创建和初始化编码通道
AW_MPI_VENC_CreateChn(pContext->mMainStream.mVEncChn, &pContext->mMainStream.mVEncChnAttr);
AW_MPI_VENC_SetRcParam(pContext->mMainStream.mVEncChn, &pContext->mMainStream.mVEncRcParam);
AW_MPI_VENC_SetFrameRate(pContext->mMainStream.mVEncChn, &pContext->mMainStream.mVEncFrameRateConfig);
1.6 设置3D 降噪高级参数
setVenc2Dnr3Dnr(pContext->mMainStream.mVEncChn);
1.7 设置超大帧重编码处理参数
setVencSuperFrameCfg(pContext->mMainStream.mVEncChn, pContext->mConfigPara.mMainEncodeBitrate, pContext->mConfigPara.mMainEncodeFrameRate);
1.8 设置编码回调函数
MPPCallbackInfo cbInfo;
cbInfo.cookie = (void*)pContext;
cbInfo.callback = (MPPCallbackFuncType)&MPPCallbackWrapper;
AW_MPI_VENC_RegisterCallback(pContext->mMainStream.mVEncChn, &cbInfo);
1.9 绑定VI和VENC组件
MPP_CHN_S ViChn = {MOD_ID_VIU, pContext->mMainStream.mVipp, pContext->mMainStream.mViChn};
MPP_CHN_S VeChn = {MOD_ID_VENC, 0, pContext->mMainStream.mVEncChn};
AW_MPI_SYS_Bind(&ViChn,&VeChn);
1.10 启动VI和VENC通道
AW_MPI_VI_EnableVirChn(pContext->mMainStream.mVipp, pContext->mMainStream.mViChn);
AW_MPI_VENC_StartRecvPic(pContext->mMainStream.mVEncChn);
1.11 初始化RTSP和支持码流类型
RtspServerAttr rtsp_attr;
memset(&rtsp_attr, 0, sizeof(RtspServerAttr));
rtsp_attr.net_type = pContext->mConfigPara.mRtspNetType;
if (PT_H264 == pContext->mMainStream.mVEncChnAttr.VeAttr.Type)
rtsp_attr.video_type = RTSP_VIDEO_TYPE_H264;
else if (PT_H265 == pContext->mMainStream.mVEncChnAttr.VeAttr.Type)
rtsp_attr.video_type = RTSP_VIDEO_TYPE_H265;
else
rtsp_attr.video_type = RTSP_VIDEO_TYPE_LAST;
1.12 初始化RTSP服务
result = rtsp_open(0, &rtsp_attr);
if (result)
{
aloge("Do rtsp_open fail! ret:%d \n", result);
return -1;
}
1.13 创建获取码流并使用RTSP推流的线程
result = pthread_create(&pContext->mMainStream.mStreamThreadId, NULL, getVencStreamThread, (void*)&pContext->mMainStream);
if (result != 0)
{
aloge("fatal error! pthread create fail[%d]", result);
}
1.14 停止并销毁VI和VENC通道
if (pContext->mConfigPara.mMainVEncChn >= 0 && pContext->mConfigPara.mMainViChn >= 0)
{
pthread_join(pContext->mMainStream.mStreamThreadId, &pRetVal);
alogd("mainStream pRetVal=%p", pRetVal);
AW_MPI_VI_DisableVirChn(pContext->mMainStream.mVipp, pContext->mMainStream.mViChn);
AW_MPI_VENC_StopRecvPic(pContext->mMainStream.mVEncChn);
AW_MPI_VENC_ResetChn(pContext->mMainStream.mVEncChn);
AW_MPI_VENC_DestroyChn(pContext->mMainStream.mVEncChn);
AW_MPI_VI_DestroyVirChn(pContext->mMainStream.mVipp, pContext->mMainStream.mViChn);
if(pContext->mMainStream.mFile)
{
fclose(pContext->mMainStream.mFile);
pContext->mMainStream.mFile = NULL;
}
}
1.15 关闭和销毁VI组件
if (pContext->mConfigPara.mMainVEncChn >= 0 && pContext->mConfigPara.mMainViChn >= 0)
{
AW_MPI_VI_DisableVipp(pContext->mMainStream.mVipp);
if (pContext->mMainIspRunFlag)
{
AW_MPI_ISP_Stop(pContext->mMainStream.mIsp);
pContext->mMainIspRunFlag = 0;
}
AW_MPI_VI_DestroyVipp(pContext->mMainStream.mVipp);
}
1.16 停止和关闭RTSP服务
rtsp_stop(0);
rtsp_stop(1);
rtsp_close(0);
rtsp_close(1);
1.17 退出MPP系统
ret = AW_MPI_SYS_Exit();
if (ret != SUCCESS)
{
aloge("fatal error! sys exit failed!");
}
2.获取码流与RTSP推理函数
2.1 获取编码数据头部
VencHeaderData stSpsPpsInfo;
memset(&stSpsPpsInfo, 0, sizeof(stSpsPpsInfo));
if(PT_H264 == pStreamContext->mVEncChnAttr.VeAttr.Type)
{
ret = AW_MPI_VENC_GetH264SpsPpsInfo(pStreamContext->mVEncChn, &stSpsPpsInfo);
if(ret != SUCCESS)
{
aloge("fatal error! get spspps fail[0x%x]!", ret);
}
}
else if(PT_H265 == pStreamContext->mVEncChnAttr.VeAttr.Type)
{
ret = AW_MPI_VENC_GetH265SpsPpsInfo(pStreamContext->mVEncChn, &stSpsPpsInfo);
if(ret != SUCCESS)
{
aloge("fatal error! get spspps fail[0x%x]!", ret);
}
}
2.2 启动RTSP服务
if (VENC_CHN_MAIN_STREAM == pStreamContext->mVEncChn)
{
rtsp_start(0);
}
else if (VENC_CHN_SUB_STREAM == pStreamContext->mVEncChn)
{
rtsp_start(1);
}
2.3 获取编码通道的码流
memset(stVencStream.mpPack, 0, sizeof(VENC_PACK_S));
ret = AW_MPI_VENC_GetStream(pStreamContext->mVEncChn, &stVencStream, nMilliSec);
2.4 计算码流数据长度
nStreamLen = stVencStream.mpPack[0].mLen0 + stVencStream.mpPack[0].mLen1 + stVencStream.mpPack[0].mLen2;
2.5 获取码流的头和帧类型
if(PT_H264 == pStreamContext->mVEncChnAttr.VeAttr.Type)
{
if (H264E_NALU_ISLICE == stVencStream.mpPack->mDataType.enH264EType)
{
if (NULL == stSpsPpsInfo.pBuffer)
{
alogd("SpsPpsInfo.pBuffer = NULL!!\n");
}
/* Get sps/pps first */
memcpy(stream_buf, stSpsPpsInfo.pBuffer, stSpsPpsInfo.nLength);
len += stSpsPpsInfo.nLength;
frame_type = RTSP_FRAME_DATA_TYPE_I;
}
else
{
frame_type = RTSP_FRAME_DATA_TYPE_P;
}
}
2.6 将编码数据保存至buf中
if (MAX_FRAME_BUF_SiZE > len + stVencStream.mpPack->mLen0)
{
memcpy(stream_buf + len, stVencStream.mpPack->mpAddr0, stVencStream.mpPack->mLen0);
len += stVencStream.mpPack->mLen0;
}
if (stVencStream.mpPack->mLen1 > 0)
{
if (MAX_FRAME_BUF_SiZE > (len + stVencStream.mpPack->mLen1))
{
memcpy(stream_buf + len, stVencStream.mpPack->mpAddr1, stVencStream.mpPack->mLen1);
len += stVencStream.mpPack->mLen1;
}
}
if (stVencStream.mpPack->mLen2 > 0)
{
if (MAX_FRAME_BUF_SiZE > (len + stVencStream.mpPack->mLen2))
{
memcpy(stream_buf + len, stVencStream.mpPack->mpAddr2, stVencStream.mpPack->mLen2);
len += stVencStream.mpPack->mLen2;
}
}
2.7 设置rtsp参数
RtspSendDataParam stRtspParam;
memset(&stRtspParam, 0, sizeof(RtspSendDataParam));
stRtspParam.buf = stream_buf;//码流数据
stRtspParam.size = len;//数据长度
stRtspParam.frame_type = frame_type;//帧类型
stRtspParam.pts = pts;//时间戳
2.8 使用rtsp发送编码数据
if (VENC_CHN_MAIN_STREAM == pStreamContext->mVEncChn)
{
rtsp_sendData(0, &stRtspParam);
}
2.9 释放编码通道图像帧
ret = AW_MPI_VENC_ReleaseStream(pStreamContext->mVEncChn, &stVencStream);
2.10 释放RTSP的数据buf
if (stream_buf)
{
free(stream_buf);
stream_buf = NULL;
}
3.封装的RTSP源码
3.1 初始化RTSP服务
int rtsp_open(int id, RtspServerAttr *rtsp_attr)
{
int ret = 0;
int port = 8554;
char net_name[32] = {0};
char ip[64] = {0};
std::string ip_addr;
if (id >= RTSP_SERVER_CHANNEL_NUM)
{
printf("%s,%d: invalid id %d >= %d\n", __func__,__LINE__, id, RTSP_SERVER_CHANNEL_NUM);
return -1;
}
//设置网络类型
if (RTSP_NET_TYPE_LO == rtsp_attr->net_type)
strncpy(net_name, "lo", 31);
else if (RTSP_NET_TYPE_ETH0 == rtsp_attr->net_type)
strncpy(net_name, "eth0", 31);
else if (RTSP_NET_TYPE_BR0 == rtsp_attr->net_type)
strncpy(net_name, "br0", 31);
else if (RTSP_NET_TYPE_WLAN0 == rtsp_attr->net_type)
strncpy(net_name, "wlan0", 31);
else
printf("%s,%d: invalid net type %d\n", __func__,__LINE__, rtsp_attr->net_type);
// get ip addr and create server
ret = get_net_dev_ip(net_name, ip);//获取网络的IP地址
if (ret)
{
printf("%s get_net_dev_ip fail! ret:%d\n", net_name, ret);
return -1;
}
printf("%s ip:%s \n", net_name, ip);
ip_addr = ip;
TinyServer *rtsp = TinyServer::createServer(ip_addr, port);//创建RTSP服务器
if (NULL == rtsp)
{
printf("%s,%d: rtsp create server failed!\n", __func__,__LINE__);
return -1;
}
gpRtspContext[id] = rtsp;
// create Media Stream
MediaStream::MediaStreamAttr attr;
//视频类型
if (RTSP_VIDEO_TYPE_H264 == rtsp_attr->video_type)
attr.videoType = MediaStream::MediaStreamAttr::VIDEO_TYPE_H264;
else if (RTSP_VIDEO_TYPE_H265 == rtsp_attr->video_type)
attr.videoType = MediaStream::MediaStreamAttr::VIDEO_TYPE_H265;
else
printf("%s,%d: invalid video type %d\n", __func__,__LINE__, rtsp_attr->video_type);
//音频类型
if (RTSP_AUDIO_TYPE_AAC == rtsp_attr->audio_type)
attr.audioType = MediaStream::MediaStreamAttr::AUDIO_TYPE_AAC;
else
printf("%s,%d: invalid audio type %d\n", __func__,__LINE__, rtsp_attr->audio_type);
//流类型(单播/多播)
if (RTSP_STREAM_TYPE_UNICAST == rtsp_attr->stream_type)
attr.streamType = MediaStream::MediaStreamAttr::STREAM_TYPE_UNICAST;
else if (RTSP_STREAM_TYPE_MULTICAST == rtsp_attr->stream_type)
attr.streamType = MediaStream::MediaStreamAttr::STREAM_TYPE_MULTICAST;
else
printf("%s,%d: invalid stream type %d\n", __func__,__LINE__, rtsp_attr->stream_type);
char ch_str[32] = {0};
sprintf(ch_str, "ch%d", id);
MediaStream *stream = rtsp->createMediaStream(ch_str, attr);//创建媒体流
if (NULL == stream)
{
printf("%s,%d: rtsp create media stream failed!\n", __func__,__LINE__);
return -1;
}
gpRtspStream[id] = stream;
printf("============================================================\n");
printf(" rtsp://%s:%d/%s \n", ip_addr.c_str(), port, ch_str);//url
printf("============================================================\n");
return 0;
}
3.2 获取指定网络设备的IP地址
static int get_net_dev_ip(const char *netdev_name, char *ip)
{
int fd = -1;//文件描述符,用于套接字操作
unsigned int u32ip = 0;//存储IP地址的无符号整数
struct ifreq ifr;//网络接口请求
struct in_addr sin_addr;//存储IP地址
if (NULL == netdev_name || NULL == ip) {
printf("%s,%d: Input netdev_name or ip is NULL!\n", __func__, __LINE__);
return -1;
}
fd = socket(AF_INET, SOCK_DGRAM, 0);//创建一个UDP套接字
if (fd <= 0) {
printf("%s,%d: Fail to create socket! errno[%d] errinfo[%s]\n", __func__, __LINE__,
errno, strerror(errno));
return -1;
}
memset(&ifr, 0, sizeof(struct ifreq));//清空ifr结构体
strncpy(ifr.ifr_name, netdev_name, sizeof(ifr.ifr_name));//复制网络设备名称到ifr.ifr_name中
ifr.ifr_addr.sa_family = AF_INET;//设置地址族
if (ioctl(fd, SIOCGIFADDR, &ifr) < 0) {//调用获取网络设备的IP地址
printf("%s,%d: Fail to ioctl SIOCGIFADDR. devname[%s] errno[%d] errinfo[%s]\n", __func__, __LINE__,
netdev_name, errno, strerror(errno));
close(fd);
return -1;
}
close(fd);
u32ip = ntohl(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr);//将获取到的IP地址从网络字节序转换为主机字节序
sin_addr.s_addr = htonl(u32ip);
sprintf(ip, "%s", inet_ntoa(sin_addr));//将sin_addr中的IP地址转换为点分十进制的字符串形式,并存储在ip字符数组中
return 0;
}
3.3 开启RTSP服务
void rtsp_start(int id)
{
if (id >= RTSP_SERVER_CHANNEL_NUM)
{
printf("%s,%d: invalid id %d >= %d\n", __func__,__LINE__, id, RTSP_SERVER_CHANNEL_NUM);
return;
}
TinyServer *rtsp = gpRtspContext[id];
MediaStream *stream = gpRtspStream[id];
std::string url_ = stream->streamURL();
rtsp->runWithNewThread();//创建事件处理线程
}
3.4 发送数据
void rtsp_sendData(int id, RtspSendDataParam *param)
{
if (id >= RTSP_SERVER_CHANNEL_NUM)
{
printf("%s,%d: invalid id %d >= %d\n", __func__,__LINE__, id, RTSP_SERVER_CHANNEL_NUM);
return;
}
if (NULL == param)
{
printf("%s,%d: invalid param!\n", __func__,__LINE__);
return;
}
MediaStream *stream = gpRtspStream[id];//获取对应id的媒体流对象
if (stream != NULL)
{
if (RTSP_FRAME_DATA_TYPE_I == param->frame_type)//如果帧类型是I帧(关键帧)
{
stream->appendVideoData(param->buf, param->size, param->pts, MediaStream::FRAME_DATA_TYPE_I);//将数据追加为I帧
}
else if (RTSP_FRAME_DATA_TYPE_P == param->frame_type)
{
stream->appendVideoData(param->buf, param->size, param->pts, MediaStream::FRAME_DATA_TYPE_P);//将数据追加为P帧
}
else
{
printf("rtsp id %d, frame_type %d is NOT support!\n", id, param->frame_type);
}
}
}
3.5 关闭RTSP服务
void rtsp_close(int id)
{
if (id >= RTSP_SERVER_CHANNEL_NUM)
{
printf("%s,%d: invalid id %d >= %d\n", __func__,__LINE__, id, RTSP_SERVER_CHANNEL_NUM);
return;
}
printf("close rtsp id %d\n", id);
}
3.6 停止RTSP服务
void rtsp_stop(int id)
{
if (id >= RTSP_SERVER_CHANNEL_NUM)
{
printf("%s,%d: invalid id %d >= %d\n", __func__,__LINE__, id, RTSP_SERVER_CHANNEL_NUM);
return;
}
TinyServer *rtsp = gpRtspContext[id];
rtsp->stop();//停止事件
}