Qt+FFmpeg工具对Rtsp协议实时视频流进行解码,并显示在QGraphicsView视图上

Qt+FFmpeg工具对Rtsp协议实时视频流进行解码,并显示在QGraphicsView视图上


前言

提示:这里可以添加本文要记录的大概内容:
Qt采用的版本为5.14.2,FFmpeg工具为官网下载的64位最新版,标识号为“3998820”,调用函数为avcodec_version()。FFmpeg版本之间差别还是挺大的,对内部的许多函数进行了弃用和修改。


提示:以下是本篇文章正文内容,下面案例可供参考

一、主要是思想过程

创新读取Rstp流的新类继承QThread,利用FFmpeg内置函数拉流进行解码,编码为RGB数据后转换为QImage形式,通过Qt发送信号的方式将其发送到显示类的槽函数中,槽函数接受到帧后显示到QGraphicsView上,利用QTimer定时器不断刷新槽函数接收到的图片实现整体实时视频流展现。

二、使用步骤

1.引入FFmpeg库

测试时打印的数据较多,不修改了直接拿上原函数,FFmpeg主要是弃用了以往一些初始化函数和处理函数,但大致流程相同。通过访问摄像头的ip网址修改编码流为H.264,有些输出可能为H.265。packet->stream_index这个是当时比较难受的一个变量 ,因为一直会在0,1这两条数据流当中交换,后来查了下应该是当前传过来的一条是视频流,一条是音频流。在这里插入图片描述

一开始断点还很好奇为什么会是1,导致视频流无法显示,后来一次测试才发现他是0,1交替读取,才输出了视频流。顺带附上FFmpeg解码线程的启动

void Video_ffm::RendStream()
{
    int videoStream, numBytes;
    videoStream = -1;
    avformat_network_init();
    pFormatCtx = new AVFormatContext();
    char url[] = "rtsp://用户名:密码@IP地址:554/Streaming/Channels/101";
    pFormatCtx = avformat_alloc_context();
    AVDictionary* avdic = NULL;
    //   char option_key[]="rtsp_transport";
      // char  m_bTcp;
      // av_dict_set(&avdic,option_key,m_bTcp?"udp":"tcp",0);
    av_dict_set(&avdic, "stimeout", "30", 0);  //设置超时断开连接时间
    av_dict_set(&avdic, "rtsp_transport", "tcp", 0);  //如果以tcp方式打开将udp替换为tcp
    //画质优化
    if (avformat_open_input(&pFormatCtx, url, nullptr, &avdic) == 0)    //打开多媒体并获取信息
    {
        qDebug() << QString::fromLocal8Bit("能打开引接视频地址");
    }
    else
    {
        qDebug() << "没能打开引接视频地址";
    }

    if (avformat_find_stream_info(pFormatCtx, NULL) >= 0)//用于给每个媒体流的AVStream结构体赋值
    {
        qDebug() << "2222"<<QString::fromLocal8Bit("能打开引接视频地址");
    }
    if (avdic != NULL)
    {
        av_dict_free(&avdic);//释放字典内存
    }
    for (unsigned i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
            qDebug() << "--------videoStream00-------------" << videoStream;
            break;
        }
    }
    if (videoStream == -1)
    {
        qDebug() << "Didn't find a video stream.\n";
        return;
    }
    av_dump_format(pFormatCtx, 0, url, 0);   //打印出当前的输入流信息 注意:最后一个参数填0,打印输入流;最后一个参数填1,打印输出流
    //查找解码器,获取指向视频流的编解码器上下文的指针
    pCodecCtx = new AVCodecContext();
    pCodecCtx = avcodec_alloc_context3(nullptr);
    //将音频流结构体拷贝给编解码结构体
    avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[videoStream]->codecpar);
    //通过解封装之后从avstream结构体里获取CodecID(指定格式流)
    pCodec = new AVCodec();
    pCodec = avcodec_find_decoder(pCodecCtx->codec_id);


    //打印一些视频信息
    qDebug() << "-------Struct-------\n"
        << "-----\n" << pCodec
        << "-----\n" << pCodecCtx->codec_id
        << "-----\n" << videoStream;

    //设置编码器参数(不同参数对视频编质量或大小的影响)
