qt5.15.2+opencv4.10 ffmpeg下载、环境配置以及视频加载与保存

        关于opencv加载视频以及保存视频,处理视频文件,要用到ffmpeg组件,ffmpeg组件有两个,一个是二进制版一个是源码版。

一. ffmpeg下载与环境配置

        1. ffmpeg下载

        首先进入ffmpeg官网

        之后点击windows图标之后

        

        会进入一个github界面,点击展示更多。

        然后选择下载,我这边选的是lpgl版而不是pgl。是共享版不是静态版

        下面是静态版和共享版区别

  1. ffmpeg-n7.0-latest-win64-lgpl-7.0.zip (116 MB)

    • 说明:这是 FFmpeg 的静态版本
    • 内容:包含所有的 FFmpeg 可执行文件和静态链接的库文件。这意味着所有功能和库都被编译到了一个可执行文件中。使用这个版本时,所有依赖项都包含在同一个可执行文件中,独立运行,不依赖外部的动态链接库(DLL)。
    • 适用场景:当你希望独立运行 FFmpeg,而不需要任何外部依赖时使用。这种版本通常体积较大,但不需要额外的 DLL 文件,使用更方便。
  2. ffmpeg-n7.0-latest-win64-lgpl-shared-7.0.zip (50.7 MB)

    • 说明:这是 FFmpeg 的共享版本
    • 内容:包含了 FFmpeg 的动态链接库(DLL 文件)和少量的可执行文件。这种版本更小巧,因为它通过动态链接来引用库文件,而不是将所有库文件静态编译到可执行文件中。
    • 适用场景:当你希望将 FFmpeg 的功能整合到你的应用程序中,并希望以动态链接的方式进行集成时使用。动态链接库的好处是更新更灵活,你可以替换 DLL 文件而不需要重新编译整个应用程序。此版本更适合需要频繁更新或需要动态库管理的场景。

        LGPL版 允许你在闭源或商业软件中动态链接使用 FFmpeg,而无需公开你的源代码,前提是你不修改 FFmpeg 的源代码。

        GPL版 要求如果你将 FFmpeg 作为一部分集成到你的软件中,无论是静态链接还是动态链接,都必须公开你的软件源代码,并允许它在同样的 GPL 许可证下分发。

        然后这边下载的是lgpl版而不是gpl版,lgpl版允许商业化闭源,所以就直接用lgpl版。

        2. ffmpeg环境配置

        点击进入高级系统设置

        之后进入你解压好的文件夹下的bin目录下,复制bin目录的绝对路径,想必到了学opencv了,应该不会是不懂啥是绝对路径了吧,这里面的dll文件就是动态库,以后打包程序会用。

        接下来按照我的步骤添加环境变量,把地址粘贴到5号框里。

        之后控制台输入命令

ffmpeg -version

        大功告成。

二. 视频加载与保存

        先粘整个模块的代码,然后慢慢说。我这边是没有给他新添按钮,函数直接构造函数调用了,想加的话可以自己加,包括保存视频时候需要设置的参数,都可以自己写一个输入框来设置。

