从零开始学习音视频编程技术(45)采集屏幕推送rtp
时间:01月26日 人气:...


前面的章节我们已经掌握了如何将h264文件用rtp协议推送出去,并使用vlc播放。

现在,我们继续实现采集屏幕推送rtp。


与推送H264文件类似,推送屏幕需要做的第一件事就是首先得生成h264的数据,

采集屏幕并编码成h264的例子之前已经写过了,忘记的可以去看看:

http://blog.yundiantech.com/?log=blog&id=20


现在,我们有了2个例子:

  1. 采集屏幕编码成h264

  2. 将H264文件用rtp推送出去


现在只需要合并这2个例子就可以实现我们要的功能了,因此代码就不做太多的解释了,直接看完整工程吧。


这里需要说的一点就是:

之前发送h264文件的时候,我们通过循环查找起始码的方式来查找一帧数据,然后将这一帧数据当成一个Nalu发送出去了。

而这里的数据是我们自己编码生成的,没次编码完获取到的数据肯定是完整的一帧了,只需要剔除掉起始码就可以直接丢给发送的线程了。


大致代码如下:

void H264Encoder::run()
{
    openEncoder();

    int y_size = pCodecCtx->width * pCodecCtx->height;

    AVPacket pkt;
    av_new_packet(&pkt,y_size*3);


    while(1)
    {
        if (mYuvBufferList.isEmpty())
        {
            if (mIsStop) break; //确认队列里面的数据全部编码完毕 再退出

            msleep(10);
            continue;
        }

        mMutex.lock();
        FrameDataNode node = mYuvBufferList.takeFirst(); //取出一帧yuv数据
        mMutex.unlock();

        if (node.buffer != NULL)
        {
            picture->data[0] = node.buffer;     // 亮度Y
            picture->data[1] = node.buffer + y_size;  // U
            picture->data[2] = node.buffer + y_size*5/4; // V

            int got_picture=0;
            //编码
            int ret = avcodec_encode_video2(pCodecCtx, &pkt,picture, &got_picture);

            if (got_picture==1)
            {
                int StartCode = 0;

                unsigned char* Buf = pkt.data;

                ///判断起始码是3位还是4位,
                if(Buf[0]==0 && Buf[1]==0 && Buf[2] ==1)
                 //Check whether buf is 0x000001
                {
                    StartCode = 3;
                }
                else if(Buf[0]==0 && Buf[1]==0 && Buf[2] ==0 && Buf[3] ==1)
                 //Check whether buf is 0x00000001
                {
                    StartCode = 4;
                }

                ///因为发送rtp的时候需要去掉起始码,因此这里生成的nalu结构不包含起始码
                int naluSize = pkt.size - StartCode; //nalu数据大小 不包含起始码
                NALU_t * nalu = AllocNALU(naluSize);//分配nal 资源

                nalu->startcodeprefix_len = StartCode;      //! 4 for parameter sets and first slice in picture, 3 for everything else (suggested)
                nalu->len = naluSize;                 //! Length of the NAL unit (Excluding the start code, which does not belong to the NALU)
            //    nalu->forbidden_bit = 0;            //! should be always FALSE
            //    nalu->nal_reference_idc = nalu_header->NRI;        //! NALU_PRIORITY_xxxx
            //    nalu->nal_unit_type = nalu_header->TYPE;            //! NALU_TYPE_xxxx
                nalu->lost_packets = false;  //! true, if packet loss is detected

                memcpy(nalu->buf, Buf + StartCode, naluSize);  //! contains the first byte followed by the EBSP

                nalu->forbidden_bit = nalu->buf[0] & 0x80; //1 bit
                nalu->nal_reference_idc = nalu->buf[0] & 0x60; // 2 bit
                nalu->nal_unit_type = (nalu->buf[0]) & 0x1f;// 5 bit

                mNaluManager->inputNalu(nalu);

            }

            av_free_packet(&pkt);
            av_free(node.buffer);
        }
    }

    closeEncoder();
}


播放rtp流依然使用vlc播放,播放方法请参考:http://blog.yundiantech.com/?log=blog&id=42

播放效果如下:


VLC播放过程中,可能会出现花屏,或者延时卡顿现象,这个暂时先不管,以后再来解决。


别的就不做解释了,具体代码直接查看完整代码吧:


完整工程下载地址:https://download.csdn.net/download/qq214517703/10939317

学习音视频技术欢迎访问 http://blog.yundiantech.com  

音视频技术交流讨论欢迎加 QQ群 121376426