当前位置:   article > 正文

Qt5 + FFmpeg_qt5使用ffmpeg arm64库

qt5使用ffmpeg arm64库

Qt5中通过FFmpeg拉流实现视频播放(简单的在Qt中使用FFmpeg的demo,没有做任何优化)
本地环境:
Qt5.15.2 + MSVC2019_64bit编译器

  1. 新建Qt项目,项目工程结构

image.png
ffmpeg头文件路径:
image.png
ffmpeg库路径:
image.png
exe输出文件夹,需要ffmpeg的dll库加进来:
image.png

  1. 在项目pro文件中添加ffmpeg头文件和库链接:
QT       += core gui concurrent

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

# 输出目录
DESTDIR = $${OUT_PWD}/../output
TARGET = FFmpegDemo

CONFIG += c++17

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

# ffmpeg头文件
INCLUDEPATH += $$PWD/../3rdparty/ffmpeg/include
# ffmpeg库链接
LIBS += -L$$PWD/../3rdparty/ffmpeg/lib -lavformat -lavfilter -lavcodec -lswresample -lswscale -lavutil


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

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  1. 在Mainwindow.ui中加入一个QLabel控件即可。
  2. 在Mainwindow.h中添加ffmpeg头文件
//当前C++兼容C语言
extern "C"
{
//avcodec:编解码(最重要的库)
#include <libavcodec/avcodec.h>
    //avformat:封装格式处理
#include <libavformat/avformat.h>
    //swscale:视频像素数据格式转换
#include <libswscale/swscale.h>
    //avdevice:各种设备的输入输出
#include <libavdevice/avdevice.h>
    //avutil:工具库(大部分库都需要这个库的支持)
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavutil/time.h>
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  1. 在Mainwindow.cpp中调用ffmpeg接口,实现对url地址的拉流播放
void MainWindow::transVideoToImage(const QString &url)
{
    //avdevice_register_all();  	  //硬件设备注册
    avformat_network_init();   	 //网络注册
    //创建封装格式上下文
    AVFormatContext *formatContext = avformat_alloc_context();

    //打开本地文件到封装格式上下文中。
    QString filename = url;
    int avformat_open_ret = avformat_open_input(&formatContext, filename.toLatin1(), NULL, NULL);
    if(avformat_open_ret < 0)
    {
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString(u8"错误信息:%1").arg(result);
    }
    qDebug() << u8"打开多媒体文件成功!";

    //在封装格式上下文中获取音视频流信息
    int avformat_find_ret = avformat_find_stream_info(formatContext, NULL);
    if(avformat_find_ret < 0)
    {
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString(u8"错误信息:%1").arg(result);
    }
    qDebug() << u8"获取音视频流信息成功!";

    //通过遍历封装格式上下文中所有的流信息,找到视频流并保存视频流索引。
    int streamIndex = -1;
    for(unsigned int i = 0; i < formatContext->nb_streams; i++)
    {
        if(formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            streamIndex = i;
            break;
        }
    }
    if(streamIndex == -1)
    {
        qDebug() << u8"找不到相应的流信息!";
    }

    //通过视频流索引,获取解码器上下文。
    AVCodecContext *codecContext = avcodec_alloc_context3(NULL);
    avcodec_parameters_to_context(codecContext , formatContext->streams[streamIndex]->codecpar);

    //通过解码上下文的属性id寻找合适的解码器。
    const AVCodec *codec = avcodec_find_decoder(codecContext->codec_id);
    if(codec == NULL)
    {
        qDebug() << u8"找不到合适的解码器!";
    }

    //通过解码器上下文,打开寻找到的解码器。
    int avcodec_open_ret = avcodec_open2(codecContext,codec,NULL);
    if(avcodec_open_ret < 0)
    {
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString(u8"错误信息:%1").arg(result);
    }
    qDebug() << u8"打开解码器成功!";

    //对接收数据的数据包、数据帧、数据缓冲区容器动态分配内存空间。
    //数据包初始化
    AVPacket *packet = av_packet_alloc();
    //输入视频帧初始化
    AVFrame *frame = av_frame_alloc();
    //输出RGB帧初始化
    AVFrame *frameRGB = av_frame_alloc();
    //给缓冲区动态分配内存
    uint8_t *pOutbuffer = static_cast<unsigned char *>(
        av_malloc(static_cast<size_t>(av_image_get_buffer_size(AV_PIX_FMT_RGB32, codecContext->width, codecContext->height, 1))));
    //初始化缓冲区
    av_image_fill_arrays(frameRGB->data, frameRGB->linesize, pOutbuffer, AV_PIX_FMT_RGB32, codecContext->width, codecContext->height, 1);

    //通过解码器上下文获取图像转换上下文
    SwsContext *sws = sws_getContext(codecContext->width,codecContext->height,codecContext->pix_fmt,
                                     codecContext->width,codecContext->height,AV_PIX_FMT_RGB32,
                                     SWS_BICUBIC,NULL,NULL,NULL);

    //从封装格式上下文读取信息到数据包,再从数据包读取到数据帧,最后将数据转为图像并显示。
    //计算解码帧数
    int frameNum = 0;
    //读取视频帧
    while(av_read_frame(formatContext,packet) >= 0)
    {
        if(packet->stream_index == streamIndex)
        {
            avcodec_send_packet(codecContext,packet);
            int decode_video_ret = avcodec_receive_frame(codecContext,frame);
            if(decode_video_ret >= 0)
            {
                //计算帧数
                frameNum++;
                qDebug() << QString(u8"正在解码播放第%1帧数据").arg(frameNum);
                //视频流数据转换为RGB图像数据
                sws_scale(sws, (const unsigned char* const*)frame->data,frame->linesize, 0,
                          codecContext->height,frameRGB->data,frameRGB->linesize);
                //数据转换为图像
                QImage *tmpImg = new QImage((uchar *)pOutbuffer,codecContext->width,
                                            codecContext->height,QImage::Format_RGB32);
                //加载到标签中显示
                // imgLabel->setPixmap(QPixmap::fromImage(img));
                emit signal_devodeImage(tmpImg);
                av_usleep(10 * 1000);
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  1. 由于在这个函数中时通过while死循环实现视频帧的读取和转化,所以需要再线程中实现该函数,不然UI界面无法加载出来。通过子线程运行该函数。
    QtConcurrent::run(this, &MainWindow::transVideoToImage, urls.at(1));
    connect(this, &MainWindow::signal_devodeImage, this, [=](QImage *img) {
        ui->label->setPixmap(QPixmap::fromImage(*img));
    });
  • 1
  • 2
  • 3
  • 4
  1. 推荐测试url地址:“rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid”(邓紫棋)
  2. 完整代码:

Mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QImage>
#include <QPixmap>
#include <QDebug>
#include <QtConcurrent>

//当前C++兼容C语言
extern "C"
{
//avcodec:编解码(最重要的库)
#include <libavcodec/avcodec.h>
    //avformat:封装格式处理
#include <libavformat/avformat.h>
    //swscale:视频像素数据格式转换
#include <libswscale/swscale.h>
    //avdevice:各种设备的输入输出
#include <libavdevice/avdevice.h>
    //avutil:工具库(大部分库都需要这个库的支持)
#include <libavutil/avutil.h>
#include <libavutil/imgutils.h>
#include <libavutil/time.h>
}

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;

    void transVideoToImage(const QString &url);

signals:
    void signal_devodeImage(QImage *);
};
#endif // MAINWINDOW_H

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50

Mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    QStringList urls;
    // todo 摄像头url
    urls << "rtmp://mobliestream.c3tv.com:554/live/goodtv.sdp"
         << "rtmp://liteavapp.qcloud.com/live/liteavdemoplayerstreamid"
         << "http://vfx.mtime.cn/Video/2021/11/16/mp4/211116131456748178.mp4";

    QtConcurrent::run(this, &MainWindow::transVideoToImage, urls.at(1));

    connect(this, &MainWindow::signal_devodeImage, this, [=](QImage *img) {
        ui->label->setPixmap(QPixmap::fromImage(*img));
    });
}

MainWindow::~MainWindow()
{
    delete ui;
}

void MainWindow::transVideoToImage(const QString &url)
{
    //avdevice_register_all();  	  //硬件设备注册
    avformat_network_init();   	 //网络注册
    //创建封装格式上下文
    AVFormatContext *formatContext = avformat_alloc_context();

    //打开本地文件到封装格式上下文中。
    QString filename = url;
    int avformat_open_ret = avformat_open_input(&formatContext, filename.toLatin1(), NULL, NULL);
    if(avformat_open_ret < 0)
    {
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString(u8"错误信息:%1").arg(result);
    }
    qDebug() << u8"打开多媒体文件成功!";

    //在封装格式上下文中获取音视频流信息
    int avformat_find_ret = avformat_find_stream_info(formatContext, NULL);
    if(avformat_find_ret < 0)
    {
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString(u8"错误信息:%1").arg(result);
    }
    qDebug() << u8"获取音视频流信息成功!";

    //通过遍历封装格式上下文中所有的流信息,找到视频流并保存视频流索引。
    int streamIndex = -1;
    for(unsigned int i = 0; i < formatContext->nb_streams; i++)
    {
        if(formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            streamIndex = i;
            break;
        }
    }
    if(streamIndex == -1)
    {
        qDebug() << u8"找不到相应的流信息!";
    }

    //通过视频流索引,获取解码器上下文。
    AVCodecContext *codecContext = avcodec_alloc_context3(NULL);
    avcodec_parameters_to_context(codecContext , formatContext->streams[streamIndex]->codecpar);

    //通过解码上下文的属性id寻找合适的解码器。
    const AVCodec *codec = avcodec_find_decoder(codecContext->codec_id);
    if(codec == NULL)
    {
        qDebug() << u8"找不到合适的解码器!";
    }

    //通过解码器上下文,打开寻找到的解码器。
    int avcodec_open_ret = avcodec_open2(codecContext,codec,NULL);
    if(avcodec_open_ret < 0)
    {
        char *result = new char[64];
        av_strerror(avformat_open_ret,result,64);
        qDebug() << QString(u8"错误信息:%1").arg(result);
    }
    qDebug() << u8"打开解码器成功!";

    //对接收数据的数据包、数据帧、数据缓冲区容器动态分配内存空间。
    //数据包初始化
    AVPacket *packet = av_packet_alloc();
    //输入视频帧初始化
    AVFrame *frame = av_frame_alloc();
    //输出RGB帧初始化
    AVFrame *frameRGB = av_frame_alloc();
    //给缓冲区动态分配内存
    uint8_t *pOutbuffer = static_cast<unsigned char *>(
        av_malloc(static_cast<size_t>(av_image_get_buffer_size(AV_PIX_FMT_RGB32, codecContext->width, codecContext->height, 1))));
    //初始化缓冲区
    av_image_fill_arrays(frameRGB->data, frameRGB->linesize, pOutbuffer, AV_PIX_FMT_RGB32, codecContext->width, codecContext->height, 1);

    //通过解码器上下文获取图像转换上下文
    SwsContext *sws = sws_getContext(codecContext->width,codecContext->height,codecContext->pix_fmt,
                                     codecContext->width,codecContext->height,AV_PIX_FMT_RGB32,
                                     SWS_BICUBIC,NULL,NULL,NULL);

    //从封装格式上下文读取信息到数据包,再从数据包读取到数据帧,最后将数据转为图像并显示。
    //计算解码帧数
    int frameNum = 0;
    //读取视频帧
    while(av_read_frame(formatContext,packet) >= 0)
    {
        if(packet->stream_index == streamIndex)
        {
            avcodec_send_packet(codecContext,packet);
            int decode_video_ret = avcodec_receive_frame(codecContext,frame);
            if(decode_video_ret >= 0)
            {
                //计算帧数
                frameNum++;
                qDebug() << QString(u8"正在解码播放第%1帧数据").arg(frameNum);
                //视频流数据转换为RGB图像数据
                sws_scale(sws, (const unsigned char* const*)frame->data,frame->linesize, 0,
                          codecContext->height,frameRGB->data,frameRGB->linesize);
                //数据转换为图像
                QImage *tmpImg = new QImage((uchar *)pOutbuffer,codecContext->width,
                                            codecContext->height,QImage::Format_RGB32);
                //加载到标签中显示
                // imgLabel->setPixmap(QPixmap::fromImage(img));
                emit signal_devodeImage(tmpImg);
                av_usleep(10 * 1000);
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139

Mainwindow.ui
image.png

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/758461
推荐阅读
相关标签
  

闽ICP备14008679号