void MainWindow::setVideo(cv::Mat &image) {
    QString inputFilePath = "C:\\Users\\wangy\\Desktop\\VID1.mp4";
    QString tempOutputFilePath = "C:\\Users\\wangy\\Desktop\\temp_output.mp4";
    QString finalOutputFilePath = "C:\\Users\\wangy\\Desktop\\VID2.mp4";

    int bitrate = 3500;  // 期望的比特率为 2500 kbps

    // 打开原视频
    cv::VideoCapture capture(inputFilePath.toStdString());

    if (!capture.isOpened()) {
        qDebug() << "Error: Unable to open video! Check the video format and path.";
        return;
    }

    int frame_width = capture.get(cv::CAP_PROP_FRAME_WIDTH);
    int frame_height = capture.get(cv::CAP_PROP_FRAME_HEIGHT);
    double fps = capture.get(cv::CAP_PROP_FPS);

    // 使用四字符编码器
    int fourcc = static_cast<int>(capture.get(cv::CAP_PROP_FOURCC));

    // 初始化 VideoWriter,保存临时文件
    cv::VideoWriter writer;
    writer.open(tempOutputFilePath.toStdString(),
                fourcc,
                fps,
                cv::Size(frame_width, frame_height),
                true);

    if (!writer.isOpened()) {
        qDebug() << "Error: Unable to open video writer!";
        return;
    }

    // 使用 QTimer 进行定时更新
    QTimer *timer = new QTimer(this);
    connect(timer, &QTimer::timeout, [=]() mutable {
        cv::Mat frame;
        capture >> frame;  // 逐帧读取

        if (frame.empty()) {
            qDebug() << "End of video!";
            timer->stop();

            // 检查并删除现有的最终输出文件
            if (QFile::exists(finalOutputFilePath)) {
                QFile::remove(finalOutputFilePath);
                qDebug() << "Existing final output file deleted.";
            }

            capture.release();
            writer.release();

            // 使用 FFmpeg 将临时文件转换为最终文件,并设置比特率
            QProcess ffmpegProcess;
            ffmpegProcess.start("ffmpeg", QStringList()
                                              << "-i" << tempOutputFilePath  // 输入临时文件
                                              << "-c:v" << "libx264"         // 强制使用 H.264 编码器
                                              << "-b:v" << QString::number(bitrate) + "k"           // 设置比特率
                                              << "-bufsize" << QString::number(bitrate * 2) + "k"       // 设置缓冲区大小
                                              << "-maxrate" << QString::number(bitrate) + "k"       // 强制最大比特率
                                              << "-minrate" << QString::number(bitrate) + "k"       // 强制最小比特率
                                              << finalOutputFilePath);       // 输出最终文件


            ffmpegProcess.waitForFinished();
            qDebug() << "Video conversion complete with target bitrate!";

            // 删除临时文件
            QFile::remove(tempOutputFilePath);
            qDebug() << "Temporary file deleted.";

            return;
        }
        writer.write(frame);  // 写入帧到临时文件

        cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
        QImage qimg(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);

        QImage pixmap = setImageSize(qimg, ui->dstImage);
        ui->dstImage->setPixmap(QPixmap::fromImage(pixmap));
    });
    // 设置帧率,假设 33fps
    timer->start(33);  // 每33ms触发一次
}

        接下来进行模块分析。首先,来看路径的选择:第一个参数是原视频的存放路径,第二个是中间转换视频的存放路径,第三个则是输出视频的存放路径。需要注意的是,中间转换视频和输出视频的文件可以不存在,程序会根据你提供的命名和路径,在对应的位置自动创建这些文件。

    QString inputFilePath = "C:\\Users\\wangy\\Desktop\\VID1.mp4";
    QString tempOutputFilePath = "C:\\Users\\wangy\\Desktop\\temp_output.mp4";
    QString finalOutputFilePath = "C:\\Users\\wangy\\Desktop\\VID2.mp4";

        下面的部分

    int frame_width = capture.get(cv::CAP_PROP_FRAME_WIDTH);
    int frame_height = capture.get(cv::CAP_PROP_FRAME_HEIGHT);
    double fps = capture.get(cv::CAP_PROP_FPS);

    // 使用四字符编码器
    int fourcc = static_cast<int>(capture.get(cv::CAP_PROP_FOURCC));

        前三个是获取视频帧的宽度,高度和帧率。这些值来自视频捕获对象 capture,cv::CAP_PROP_FOURCC 用于获取 FourCC 编码。FourCC 是一个四字节的编码,表示视频的编码格式(压缩/解压缩器)。FourCC 编码代表视频压缩所使用的编解码器,如 'XVID'、'MJPG' 或 'H264'。

if (frame.empty())

        如果视频帧已经读取完,删除保存路径下的内容,然后先输出中间视频,最后将中间视频按照想要的格式转换成最终视频,再删除中间视频。需要注意的是视频比特率,视频比特率的处理是存在误差的,根据处理方法,比特率的误差不同,如果需要更精确的比特率,需要更复杂的处理方式。例如两次编码,因为视频中的比特率是会变化的,所以在第一次编码时先对视频进行初步压缩,再根据目标比特率和第一次编码的结果进行调整,以保证最终视频的比特率尽可能接近设定值。如下代码:

            // 第一阶段:分析视频并生成统计文件
            QProcess ffmpegPass1;
            ffmpegPass1.start("ffmpeg", QStringList()
                                            << "-y"  // 强制覆盖
                                            << "-i" << tempOutputFilePath  // 输入临时文件
                                            << "-c:v" << "libx264"         // 使用 H.264 编码器
                                            << "-b:v" << QString::number(bitrate) + "k" // 目标比特率
                                            << "-pass" << "1"              // 第一次分析
                                            << "-f" << "mp4"               // 强制输出格式
                                            << "NUL");                     // Windows 下不生成输出文件
            ffmpegPass1.waitForFinished();

            // 第二阶段:使用第一次的统计数据,进行实际编码
            QProcess ffmpegPass2;
            ffmpegPass2.start("ffmpeg", QStringList()
                                            << "-y"  // 强制覆盖
                                            << "-i" << tempOutputFilePath  // 输入临时文件
                                            << "-c:v" << "libx264"         // 使用 H.264 编码器
                                            << "-b:v" << QString::number(bitrate) + "k" // 目标比特率
                                            << "-pass" << "2"              // 第二次编码
                                            << finalOutputFilePath);       // 输出最终文件

            ffmpegPass2.waitForFinished();

        这样就可以有效的降低比特率误差,但是也需要更长的视频处理时间,如果你想像pr一样要求视频输出更加准确,就需要更多处理步骤,比如进行两次或多次编码,调整关键帧间距,优化压缩算法,或者使用自适应比特率控制(ABR)和恒定质量(CQ)等高级编码设置。此外,可以引入分析工具对第一遍编码的结果进行详细评估,根据帧间的复杂性、运动场景和图像细节对比特率分布进行优化调整,从而在后续编码过程中进行更精准的调整。如果对比特率的精确控制要求非常高,还可以结合统计复查、帧级调整等方式,逐步细化编码过程,最终输出精度更高的视频。

        capture.release() 和writer.release() 为什么要在输出前面?

  • 在调用 FFmpeg 进行文件转换之前,临时文件应已完全写入并关闭,否则 ffmpeg 可能会读取不完整或锁定的文件,导致转换失败或产生错误输出。
  • 通过提前 release() 确保临时文件处于关闭状态,ffmpeg 可以顺利进行处理,确保最终输出的文件完整且符合预期。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值