国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

用Qt開發(fā)的ffmpeg流媒體播放器,支持截圖、錄像,支持音視頻播放,支持本地文件播放、網(wǎng)絡(luò)流播放

這篇具有很好參考價(jià)值的文章主要介紹了用Qt開發(fā)的ffmpeg流媒體播放器,支持截圖、錄像,支持音視頻播放,支持本地文件播放、網(wǎng)絡(luò)流播放。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

前言

本工程qt用的版本是5.8-32位,ffmpeg用的版本是較新的5.1版本。它支持TCP或UDP方式拉取實(shí)時(shí)流,實(shí)時(shí)流我采用的是監(jiān)控?cái)z像頭的RTSP流。音頻播放采用的是QAudioOutput,視頻經(jīng)ffmpeg解碼并由YUV轉(zhuǎn)RGB后是在QOpenGLWidget下進(jìn)行渲染顯示。本工程的代碼有注釋,可以通過本博客查看代碼或者在播放最后的鏈接處下載工程demo。

一、界面展示

qt ffmpeg,音視頻,ffmpeg,qt,音視頻

二、功能代碼

1.以下是主界面相關(guān)代碼:mainwindow.h mainwindow.cpp

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "commondef.h"
#include "mediathread.h"
#include "ctopenglwidget.h"

namespace Ui {
class MainWindow;
}

class MainWindow : public QMainWindow
{
    Q_OBJECT

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

    void Init();

private slots:
    void on_btn_play_clicked();

    void on_btn_open_clicked();

    void on_btn_play2_clicked();

    void on_btn_stop_clicked();

    void on_btn_record_clicked();

    void on_btn_snapshot_clicked();

    void on_btn_open_audio_clicked();

    void on_btn_close_audio_clicked();

    void on_btn_stop_record_clicked();

private:
    Ui::MainWindow *ui;
    MediaThread* m_pMediaThread = nullptr;
};

#endif // MAINWINDOW_H

mainwindow.cpp

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMessageBox>
#include <QFileDialog>
#include "ctaudioplayer.h"

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

    ui->setupUi(this);

    Init();
}

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

void MainWindow::Init()
{
    ui->radioButton_TCP->setChecked(false);
    ui->radioButton_UDP->setChecked(true);
}

void MainWindow::on_btn_play_clicked()
{
    QString sUrl = ui->lineEdit_Url->text();
    if(sUrl.isEmpty())
    {
        QMessageBox::critical(this, "myFFmpeg", "錯(cuò)誤:實(shí)時(shí)流url不能為空.");
        return;
    }

    if(nullptr == m_pMediaThread)
    {
        MY_DEBUG << "new MediaThread";
        m_pMediaThread = new MediaThread;
    }
    else
    {
        if(m_pMediaThread->isRunning())
        {
            QMessageBox::critical(this, "myFFmpeg", "錯(cuò)誤:請(qǐng)先點(diǎn)擊停止按鈕關(guān)閉視頻.");
            return;
        }
    }

    connect(m_pMediaThread, SIGNAL(sig_emitImage(const QImage&)),
            ui->openGLWidget, SLOT(slot_showImage(const QImage&)));

    bool bMediaInit = false;
    if(ui->radioButton_TCP->isChecked())
    {
        bMediaInit = m_pMediaThread->Init(sUrl, PROTOCOL_TCP);
    }
    else
    {
        bMediaInit = m_pMediaThread->Init(sUrl, PROTOCOL_UDP);
    }

    if(bMediaInit)
    {
        m_pMediaThread->startThread();
    }
}

void MainWindow::on_btn_open_clicked()
{
    QString sFileName = QFileDialog::getOpenFileName(this, QString::fromLocal8Bit("選擇視頻文件"));
    if(sFileName.isEmpty())
    {
        QMessageBox::critical(this, "myFFmpeg", "錯(cuò)誤:文件不能為空.");
        return;
    }

    ui->lineEdit_File->setText(sFileName);

}

void MainWindow::on_btn_play2_clicked()
{
    QString sFileName = ui->lineEdit_File->text();
    if(sFileName.isEmpty())
    {
        QMessageBox::critical(this, "myFFmpeg", "錯(cuò)誤:文件不能為空.");
        return;
    }

    if(nullptr == m_pMediaThread)
    {
        m_pMediaThread = new MediaThread;
    }
    else
    {
        if(m_pMediaThread->isRunning())
        {
            QMessageBox::critical(this, "myFFmpeg", "錯(cuò)誤:請(qǐng)先點(diǎn)擊停止按鈕關(guān)閉視頻.");
            return;
        }
    }

    connect(m_pMediaThread, SIGNAL(sig_emitImage(const QImage&)),
            ui->openGLWidget, SLOT(slot_showImage(const QImage&)));

    if(m_pMediaThread->Init(sFileName))
    {
        m_pMediaThread->startThread();
    }
}

void MainWindow::on_btn_stop_clicked()
{
    if(m_pMediaThread)
    {
        qDebug() << "on_btn_stop_clicked 000";
        disconnect(m_pMediaThread, SIGNAL(sig_emitImage(const QImage&)),
                ui->openGLWidget, SLOT(slot_showImage(const QImage&)));

        m_pMediaThread->stopThread();

        qDebug() << "on_btn_stop_clicked 111";
        m_pMediaThread->quit();
        m_pMediaThread->wait();

        qDebug() << "on_btn_stop_clicked 222";
        m_pMediaThread->DeInit();

        qDebug() << "on_btn_stop_clicked 333";
    }

}

void MainWindow::on_btn_record_clicked()
{
    if(m_pMediaThread)
        m_pMediaThread->startRecord();
}

