QT 使用ffmpeg 学习4播放RTSP

Posted 编程圈子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了QT 使用ffmpeg 学习4播放RTSP相关的知识,希望对你有一定的参考价值。

一、Ffmpeg 播放rtsp流程

二、实现

1. ffmpegutils类

ffmpegutils.h

#ifndef MYFFMPEG_H
#define MYFFMPEG_H

#include <QObject>
#include <QImage>

#define __STDC_CONSTANT_MACORS
extern "C"

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavfilter/avfilter.h>
#include <libswscale/swscale.h>
#include <libavutil/frame.h>



class FFmpegUtils : public QObject

    Q_OBJECT
public:
    explicit FFmpegUtils(QObject *parent = 0);
    ~FFmpegUtils();

    void MyFFmpegSetUrl(QString rtspUrl);
    void MyFFmpegInfo();

    int MyFFmpegInit();
    void MyFFmpegDestroy();
    int MyFFmpepReadFrame();
signals:
    void MyFFmpegSigGetOneFrame(QImage img);

public slots:

private:
    int              m_videoIndex;
    QString          m_rtspUrl;
    AVPicture        m_AVPicture;
    AVCodec         *m_AVCodec;
    AVFormatContext *m_AVFormatContext;
    AVCodecContext  *m_AVCodecContext;
    AVFrame         *m_AVFrame;
    AVFrame         *m_AVFrameRGB;
    AVPacket        *m_AVPacket;
    SwsContext      *m_SwsContext;
    uint8_t         *m_OutBuffer;
    AVPixelFormat ConvertDeprecatedFormat(enum AVPixelFormat format);
;

#endif // MYFFMPEG_H

ffmpegutils.cpp

#include "ffmpegutils.h"

FFmpegUtils::FFmpegUtils(QObject *parent) : QObject(parent)




FFmpegUtils::~FFmpegUtils()

    MyFFmpegDestroy();


void FFmpegUtils::MyFFmpegInfo()

    qDebug("------------------------------------------------------------------");
    qDebug("%s", avcodec_configuration());
    qDebug("version: %d", avcodec_version());
    qDebug("------------------------------------------------------------------");


void FFmpegUtils::MyFFmpegSetUrl(QString rtspUrl)

    m_rtspUrl = rtspUrl;


void FFmpegUtils::MyFFmpegDestroy()

    av_free(m_OutBuffer);
    av_free(m_AVFrameRGB);
    av_frame_free(&m_AVFrame);
    av_frame_free(&m_AVFrameRGB);
    sws_freeContext(m_SwsContext);
    av_free_packet(m_AVPacket);
    free(m_AVPacket);
    avcodec_close(m_AVCodecContext);
    avformat_close_input(&m_AVFormatContext);
    avformat_free_context(m_AVFormatContext);
    avformat_network_deinit();


