8-8_MPP平台下使用RTSP实现摄像头数据推流源码解析

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();//停止事件
}