void MainWindow::on_btn_snapshot_clicked()
{
    if(m_pMediaThread)
        m_pMediaThread->Snapshot();
}

void MainWindow::on_btn_open_audio_clicked()
{
    ctAudioPlayer::getInstance().isPlay(true);
}

void MainWindow::on_btn_close_audio_clicked()
{
    ctAudioPlayer::getInstance().isPlay(false);
}

void MainWindow::on_btn_stop_record_clicked()
{
    if(m_pMediaThread)
        m_pMediaThread->stopRecord();
}

2.以下是流媒體線程相關(guān)代碼:mediathread.h、mediathread.cpp

mediathread.h

#ifndef MEDIATHREAD_H
#define MEDIATHREAD_H

#include <QThread>
#include <QImage>
#include "ctffmpeg.h"
#include "commondef.h"
#include "mp4recorder.h"

#define MAX_AUDIO_OUT_SIZE 8*1152


class MediaThread : public QThread
{
    Q_OBJECT
public:
    MediaThread();
    ~MediaThread();

    bool Init(QString sUrl, int nProtocolType = PROTOCOL_UDP);
    void DeInit();

    void startThread();
    void stopThread();

    void setPause(bool bPause);

    void Snapshot();
    void startRecord();
    void stopRecord();

public:
    int m_nMinAudioPlayerSize = 640;

private:
    void run() override;

signals:
    void sig_emitImage(const QImage&);

private:
    bool m_bRun = false;
    bool m_bPause = false;
    bool m_bRecord = false;
    ctFFmpeg* m_pFFmpeg = nullptr;
    mp4Recorder m_pMp4Recorder;

};

#endif // MEDIATHREAD_H

mediathread.cpp

#include "mediathread.h"
#include "ctaudioplayer.h"
#include <QDate>
#include <QTime>

MediaThread::MediaThread()
{
}

MediaThread::~MediaThread()
{
    if(m_pFFmpeg)
    {
        m_pFFmpeg->DeInit();
        delete m_pFFmpeg;
        m_pFFmpeg = nullptr;
    }
}

bool MediaThread::Init(QString sUrl, int nProtocolType)
{
    if(nullptr == m_pFFmpeg)
    {
        MY_DEBUG << "new ctFFmpeg";
        m_pFFmpeg = new ctFFmpeg;
        connect(m_pFFmpeg, SIGNAL(sig_getImage(const QImage&)),
                this, SIGNAL(sig_emitImage(const QImage&)));
    }

    if(m_pFFmpeg->Init(sUrl, nProtocolType) != 0)
    {
        MY_DEBUG << "FFmpeg Init error.";
        return false;
    }
    return true;
}

void MediaThread::DeInit()
{
    MY_DEBUG << "DeInit 000";
    m_pFFmpeg->DeInit();

    MY_DEBUG << "DeInit 111";
    if(m_pFFmpeg)
    {
        delete m_pFFmpeg;
        m_pFFmpeg = nullptr;
    }
    MY_DEBUG << "DeInit end";
}

void MediaThread::startThread()
{
    m_bRun = true;
    start();
}

void MediaThread::stopThread()
{
    m_bRun = false;
}

void MediaThread::setPause(bool bPause)
{
    m_bPause = bPause;
}

void MediaThread::Snapshot()
{
    m_pFFmpeg->Snapshot();
}

void MediaThread::startRecord()
{
    QString sPath = "./record/";
    QDate date = QDate::currentDate();
    QTime time = QTime::currentTime();
    QString sRecordPath = QString("%1%2-%3-%4-%5%6%7.mp4").arg(sPath).arg(date.year()). \
            arg(date.month()).arg(date.day()).arg(time.hour()).arg(time.minute()). \
            arg(time.second());

    MY_DEBUG << "sRecordPath:" << sRecordPath;

    if(nullptr != m_pFFmpeg->m_pAVFmtCxt && m_bRun)
    {
        m_bRecord = m_pMp4Recorder.Init(m_pFFmpeg->m_pAVFmtCxt, sRecordPath);
    }
}

void MediaThread::stopRecord()
{
    if(m_bRecord)
    {
        MY_DEBUG << "stopRecord...";
        m_pMp4Recorder.DeInit();
        m_bRecord = false;
    }
}

void MediaThread::run()
{
    char audioOut[MAX_AUDIO_OUT_SIZE] = {0};
    while(m_bRun)
    {
        if(m_bPause)
        {
            msleep(100);
            continue;
        }

        //獲取播放器緩存大小
        if(m_pFFmpeg->m_bSupportAudioPlay)
        {
            int nFreeSize = ctAudioPlayer::getInstance().getFreeSize();
            if(nFreeSize < m_nMinAudioPlayerSize)
            {
                msleep(1);
                continue;
            }
        }

        AVPacket pkt = m_pFFmpeg->getPacket();
        if (pkt.size <= 0)
        {
            msleep(10);
            continue;
        }

        //解碼播放
        if (pkt.stream_index == m_pFFmpeg->m_nAudioIndex &&
                m_pFFmpeg->m_bSupportAudioPlay)
        {
            if(m_pFFmpeg->Decode(&pkt))
            {
               int nLen = m_pFFmpeg->getAudioFrame(audioOut);//獲取一幀音頻的pcm
               if(nLen > 0)
                    ctAudioPlayer::getInstance().Write(audioOut, nLen);
            }
        }
        else
        {
            //目前只支持錄制視頻
            if(m_bRecord)
            {
                //MY_DEBUG << "record...";
                AVPacket* pPkt = av_packet_clone(&pkt);
                m_pMp4Recorder.saveOneFrame(*pPkt);
                av_packet_free(&pPkt);
            }

            if(m_pFFmpeg->Decode(&pkt))
            {
                m_pFFmpeg->getVideoFrame();
            }
        }
        av_packet_unref(&pkt);
    }
    MY_DEBUG << "run end";
}

