Qt 集成 FFmpeg 实现颜色格式转换

news2024/9/21 0:31:38

目录

1. Qt 集成 FFmpeg

1.1 下载 FFmpeg

1.2 Qt 集成 FFmpeg

1.2.1 修改 .pro 文件

1.2.2 放入 dll 文件

1.2.3 代码中使用 FFmpeg

2. 图像格式转换

3. 预览

4. 项目地址


项目需要,写个小工具来实现图像颜色格式的转换,主要的 Feature 如下:

  1. 支持输入输出图像格式选择:
  2. 支持对输入图像进行缩放;
  3. 支持对输入输出图像预览;

支持的图像格式转换如下:

  •  YUV420P
  •  NV12
  •  NV21
  •  YUV422P
  •  NV16
  •  YUYV422
  •  UYVY422
  •  YVYU422
  •  RGB24
  •  BGR24
  •  ARGB
  •  RGBA
  •  RGB565BE
  •  RGB565LE
  •  RGB444BE
  •  RGB444LE

由于输入输出为纯图片文件,所以需要指定图像的宽高信息;

 

1. Qt 集成 FFmpeg

Qt 中集成 FFmpeg 的文章非常多,大家可以自行搜索,这里简单的提一下集成的过程;

Windows 上,集成 FFmpeg 的方式有 2 种:

1. 下载 FFmpeg 的库,代码中引用头文件来使用 FFmpeg;

2. 下载 FFmpeg 源码,自行构建;

这里选择第一种,下载 FFmpeg 的 DLL 来集成;

1.1 下载 FFmpeg

下载地址:Download FFmpeg

 选择 Windows,下面有 2 个 build,随便选一个,我选择的是第一个;

进入后,最先看到的是 master 的 build,咱们选个 Release 的稳定版的,shared 代表是动态链接库,我们选他:

 解压后如下:

 我们要 3 部分,头文件、动态库、和导入库:

动态库在:ffmpeg-5.1.2-full_build-shared\bin

头文件在:ffmpeg-5.1.2-full_build-shared\include

导入库在:ffmpeg-5.1.2-full_build-shared\include\lib

1.2 Qt 集成 FFmpeg

1.2.1 修改 .pro 文件

在 Qt 的工程文件中,指定 FFmpeg 导入的头文件以及库的地方

#-------------------------------------------------
#
# Project created by QtCreator 2022-10-16T17:35:35
#
#-------------------------------------------------

QT       += core gui
RC_ICONS = logo.ico

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = YUVEyes
TEMPLATE = app

# The following define makes your compiler emit warnings if you use
# any feature of Qt which has been marked as 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 you use 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

CONFIG += c++11

SOURCES += \
        main.cpp \
        mainwindow.cpp \
    qtffmpegutils.cpp

HEADERS += \
        mainwindow.h \
    qtffmpegutils.h \
    error_code.h

FORMS += \
        mainwindow.ui

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

win32: {
    FFMPEG_HOME=G:\MediaCodeC\ffmpeg-5.1.2-full_build-shared
    #设置 ffmpeg 的头文件
    INCLUDEPATH += $$FFMPEG_HOME/include

    #设置导入库的目录一边程序可以找到导入库
    # -L :指定导入库的目录
    # -l :指定要导入的 库名称
    LIBS +=  -L$$FFMPEG_HOME/lib \
             -lavcodec          \
             -lavdevice         \
             -lavfilter         \
             -lavformat         \
             -lavutil           \
             -lpostproc         \
             -lswresample       \
             -lswscale
}

RESOURCES += \
    src.qrc

1.2.2 放入 dll 文件

Qt 编译的时候,会生成一个编译的文件夹,将我们用到的 dll 扔进去:

 好了,基本环境准备完毕;

1.2.3 代码中使用 FFmpeg

在代码中,包含 FFmpeg 的头文件即可使用 FFmpeg 的库了:

 打印 FFmpeg 的 AVCodec 版本号:

 

 

2. 图像格式转换

图像格式转换可以使用 FFmpeg 的 sws_scale 函数进行,该函数的原型为:

能够支持不同 AVPixelFormat 的像素格式转换,并且能够进行缩放功能;

这里定义一个专门使用的类 qtffmpegutils.h,来进行转换工作:

