qt+ffmpeg实现通过rtsp调用网络摄像机的监控画面

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11

# The following define makes your compiler emit warnings if you use
# any Qt feature that has been marked deprecated (the exact warnings
# depend on your compiler). Please consult the documentation of the
# deprecated API in order to know how to port your code away from it.
DEFINES += QT_DEPRECATED_WARNINGS

# You can also make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
# You can also select to disable deprecated APIs only up to a certain version of Qt.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    widget.cpp

HEADERS += \
    widget.h

FORMS += \
    widget.ui
INCLUDEPATH += $$PWD/include

LIBS += $$PWD/lib/avcodec.lib  \
        $$PWD/lib/avdevice.lib  \
        $$PWD/lib/avfilter.lib  \
        $$PWD/lib/avformat.lib  \
        $$PWD/lib/avutil.lib  \
        $$PWD/lib/postproc.lib  \
        $$PWD/lib/swresample.lib  \
        $$PWD/lib/swscale.lib

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
  •  widget.h
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QLabel>
#include <QTimer>
#include <iostream>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
}
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
/*
*   在类实例化后(构造函数执行时),这些成员变量会被初始化,
*   然后定时器启动后会定期调用readFrameAndUpdate槽函数来处理视频帧读取和显示更新的工作。
*/
class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

private:
   // Ui::Widget *ui;
    //QString username;  //存储用户名
    QLabel *videolable; // 用于显示视频画面的QLabel控件
    AVFormatContext *formatCtx;  // FFmpeg格式上下文,用于处理输入的视频流格式相关操作
    AVCodecContext *codecCtx;    // FFmpeg编解码器上下文,用于编解码相关操作
    AVFrame *frame, *rgbFrame;   // AVFrame用于存储原始视频帧和转换后的RGB格式视频帧
    AVPacket packet;             // AVPacket用于存储从输入流读取的数据包
    struct SwsContext *swsCtx;  // SwsContext用于图像格式转换,比如将原始视频格式转换为RGB用于在Qt中显示
    int videoStreamIndex;        // 视频流在输入流中的索引
    QTimer *timer;               // 定时器,用于定期读取和更新视频帧

private slots:
    void readFrameAndUpdate();  // 槽函数,用于读取视频帧并更新界面显示

};
#endif // WIDGET_H
  • main.cpp