3.以下是ffmpeg處理的相關(guān)代碼:ctffmpeg.h、ctffmpeg.cpp

ctffmpeg.h

#ifndef CTFFMPEG_H
#define CTFFMPEG_H

#include <QObject>
#include <QMutex>

extern "C"
{
    #include "libavcodec/avcodec.h"
    #include "libavcodec/dxva2.h"
    #include "libavutil/avstring.h"
    #include "libavutil/mathematics.h"
    #include "libavutil/pixdesc.h"
    #include "libavutil/imgutils.h"
    #include "libavutil/dict.h"
    #include "libavutil/parseutils.h"
    #include "libavutil/samplefmt.h"
    #include "libavutil/avassert.h"
    #include "libavutil/time.h"
    #include "libavformat/avformat.h"
    #include "libswscale/swscale.h"
    #include "libavutil/opt.h"
    #include "libavcodec/avfft.h"
    #include "libswresample/swresample.h"
    #include "libavfilter/buffersink.h"
    #include "libavfilter/buffersrc.h"
    #include "libavutil/avutil.h"
}
#include "commondef.h"

class ctFFmpeg : public QObject
{
    Q_OBJECT
public:
    ctFFmpeg();
    ~ctFFmpeg();

    int Init(QString sUrl, int nProtocolType = PROTOCOL_UDP);
    void DeInit();

    AVPacket getPacket(); //讀取一幀
    bool Decode(const AVPacket *pkt); //解碼

    int getVideoFrame();
    int getAudioFrame(char* pOut);

    void Snapshot();

private:
    int InitVideo();
    int InitAudio();

signals:
    void sig_getImage(const QImage &image);

public:
    int m_nVideoIndex = -1;
    int m_nAudioIndex = -1;
    bool m_bSupportAudioPlay = false;
    AVFormatContext *m_pAVFmtCxt = nullptr;              //流媒體的上下文

private:
    AVCodecContext* m_pVideoCodecCxt = nullptr;          //視頻解碼器上下文
    AVCodecContext* m_pAudioCodecCxt = nullptr;          //音頻解碼器上下文
    AVFrame *m_pYuvFrame = nullptr;                      //解碼后的視頻幀數(shù)據(jù)
    AVFrame *m_pPcmFrame = nullptr;                      //解碼后的音頻數(shù)據(jù)
    SwrContext *m_pAudioSwrContext = nullptr;            //音頻重采樣上下文
    SwsContext *m_pVideoSwsContext = nullptr;            //處理像素問題,格式轉(zhuǎn)換 yuv->rbg
    AVPacket m_packet;                                   //每一幀數(shù)據(jù) 原始數(shù)據(jù)
    AVFrame* m_pFrameRGB = nullptr;                      //轉(zhuǎn)換后的RGB數(shù)據(jù)
    enum AVCodecID m_CodecId;
    uint8_t* m_pOutBuffer = nullptr;

    int m_nAudioSampleRate = 8000;                       //音頻采樣率
    int m_nAudioPlaySampleRate = 44100;                  //音頻播放采樣率
    int m_nAudioPlayChannelNum = 1;                      //音頻播放通道數(shù)
    int64_t m_nLastReadPacktTime = 0;
    bool m_bSnapshot = false;
    QString m_sSnapPath = "./snapshot/test.jpg";
    QMutex m_mutex;
};

#endif // CTFFMPEG_H

ctffmpeg.cpp

#include "ctffmpeg.h"
#include <QImage>
#include "ctaudioplayer.h"
#include <QPixmap>
#include <QDate>
#include <QTime>

ctFFmpeg::ctFFmpeg()
{
}

ctFFmpeg::~ctFFmpeg()
{
}

int ctFFmpeg::Init(QString sUrl, int nProtocolType)
{
    DeInit();

    avformat_network_init();//初始化網(wǎng)絡(luò)流

    //參數(shù)設(shè)置
    AVDictionary* pOptDict = NULL;
    if(nProtocolType == PROTOCOL_UDP)
        av_dict_set(&pOptDict, "rtsp_transport", "udp", 0);
    else
        av_dict_set(&pOptDict, "rtsp_transport", "tcp", 0);

    av_dict_set(&pOptDict, "stimeout", "5000000", 0);
    av_dict_set(&pOptDict, "buffer_size", "8192000", 0);

    if(nullptr == m_pAVFmtCxt)
        m_pAVFmtCxt = avformat_alloc_context();
    if(nullptr == m_pYuvFrame)
        m_pYuvFrame = av_frame_alloc();
    if(nullptr == m_pPcmFrame)
        m_pPcmFrame = av_frame_alloc();

    //加入中斷處理
    m_nLastReadPacktTime = av_gettime();
    m_pAVFmtCxt->interrupt_callback.opaque = this;
    m_pAVFmtCxt->interrupt_callback.callback = [](void* ctx)
    {
        ctFFmpeg* pThis = (ctFFmpeg*)ctx;
        int nTimeout = 3;
        if (av_gettime() - pThis->m_nLastReadPacktTime > nTimeout * 1000 * 1000)
        {
            return -1;
        }
        return 0;
    };

    //打開碼流
    int nRet = avformat_open_input(&m_pAVFmtCxt, sUrl.toStdString().c_str(), nullptr, nullptr);
    if(nRet < 0)
    {
        MY_DEBUG << "avformat_open_input failed nRet:" << nRet;
        return nRet;
    }

    //設(shè)置探測時(shí)間,獲取碼流信息
    m_pAVFmtCxt->probesize = 400 * 1024;
    m_pAVFmtCxt->max_analyze_duration = 2 * AV_TIME_BASE;
    nRet = avformat_find_stream_info(m_pAVFmtCxt, nullptr);
    if(nRet < 0)
    {
        MY_DEBUG << "avformat_find_stream_info failed nRet:" << nRet;
        return nRet;
    }

    //打印碼流信息
    av_dump_format(m_pAVFmtCxt, 0, sUrl.toStdString().c_str(), 0);

    //查找碼流
    for (int nIndex = 0; nIndex < m_pAVFmtCxt->nb_streams; nIndex++)
    {
        if (m_pAVFmtCxt->streams[nIndex]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            m_nVideoIndex = nIndex;
        }

        if (m_pAVFmtCxt->streams[nIndex]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            m_nAudioIndex = nIndex;
        }
    }

    //初始化視頻
    if(InitVideo() < 0)
    {
        MY_DEBUG << "InitVideo() error";
        return -1;
    }

    //初始化音頻
    if(m_nAudioIndex != -1)
    {
        if(InitAudio() < 0)
        {
            MY_DEBUG << "InitAudio() error";
            m_bSupportAudioPlay = false;
        }
        else
            m_bSupportAudioPlay = true;
    }

    return 0;
}