#ifndef QTFFMPEGUTILS_H
#define QTFFMPEGUTILS_H

extern "C"{
#include "libswscale/swscale.h"
#include "libavutil/opt.h"
#include "libavutil/imgutils.h"
}

#include "error_code.h"

typedef struct {
    const char *filename;
    int width;
    int height;
    AVPixelFormat format;
} RawVideoFile;

typedef struct {
    char *pixels;
    int width;
    int height;
    AVPixelFormat format;
} RawVideoFrame;

class QtFFmpegUtils
{
public:
    QtFFmpegUtils();
    static int convertRawVideo(RawVideoFile &in, RawVideoFile &out);
    static int convertToRGB888(RawVideoFile &in, uint8_t *rgb888_data);
    static int getBufferSize(AVPixelFormat pix_fmt, int width, int height);
    //static void convertRawFrame(RawVideoFrame &in, RawVideoFrame &out);
};

#endif // QTFFMPEGUTILS_H

在 qtffmpegutils.cpp 中调用了 FFmpeg 的 sws_scale 逻辑:

#include "qtffmpegutils.h"
#include <QFile>
#include <QDebug>

QtFFmpegUtils::QtFFmpegUtils()
{

}

int QtFFmpegUtils::convertRawVideo(RawVideoFile &in, RawVideoFile &out)
{
    int ret = APP_SUCCESS;
    SwsContext *ctx = nullptr;
    uint8_t *inData[4], *outData[4];
    int inStrides[4], outStrides[4];
    int inFrameSize, outFrameSize;

    QFile inFile(in.filename);
    QFile outFile(out.filename);

    ret = av_image_alloc(inData, inStrides, in.width, in.height, in.format, 1);
    if(ret < 0){
        char errbuf[1024];
        av_strerror(ret,errbuf,sizeof (errbuf));
        qDebug() << "av_image_alloc inData error:" << errbuf;
        ret = APP_FFMPEG_ALLOC_FAILED;
        goto alloc_failed_1;
    }

    ret = av_image_alloc(outData, outStrides, out.width, out.height, out.format, 1);
    if (ret < 0) {
        char errbuf[1024];
        av_strerror(ret, errbuf, sizeof (errbuf));
        qDebug() << "av_image_alloc outData error:" << errbuf;
        ret = APP_FFMPEG_ALLOC_FAILED;
        goto alloc_failed_2;
    }

    ret = APP_SUCCESS;

    ctx = sws_getContext(in.width, in.height, in.format,
                         out.width, out.height, out.format,
                         SWS_BILINEAR, nullptr, nullptr, nullptr);
    if (!ctx) {
        qDebug() << "sws_getContext error";
        ret = APP_FFMPEG_SWS_GETCTX_FAILED;
        goto get_ctx_failed;
    }

    if (!inFile.open(QFile::ReadOnly)) {
        qDebug() << "open in file failure";
        ret = APP_OPENFILE_FAILED;
        goto in_file_open_falied;
    }

    if (!outFile.open(QFile::WriteOnly)) {
        qDebug() << "open out file failure";
        ret = APP_OPENFILE_FAILED;
        goto out_file_open_failed;
    }

    inFrameSize  = av_image_get_buffer_size(in.format, in.width, in.height, 1);
    outFrameSize = av_image_get_buffer_size(out.format, out.width, out.height, 1);

    while (inFile.read((char *)inData[0], inFrameSize) == inFrameSize) {
        sws_scale(ctx, inData, inStrides, 0, in.height, outData, outStrides);
        outFile.write((char *)outData[0], outFrameSize);
    }

    outFile.close();
out_file_open_failed:
    inFile.close();
in_file_open_falied:
    sws_freeContext(ctx);
get_ctx_failed:
    av_freep(&outData[0]);
alloc_failed_2:
    av_freep(&inData[0]);
alloc_failed_1:

    return ret;
}

