使用Qt+FFmpeg接收rtsp视频流进行显示,并实时解出SEI信息

 FFmpegPlayer.h

#pragma once

#include <QObject>

class QMutex;
template<typename> class QFutureWatcher;

class FFmpegPlayer : public QObject
{
    Q_OBJECT

public:
    FFmpegPlayer(QObject *parent = nullptr);
    ~FFmpegPlayer();

    inline void setUrl(const QString &url) { murl = url; }
    
    void start();
    void stop();
    bool isRunning() const;
    void waitForFinished();
    
signals:
    void imageChanged(const QImage &img);
    
protected:
    void run();

private:
    bool isStoped() const;
    
private:
    QString murl;
    QMutex *mmutex;
    bool mstoped;
    QFutureWatcher<void> *mwatcher;
};

FFmpegPlayer.cpp

extern "C" {
#include "libavcodec/avcodec.h"
#include "libavdevice/avdevice.h"
#include "libavformat/avformat.h"
#include "libswscale/swscale.h"
#include "libavutil/time.h"
#include "libavutil/imgutils.h"
}

#include <QMutex>
#include <QMutexLocker>
#include <QImage>
#include <QtConcurrent/QtConcurrent>

#include "FFmpegPlayer.h"

static void avError(const QString &what, int errNum)
{
    char text[1024];
    av_strerror(errNum, text, sizeof(text));
    fprintf(stdout, "%s: %s\n", what.toLatin1().constData(), text);
}

class FFmpegIniter
{
public:
    FFmpegIniter() {
        avdevice_register_all();
        avformat_network_init();
    }
};


FFmpegPlayer::FFmpegPlayer(QObject *parent)
    : QObject(parent),
    mstoped(false),
    mmutex(new QMutex),
    mwatcher(new QFutureWatcher<void>(this))
{
}

FFmpegPlayer::~FFmpegPlayer()
{
    if (mwatcher->isRunning())
    {
        stop();
        mwatcher->waitForFinished();
    }
    delete mmutex;
}

void FFmpegPlayer::start()
{
    if (mwatcher->isRunning())
    {
        stop();
        mwatcher->waitForFinished();
    }
    mstoped = false;
    auto future = QtConcurrent::run(this, &FFmpegPlayer::run);
    mwatcher->setFuture(future);
}

void FFmpegPlayer::stop()
{
    QMutexLocker locker(mmutex);
    mstoped = true;
}

bool FFmpegPlayer::isRunning() const
{
    return mwatcher->isRunning();
}

void FFmpegPlayer::waitForFinished()
{
    mwatcher->waitForFinished();
}

bool FFmpegPlayer::isStoped() const
{
    auto _this = const_cast<FFmpegPlayer *>(this);
    QMutexLocker locker(_this->mmutex);
    return mstoped;
}

void FFmpegPlayer::run()
{
    static FFmpegIniter initer;

    // 创建输入目标
    AVFormatContext *srcContext = nullptr;
    int code = avformat_open_input(&srcContext, murl.toUtf8(), nullptr, nullptr);
    if (code != 0)
        return avError("avformat_open_input", code);

    code = avformat_find_stream_info(srcContext, nullptr);
    if (code < 0)
        return avError("avformat_find_stream_info", code);

    int srcVideoStreamIndex = -1;
    for (uint i = 0; i < srcContext->nb_streams; ++i)
    {
        if (srcContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            srcVideoStreamIndex = i;
            break;
        }
    }
    if (srcVideoStreamIndex == -1)
    {
        qInfo("error find srcVideoStreamIndex!");
        return;
    }

    AVStream *srcStream = srcContext->streams[srcVideoStreamIndex];
    const AVCodec *srcCodec = avcodec_find_decoder(srcStream->codecpar->codec_id);
    AVCodecContext *srcCodecContext = avcodec_alloc_context3(srcCodec); 
    code = avcodec_parameters_to_context(srcCodecContext, srcStream->codecpar);
    if (code < 0)
        return avError("copy srcCodecContext", code);

    code = avcodec_open2(srcCodecContext, srcCodec, nullptr);
    if (code < 0)
        return avError("open srcCodecContext", code);
    
    av_dump_format(srcContext, 0, srcContext->url, 0);

    AVPixelFormat desFormat = AV_PIX_FMT_RGB24;
    
    SwsContext *swscontext = sws_getContext(srcCodecContext->width, srcCodecContext->height, srcCodecContext->pix_fmt,
        srcCodecContext->width, srcCodecContext->height, desFormat,
        SWS_BICUBIC, nullptr, nullptr, nullptr);
    
    AVPacket *srcPkt = av_packet_alloc();
    AVFrame *srcFrame = av_frame_alloc();
    
    AVFrame *desFrame = av_frame_alloc();
    desFrame->width = srcCodecContext->width;
    desFrame->height = srcCodecContext->height;
    desFrame->format = desFormat;
    desFrame->linesize[0] = desFrame->width;
    desFrame->linesize[1] = desFrame->width;
    desFrame->linesize[2] = desFrame->width;
    
    int bufferSize = av_image_get_buffer_size(desFormat, srcCodecContext->width, srcCodecContext->height, 1);
    uint8_t *buffer = (uint8_t *)av_malloc(bufferSize);
    av_image_fill_arrays(desFrame->data, desFrame->linesize, buffer,
        desFormat, desFrame->width, desFrame->height, 1);
    
    while (!isStoped())
    {
        // 解码
        code = av_read_frame(srcContext, srcPkt);
        if (code < 0)
        {
            avError("av_read_frame", code);
            break;
        }

        if (srcPkt->stream_index != srcVideoStreamIndex)
            continue;

        code = avcodec_send_packet(srcCodecContext, srcPkt);
        if(code < 0)
        {
            avError("avcodec_send_packet", code);
            continue;
        }

        code = avcodec_receive_frame(srcCodecContext, srcFrame);
        if (code < 0)
        {
            avError("avcodec_receive_frame", code);
            continue;
        }

        AVFrameSideData *sideData = av_frame_get_side_data(srcFrame, AV_FRAME_DATA_SEI_UNREGISTERED);
        if (sideData)
            fprintf(stdout, "side data %d: %s\n", sideData->type, sideData->data + 16);
        
        sws_scale(swscontext, (const unsigned char *const *)srcFrame->data, srcFrame->linesize, 0, srcFrame->height,
            desFrame->data, desFrame->linesize);

        QImage image(buffer, desFrame->width, desFrame->height, QImage::Format_RGB888);
        emit imageChanged(image.copy());
        
        av_packet_unref(srcPkt);
    }
    av_free(buffer);
    av_packet_free(&srcPkt);
    av_frame_free(&srcFrame);
    av_frame_free(&desFrame);
    avcodec_close(srcCodecContext);
    avformat_close_input(&srcContext);
}

接收rtsp视频流,并将解出的帧进行格式转换,转换为QImage所支持的格式。例如AV_PIX_FMT_RGB24,对应于QImage的QImage::Format_RGB888格式。

采用以下方法读取自定义的SEI信息:

 AVFrameSideData *sideData = av_frame_get_side_data(srcFrame, AV_FRAME_DATA_SEI_UNREGISTERED);
 if (sideData)
     fprintf(stdout, "side data %d: %s\n", sideData->type, sideData->data + 16);

side data 需要向后偏移 16 字节,前16字节为UUID信息。

使用:

auto player = new FFmpegPlayer(this);
player->setUrl(url);
player->start();

connect(player, &FFmpegPlayer::imageChanged, this, [=](const QImage &img) {
	viewport->setImage(img);
});

viewport 负责绘制生成的 QImage

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值