void ctFFmpeg::DeInit()
{
    MY_DEBUG << "DeInit 000";
    m_mutex.lock();

    MY_DEBUG << "DeInit 111";
    if (nullptr != m_pVideoSwsContext)
    {
        sws_freeContext(m_pVideoSwsContext);
    }

    MY_DEBUG << "DeInit 222";
    if (nullptr != m_pAudioSwrContext)
    {
        swr_free(&m_pAudioSwrContext);
        m_pAudioSwrContext = nullptr;
    }

    MY_DEBUG << "DeInit 333";
    if(nullptr != m_pYuvFrame)
        av_free(m_pYuvFrame);

    MY_DEBUG << "DeInit 444";
    if(nullptr != m_pPcmFrame)
        av_free(m_pPcmFrame);

    MY_DEBUG << "DeInit 555";
    if(nullptr != m_pOutBuffer)
    {
        av_free(m_pOutBuffer);
    }

    MY_DEBUG << "DeInit 666";
    if(nullptr != m_pVideoCodecCxt)
    {
        avcodec_close(m_pVideoCodecCxt);
        //avcodec_free_context(&m_pVideoCodecCxt);
        m_pVideoCodecCxt = nullptr;
    }

    MY_DEBUG << "DeInit 777";
    if (nullptr != m_pAudioCodecCxt)
    {
       avcodec_close(m_pAudioCodecCxt);
       //avcodec_free_context(&m_pAudioCodecCxt);
       m_pAudioCodecCxt = nullptr;
    }

    MY_DEBUG << "DeInit 888";
    if(nullptr != m_pAVFmtCxt)
    {
        avformat_close_input(&m_pAVFmtCxt);
        MY_DEBUG << "DeInit 999";
        avformat_free_context(m_pAVFmtCxt);
        m_pAVFmtCxt = nullptr;
    }
    MY_DEBUG << "DeInit end";
    m_mutex.unlock();
}

AVPacket ctFFmpeg::getPacket()
{
    AVPacket pkt;
    memset(&pkt, 0, sizeof(AVPacket));

    if(!m_pAVFmtCxt)
    {
        return pkt;
    }
    m_nLastReadPacktTime = av_gettime();
    int nErr = av_read_frame(m_pAVFmtCxt, &pkt);
    if(nErr < 0)
    {
        //錯(cuò)誤信息
        char errorbuff[1024];
        av_strerror(nErr, errorbuff, sizeof(errorbuff));
    }
    return pkt;
}

bool ctFFmpeg::Decode(const AVPacket *pkt)
{
    m_mutex.lock();
    if(!m_pAVFmtCxt)
    {
        m_mutex.unlock();
        return false;
    }

    AVCodecContext* pCodecCxt = nullptr;
    AVFrame *pFrame;
    if (pkt->stream_index == m_nAudioIndex)
    {
        pFrame = m_pPcmFrame;
        pCodecCxt = m_pAudioCodecCxt;
    }
    else
    {
        pFrame = m_pYuvFrame;
        pCodecCxt = m_pVideoCodecCxt;
    }

    //發(fā)送編碼數(shù)據(jù)包
    int nRet = avcodec_send_packet(pCodecCxt, pkt);
    if (nRet != 0)
    {
        m_mutex.unlock();
        MY_DEBUG << "avcodec_send_packet error---" << nRet;
        return false;
    }

    //獲取解碼的輸出數(shù)據(jù)
    nRet = avcodec_receive_frame(pCodecCxt, pFrame);
    if (nRet != 0)
    {
        m_mutex.unlock();
        qDebug()<<"avcodec_receive_frame error---" << nRet;
        return false;
    }
    m_mutex.unlock();
    return true;
}

int ctFFmpeg::getVideoFrame()
{
    m_mutex.lock();
    auto nRet = sws_scale(m_pVideoSwsContext, (const uint8_t* const*)m_pYuvFrame->data,
                          m_pYuvFrame->linesize, 0, m_pVideoCodecCxt->height,
                          m_pFrameRGB->data, m_pFrameRGB->linesize);
    if(nRet < 0)
    {
        //MY_DEBUG << "sws_scale error.";
        m_mutex.unlock();
        return -1;
    }

    //發(fā)送獲取一幀圖像信號(hào)
    QImage image(m_pFrameRGB->data[0], m_pVideoCodecCxt->width,
            m_pVideoCodecCxt->height, QImage::Format_ARGB32);

    //截圖
    if(m_bSnapshot)
    {
        QPixmap pixPicture = QPixmap::fromImage(image);

        QString sPath = "./snapshot/";
        QDate date = QDate::currentDate();
        QTime time = QTime::currentTime();
        m_sSnapPath = QString("%1%2-%3-%4-%5%6%7.jpg").arg(sPath).arg(date.year()). \
                arg(date.month()).arg(date.day()).arg(time.hour()).arg(time.minute()). \
                arg(time.second());
        MY_DEBUG << "Snapshot... m_sSnapPath:" << m_sSnapPath;
        pixPicture.save(m_sSnapPath, "jpg");
        m_bSnapshot = false;
    }

    emit sig_getImage(image);

    m_mutex.unlock();

    return 0;
}