int QtFFmpegUtils::convertToRGB888(RawVideoFile &in, uint8_t *rgb888_data)
{
    int ret = APP_SUCCESS;

    SwsContext *ctx = nullptr;
    uint8_t *inData[4], *outData[4];
    int inStrides[4], outStrides[4];
    int inFrameSize, outFrameSize;

    QFile inFile(in.filename);

    ret = av_image_alloc(inData, inStrides, in.width, in.height, in.format, 1);
    if(ret < 0){
        char errbuf[1024];
        av_strerror(ret,errbuf,sizeof (errbuf));
        qDebug() << "av_image_alloc inData error:" << errbuf;
        ret = APP_FFMPEG_ALLOC_FAILED;
        goto alloc_failed_1;
    }

    ret = av_image_alloc(outData, outStrides, in.width, in.height, AV_PIX_FMT_RGB24, 1);
    if (ret < 0) {
        char errbuf[1024];
        av_strerror(ret, errbuf, sizeof (errbuf));
        qDebug() << "av_image_alloc outData error:" << errbuf;
        ret = APP_FFMPEG_ALLOC_FAILED;
        goto alloc_failed_2;
    }

    ret = APP_SUCCESS;

    ctx = sws_getContext(in.width, in.height, in.format,
                         in.width, in.height, AV_PIX_FMT_RGB24,
                         SWS_BILINEAR, nullptr, nullptr, nullptr);
    if (!ctx) {
        qDebug() << "sws_getContext error";
        ret = APP_FFMPEG_SWS_GETCTX_FAILED;
        goto get_ctx_failed;
    }

    if (!inFile.open(QFile::ReadOnly)) {
        qDebug() << "open in file failure";
        ret = APP_OPENFILE_FAILED;
        goto in_file_open_falied;
    }

    inFrameSize  = av_image_get_buffer_size(in.format, in.width, in.height, 1);
    outFrameSize = av_image_get_buffer_size(AV_PIX_FMT_RGB24, in.width, in.height, 1);

    if (inFile.read((char *)inData[0], inFrameSize) == inFrameSize) {
        sws_scale(ctx, inData, inStrides, 0, in.height, outData, outStrides);
        memcpy(rgb888_data, outData[0], outFrameSize);
    }
    else {
        qDebug("Read not finished");
        ret = APP_READ_FILE_NOT_FINISHED;
    }

    inFile.close();
in_file_open_falied:
    sws_freeContext(ctx);
get_ctx_failed:
    av_freep(&outData[0]);
alloc_failed_2:
    av_freep(&inData[0]);
alloc_failed_1:

    return ret;
}

int QtFFmpegUtils::getBufferSize(AVPixelFormat pix_fmt, int width, int height)
{
    return av_image_get_buffer_size(pix_fmt, width, height, 1);
}

3. 预览

代码中,使用一个 QLabel 作为预览输入输出的图片,mainwindow.h 定义如下:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QLabel>

// ffmpeg header
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavdevice/avdevice.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/pixfmt.h>
}

#include "error_code.h"
#include "qtffmpegutils.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

private:
    Ui::MainWindow *ui;
    AVPixelFormat mInputFormat;
    AVPixelFormat mOutputFormat;
    QLabel *mLabel;

    // Format debug function
    QString GetAVPixelFormatString(AVPixelFormat format);
    void ShowImageInRGB888(RawVideoFile in);

private slots:
    void on_OpenInputFileBtn_Clicked();
    void on_SelectOutputFileBtn_Clicked();
    void on_StartConvertBtn_Clicked();
    void on_ShowInputImageBtn_Clicked();
    void on_ShowOutputImageBtn_Clicked();

    void on_InputFormatComboBox_Activated(int item_idx);
    void on_OutputFormatComboBox_Activated(int item_idx);

    void on_AboutActicon_Clicked(bool trigger);
};

#endif // MAINWINDOW_H

mainwindow.cpp 逻辑如下,主要处理一些按键之类的逻辑和显示 QLabel:

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

#include <QDebug>
#include <QFileDialog>
#include <QMessageBox>
#include <QFileInfo>
#include <QImage>
#include <QPixmap>

#define WIDTH_IN_4K             3840
#define HEIGHT_IN_4K            2160

#define MAX_SUPPORT_WIDTH       (WIDTH_IN_4K)
#define MAX_SUPPORT_HEIGHT      (HEIGHT_IN_4K)

#define RGB888_IN_BYTE          (8)

typedef struct _DebugFormatInfo {
    AVPixelFormat format;
    QString string;
} _DebugFormatInfo_t;