int FFmpegUtils::MyFFmpegInit()

    int i;
    int ret = -1;

    // 获取视频播放URL
    QByteArray byteRtspUrl =m_rtspUrl.toLocal8Bit();
    char *pRtspUrl = byteRtspUrl.data();

    // 初始化所有组件,调用该函数后,才能使用复用器和编解码器
    av_register_all();

    // 初始化网络库
    avformat_network_init();

    // 分配AVFormatContext,它是FFMPEG解封装(flv,mp4,rmvb,avi)功能的结构体,
    // 具体可参考https://blog.csdn.net/leixiaohua1020/article/details/14214705
    m_AVFormatContext = avformat_alloc_context();


    // 设置参数
    AVDictionary *options = NULL;
    // 设置传输协议为TCP协议
    av_dict_set(&options, "rtsp_transport", "tcp", 0);

    // 设置TCP连接最大延时时间
    av_dict_set(&options, "max_delay", "100", 0);

    // 设置“buffer_size”缓存容量
    av_dict_set(&options, "buffer_size", "1024000", 0);

    // 设置avformat_open_input超时时间为3秒
    av_dict_set(&options, "stimeout", "3000000", 0);

    // 打开网络流或文件流
    ret = avformat_open_input(&m_AVFormatContext, pRtspUrl, NULL, &options);
    if (ret != 0)
    
        qDebug("Couldn't open input stream, ret=%d\\n", ret);
        return -1;
    

    // 读取流数据包并获取流的相关信息
    if (avformat_find_stream_info(m_AVFormatContext, NULL) < 0)
    
        qDebug("Couldn't find stream information.\\n");
        return -1;
    

    // 确定流格式是否为视频
    for (i = 0; i < m_AVFormatContext->nb_streams; i++)
    
        if (m_AVFormatContext->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        
            m_videoIndex = i;
            break;
        
    

    if (m_videoIndex == -1)
    
        qDebug("Didn't find a video stream.\\n");
        return -1;
    

    m_AVCodecContext = m_AVFormatContext->streams[m_videoIndex]->codec;

    // 根据编码器的ID号查找对应的解码器
    m_AVCodec = avcodec_find_decoder(m_AVCodecContext->codec_id);
    if (NULL == m_AVCodec)
    
        qDebug("avcodec_find_decoder AV_CODEC_ID_H264 fail!\\n");
        return -1;
    

    // 配置编码器上下文的参数
    m_AVCodecContext->bit_rate = 0;         //码率
    m_AVCodecContext->time_base.den = 25;   // 下面2行设置帧率,每秒/25帧
    m_AVCodecContext->time_base.num = 1;
    m_AVCodecContext->frame_number = 1;     //每包一个视频帧

    // Initialize the AVCodecContext to use the given AVCodec
    if (avcodec_open2(m_AVCodecContext, m_AVCodec, NULL) < 0)
    
        qDebug("avcodec_open2 fail");
        return -1;
    

    // alloc AVFrame
    m_AVFrame = av_frame_alloc();
    m_AVFrameRGB = av_frame_alloc();

    // 图像色彩空间转换、分辨率缩放、前后图像滤波处理
    m_SwsContext = sws_getContext(m_AVCodecContext->width, m_AVCodecContext->height,
            m_AVCodecContext->pix_fmt, m_AVCodecContext->width, m_AVCodecContext->height,
            AV_PIX_FMT_RGB32, SWS_BICUBIC, NULL, NULL, NULL);

    int bytes = avpicture_get_size(AV_PIX_FMT_RGB32, m_AVCodecContext->width, m_AVCodecContext->height);
    m_OutBuffer = (uint8_t *)av_malloc(bytes * sizeof(uint8_t));

    // 将分配的内存空间给m_AVFrameRGB使用
    avpicture_fill((AVPicture *)m_AVFrameRGB, m_OutBuffer, AV_PIX_FMT_RGB32, m_AVCodecContext->width, m_AVCodecContext->height);

    // 为AVPacket分别内存空间
    int packSize = m_AVCodecContext->width * m_AVCodecContext->height;
    m_AVPacket = (AVPacket *)malloc(sizeof(AVPacket));
    av_new_packet(m_AVPacket, packSize);

    qDebug("============== MyFFmpegInit ok! ====================== ");

    return 0;


/**
 * @brief MyFFmpeg::MyFFmpepReadFrame 读取一帧数据
 * @return
 */
int FFmpegUtils::MyFFmpepReadFrame()

    int ret = -1;
    int getPicture = 0;

    // 获取下一帧数据
    ret = av_read_frame(m_AVFormatContext, m_AVPacket);
    if (ret < 0)
    
        qDebug("av_read_frame fail!\\n");
        return -1;
    

    if (m_AVPacket->stream_index != m_videoIndex)
    
        av_free_packet(m_AVPacket);
        return 0;
    


    //  解码m_AVPacket,Decode the video frame of size avpkt->size from avpkt->data into picture
    ret = avcodec_decode_video2(m_AVCodecContext, m_AVFrame, &getPicture, m_AVPacket);
    if (ret < 0)
    
        qDebug("avcodec_decode_video2 fail!\\n");
        av_free_packet(m_AVPacket);
        return 0;
    

    // got_picture_ptr Zero if no frame could be decompressed, otherwise, it is nonzero.
    // 判断是否已有视频帧被解码了
    if (getPicture)
    
        qDebug("# get frame: width=%d,height=%d,format=%d,key_frame=%d", m_AVFrame->width,
               m_AVFrame->height, m_AVFrame->format, m_AVFrame->key_frame);

        // 对解码视频帧进行缩放、格式转换等操作
        sws_scale(m_SwsContext, (uint8_t const * const *)m_AVFrame->data,
                 m_AVFrame->linesize, 0, m_AVCodecContext->height,
                 m_AVFrameRGB->data, m_AVFrameRGB->linesize);

        // 转换到QImage
        QImage tmmImage((uchar *)m_OutBuffer, m_AVCodecContext->width, m_AVCodecContext->height, QImage::Format_RGB32);
        QImage image = tmmImage.copy();

        // 发送QImage
        emit MyFFmpegSigGetOneFrame(image);
    else
        qDebug("# get picture false");
    

    // 释放资源
    av_free_packet(m_AVPacket);

    return 0;


2. rtsp_player类

rtsp_player.h

#ifndef RTSP_PLAYER_H
#define RTSP_PLAYER_H

#include "ffmpegutils.h"
#include <QObject>


class RTSP_Player : public QObject

    Q_OBJECT
public:
    enum PlayerState 
        FFmpegInitFail = 0,
        FFmpegInitSucc,
        FFmpegStoped
    ;

    explicit RTSP_Player(FFmpegUtils *ffmpeg = nullptr, QObject *parent = nullptr);
    void SetPlayerUrl(QString playerUrl);

signals:
    void SigOpenUrlResult(int result);

public slots:
    void PlayerStart();
    void PlayerStop();

private:
    volatile bool   m_stopped;				// 停止播放标识,为true时停止播放,退出播放循环
    QString         m_playerUrl;			// 播放url
    FFmpegUtils        *m_ffmpeg;				// MyFFmepg指针
;

#endif // RTSP_PLAYER_H

rtsp_player.cpp

#include "RTSP_Player.h"
#include <QThread>

RTSP_Player::RTSP_Player(FFmpegUtils *ffmpeg, QObject *parent)
    : QObject(parent)
    , m_ffmpeg(ffmpeg)




void RTSP_Player::SetPlayerUrl(QString playerUrl)

    this->m_playerUrl = playerUrl;


void RTSP_Player::PlayerStart()

    qDebug("player start");
    if (m_ffmpeg == NULL)
        emit SigOpenUrlResult(RTSP_Player::FFmpegInitFail);
        return;
    

    m_ffmpeg->MyFFmpegSetUrl(this->m_playerUrl);
    if (m_ffmpeg->MyFFmpegInit() != 0) 
        emit SigOpenUrlResult(RTSP_Player::FFmpegInitFail);
        return;
    

    emit SigOpenUrlResult(RTSP_Player::FFmpegInitSucc);

    m_stopped = false;

    while (1) 
        if (m_stopped)
            qDebug("--------------- stop ----------------");
            break;
        

        if (!m_ffmpeg) 
            break;
        

        if (m_ffmpeg->MyFFmpepReadFrame() < 0) 
            qDebug("--------------- get frame fail, stop -----------");
            break;
        

        QThread::msleep(0.02);
    

    m_ffmpeg->MyFFmpegDestroy();
    emit SigOpenUrlResult(RTSP_Player::FFmpegStoped);
    qDebug("---------------- quit -----------------");

    return;


void RTSP_Player::PlayerStop()

    m_stopped = true;


mainwindow主类

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include "ffmpegutils.h"
#include "rtsp_player.h"
#include <QMainWindow>

#include <QThread>

namespace Ui 
class MainWindow;


class MainWindow : public QMainWindow

    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();
protected:
    void paintEvent(QPaintEvent *event);
private:
    Ui::MainWindow *ui;
    QImage m_image;
    FFmpegUtils *m_ffmpeg=nullptr;
    void MyFFmpegTest();
    void PlayStart();
    QThread *m_playThread = nullptr;
    RTSP_Player *m_player=nullptr;
    void PlayStop();

private slots:
    void SlotGetOneFrame(QImage img);

    void on_pushButton_clicked();
    void on_pushButton_2_clicked();

signals:
    void SigPlayStart();



;

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <qpainter.h>

extern "C"

#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavdevice/avdevice.h>
#include <libavformat/version.h>
#include <libavutil/time.h>
#include <libavutil/mathematics.h>


MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)

    ui->setupUi(this);

    m_ffmpeg = new FFmpegUtils;
    connect(m_ffmpeg, SIGNAL(MyFFmpegSigGetOneFrame(QImage)), this, SLOT(SlotGetOneFrame(QImage)));