int ctFFmpeg::getAudioFrame(char *pOut)
{
    m_mutex.lock();

    uint8_t  *pData[1];
    pData[0] = (uint8_t *)pOut;

    //獲取目標(biāo)樣本數(shù)
    auto nDstNbSamples = av_rescale_rnd(m_pPcmFrame->nb_samples,
                                            m_nAudioPlaySampleRate,
                                            m_nAudioSampleRate,
                                            AV_ROUND_ZERO);

    //重采樣
    int nLen = swr_convert(m_pAudioSwrContext, pData, nDstNbSamples,
                          (const uint8_t **)m_pPcmFrame->data,
                          m_pPcmFrame->nb_samples);
    if(nLen <= 0)
    {
        MY_DEBUG << "swr_convert error";
        m_mutex.unlock();
        return -1;
    }

    //獲取樣本保存的緩存大小
    int nOutsize = av_samples_get_buffer_size(nullptr, m_pAudioCodecCxt->channels,
                                             m_pPcmFrame->nb_samples,
                                             AV_SAMPLE_FMT_S16,
                                             0);
    m_mutex.unlock();

    return nOutsize;
}

void ctFFmpeg::Snapshot()
{
    m_bSnapshot = true;
}

int ctFFmpeg::InitVideo()
{
    if(m_nVideoIndex == -1)
    {
        MY_DEBUG << "m_nVideoIndex == -1 error";
        return -1;
    }

    //查找視頻解碼器
    const AVCodec *pAVCodec = avcodec_find_decoder(m_pAVFmtCxt->streams[m_nVideoIndex]->codecpar->codec_id);
    if(!pAVCodec)
    {
        MY_DEBUG << "video decoder not found";
        return -1;
    }

    //視頻解碼器參數(shù)配置
    m_CodecId = pAVCodec->id;
    if(!m_pVideoCodecCxt)
        m_pVideoCodecCxt = avcodec_alloc_context3(nullptr);
    if(nullptr == m_pVideoCodecCxt)
    {
        MY_DEBUG << "avcodec_alloc_context3 error m_pVideoCodecCxt=nullptr";
        return -1;
    }
    avcodec_parameters_to_context(m_pVideoCodecCxt, m_pAVFmtCxt->streams[m_nVideoIndex]->codecpar);
    if(m_pVideoCodecCxt)
    {
        if (m_pVideoCodecCxt->width == 0 || m_pVideoCodecCxt->height == 0)
        {
            MY_DEBUG << "m_pVideoCodecCxt->width=0 or m_pVideoCodecCxt->height=0 error";
            return -1;
        }
    }

    //打開視頻解碼器
    int nRet = avcodec_open2(m_pVideoCodecCxt, pAVCodec, nullptr);
    if(nRet < 0)
    {
        MY_DEBUG << "avcodec_open2 video error";
        return -1;
    }

    //申請(qǐng)并分配內(nèi)存,初始化轉(zhuǎn)換上下文,用于YUV轉(zhuǎn)RGB
    m_pOutBuffer = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_BGRA,
                                                 m_pVideoCodecCxt->width, m_pVideoCodecCxt->height, 1));
    if(nullptr == m_pOutBuffer)
    {
        MY_DEBUG << "nullptr == m_pOutBuffer error";
        return -1;
    }

    if(nullptr == m_pFrameRGB)
        m_pFrameRGB = av_frame_alloc();
    if(nullptr == m_pFrameRGB)
    {
        MY_DEBUG << "nullptr == m_pFrameRGB error";
        return -1;
    }
    av_image_fill_arrays(m_pFrameRGB->data, m_pFrameRGB->linesize, m_pOutBuffer,
                                    AV_PIX_FMT_BGRA, m_pVideoCodecCxt->width, m_pVideoCodecCxt->height, 1);

    m_pVideoSwsContext = sws_getContext(m_pVideoCodecCxt->width, m_pVideoCodecCxt->height,
                                   m_pVideoCodecCxt->pix_fmt, m_pVideoCodecCxt->width, m_pVideoCodecCxt->height,
                                   AV_PIX_FMT_BGRA, SWS_FAST_BILINEAR, NULL, NULL, NULL);
    if(nullptr == m_pVideoSwsContext)
    {
        MY_DEBUG << "nullptr == m_pVideoSwsContex error";
        return -1;
    }
    return 0;
}