static AVPixelFormat ComboxSupportFormatList[] = {
    AV_PIX_FMT_YUV420P,
    AV_PIX_FMT_NV12,
    AV_PIX_FMT_NV21,
    AV_PIX_FMT_YUV422P,
    AV_PIX_FMT_NV16,
    AV_PIX_FMT_YUYV422,
    AV_PIX_FMT_UYVY422,
    AV_PIX_FMT_YVYU422,
    AV_PIX_FMT_RGB24,
    AV_PIX_FMT_BGR24,
    AV_PIX_FMT_ARGB,
    AV_PIX_FMT_RGBA,
    AV_PIX_FMT_RGB565BE,
    AV_PIX_FMT_RGB565LE,
    AV_PIX_FMT_RGB444BE,
    AV_PIX_FMT_RGB444LE
};

static _DebugFormatInfo_t DebugFormatInfo[] = {
    {AV_PIX_FMT_YUV420P,        "AV_PIX_FMT_YUV420P "},
    {AV_PIX_FMT_NV12,           "AV_PIX_FMT_NV12    "},
    {AV_PIX_FMT_NV21,           "AV_PIX_FMT_NV21    "},
    {AV_PIX_FMT_YUV422P,        "AV_PIX_FMT_YUV422P "},
    {AV_PIX_FMT_NV16,           "AV_PIX_FMT_NV16    "},
    {AV_PIX_FMT_YUYV422,        "AV_PIX_FMT_YUYV422 "},
    {AV_PIX_FMT_UYVY422,        "AV_PIX_FMT_UYVY422 "},
    {AV_PIX_FMT_YVYU422,        "AV_PIX_FMT_YVYU422 "},
    {AV_PIX_FMT_RGB24,          "AV_PIX_FMT_RGB24   "},
    {AV_PIX_FMT_BGR24,          "AV_PIX_FMT_BGR24   "},
    {AV_PIX_FMT_ARGB,           "AV_PIX_FMT_ARGB    "},
    {AV_PIX_FMT_RGBA,           "AV_PIX_FMT_RGBA    "},
    {AV_PIX_FMT_RGB565BE,       "AV_PIX_FMT_RGB565BE"},
    {AV_PIX_FMT_RGB565LE,       "AV_PIX_FMT_RGB565LE"},
    {AV_PIX_FMT_RGB444BE,       "AV_PIX_FMT_RGB444BE"},
    {AV_PIX_FMT_RGB444LE,       "AV_PIX_FMT_RGB444LE"}
};

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

    qDebug("AVCodeC Version = %s", av_version_info());

    mInputFormat  = ComboxSupportFormatList[0];
    mOutputFormat = ComboxSupportFormatList[0];

    mLabel = new QLabel();

    ui->lineEdit_Input_W->setValidator(new QIntValidator(0, MAX_SUPPORT_WIDTH, this));
    ui->lineEdit_Input_H->setValidator(new QIntValidator(0, MAX_SUPPORT_HEIGHT, this));

    ui->lineEdit_Output_W->setValidator(new QIntValidator(0, MAX_SUPPORT_WIDTH, this));
    ui->lineEdit_Output_H->setValidator(new QIntValidator(0, MAX_SUPPORT_HEIGHT, this));

    connect(ui->Btn_OpenInputFile, SIGNAL(clicked()), this, SLOT(on_OpenInputFileBtn_Clicked()));
    connect(ui->Btn_SelectOutputFile, SIGNAL(clicked()), this, SLOT(on_SelectOutputFileBtn_Clicked()));
    connect(ui->Btn_ShowInputImage, SIGNAL(clicked()), this, SLOT(on_ShowInputImageBtn_Clicked()));
    connect(ui->Btn_ShowOutputImage, SIGNAL(clicked()), this, SLOT(on_ShowOutputImageBtn_Clicked()));

    connect(ui->Btn_StartConvert, SIGNAL(clicked()), this, SLOT(on_StartConvertBtn_Clicked()));

    connect(ui->comboBoxIn, SIGNAL(activated(int)), this, SLOT(on_InputFormatComboBox_Activated(int)));
    connect(ui->comboBoxOut,SIGNAL(activated(int)), this, SLOT(on_OutputFormatComboBox_Activated(int)));

    connect(ui->actionVersion, SIGNAL(triggered(bool)), this, SLOT(on_AboutActicon_Clicked(bool)));
}

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

