firefly-rk3399 开发记录3-编码与RTSP实现

六月ゝ 毕业季﹏ 提交于 2020-08-12 13:37:47

1.帧捕获

在前面的实现过程中,采用了QVideoProbe捕获摄像头数据,在信号绑定之后,传输到QSmartVenc,编码模块是额外放在另外一个线程处理的

    QVencParm param;
    param.width  = 1280;
    param.height = 720;
    param.code   = MPP_VIDEO_CodingAVC;
    param.fmt    = MPP_FMT_YUV420P;
    m_venc.reset(new QSmartVenc(param));
    QThread *vencThread = new QThread(this);
    m_venc->moveToThread(vencThread);
    vencThread->start();

将probe数据绑定到编码模块

connect(m_probe.data(),&QVideoProbe::videoFrameProbed,m_venc.data(),&QSmartVenc::needHandleImage);

2.编码

编码是使用的瑞芯微MPP,参考历程mpi_enc_test,整体接口略显麻烦,基本可以跟着流程不动进行修改。其中需要注意一点的是stride在MPP中需要是16字节对齐,这里最好将输入分辨率也做16字节对齐,这样宽和高就刚好和stride匹配,避免后面填充的时候还有手动计算YUV分量位置.

  MppFrame frame = NULL;
    MppPacket packet = NULL;
    void *buf = mpp_buffer_get_ptr(frm_buf);
    
    f.map(QAbstractVideoBuffer::ReadOnly);
    memcpy(buf,f.bits(),frame_size);
    f.unmap();

    int ret = mpp_frame_init(&frame);
    if(ret)
    {
        qWarning("Init frame failed");
        return;
    }
   
    mpp_frame_set_width(frame, par.width);
    mpp_frame_set_height(frame, par.height);
    mpp_frame_set_hor_stride(frame, hor_stride);
    mpp_frame_set_ver_stride(frame, ver_stride);
    mpp_frame_set_fmt(frame, par.fmt);
    mpp_frame_set_eos(frame, 0);

    mpp_frame_set_buffer(frame, frm_buf);
    ret = mpi->encode_put_frame(ctx, frame);
    if(ret)
    {
        qWarning( "encode_put_frame failed");
        return;
    }

    ret = mpi->encode_get_packet(ctx, &packet);
    if (ret) {
      qWarning("mpp encode get packet failed\n");
    }
    if(!packet)
        return;

    // write packet to file here
    void *ptr   = mpp_packet_get_pos(packet);
    size_t len  = mpp_packet_get_length(packet);
    encFile.write((char *)ptr,len);
    //p->pkt_eos = mpp_packet_get_eos(packet);
    {
        QMutexLocker locker(&encDataLock);
        if((encDataLen + len) > MAX_STREAM_BUFFER)
        {
            auto ba = encDataList.takeFirst(); //右值
            encDataLen -= ba.length();
        }
        qDebug() << "encode len " << len;
        encDataList.append(rtcpInfo + QByteArray((char *)ptr,len)); //右值,这里一定要加上sps,pps
        encDataLen += (len + rtcpInfo.size());;
    }
   
    mpp_packet_deinit(&packet);

    //qDebug("encoded frame %u size %lu\n", frame_count, len);
    frame_count++;

在memcpy的位置,如果宽高和stride不匹配的,就不能直接拷贝了,手动计算,麻烦得一逼。。

这里在编码完成之后,通过 QList<QByteArray>存储起来,需要做缓冲大小限制,这个大小限制和具体业务相关了。

3.RTSP

RTSP的实现采用的是live555,根据livemedia进行的修改,目前跑多路视频流,1080P,没有问题,live555这里是经过修改过的和原生略有不同。
首先提供码流信息和读取回调函数,live555是单独一个线程运行的,在合适的时候会调用到提供的函数读取视频流。

int rtsp_sever_callback(void *data,unsigned int max,const char * des,int *fps);
VIDEO_RTSP_INFO rtspServerInfo[] = {
    {PT_H264,"smartcam","smartcam",rtsp_sever_callback}
};

通过此接口将信息注册到RTSP服务上,video_init_rtsp是自己实现的和live555交互的处理逻辑。

void QSmartVenc::init_rtsp_server(void)
{
    MESSAGE msg;
    msg.pstInfo = rtspServerInfo;
    msg.size    = sizeof(rtspServerInfo)/sizeof(rtspServerInfo[0]);
    video_init_rtsp(&msg);
}

rtsp_sever_callback实现如下,这里需要注意的就是生产和消费关系了,如果每次取多了,可能会导致下次来取无数据,从而断流,如果取少了,会导致数据丢失,从而花屏。

int rtsp_sever_callback(void *data,unsigned int max,const char * des,int *fps)
{
    //可通过des判断是哪一条流信息
    QMutexLocker locker(&encDataLock);
    if(!encDataList.size()) //无数据
        return -1;
    unsigned int  remain = max;
    int num = 0;
    //while(!encDataList.isEmpty() && num++ < 2) //最多取两针,这里根据实际情况来做处理吧,协调好生产消费的关系,不然很容易掉帧,花屏
    {
        auto ba = encDataList.takeFirst();
        int readLen = ba.size() < remain ? ba.size()  : remain;
        memcpy(data,ba.data(),readLen);
        if(readLen < ba.size()) //此帧没有读取完毕
        {
            encDataList.insert(0,ba.remove(0,readLen));
        }
        encDataLen -= readLen;
        remain -= readLen;

        // if(remain < 512)
        //     break;
    }
    qDebug() << "left data "<< encDataLen;
    *fps = encDataLen < 2 * 1024 ? 10 : 30;
    return (max - remain);
}

这里的实现也有个问题,没有处理I帧,会出现刚开始获取视频流时,有点花屏,过一会就好了,初步分析是因为第一次获取到的可能不是I帧,后面读到I帧之后,就恢复正常

 

执行文件和相关库文件在链接上

https://download.csdn.net/download/huahuang1508/12694057

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!