int ctFFmpeg::InitAudio()
{
    if(m_nAudioIndex == -1)
    {
        MY_DEBUG << "m_nAudioIndex == -1";
        return -1;
    }

    //查找音頻解碼器
    const AVCodec *pAVCodec = avcodec_find_decoder(m_pAVFmtCxt->streams[m_nAudioIndex]->codecpar->codec_id);
    if(!pAVCodec)
    {
        MY_DEBUG << "audio decoder not found";
        return -1;
    }

    //音頻解碼器參數(shù)配置
    if (!m_pAudioCodecCxt)
        m_pAudioCodecCxt = avcodec_alloc_context3(nullptr);
    if(nullptr == m_pAudioCodecCxt)
    {
        MY_DEBUG << "avcodec_alloc_context3 error m_pAudioCodecCxt=nullptr";
        return -1;
    }
    avcodec_parameters_to_context(m_pAudioCodecCxt, m_pAVFmtCxt->streams[m_nAudioIndex]->codecpar);

    //打開音頻解碼器
    int nRet = avcodec_open2(m_pAudioCodecCxt, pAVCodec, nullptr);
    if(nRet < 0)
    {
        avcodec_close(m_pAudioCodecCxt);
        MY_DEBUG << "avcodec_open2 error m_pAudioCodecCxt";
        return -1;
    }

    //音頻重采樣初始化
    if (nullptr == m_pAudioSwrContext)
    {
        if(m_pAudioCodecCxt->channel_layout <= 0 || m_pAudioCodecCxt->channel_layout > 3)
            m_pAudioCodecCxt->channel_layout = 1;

        qDebug() << "m_audioCodecContext->channel_layout:" << m_pAudioCodecCxt->channel_layout;
        qDebug() << "m_audioCodecContext->channels:" << m_pAudioCodecCxt->channels;

        m_pAudioSwrContext = swr_alloc_set_opts(0,
                                               m_pAudioCodecCxt->channel_layout,
                                               AV_SAMPLE_FMT_S16,
                                               m_pAudioCodecCxt->sample_rate,
                                               av_get_default_channel_layout(m_pAudioCodecCxt->channels),
                                               m_pAudioCodecCxt->sample_fmt,
                                               m_pAudioCodecCxt->sample_rate,
                                               0,
                                               0);
        auto nRet = swr_init(m_pAudioSwrContext);
        if(nRet < 0)
        {
            MY_DEBUG << "swr_init error";
            return -1;
        }
    }

    //音頻播放設(shè)備初始化
    int nSampleSize = 16;
    switch (m_pAudioCodecCxt->sample_fmt)//樣本大小
    {
        case AV_SAMPLE_FMT_S16:
            nSampleSize = 16;
            break;
        case  AV_SAMPLE_FMT_S32:
            nSampleSize = 32;
        default:
            break;
    }

    m_nAudioSampleRate = m_pAudioCodecCxt->sample_rate;
    ctAudioPlayer::getInstance().m_nSampleRate = m_nAudioSampleRate;//采樣率
    ctAudioPlayer::getInstance().m_nChannelCount = m_pAudioCodecCxt->channels;//通道數(shù)
    ctAudioPlayer::getInstance().m_nSampleSize = nSampleSize;//樣本大小
    if(!ctAudioPlayer::getInstance().Init())
        return -1;

    return 0;
}

4.以下是opengl處理的相關(guān)代碼:ctopenglwidget.h、ctopenglwidget.cpp

ctopenglwidget.h

#ifndef CTOPENGLWIDGET_H
#define CTOPENGLWIDGET_H

#include <QOpenGLWidget>

class ctOpenglWidget : public QOpenGLWidget
{
    Q_OBJECT
public:
    ctOpenglWidget(QWidget *parent = nullptr);
    ~ctOpenglWidget();

protected:
    void paintEvent(QPaintEvent *e);

private slots:
    void slot_showImage(const QImage& image);

private:
    QImage m_image;

};

#endif // CTOPENGLWIDGET_H

ctopenglwidget.cpp

#include "ctopenglwidget.h"
#include <QPainter>
#include "commondef.h"

ctOpenglWidget::ctOpenglWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}

ctOpenglWidget::~ctOpenglWidget()
{
}

void ctOpenglWidget::paintEvent(QPaintEvent *e)
{
    Q_UNUSED(e)
    QPainter painter;

    painter.begin(this);//清理屏幕
    painter.drawImage(QPoint(0, 0), m_image);//繪制FFMpeg解碼后的視頻
    painter.end();
}

void ctOpenglWidget::slot_showImage(const QImage &image)
{
    if(image.width() > image.height())
        m_image = image.scaledToWidth(width(),Qt::SmoothTransformation);
    else
        m_image = image.scaledToHeight(height(),Qt::SmoothTransformation);

    update();
}

4.以下是QAudioOutput音頻播放器處理的相關(guān)代碼:ctaudioplayer.h、ctaudioplayer.cpp

ctaudioplayer.h

#ifndef CTAUDIOPLAYER_H
#define CTAUDIOPLAYER_H

#include <QObject>
#include <QAudioDeviceInfo>
#include <QAudioFormat>
#include <QAudioOutput>
#include <QMutex>
#include "commondef.h"

class ctAudioPlayer
{
public:
    ctAudioPlayer();
    ~ctAudioPlayer();

    static ctAudioPlayer& getInstance();
    bool Init();
    void DeInit();
    void isPlay(bool bPlay);
    void Write(const char *pData, int nDatasize);
    int getFreeSize();

public:
    int m_nSampleRate = 8000;//采樣率
    int m_nSampleSize = 16;//采樣大小
    int m_nChannelCount = 1;//通道數(shù)
private:
    QAudioDeviceInfo m_audio_device;
    QAudioOutput* m_pAudioOut = nullptr;
    QIODevice* m_pIODevice = nullptr;
    QMutex m_mutex;
};

#endif // CTAUDIOPLAYER_H

ctaudioplayer.cpp

#include "ctaudioplayer.h"

ctAudioPlayer::ctAudioPlayer()
{

}

ctAudioPlayer::~ctAudioPlayer()
{

}

ctAudioPlayer &ctAudioPlayer::getInstance()
{
    static ctAudioPlayer s_obj;
    return s_obj;
}