void MainWindow::on_OpenInputFileBtn_Clicked()
{
    QFileDialog *fileDialog = new QFileDialog(this);
    fileDialog->setWindowTitle(tr("Open File"));
    fileDialog->setDirectory(".");
    fileDialog->setViewMode(QFileDialog::Detail);
    if(fileDialog->exec() == QDialog::Accepted) {
        QString path = fileDialog->selectedFiles()[0];
        QFileInfo fileInfo(path);
        if (fileInfo.isFile()) {
            ui->Label_InputFilePath->setText(path);
        } else {
            QMessageBox::information(nullptr, tr("Warning"), tr("Not exist :") + path);
        }
    }
}

void MainWindow::on_SelectOutputFileBtn_Clicked()
{
    QFileDialog *fileDialog = new QFileDialog(this);
    fileDialog->setWindowTitle(tr("Select Location"));
    fileDialog->setDirectory(".");
    fileDialog->setViewMode(QFileDialog::Detail);
    if(fileDialog->exec() == QDialog::Accepted) {
        QString path = fileDialog->selectedFiles()[0];
        QFileInfo fileInfo(path);
        if (fileInfo.isFile()) {
            QMessageBox::StandardButton rb = QMessageBox::question(nullptr,
                                                                   tr("Question"),
                                                                   tr("Output File exist, Do you want cover it?"),
                                                                   QMessageBox::Yes | QMessageBox::No,
                                                                   QMessageBox::Yes);
            if(rb == QMessageBox::Yes) {
                ui->Label_OutputFilePath->setText(path);
            }
        } else {
            ui->Label_OutputFilePath->setText(path);
        }
    }
}

void MainWindow::on_ShowInputImageBtn_Clicked()
{
    bool ok = false;
    int input_w, input_h;
    QString input_file_path;
    RawVideoFile video_info;

    input_w = ui->lineEdit_Input_W->text().toInt(&ok, 10);
    input_h = ui->lineEdit_Input_H->text().toInt(&ok, 10);

    // Check if the input file not be set
    input_file_path = ui->Label_InputFilePath->text();
    if (input_file_path.isEmpty())
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("Select a input file first"));
        return;
    }

    std::string in_str = input_file_path.toStdString();
    video_info.width = input_w;
    video_info.height = input_h;
    video_info.format = mInputFormat;
    video_info.filename = in_str.c_str();

    ShowImageInRGB888(video_info);
}

void MainWindow::on_ShowOutputImageBtn_Clicked()
{
    bool ok = false;
    int output_w, output_h;
    QString output_file_path;
    RawVideoFile video_info;

    output_w = ui->lineEdit_Output_W->text().toInt(&ok, 10);
    output_h = ui->lineEdit_Output_H->text().toInt(&ok, 10);

    // Check if the output file not be set
    output_file_path = ui->Label_OutputFilePath->text();
    if (output_file_path.isEmpty())
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("Select a input file first"));
        return;
    }

    std::string in_str = output_file_path.toStdString();
    video_info.width = output_w;
    video_info.height = output_h;
    video_info.format = mOutputFormat;
    video_info.filename = in_str.c_str();

    ShowImageInRGB888(video_info);
}

void MainWindow::on_InputFormatComboBox_Activated(int item_idx)
{
    mInputFormat = ComboxSupportFormatList[item_idx];
    qDebug("InputFormatComboBox Item=%d", item_idx);
}

void MainWindow::on_OutputFormatComboBox_Activated(int item_idx)
{
    mOutputFormat = ComboxSupportFormatList[item_idx];
    qDebug("OutputFormatComboBox Item=%d", item_idx);
}

