基于QT以及FFmpeg开发的录屏软件以及视频播放器代码

       基本介绍

        本项目是基于QT图形化编程环境使用C++编程语言完成的,有录音、录屏、截屏、音频播放以及视频播放五个模块,在实现功能的过程中使用了DirectShow完成对音频的录制;使用FFmpeg库文件完成对屏幕的录制;使用SDL完成对音频播放时的渲染功能。        

代码模块

        在这里对实现录音、音频播放以及截屏的代码进行展示,并将整个项目以及项目所用到的资源如FFmpeg以及SDL库文件传到了CSDN,如有需要可以联系本人,各位可以根据自己的需要下载,希望能对各位有所帮助。

        音频录制以及播放模块参考了B站用户绯夏之雨的代码原理,需要讲解的可以去参考一下,可以对代码有更深刻的理解。

音频录制

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

AudioSubThread::AudioSubThread(QObject *parent)
    :QThread(parent)
{
    status_flag=false;              //初始化操作
}

void AudioSubThread::run()
{
    //获取Windows采集数据所支持的格式(dshow)
    AVInputFormat *fmt= av_find_input_format("dshow");

    //3.定义一个录制音视频的格式上下文
    AVFormatContext* ctx=nullptr;
    //4.打开音频设备,并将其绑定到ctx上
    const char* audio_device="audio=virtual-audio-capturer";
    avformat_open_input(&ctx,audio_device,fmt,nullptr);
    //5.定义一个文件用来保存所采集的音频数据
    QFile file(outputfile);
    file.open(QIODevice::WriteOnly);
    if(!file.isOpen())
    {
        qDebug()<<"文件没有打开";
        return;
    }
    //6.采集音频数据:
    AVPacket pkt;
    while(!status_flag&&av_read_frame(ctx,&pkt)==0)
    {
        //采集数据后把数据放入到文件之中
        file.write((const char*)pkt.data,pkt.size);     //进行一帧一帧的读入操作
    }
    //7.释放所用到的资源
    file.close();
    avformat_close_input(&ctx);
}

void AudioSubThread::set_status_flag(bool flag)
{
    this->status_flag=flag;
}

音频播放

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

AudioPlay_Thread::AudioPlay_Thread(QObject *parent)
    : QThread(parent)
{

}

//定义一个全局变量,进行对要录制的视频的保存
int bufferLen;
char* bufferData;

//音频回调函数,在每次需要填充音频数据到stream流中时被调用
void _audioCallback (void *userdata,Uint8 * stream,int len)
{
    SDL_memset(stream,0,len);              //SDL_memset将stream中的数据全部置零
    if(bufferLen==0)                       //播放结束
    {
        return;
    }
    //每次len的长度不同,获取len的长度
    //根据实际可用的音频数据长度和当前回调函数需要填充的长度,选择较小的值作为本次回调函数要处理的音频数据长度
    len=(len>bufferLen)?bufferLen:len;
    //填充数据buffer
    SDL_MixAudio(stream,(Uint8*)bufferData,len,SDL_MIX_MAXVOLUME);
    //数据的指针要进行移动
    bufferData +=len;
    bufferLen-=len;
}

void AudioPlay_Thread::run()
{
    //SDL_version version;
    //SDL_VERSION(&version);
    //qDebug()<<version.major<<version.minor<<version.patch;
    //1.SDL分为各种各样的子系统
    //1.初始化音频子系统
    SDL_Init(SDL_INIT_AUDIO);
    //2.要使用哪种方式播放音频
    SDL_AudioSpec spc;
    spc.freq=44100;
    spc.format =AUDIO_S16LSB;
    spc.channels=2;
    spc.callback=_audioCallback;
    spc.samples=1024;

    //打开音频文件
    QFile file(outputfile);
    file.open(QIODevice::ReadOnly);
    if(!file.isOpen())
    {
        qDebug()<<"文件打开失败!";
        SDL_Quit();
        return;
    }
    //打开音频设备
    SDL_OpenAudio(&spc,nullptr);
    //播放音频,都是通过推流和拉流实现的
    //PUSH:程序主动推送数据给设备
    //PULL:音频设备主动向程序拉取数据
    SDL_PauseAudio(0);//0为播放,1为暂停
    char audio_buf[4096];                   //因为spc.samples为1024
    while (!isFinished())
    {
        //将音频文件读取到缓冲区当中
        bufferLen=file.read(audio_buf,sizeof(char[4096]));
        if(bufferLen<=0)
        {
            //代表读到文件尾,及文件读取结束
            break;
        }
        //将缓冲区读取的值赋给bufferData
        bufferData =audio_buf;
        //让这个线程休眠,等待下次拉取
        //即进行延时等待,直到缓冲区中的数据被完全播放
        while (bufferLen>0)
        {
            SDL_Delay(1);
        }
    }
}

AudioPlay_Thread::~AudioPlay_Thread()
{

}

截屏

        截屏的代码我使用了QT当中的屏幕截取模块,如下所示。

#include <QtWidgets>
#include <QDebug>
#include "screenshot.h"