bool ctAudioPlayer::Init()
{
    DeInit();

    m_mutex.lock();

    MY_DEBUG << "m_nSampleRate:" << m_nSampleRate;
    MY_DEBUG << "m_nSampleSize:" << m_nSampleSize;
    MY_DEBUG << "m_nChannelCount:" << m_nChannelCount;

    m_audio_device = QAudioDeviceInfo::defaultOutputDevice();
    MY_DEBUG << "m_audio_device.deviceName():" << m_audio_device.deviceName();
    if(m_audio_device.deviceName().isEmpty())
    {
        return false;
    }

    QAudioFormat format;
    format.setSampleRate(m_nSampleRate);
    format.setSampleSize(m_nSampleSize);
    format.setChannelCount(m_nChannelCount);
    format.setCodec("audio/pcm");
    format.setByteOrder(QAudioFormat::LittleEndian);
    format.setSampleType(QAudioFormat::UnSignedInt);

    if(!m_audio_device.isFormatSupported(format))
    {
        MY_DEBUG << "QAudioDeviceInfo format No Supported.";
        //format = m_audio_device.nearestFormat(format);
        m_mutex.unlock();
        return false;
    }

    if(m_pAudioOut)
    {
        m_pAudioOut->stop();
        delete m_pAudioOut;
        m_pAudioOut = nullptr;
        m_pIODevice = nullptr;
    }
    m_pAudioOut = new QAudioOutput(format);
    m_pIODevice = m_pAudioOut->start();

    m_mutex.unlock();
    return true;
}

void ctAudioPlayer::DeInit()
{
    m_mutex.lock();

    if(m_pAudioOut)
    {
        m_pAudioOut->stop();
        delete m_pAudioOut;
        m_pAudioOut = nullptr;
        m_pIODevice = nullptr;
    }

    m_mutex.unlock();
}

void ctAudioPlayer::isPlay(bool bPlay)
{
    m_mutex.lock();

    if(!m_pAudioOut)
    {
        m_mutex.unlock();
        return;
    }

    if(bPlay)
        m_pAudioOut->resume();//恢復(fù)播放
    else
        m_pAudioOut->suspend();//暫停播放

    m_mutex.unlock();
}

void ctAudioPlayer::Write(const char *pData, int nDatasize)
{
    m_mutex.lock();

    if(m_pIODevice)
        m_pIODevice->write(pData, nDatasize);//將獲取的音頻寫入到緩沖區(qū)中

    m_mutex.unlock();
}

int ctAudioPlayer::getFreeSize()
{
    m_mutex.lock();

    if(!m_pAudioOut)
    {
        m_mutex.unlock();
        return 0;
    }
    int nFreeSize = m_pAudioOut->bytesFree();//剩余空間

    m_mutex.unlock();

    return nFreeSize;
}

三、測試成果

1.實(shí)時(shí)流

qt ffmpeg,音視頻,ffmpeg,qt,音視頻

2.文件流

qt ffmpeg,音視頻,ffmpeg,qt,音視頻

2.錄像截圖

qt ffmpeg,音視頻,ffmpeg,qt,音視頻

3.音頻測試

qt ffmpeg,音視頻,ffmpeg,qt,音視頻

四、demo下載

下載鏈接:https://download.csdn.net/download/linyibin_123/87435635

五、參考

https://blog.csdn.net/qq871580236/article/details/120364013文章來源地址http://www.zghlxwxcb.cn/news/detail-780646.html