void MainWindow::on_StartConvertBtn_Clicked()
{
    bool ok = false;
    int input_w, input_h, output_w, output_h;
    QString input_file_path, output_file_path;
    RawVideoFile in;
    RawVideoFile out;
    int ret;

    // Check if the input file is null
    if (ui->lineEdit_Input_W->text().isEmpty()      ||
            ui->lineEdit_Input_H->text().isEmpty()  ||
            ui->lineEdit_Output_H->text().isEmpty() ||
            ui->lineEdit_Output_W->text().isEmpty())
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("Please add input/output width/height info!"));
        return;
    }

    input_w = ui->lineEdit_Input_W->text().toInt(&ok, 10);
    if (ok != true)
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("input_w String to Int failed"));
        return;
    }

    input_h = ui->lineEdit_Input_H->text().toInt(&ok, 10);
    if (ok != true)
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("input_h String to Int failed"));
        return;
    }

    output_w = ui->lineEdit_Output_W->text().toInt(&ok, 10);
    if (ok != true)
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("output_w String to Int failed"));
        return;
    }

    output_h = ui->lineEdit_Output_H->text().toInt(&ok, 10);
    if (ok != true)
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("output_h String to Int failed"));
        return;
    }

    // Check if the input file not be set
    input_file_path = ui->Label_InputFilePath->text();
    if (input_file_path.isEmpty())
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("Select a input file first"));
        return;
    }

    // Check if the output file not be set
    output_file_path = ui->Label_OutputFilePath->text();
    if (output_file_path.isEmpty())
    {
        QMessageBox::information(nullptr, tr("Warning"), tr("Select a output file"));
        return;
    }

    std::string in_str = input_file_path.toStdString();
    std::string out_str = output_file_path.toStdString();

    in.width = input_w;
    in.height = input_h;
    in.format = mInputFormat;
    in.filename = in_str.c_str();

    out.width = output_w;
    out.height = output_h;
    out.format = mOutputFormat;
    out.filename = out_str.c_str();

    qDebug("StartConvert input w=%d, h=%d", input_w, input_h);
    qDebug("             input format=%s", GetAVPixelFormatString(mInputFormat).toStdString().data());
    qDebug("             output w=%d, h=%d", output_w, output_h);
    qDebug("             output format=%s", GetAVPixelFormatString(mOutputFormat).toStdString().data());

    ret = QtFFmpegUtils::convertRawVideo(in, out);
    if (ret == APP_SUCCESS) {
        QMessageBox::information(nullptr, tr("Congratulation!"), tr("Convert Successfully"),
                                 QMessageBox::Yes, QMessageBox::Yes);
    } else {
        QMessageBox::information(nullptr, tr("Oops"), tr("Convert Failed : ") + QString::number(ret, 10),
                                 QMessageBox::Yes, QMessageBox::Yes);
    }
}

void MainWindow::ShowImageInRGB888(RawVideoFile in)
{
    uint8_t *rgb888_data = nullptr;
    int ret = APP_SUCCESS;
    int buffer_size = 0;

    buffer_size = QtFFmpegUtils::getBufferSize(AV_PIX_FMT_RGB24, in.width, in.height);

    //rgb888_data = (uint8_t *)malloc(in.width * in.height * RGB888_IN_BYTE);
    rgb888_data = (uint8_t *)malloc(buffer_size);

    if (rgb888_data == nullptr) {
        QMessageBox::information(nullptr, tr("Warning"), tr("Malloc failed"));
        return;
    }

    ret = QtFFmpegUtils::convertToRGB888(in, rgb888_data);
    if (ret == APP_SUCCESS) {
        qDebug("Convert to RGB888 Successful");
    } else {
        QMessageBox::information(nullptr, tr("Oops"), tr("Show Image Failed : ") + QString::number(ret, 10),
                                 QMessageBox::Yes, QMessageBox::Yes);
    }

    QImage *qimage = new QImage(rgb888_data, in.width, in.height, QImage::Format_RGB888);
    QPixmap pixmap = QPixmap::fromImage(*qimage);
    //mLabel->setGeometry(100, 100, in.width, in.height);
    mLabel->setMaximumSize(in.width, in.height);
    mLabel->resize(in.width, in.height);
    mLabel->setPixmap(pixmap);
    mLabel->show();

    free(rgb888_data);
}

void MainWindow::on_AboutActicon_Clicked(bool trigger)
{
    QMessageBox MBox;
    MBox.setWindowTitle("CSC Tools");
    MBox.setText("Powered by StephenZhou\n\nSoftware Version : V1.0 Beta \n\nGenerate at 2022.10.29");
    MBox.setIconPixmap(QPixmap(":/logo.ico"));
    MBox.exec();
}