ScreenShot::ScreenShot()
    :  screenshotLabel(new QLabel(this))
{
    screenshotLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    screenshotLabel->setAlignment(Qt::AlignCenter);

    const QRect screenGeometry = screen()->geometry();
    screenshotLabel->setMinimumSize(screenGeometry.width() / 8, screenGeometry.height() / 8);

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(screenshotLabel);

    QGroupBox *optionsGroupBox = new QGroupBox(tr("Options"), this);
    delaySpinBox = new QSpinBox(optionsGroupBox);
    delaySpinBox->setSuffix(tr(" s"));
    delaySpinBox->setMaximum(60);

    connect(delaySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
            this, &ScreenShot::updateCheckBox);

    hideThisWindowCheckBox = new QCheckBox(tr("Hide This Window"), optionsGroupBox);

    QGridLayout *optionsGroupBoxLayout = new QGridLayout(optionsGroupBox);
    optionsGroupBoxLayout->addWidget(new QLabel(tr("Screenshot Delay:"), this), 0, 0);
    optionsGroupBoxLayout->addWidget(delaySpinBox, 0, 1);
    optionsGroupBoxLayout->addWidget(hideThisWindowCheckBox, 1, 0, 1, 2);

    mainLayout->addWidget(optionsGroupBox);

    QHBoxLayout *buttonsLayout = new QHBoxLayout;
    newScreenshotButton = new QPushButton(tr("New Screenshot"), this);
    connect(newScreenshotButton, &QPushButton::clicked, this, &ScreenShot::newScreenshot);
    buttonsLayout->addWidget(newScreenshotButton);
    QPushButton *saveScreenshotButton = new QPushButton(tr("Save Screenshot"), this);
    connect(saveScreenshotButton, &QPushButton::clicked, this, &ScreenShot::saveScreenshot);
    buttonsLayout->addWidget(saveScreenshotButton);
    QPushButton *quitScreenshotButton = new QPushButton(tr("Quit"), this);
    quitScreenshotButton->setShortcut(Qt::CTRL + Qt::Key_Q);
    connect(quitScreenshotButton, &QPushButton::clicked, this, &QWidget::close);
    buttonsLayout->addWidget(quitScreenshotButton);
    buttonsLayout->addStretch();
    mainLayout->addLayout(buttonsLayout);

    shootScreen();
    delaySpinBox->setValue(5);

    setWindowTitle(tr("Screenshot"));
    resize(300, 200);
}

void ScreenShot::resizeEvent(QResizeEvent * /* event */)
{
    QSize scaledSize = originalPixmap.size();
    scaledSize.scale(screenshotLabel->size(), Qt::KeepAspectRatio);
    if (!screenshotLabel->pixmap() || scaledSize != screenshotLabel->pixmap()->size())
    {
        updateScreenshotLabel();
    }
}

void ScreenShot::newScreenshot()
{
    if (hideThisWindowCheckBox->isChecked())
    {
        hide();
    }
    newScreenshotButton->setDisabled(true);

    QTimer::singleShot(delaySpinBox->value() * 1000, this, &ScreenShot::shootScreen);
}

void ScreenShot::saveScreenshot()
{
    const QString format = "png";
    //获取默认的保存路径,是系统中的图片文件夹
    QString initialPath = QStandardPaths::writableLocation(QStandardPaths::PicturesLocation);
    if (initialPath.isEmpty())
    {
        initialPath = QDir::currentPath();
    }
    initialPath += tr("/untitled.") + format;
    //构建一个文件对话框,并设置其初始路径和属性
    QFileDialog fileDialog(this, tr("Save As"), initialPath);
    fileDialog.setAcceptMode(QFileDialog::AcceptSave);
    fileDialog.setFileMode(QFileDialog::AnyFile);
    fileDialog.setDirectory(initialPath);
    //构建一个支持的MIME类型列表,以过滤可保存的文件类型
    QStringList mimeTypes;
    const QList<QByteArray> baMimeTypes = QImageWriter::supportedMimeTypes();
    for (const QByteArray &bf : baMimeTypes)
        mimeTypes.append(QLatin1String(bf));
    fileDialog.setMimeTypeFilters(mimeTypes);
    fileDialog.selectMimeTypeFilter("image/" + format);
    //设置默认的后缀名为格式
    fileDialog.setDefaultSuffix(format);
    //打开文件对话框并等待用户选择一个文件或取消操作
    if (fileDialog.exec() != QDialog::Accepted)
    {
        return;
    }
    //获取用户选择的文件名,并使用selectedFiles().first()取第一个文件名
    const QString fileName = fileDialog.selectedFiles().first();
    //将截图保存为文件,如果保存失败,则显示错误消息框提示保存错误
    if (!originalPixmap.save(fileName))
    {
        QMessageBox::warning(this, tr("Save Error"), tr("The image could not be saved to \"%1\".")
                             .arg(QDir::toNativeSeparators(fileName)));
    }
}
//执行截屏操作
void ScreenShot::shootScreen()
{
    //返回当前显示器上的主屏幕对象
    QScreen *screen = QGuiApplication::primaryScreen();
    if (const QWindow *window = windowHandle())
    {
        screen = window->screen();
    }
    if (!screen)
    {
        qDebug()<<"屏幕对象不存在,无法截屏!";
        return;
    }
    //发出吡声提醒
    if (delaySpinBox->value() != 0)
    {
        QApplication::beep();
    }

    originalPixmap = screen->grabWindow(0);
    updateScreenshotLabel();

    newScreenshotButton->setDisabled(false);
    if (hideThisWindowCheckBox->isChecked())
    {
        show();
    }
}
//根据延迟选项的值来更新隐藏窗口的选项状态
void ScreenShot::updateCheckBox()
{
    if (delaySpinBox->value() == 0)
    {
        hideThisWindowCheckBox->setDisabled(true);
        hideThisWindowCheckBox->setChecked(false);
    }
    else
    {
        hideThisWindowCheckBox->setDisabled(false);
    }
}
//将截图按比例缩放并显示在截图标签上
void ScreenShot::updateScreenshotLabel()
{
    screenshotLabel->setPixmap(originalPixmap.scaled(screenshotLabel->size(),
                                                     Qt::KeepAspectRatio,
                                                     Qt::SmoothTransformation));
}


ScreenShot::~ScreenShot()
{

}

        

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值