到了這里,關(guān)于用Qt開發(fā)的ffmpeg流媒體播放器,支持截圖、錄像,支持音視頻播放,支持本地文件播放、網(wǎng)絡(luò)流播放的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 【FAQ】H.265視頻無插件流媒體播放器EasyPlayer.js播放webrtc斷流重連的異常修復(fù)

    【FAQ】H.265視頻無插件流媒體播放器EasyPlayer.js播放webrtc斷流重連的異常修復(fù)

    H5無插件流媒體播放器EasyPlayer屬于一款高效、精煉、穩(wěn)定且免費(fèi)的流媒體播放器,可支持多種流媒體協(xié)議播放,可支持H.264與H.265編碼格式,性能穩(wěn)定、播放流暢,能支持WebSocket-FLV、HTTP-FLV,HLS(m3u8)、WebRTC等格式的視頻流,并且已實(shí)現(xiàn)網(wǎng)頁端實(shí)時(shí)錄像、在iOS上實(shí)現(xiàn)低延時(shí)直

    2024年02月11日
    瀏覽(92)
  • 使用EasyDarwin + ffmpeg 搭建流媒體服務(wù)器,實(shí)現(xiàn)多臺(tái)智能電視同步播放宣傳視頻

    使用EasyDarwin + ffmpeg 搭建流媒體服務(wù)器,實(shí)現(xiàn)多臺(tái)智能電視同步播放宣傳視頻

    近期單位用戶提出需求,需要在單位內(nèi)部的9臺(tái)安卓智能電視(小米電視)上同步播放用戶提供的宣傳視頻,希望能夠做到所有電視音視頻同步播放(電視均位于食堂內(nèi)部,使用內(nèi)置揚(yáng)聲器,各電視間音頻延遲不同會(huì)導(dǎo)致混響) 。 由于電視在安裝時(shí)只預(yù)留了電源線,使用HDM

    2024年02月10日
    瀏覽(105)
  • QT軟件開發(fā)-基于FFMPEG設(shè)計(jì)視頻播放器-軟解圖像(一)

    QT軟件開發(fā)-基于FFMPEG設(shè)計(jì)視頻播放器-軟解圖像(一)

    QT軟件開發(fā)-基于FFMPEG設(shè)計(jì)視頻播放器-CPU軟解視頻(一) https://xiaolong.blog.csdn.net/article/details/126832537 QT軟件開發(fā)-基于FFMPEG設(shè)計(jì)視頻播放器-GPU硬解視頻(二) https://xiaolong.blog.csdn.net/article/details/126833434 QT軟件開發(fā)-基于FFMPEG設(shè)計(jì)視頻播放器-解碼音頻(三) https://xiaolong.blog.csdn.

    2023年04月08日
    瀏覽(93)
  • Qt/C++音視頻開發(fā)51-推流到各種流媒體服務(wù)程序

    Qt/C++音視頻開發(fā)51-推流到各種流媒體服務(wù)程序

    最近將推流程序完善了很多功能,尤其是增加了對(duì)多種流媒體服務(wù)程序的支持,目前支持mediamtx、LiveQing、EasyDarwin、nginx-rtmp、ZLMediaKit、srs、ABLMediaServer等,其中經(jīng)過大量的對(duì)比測試,個(gè)人比較建議使用mediamtx和ZLMediaKit,因?yàn)檫@兩者支持的格式眾多,不僅同時(shí)支持rtsp/rtmp推流,

    2024年02月09日
    瀏覽(27)
  • linux+nginx-http-flv-module+ffmpeg實(shí)現(xiàn)搭建簡易流媒體服務(wù)器將rtsp流轉(zhuǎn)flv格式在web端和微信小程序?qū)崟r(shí)播放監(jiān)控視頻

    linux+nginx-http-flv-module+ffmpeg實(shí)現(xiàn)搭建簡易流媒體服務(wù)器將rtsp流轉(zhuǎn)flv格式在web端和微信小程序?qū)崟r(shí)播放監(jiān)控視頻

    一.介紹背景 公司項(xiàng)目開發(fā)需求:將海康攝像頭的rtsp流在web端及微信小程序端進(jìn)行播放。之前我寫過一篇關(guān)于web端使用webtrc+videojs播放rtsp流的文章,確實(shí)能夠解決web端播放rtsp流的需求,但是這次多加了一個(gè)微信小程序....所以要考慮小程序的播放問題。本著探索實(shí)踐的精神在

    2024年02月08日
    瀏覽(38)
  • 安卓手機(jī)APP開發(fā)__媒體開發(fā)部分__播放器的接口

    安卓手機(jī)APP開發(fā)__媒體開發(fā)部分__播放器的接口 目錄 概述 組件之間的共同的接口 媒體3的播放的架構(gòu) 播放器的狀態(tài) 對(duì)改變的監(jiān)聽 當(dāng)前的播放 定制的播放器實(shí)現(xiàn) 一個(gè)播放器是你的APP中完成媒體項(xiàng)的播放的組件. 媒體3的播放器接口提供了一個(gè)關(guān)于播放器處理的功能的總結(jié). 這包

    2024年04月24日
    瀏覽(26)
  • FFmpeg流媒體處理的收流與推流

    FFmpeg流媒體處理的收流與推流

    1. 簡介 流媒體是使用了流式傳輸?shù)亩嗝襟w應(yīng)用技術(shù)。如下是維基百科關(guān)于流媒體概念的定義: 流媒體 (streaming media) 是指將一連串的媒體數(shù)據(jù)壓縮后,經(jīng)過網(wǎng)絡(luò)分段發(fā)送數(shù)據(jù),在網(wǎng)絡(luò)上即時(shí)傳輸影音以供觀賞的一種技術(shù)與過程,此技術(shù)使得數(shù)據(jù)包得以像流水一樣發(fā)送;如果不

    2024年02月11日
    瀏覽(27)
  • FFmpeg 的使用與Docker安裝流媒體服務(wù)器

    FFmpeg 的使用與Docker安裝流媒體服務(wù)器

    本文闡述的均為命令行的使用方式,并不牽扯FFmpeg 的 C++音視頻開發(fā)內(nèi)容,補(bǔ)充一句,C++的資料真的少,能把C++學(xué)好的人,我真的是覺得巨佬。 我主要是使用FFmpeg 推流方面的知識(shí),案例大都是靠近這方面。 一、FFmpeg 介紹 本文讀者會(huì)比較小眾~ 看到此文的朋友,大概率是需要

    2024年01月17日
    瀏覽(24)
  • opencv+ffmpeg+QOpenGLWidget開發(fā)的音視頻播放器demo

    opencv+ffmpeg+QOpenGLWidget開發(fā)的音視頻播放器demo

    ????本篇文檔的demo包含了 1.使用OpenCV對(duì)圖像進(jìn)行處理,對(duì)圖像進(jìn)行置灰,旋轉(zhuǎn),摳圖,高斯模糊,中值濾波,部分區(qū)域清除置黑,背景移除,邊緣檢測等操作;2.單純使用opencv播放顯示視頻;3.使用opencv和openGL播放顯示視頻;4.在ffmpeg解碼后,使用opencv顯示視頻,并支持對(duì)視

    2024年02月12日
    瀏覽(34)
  • 【音視頻流媒體】 3、ffmpeg、ffplay、ffprobe 超詳細(xì)介紹

    【音視頻流媒體】 3、ffmpeg、ffplay、ffprobe 超詳細(xì)介紹

    ffmpeg中文文檔 常見測試視頻 ffmpeg 大神筆記 ffmpeg 的 release 是 Linux Static Build ,即靜態(tài)庫(各庫已被打包到可執(zhí)行程序中),而不是動(dòng)態(tài)鏈接庫(共享機(jī)器上的 so),所以非常方便部署(sqlite 也是這樣),這是ffmpeg官網(wǎng)下載地址 rtsp-simple-server用法 注意,該命令會(huì)找時(shí)間點(diǎn)附近

    2024年02月08日
    瀏覽(91)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包