/*
*  先创建QApplication实例来初始化 Qt 应用程序框架,接着创建Widget类的对象,
*  随后调用show方法将窗口显示出来,最后调用exec方法启动事件循环,让程序可以持续运行并响应各种事件。
*/
#include <QApplication>
#include "widget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    Widget w;


            w.show();  // 显示监控界面






    return a.exec();
}
  • widget.cpp
  • #include "widget.h"
    #include <QMessageBox>
    
    Widget::Widget(QWidget *parent)
        : QWidget(parent)
        //, ui(new Ui::Widget)
    {
        //ui->setupUi(this);
        videolable = new QLabel(this);
        videolable->setGeometry(0, 0, 1024, 1024);  // 设置显示区域大小,可根据实际情况调整
        // 初始化FFmpeg库
        avformat_network_init();  //网络初始化
        // 打开RTSP流
        int ret = avformat_open_input(&formatCtx, "rtsp://192.168.43.2:554/stream0", nullptr, nullptr);
        if (ret!= 0) {
            char errbuf[AV_ERROR_MAX_STRING_SIZE];
            av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
            QMessageBox::critical(nullptr, "Error", QString("Failed to open RTSP input stream. Error: %1").arg(errbuf));
            return ;
         }
        // 查找流信息:读取媒体文件的头部信息,以获取流的相关参数和元数据
        ret = avformat_find_stream_info(formatCtx, nullptr);
        if (ret < 0) {
            char errbuf[AV_ERROR_MAX_STRING_SIZE];
            av_strerror(ret, errbuf, AV_ERROR_MAX_STRING_SIZE);
            std::cerr << "Failed to find stream information. Error: " << errbuf << std::endl;
            return ;
        }
        // 查找视频流索引
        videoStreamIndex = -1;
        for (unsigned int i = 0; i < formatCtx->nb_streams; i++) {
            if (formatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
                videoStreamIndex = i;
                break;
            }
        }
        if (videoStreamIndex == -1) {
             // 如果没找到视频流,进行错误处理
             avformat_close_input(&formatCtx);
             return;
        }
        // 获取视频流的编解码器上下文
        const AVCodec *codec = avcodec_find_decoder(formatCtx->streams[videoStreamIndex]->codecpar->codec_id);
        codecCtx = avcodec_alloc_context3(codec);
        avcodec_parameters_to_context(codecCtx, formatCtx->streams[videoStreamIndex]->codecpar);
        if (avcodec_open2(codecCtx, codec, nullptr) < 0) {
             // 如果打开编解码器失败,进行错误处理
             avcodec_free_context(&codecCtx);
             avformat_close_input(&formatCtx);
             return;
        }
        // 分配帧内存空间
        frame = av_frame_alloc();
        rgbFrame = av_frame_alloc();
        if (!frame ||!rgbFrame) {
            // 如果帧内存分配失败,进行错误处理
            // 如果帧内存分配失败,释放之前已分配的其他相关资源
                if (codecCtx) {
                    avcodec_free_context(&codecCtx);
                }
                if (formatCtx) {
                    avformat_close_input(&formatCtx);
                }
            return;
        }
        // 设置图像转换参数,将原始视频格式转换为RGB格式
        int numBytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, codecCtx->width, codecCtx->height, 1);
        uint8_t *buffer = (uint8_t *)av_malloc(numBytes * sizeof(uint8_t));
        av_image_fill_arrays(rgbFrame->data, rgbFrame->linesize, buffer, AV_PIX_FMT_RGB24, codecCtx->width, codecCtx->height, 1);
        swsCtx = sws_getContext(codecCtx->width, codecCtx->height, codecCtx->pix_fmt,
                                    codecCtx->width, codecCtx->height, AV_PIX_FMT_RGB24,
                                    SWS_BICUBIC, nullptr, nullptr, nullptr);
    
        //创建定时器用于定时更新视频帧
        timer = new QTimer(this);
        connect(timer, &QTimer::timeout, this, &Widget::readFrameAndUpdate);
        timer->start(30);  // 每30毫秒触发一次,可根据实际情况调整帧率
    
    
    
    }
    
    Widget::~Widget()
    {
        // 释放相关资源
        av_frame_free(&frame);
        av_frame_free(&rgbFrame);
        avcodec_free_context(&codecCtx);
        avformat_close_input(&formatCtx);
        sws_freeContext(swsCtx);
        delete videolable;
        delete timer;
    }
    
    void Widget::readFrameAndUpdate()
    {
        if (av_read_frame(formatCtx, &packet) >= 0) {
            if (packet.stream_index == videoStreamIndex) {
                int response = avcodec_send_packet(codecCtx, &packet);
                if (response >= 0) {
                    while (avcodec_receive_frame(codecCtx, frame) == 0) {
                        // 转换图像格式为RGB
                        sws_scale(swsCtx, (const uint8_t *const *)frame->data, frame->linesize, 0, codecCtx->height,
                                  rgbFrame->data, rgbFrame->linesize);
                        // 将RGB图像数据转换为QImage用于在Qt中显示
                        QImage img((uchar *)rgbFrame->data[0], codecCtx->width, codecCtx->height, QImage::Format_RGB888);
                        if (!img.isNull()) {  // 添加判断,确保QImage对象创建成功
                            // 在QLabel上显示图像,添加边界条件判断确保缩放后的图像能正确设置为Pixmap
                            QPixmap pixmap = QPixmap::fromImage(img.scaled(videolable->width(), videolable->height(),
                                                                           Qt::KeepAspectRatio));
                            if (!pixmap.isNull()) {  // 判断QPixmap对象是否有效
                                videolable->setPixmap(pixmap);
                            } else {
                                std::cerr << "Failed to create valid QPixmap from scaled QImage." << std::endl;
                            }
                        } else {
                            std::cerr << "Failed to create valid QImage from RGB frame data." << std::endl;
                        }
                    }
                }
            }
            av_packet_unref(&packet);
        }
    }
    

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值