//      pCodecCtx->bit_rate =0;   //初始化为0   比特率
//      pCodecCtx->time_base.num=1;  //下面两行:一秒钟25帧
//      pCodecCtx->time_base.den=25;
    if (pCodec == nullptr) // 找不到解码器
    {
        qDebug() << "]]]]]]]]]]]]]]]]]]]]]]]]]";
    }
    if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0) // 无法打开解码器
    {
        qDebug() << "]]]]]]]]]]]]]]]]]]]]]]]]]";
        return;
    }
    pFrame = new AVFrame();
    pFrameRGB = new AVFrame();
    pFrame = av_frame_alloc();    //创建  存储解码器信息*/
    pFrameRGB = av_frame_alloc();
    static struct SwsContext* ctx_convert_img;
    static uint8_t* out_buffer;
    //解码后的h.264数据转换成RGB32,方便利用Qt显示
    ctx_convert_img = sws_getContext(pCodecCtx->width, pCodecCtx->height,
        pCodecCtx->pix_fmt, pCodecCtx->width, pCodecCtx->height,
        AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);
    numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, 1);
    /*瓜分分配的空间*/
    //瓜分上一步分配到的buffer.
    out_buffer = new uint8_t();
    out_buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));
    av_image_fill_arrays(pFrameRGB->data, pFrameRGB->linesize, out_buffer, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height, 1);

    //int y_size = pCodecCtx->width * pCodecCtx->height;
    packet = (AVPacket*)malloc(sizeof(AVPacket)); //申请一个视频帧包的大小
    //av_new_packet(packet, y_size); //分配packet的数据,为packet分配一个指定大小的内存,设置为packet->stream_index=0
    qDebug() << "---------packet->stream_index---------" << packet->stream_index;
    while (av_read_frame(pFormatCtx, packet) >= 0)
    {
        // 此处代码负责读取每一帧
       // qDebug() << "---------av_read_frame---------" << av_read_frame(pFormatCtx, packet);
        qDebug() << "---------packet->stream_index---------" << packet->stream_index;
        // av_read_frame(pFormatCtx, packet);
        qDebug() << "---------packet->stream_index---------" << packet->stream_index;
        if (packet->stream_index == videoStream)
        {
            ret = avcodec_send_packet(pCodecCtx, packet);                    //发送数据到ffmepg,放到解码队列中
            got_picture = avcodec_receive_frame(pCodecCtx, pFrame);          //将成功的解码队列中取出1个frame
           
            if (ret < 0)
            {
                usleep(1000);
                printf("decode error.\n");
                continue;
            }
            if (!got_picture)
            {
                //颜色空间转换,最后输出到out_buffer
                sws_scale(ctx_convert_img, (uint8_t const* const*)pFrame->data,
                    pFrame->linesize, 0, pCodecCtx->height, pFrameRGB->data,
                    pFrameRGB->linesize);//sws_scale库可以在一个函数里面同时实现:1.图像色彩空间转换;2.分辨率缩放;3.前后图像滤波处理。

                //把这个RGB数据 用QImage加载
                QImage tmpImg((uchar*)out_buffer, pCodecCtx->width, pCodecCtx->height, QImage::Format_RGB32);
                QImage image = tmpImg.copy(); //把图像复制一份 传递给界面显示
                emit sig_GetOneFrame(image);  //发送信号
            }
        }
        av_packet_unref(packet); // 释放packet资源
    }
    // 释放资源
    av_frame_free(&pFrame);
    avcodec_free_context(&pCodecCtx);
    avformat_close_input(&pFormatCtx);
    qDebug() << "=================wdqwdqwd";
}

总结

以上就是Qt+FFmpeg工具对Rtsp协议实时视频流进行解码,并显示在QGraphicsView视图上的介绍。,上方为一个简易的视频播放器,功能截图如下:在这里插入图片描述

  • 9
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
qt是一种跨平台的开发框架,ffmpeg是一套音视频解码的库,rtsp是一种用于传输音视频流的协议。所以如果想要实现多摄像头实时显示的功能,可以通过qt结合ffmpeg来实现。 首先,我们需要使用ffmpeg获取每个摄像头的rtsp流并解码。可以使用ffmpeg提供的函数来打开rtsp流并将其解码成原始的音视频数据。通过设置ffmpeg的参数,我们可以指定使用不同的摄像头,并可以同时从多个摄像头获取音视频数据。 接着,我们可以使用qt的图像显示控件来显示从摄像头解码得到的视频帧。可以使用qt提供的QGraphicsView、QLabel等控件,将视频帧数据转换成qt能够识别的格式并在界面上实时显示。 为了实现多摄像头实时显示,我们可以在qt中使用多线程来同时处理多个摄像头的数据。可以为每个摄像头开启一个线程,用于获取该摄像头的rtsp流并解码。然后将解码得到的视频帧数据通过线程间的通信机制传递给主线程,然后在主线程中更新界面并显示视频帧。 另外,为了提高实时性,我们可以对视频帧进行硬件加速处理,比如使用OpenGL进行渲染,这样可以减少CPU的使用率,提高视频的播放效果。 总结来说,实现qtffmpeg结合实现多摄像头实时显示的功能,主要是通过ffmpeg获取rtsp流并解码,然后通过qt的图像显示控件实时显示解码得到的视频帧,在多线程中同时处理多个摄像头的数据,最终实现多摄像头的实时显示效果。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值