QString MainWindow::GetAVPixelFormatString(AVPixelFormat format)
{
    QString str = "N/A";

    for (unsigned int i = 0; i < sizeof(DebugFormatInfo) / sizeof(_DebugFormatInfo_t); i++) {
        if (format == DebugFormatInfo[i].format)
        {
            str = DebugFormatInfo[i].string;
            break;
        }
    }

    return str;
}

 

4. 项目地址

https://gitee.com/stephenzhou-tech/csc_tools

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2128.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

百度最强中文AI作画大模型

前言 最近文生图领域的发展可谓是分生水起&#xff0c;这主要是得益于最近大火的扩散模型&#xff0c;之前笔者也写过一篇关于文本生产3D模型的文章&#xff0c;大家感兴趣的可以穿梭&#xff1a; https://zhuanlan.zhihu.com/p/570332906 今天要给大家介绍的这一篇paper是百度…

AXI协议详解(10)-非对齐传输

非对齐传输 本章介绍 AXI 协议如何处理未对齐的传输。 它包含以下部分&#xff1a; • 未对齐的传输 • 示例 10.1 关于未对齐传输 AXI 协议使用基于突发的寻址&#xff0c;这意味着每个事务由多个数据传输组成。 通常&#xff0c;每个数据传输都与传输大小对齐。 例如&…

Linux入门

Liunx 一计算机发展历史第一台计算机的诞生时代背景线计算机的发展线国家线时间线收束Liunx诞生操作系统简述购买云服务器以及登录增加用户总结学习的同时别忘了每天运动&#xff01; 身体才是革命的本钱 计算机发展历史 第一台计算机的诞生 第一台计算机在1946年2月14日在美国…

webpack学习踩坑笔记(持续更新中...)

目录 学习内容&#xff1a; 1.5使用plugin 1.6 使用DevServer 学习内容&#xff1a; 《深入浅出webpack》 深入浅出Webpack Dive Into GitBook 因为很多内容书上已经写了&#xff0c;这里主要是记录一下个人看书过程中遇到的坑 1.5使用plugin 这一部分介绍了一个可以把…

vue3事件处理

获取到用户点击或者勾选的值 <li click“clici(item)” v-for"item in data" :key"id"> {{item}}</li> <script> clici(item){ } </script> v-model 实现数据的双向绑定 v-model.lazy 只有当用户提交或enter键后才触…

【数据结构】动态顺序表(C语言实现)

文章目录0. 前言1. 线性表2. 顺序表2.1 概念及结构3. 动态顺序表的实现3.1 定义结构3.2 接口函数总览3.3 初始化3.4 检查增容3.5 尾插3.6 尾删3.7 头插3.8 头删3.9 查找3.10 指定下标位置插入3.11 指定下标位置删除3.12 修改3.13 打印3.14 销毁4. 完整代码SeqList.hSeqList.cte…

HTML常见标签总结

目录 1.标题标签 2.段落标签 3.字体修饰标签 4.图片标签 5.超链接标签 6.表格标签 7.列表标签 8.表单标签 9.下拉菜单 10 多行文本框 1.标题标签 一级标题是<h1></h1>中间填上标题的内容,一共可以设置六级标题,数字越小,标题就越大越粗 我们测试一段代码 …

深度残差收缩网络(Deep Residual Shrinkage Networks for Fault Diagnosis )

摘要-本文开发了新的深度学习方法&#xff0c;即深度残余收缩网络&#xff0c;提高来自高噪声振动信号的特征学习能力&#xff0c;并实现较高的故障诊断准确性。软阈值作为非线性转换层插入到深层体系结构中&#xff0c;以消除不重要的特征。此外&#xff0c;考虑到通常为阈值设…

大数据编程实验一:HDFS常用操作和Spark读取文件系统数据

大数据编程实验一&#xff1a;HDFS常用操作和Spark读取文件系统数据 文章目录大数据编程实验一&#xff1a;HDFS常用操作和Spark读取文件系统数据一、前言二、实验目的与要求三、实验内容四、实验步骤1、HDFS常用操作2、Spark读取文件系统的数据五、最后我想说一、前言 这是我…

Swift基础——字典