MainWindow::~MainWindow()

    delete ui;


void MainWindow::PlayStart()
    this->PlayStop();
    qDebug("begin");
    m_playThread = new QThread();
    m_player = new RTSP_Player(m_ffmpeg);
    m_player->SetPlayerUrl("rtsp地址");

    connect(this,SIGNAL(SigPlayStart()),m_player, SLOT(PlayerStart()));

    m_player->moveToThread(m_playThread);
    m_playThread->start();


void MainWindow::SlotGetOneFrame(QImage img)
    m_image = img;
    this->update();


void MainWindow::paintEvent(QPaintEvent *event)

    QPainter painter(this);

    if (m_image.size().width() <= 0)
        return;

    QImage img = m_image.scaled(ui->labVideo->size(), Qt::IgnoreAspectRatio, Qt::SmoothTransformation);

    int x = ui->labVideo->geometry().x();
    int y = ui->labVideo->geometry().y();
    painter.drawImage(QPoint(x, y), img);

void MainWindow::PlayStop()

    if (m_player)
    
        m_player->PlayerStop();
    

    if (m_playThread)
    
        m_playThread->quit();
        m_playThread->wait(1000);
        delete m_playThread;
        m_playThread = nullptr;
    

    if (m_player)
    
        delete m_player;
        m_player = nullptr;
    


void MainWindow::on_pushButton_clicked()

      this->PlayStart();
      emit SigPlayStart();

void MainWindow::on_pushButton_2_clicked()

    this->PlayStop();



代码参考:
https://blog.csdn.net/eastcnme/article/details/94850984

以上是关于QT 使用ffmpeg 学习4播放RTSP的主要内容,如果未能解决你的问题,请参考以下文章

QT软件开发: 基于FFMPGE设计的流媒体播放器(rtmp/rtsp)

Qt编写的RTSP播放器+视频监控(android版本)

基于FFmpeg+rtsp读取摄像头实时图像

QT Web引擎支持rtsp流吗

vue中播放rtsp流

基于Qt和ffmpeg的抓屏rtsp服务