Swift基础——字典 嗯。。。前面我们已经学习了数组&#xff08;相关文章地址&#xff09;&#xff0c;我们知道了在Swift中&#xff0c;苹果提供了两种集合类型来存储集合的值即Array和Dictionary。 Dictionary字典 字典&#xff1a;一种存储多个相同类型值的容器&#xff…

谈谈Java对象的生命周期

经过前面的分析 &#xff0c;我们现在来看一下创建的对象到底是什么东西&#xff0c;并且完整的总结一下一个对象从创建到回收到底经过了哪些阶段。 1 对象的创建 对象创建的主要流程: 1.类加载检查 虚拟机遇到一条new指令时&#xff0c;首先将去检查这个指令的参数是否能在常…

【趣学算法】贪心算法、海盗古董装船问题

14天阅读挑战赛 努力是为了不平庸~ 算法学习有些时候是枯燥的&#xff0c;这一次&#xff0c;让我们先人一步&#xff0c;趣学算法&#xff01; 文章目录贪心本质贪心选择最优子结构最优装载问题sort函数总结贪心本质 一个贪心算法总是做出当前最好的选择&#xff0c;也就是说…

R语言“优雅地“进行医学统计分析

本文首发于公众号&#xff1a;医学和生信笔记&#xff0c;完美观看体验请至公众号查看本文。 医学和生信笔记&#xff0c;专注R语言在临床医学中的使用&#xff0c;R语言数据分析和可视化。 文章目录主要函数描述性统计比较均值增强R中的ANOVA事后检验&#xff08;post-hoc&…

嘉立创EDA的一些使用技巧

立创EDA专业版-使用教程 (lceda.cn):https://prodocs.lceda.cn/cn/faq/editor/index.html绘制板框&#xff1a;https://blog.csdn.net/gutie_bartholomew/article/details/122936253和 mil 的切换&#xff0c;按【Q】切换单位测量 AltM&#xff0c;方便地测量物件之间的距离。按…

MySQL调优之索引在什么情况下会失效?

MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引。索引提供了高效访问数据的方法&#xff0c;并且加快查询的速度&#xff0c;因此索引对查询的速度有着至关重要的影响。 使用索引可以快速地定位表中的某条记录&#xff0c;从而提高数据库查询的速度&#xff0c;提…

Spring JdbcTemplate.queryForObject()

Spring JdbcTemplate 是JDBC核心包中的中心类。它简化了 JDBC 与 Spring 的使用&#xff0c;并有助于避免常见错误。在此页面上&#xff0c;我们将学习使用它的queryForObject 方法。 JdbcTemplate.queryForObject不同参数的方法。1. <T> T queryForObject(String sql, …

继承-安全-设计模式

继承 与 原型、原型链 1. 继承是什么&#xff1f; 继承就是一个对象可以访问另外一个对象中的属性和方法 2. 继承的目的&#xff1f; 继承的目的就是实现原来设计与代码的重用 3. 继承的方式 java、c等&#xff1a;class**javaScript&#xff1a; 原型链 ** ES2015/ES6 中…

数据导入与预处理-拓展-pandas可视化

数据导入与预处理-拓展-pandas可视化1. 折线图1.1 导入数据1.2 绘制单列折线图1.3 绘制多列折线图1.4 绘制折线图-双y轴2. 条形图2.1 单行垂直/水平条形图2.2 多行条形图3. 直方图3.1 生成数据3.2 透明度/刻度/堆叠直方图3.3 拆分子图4. 散点图4.1生成数据4.2 绘制大小不一的散…

自动化测试的使用场景有哪些?如何正确使用?

目录 前言 什么是自动化测试&#xff1f; 自动化测试的使用场景有哪些&#xff1f; 自动化测试有什么好处&#xff1f; 总结 前言 本文将通过介绍 自动化测试是什么 &#xff0c; 哪些场景适用于自动化测试 &#xff0c; 自动化测试的好处 &#xff0c; 以及通过 具体的自…

vue如何二次封装一个高频可复用的组件

在我们的业务里&#xff0c;我们通常会二次封装一些高频业务组件&#xff0c;比如弹框&#xff0c;抽屉&#xff0c;表单等这些业务组件&#xff0c;为什么要二次封装&#xff1f;我们所有人心里的答案肯定是&#xff0c;同样类似的代码太多了&#xff0c;我想复用组件&#xf…