FFmpeg 开启QSV硬解加速

简介

       QSV 全称:Quick Sync Video Acceleratio ,是Intel媒体开发库(The Intel® Media Software Development Kit)提供了一个对数字视频的通用解决方案,该解决方案支持多种图形平台(graphics platforms),实现了通用功能,能对数字视频进行预处理、编解码、以及不同编码格式的转换。

环境配置  

       1.安装intel media sdk ,官网下载后直接双击安装即可

       2.下载mfx源码: https://github.com/lu-zero/mfx_dispatch.git,下载完成后使用msys2进行编译,分别执行以下命令

autoreconf -i
./configure --prefix=/mingw64
make -j$(nproc) install

       3.下载ffmpeg源码 :https://github.com/FFmpeg/FFmpeg.git,下载完成后使用msys2进行编译,添加--enable-libmfx选项。设置PKG_CONFIG_PATH变量(默认安装的话  export PKG_CONFIG_PATH=/usr/local/lib/pkgconfig)具体路径时编译intel media sdk 时生成的pkgconfig/libmfx.pc 所在位置

添加以下参数:

  --enable-libmfx

  --enable-encoder=h264_qsv

  --enable-decoder=h264_qsv

(注意使用硬解码的时候应该关闭多路复解--disable-demuxers)

完整命令(prefix 指定安装目录,执行make install 后的结果会放在prefix目录下 工 bin 、include 、lib 、 share 4个文件夹,make - 8 指定多核编译):

./configure --enable-shared 
            --prefix=./build  
            --enable-libmfx 
            --enable-encoder=h264_qsv 
            --enable-decoder=h264_qsv 
            --disable-demuxers

make -j8 
make install
make clean

 

代码调用

//加入头文件,由于ffmpeg是C语言完成的,导入头文件要加入extern "C" {}
extern "C" {


#include "libavutil/avutil.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
#include "libavutil/time.h"

}
//准备ffmpeg解码

    av_register_all(); // 注册支持的文件格式及对应的codec
    avformat_network_init();
    //m_url 表示视频源,可以是rtsp地址,也可以是文件路径(文件路径不能存在中文)
    QByteArray byte = m_url.toLatin1();
    char *m_rtsp = byte.data();

   
    pFormatCtx = nullptr;
    options = NULL;
    firstFramePts = 0;
    packet = nullptr;
    qDebug()<<"start "<<m_rtsp;
    AVDictionary* options = NULL;
    // 设置连接方式
    av_dict_set(&options, "rtsp_transport", "tcp", 0);
    //设置buffer_size,提高画质,减少花屏现象
    av_dict_set(&options, "buffer_size", "2048000", 0);
    //如设置20s超时:
    av_dict_set(&options, "stimeout", "20000000", 0);
    //设置超时断开连接时间
    av_dict_set(&options, "max_delay", "500000", 0);
    av_dict_set(&options, "genpts", "10", 0);

    // 读取文件头,将格式相关信息存放在AVFormatContext结构体中
    if (avformat_open_input(&pFormatCtx, m_rtsp, nullptr, &options) != 0)
    {
        qDebug()<<"open input failed:"<<m_rtsp;
        if(pTimer)
        {
            pTimer->stop();
        }
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ; // 打开失败
    }

    // 检测文件的流信息

    QFFmpegThreadMutexManager::instance()->lock();
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        QFFmpegThreadMutexManager::instance()->unlock();
        qDebug()<<"avformat_find_stream_info filed";
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ; // 没有检测到流信息 stream infomation
    }
    QFFmpegThreadMutexManager::instance()->unlock();

    // 在控制台输出文件信息
    av_dump_format(pFormatCtx, 0, m_rtsp, 0);

    //查找视频流 video stream
    videoStream = -1;
    for (int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
            break;
        }
    }
    if (videoStream == -1)
    {
        qDebug()<<"not fint video stream";
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ; // 没有找到视频流video stream
    }
    pCodecCtxOrg = nullptr;
    pCodecCtx = nullptr;
    pCodec = nullptr;
    pCodecCtxOrg = pFormatCtx->streams[videoStream]->codec; // codec context
    // 找到video stream的 decoder
    QFFmpegThreadMutexManager::instance()->lock();
//    pCodec = avcodec_find_decoder(pCodecCtxOrg->codec_id);//h264_qsv h264_dxva2
     pCodec = avcodec_find_decoder_by_name("h264_qsv");
    QFFmpegThreadMutexManager::instance()->unlock();
    if (!pCodec)
    {
        qDebug()<<"not find dcoder";
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ;
    }
    // 不直接使用从AVFormatContext得到的CodecContext,要复制一个
    pCodecCtx = avcodec_alloc_context3(pCodec);
    av_opt_set(pCodecCtx->priv_data, "tune", "zerolatency", 0);
    if (avcodec_copy_context(pCodecCtx, pCodecCtxOrg) != 0)
    {
        qDebug() << "Could not copy codec context!" ;
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ;
    }

    if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
    {
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ; // Could open codec
    }
    pFrame = nullptr;
    pFrameRGB = nullptr;

    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();
    // 使用的缓冲区的大小
    int numBytes = 0;
    buffer = nullptr;

    numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);
    buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));

    avpicture_fill((AVPicture*)pFrameRGB, buffer, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);

    qDebug()<<"pCodecCtx->pix_fmt:"<<pCodecCtx->pix_fmt;
    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, /* pCodecCtx->pix_fmt,*/ AV_PIX_FMT_NV12, //输入格式
                             pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BILINEAR, nullptr, nullptr, nullptr);

    packet = av_packet_alloc();



    // 拉流解码
    while(1)
    {
        QFFmpegThreadMutexManager::instance()->lock();//如果不加锁,多视频流时可能会导致内存崩溃
        int ret = av_read_frame(pFormatCtx, packet);
        QFFmpegThreadMutexManager::instance()->unlock();
        if(ret >= 0)
        {
             if (packet->stream_index == videoStream)
            {
                QFFmpegThreadMutexManager::instance()->lock();
                int frameFinished = 0;
                avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, packet);
                QFFmpegThreadMutexManager::instance()->unlock();
                //暂时没做显卡直接显示视频帧,所以拷贝到内存后显示
                if (frameFinished)
                {
                    sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0,
                              pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);
    
                    QImage img((uchar *)buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);
    
                    if(!img.isNull())
                    {
                        emit sigShowFrame(img);
                    }else{
                        qDebug()<<"get img error";
                    }
                }
                else{
                    qDebug()<<"no output frame";
                }
            }
            else
            {
                if(player::PLAY_FILE == playType)
                    onReadFrame();
            }
            av_free_packet(packet);
    
        }
        else
        {
    
            av_packet_unref(packet);
            emit signalPlayStatus(player::ERROR);
            free_player();
            return break;
    
        }
    }

注意:ffmpeg使用qsv硬解码出来的视频帧格式是AV_PIX_FMT_NV12格式的,在调用sws_getContext函数时第三个参数必须强制传入AV_PIX_FMT_NV12,否则会导致视频数据转换rgb时失败(也是没做显卡显示,否则不会有这一步),此版本完成后的效果是可以运用GPU进行解码(从GPU-Z中集显得系统内存使用可以看出,GPU负载一直是0),但是CPU消耗反而更严重了,初步判定是从GPU向CPU拷贝数据占用了大量资源。

完整代码(代码是从项目中截取的,可能有些结构体或函数无效):

playerutils.hpp
#ifndef PLAYERUTILS_HPP
#define PLAYERUTILS_HPP
extern "C" {


#include "libavutil/avutil.h"
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
#include "libswscale/swscale.h"
#include "libavutil/imgutils.h"
#include "libavutil/opt.h"
#include "libavutil/time.h"

}
#include <QPushButton>
namespace MediaTool {
enum TOOLBTN{
    NOT,
    // rtsp
    RECORD_START,     //开始录制
    RECORD_STOP,      //结束录制
    SCREENSHOT,       //截图拍照
    CLOSE,            //关闭
    //file

    PLAY,              //播放
    STOP,              //停止
    PAUSE,             //暂停
    REPLAY,            //继续播放
    SPEEDON,           //加速播放
    SPEEDDOWN,         //减速播放
    SIGLEFRAME,        //单帧(下一帧)
    PLAYSEEK,          //跳转
};
struct ToolBtn{
    QPushButton* btn;
    bool isDoubleStatusBtn; // 是否是双态按钮
    int uuid0;
    QString text0;
    QString resource0;
    QString resourcePress0;
    // 普通按钮以下无效
    int uuid1;
    QString text1;
    QString resource1;
    QString resourcePress1;
    int selectID;

};
}
namespace player {

enum PlayType{
    NOT,
    PLAY_RTSP,   //rtsp直播流
    PLAY_FILE,   //file 回放

};
enum PlayStatus{  //播放状态
    PLAYING,
    FINISH,
    ERROR
 };
}
#endif // PLAYERUTILS_HPP

 QFFmpegThreadMutexManager.h

#ifndef QFFMPEGTHREADMUTEXMANAGER_H
#define QFFMPEGTHREADMUTEXMANAGER_H

#include <QObject>
#include <QMutex>
#include <QMutex>

/// \brief The QFFmpegThreadMutexManager class
///   单例模式
///   了解到ffmpeg 如果一个线程调用了avcodec_open(),
///   但还没有调用avcodec_close(),
///   此时再有一个线程来调用avcodec_open(),就可能会发生错误
///   需要给使用ffmpeg的地方使用同一个线程锁
///
class QFFmpegThreadMutexManager : public QObject
{
    Q_OBJECT
private:
    explicit QFFmpegThreadMutexManager();
    static QFFmpegThreadMutexManager* p;
public:
    ~QFFmpegThreadMutexManager();
    static QFFmpegThreadMutexManager* instance();
    void initMutex();
    void lock();
    void unlock();
    bool trylock();
    QMutex* getMutex();

private:
    QMutex* mutex;
};

#endif // QFFMPEGTHREADMUTEXMANAGER_H

 QFFmpegThreadMutexManager.cpp

#include "QFFmpegThreadMutexManager.h"

QFFmpegThreadMutexManager *QFFmpegThreadMutexManager:: p = new QFFmpegThreadMutexManager();
QFFmpegThreadMutexManager::QFFmpegThreadMutexManager()
{
    mutex = nullptr;
    initMutex();
}

QFFmpegThreadMutexManager::~QFFmpegThreadMutexManager()
{
    if(mutex)
        delete mutex;
}

QFFmpegThreadMutexManager *QFFmpegThreadMutexManager::instance()
{
    return p;
}

void QFFmpegThreadMutexManager::initMutex()
{
    if(!mutex)
        mutex = new QMutex;
}

void QFFmpegThreadMutexManager::lock()
{
    if(mutex)
        mutex->lock();
}

void QFFmpegThreadMutexManager::unlock()
{
    if(mutex)
        mutex->unlock();
}

bool QFFmpegThreadMutexManager::trylock()
{
    if(mutex)
        return mutex->tryLock();
    else
        return false;
}

QMutex *QFFmpegThreadMutexManager::getMutex()
{
    initMutex();
    return mutex;
}
FFmpegDecoder.h
#ifndef FFMPEGDECODERFILE_H
#define FFMPEGDECODERFILE_H

#include <QObject>
#include <QThread>
#include <QTimer>
#include <QMutex>
#include <QDateTime>
#include <QImage>
#include <QQueue>
#include <playerutils.hpp>
#include <QFFmpegThreadMutexManager.h>
#include <Recording.h>


#define  MAXFRAMEBUFFER 5 //最大缓存帧
typedef struct DecodeContext {
    AVBufferRef *hw_device_ref;
} DecodeContext;

class FFmpegDecoder : public QObject
{
    Q_OBJECT
public:
    explicit FFmpegDecoder(QObject *parent = 0);
    ~FFmpegDecoder();
    void SetUrl(QString url);
    void Start(); // 开始播放
    void setPlayType(player::PlayType type); //设置播放类型 rtsp/file
    // 仅对播放文件有效
    void Stop();  // 停止播放
    void Pause(); //暂停
    void Starting();// 用于暂停后的继续播放
    void SetSpeed(double speed);//设置播放速度
    void NextFrame();
    void Seek(int pts);//跳转


private slots:
    void slotSetUrl(QString url);
    void slotStartPlay();
    void slotStop();
    void slotSetPlayType(player::PlayType type);

    bool onReadFrame();
    void onPause(); //暂停
    void onStarting();// 用于暂停后的继续播放
    void onSetSpeed(double speed);//设置播放速度
    void onNextFrame();
    void onSeek(int pts);//跳转



signals:
    void signalTimerStart();
    void signalTimerStop();

    void signalSetUrl(QString);
    void signalStart();
    void signalSetPlayType(player::PlayType);
    void signalStop();  // 停止播放
    void signalPause(); //暂停
    void signalStarting();// 用于暂停后的继续播放
    void signalSetSpeed(double speed);//设置播放速度
    void signalNextFrame();
    void signalSeek(int pts);//跳转
    //解码出来的视频帧
    void sigShowFrame(QImage);

    //更新界面播放value 为播放位置 maxValue 为视频长度(默认为-1 表示播放过程中,不用设置)
    void signalPlayProgress(int value,int maxValue = -1); //
    void signalRecordFinished();//录制完成,界面刷新历史视频列表
    void signalPlayStatus(player::PlayStatus status); // 播放状态


private:
    void StartReadFrame();
    void free_player(); //释放播放器资源
    void saveH264Stream(AVPacket* pkg);//保存h264纯码流
    bool mkSavePath(QString path);// 根据传入文件名判断路径是否存在,不存在则创建

private:
    int videoControl(); //读取视频帧前每次去读取参数,实现视频控制(rtsp),返回0表示继续播放 返回1 表示结束播放
public:            // 播放rtsp视频时控制视频播放
    MediaTool::TOOLBTN ctrlParam; //控制视频参数
    player::PlayType playType;

    bool isRecord; // 是否录制
    QString recordFileName; //录制视频保存路径


private:
    QThread* pThread;
    QString         m_url;
    AVFormatContext* pFormatCtx;
    AVOutputFormat* pOutFormat;
    AVDictionary* options;
    AVCodecContext* pCodecCtxOrg;
    AVCodecContext* pCodecCtx;
    AVCodec* pCodec;
    AVFrame* pFrame;
    AVFrame* pFrameRGB;
    uint8_t* buffer;
    AVStream *pStream;
    AVPacket* packet;
    SwsContext* sws_ctx = nullptr;
    int videoStream;
    QTimer* pTimer;

    qint64 duration; //视频长度
    int firstFramePts; //第一帧时间

    double frameRate;
    int num = 0;
    Recording* pRecord;


};

#endif // FFmpegDecoderFile_H
FFmpegDecoder.cpp
#include "FFmpegDecoder.h"
#include <QDebug>
#include <QDir>

FFmpegDecoder::FFmpegDecoder(QObject *parent) :
    QObject(parent),
    playType(player::PLAY_RTSP),
    pFrameRGB(nullptr),
    pFrame(nullptr),
    packet(nullptr),
    pCodecCtx(nullptr),
    pCodecCtxOrg(nullptr),
    pFormatCtx(nullptr),
    pTimer(nullptr),
    pRecord(nullptr),
    isRecord(false),
    firstFramePts(0),
    pThread(new QThread)
{
    qRegisterMetaType<player::PlayStatus>("player::PlayStatus");
    qRegisterMetaType<player::PlayType>("player::PlayType");
    connect(this,&FFmpegDecoder::signalSetUrl,this,&FFmpegDecoder::slotSetUrl);
    connect(this,&FFmpegDecoder::signalStart,this,&FFmpegDecoder::slotStartPlay);
    connect(this,&FFmpegDecoder::signalSetPlayType,this,&FFmpegDecoder::slotSetPlayType);
    connect(this,&FFmpegDecoder::signalStop,this,&FFmpegDecoder::slotStop);
    connect(this,&FFmpegDecoder::signalPause,this,&FFmpegDecoder::onPause);
    connect(this,&FFmpegDecoder::signalStarting,this,&FFmpegDecoder::onStarting);
    connect(this,&FFmpegDecoder::signalSetSpeed,this,&FFmpegDecoder::onSetSpeed);
    connect(this,&FFmpegDecoder::signalNextFrame,this,&FFmpegDecoder::onNextFrame);
    connect(this,&FFmpegDecoder::signalSeek,this,&FFmpegDecoder::onSeek);
    this->moveToThread(pThread);
    pThread->start();
}

FFmpegDecoder::~FFmpegDecoder()
{

    free_player();
    if(pTimer)
    {
        delete pTimer;
        pTimer = nullptr;
    }

    pThread->quit();
    pThread->wait();
    delete pThread;
}

void FFmpegDecoder::SetUrl(QString url)
{
    emit  signalSetUrl(url);
}

void FFmpegDecoder::Start()
{
    emit signalStart();
}

void FFmpegDecoder::setPlayType(player::PlayType type)
{
    emit signalSetPlayType(type);
}

void FFmpegDecoder::Stop()
{
    emit signalStop();
}

void FFmpegDecoder::Pause()
{
    emit signalPause();
}

void FFmpegDecoder::Starting()
{
    emit signalStarting();
}

void FFmpegDecoder::SetSpeed(double speed)
{
    emit signalSetSpeed(speed);
}

void FFmpegDecoder::NextFrame()
{
    emit signalNextFrame();
}

void FFmpegDecoder::Seek(int pts)
{
    emit signalSeek(pts);
}


void FFmpegDecoder::slotSetUrl(QString url)
{
    m_url = url;
}

void FFmpegDecoder::slotStartPlay()
{


    av_register_all(); // 注册支持的文件格式及对应的codec
    avformat_network_init();

    QByteArray byte = m_url.toLatin1();
    char *m_rtsp = byte.data();

    // 打开audio文件
    pFormatCtx = nullptr;
    options = NULL;
    firstFramePts = 0;
    packet = nullptr;
    qDebug()<<"start "<<m_rtsp;
    AVDictionary* options = NULL;
    // 设置连接方式
    av_dict_set(&options, "rtsp_transport", "tcp", 0);
    //设置buffer_size,提高画质,减少花屏现象
    av_dict_set(&options, "buffer_size", "2048000", 0);
    //如设置20s超时:
    av_dict_set(&options, "stimeout", "20000000", 0);
    //设置超时断开连接时间
    av_dict_set(&options, "max_delay", "500000", 0);
    av_dict_set(&options, "genpts", "10", 0);

    // 读取文件头,将格式相关信息存放在AVFormatContext结构体中
    if (avformat_open_input(&pFormatCtx, m_rtsp, nullptr, &options) != 0)
    {
        qDebug()<<"open input failed:"<<m_rtsp;
        if(pTimer)
        {
            pTimer->stop();
        }
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ; // 打开失败
    }

    // 检测文件的流信息

    QFFmpegThreadMutexManager::instance()->lock();
    if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
    {
        QFFmpegThreadMutexManager::instance()->unlock();
        qDebug()<<"avformat_find_stream_info filed";
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ; // 没有检测到流信息 stream infomation
    }
    QFFmpegThreadMutexManager::instance()->unlock();

    // 在控制台输出文件信息
    av_dump_format(pFormatCtx, 0, m_rtsp, 0);

    //查找视频流 video stream
    videoStream = -1;
    for (int i = 0; i < pFormatCtx->nb_streams; i++)
    {
        if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            videoStream = i;
            break;
        }
    }
    if (videoStream == -1)
    {
        qDebug()<<"not fint video stream";
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ; // 没有找到视频流video stream
    }
    pCodecCtxOrg = nullptr;
    pCodecCtx = nullptr;
    pCodec = nullptr;
    pCodecCtxOrg = pFormatCtx->streams[videoStream]->codec; // codec context
    // 找到video stream的 decoder
    QFFmpegThreadMutexManager::instance()->lock();
//     pCodec = avcodec_find_decoder(pCodecCtxOrg->codec_id);
     pCodec = avcodec_find_decoder_by_name("h264_qsv");

    QFFmpegThreadMutexManager::instance()->unlock();
    if (!pCodec)
    {
        qDebug()<<"not find dcoder";
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ;
    }
    // 不直接使用从AVFormatContext得到的CodecContext,要复制一个
    pCodecCtx = avcodec_alloc_context3(pCodec);
    av_opt_set(pCodecCtx->priv_data, "tune", "zerolatency", 0);
    if (avcodec_copy_context(pCodecCtx, pCodecCtxOrg) != 0)
    {
        qDebug() << "Could not copy codec context!" ;
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ;
    }

    if (avcodec_open2(pCodecCtx, pCodec, nullptr) < 0)
    {
        emit signalPlayStatus(player::ERROR);
        free_player();
        return ; // Could open codec
    }
    pFrame = nullptr;
    pFrameRGB = nullptr;

    pFrame = av_frame_alloc();
    pFrameRGB = av_frame_alloc();
    // 使用的缓冲区的大小
    int numBytes = 0;
    buffer = nullptr;

    numBytes = avpicture_get_size(AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);
    buffer = (uint8_t*)av_malloc(numBytes * sizeof(uint8_t));

    avpicture_fill((AVPicture*)pFrameRGB, buffer, AV_PIX_FMT_RGB32, pCodecCtx->width, pCodecCtx->height);

    sws_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_NV12,
                             pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_RGB32, SWS_BILINEAR, nullptr, nullptr, nullptr);

    packet = av_packet_alloc();
    av_read_play(pFormatCtx);
    //    AVStream *stream=pFormatCtx->streams[videoStream];
    //    qDebug()<<"pts:"<<stream->codec->framerate.num<<stream->codec->framerate.den;
    pStream=pFormatCtx->streams[videoStream];
    printf("---------------- File Information ---------------\n");
    av_dump_format(pFormatCtx, 0, m_rtsp, 0);
    printf("-------------------------------------------------\n");

    StartReadFrame();

}

void FFmpegDecoder::slotStop()
{
    if(player::PLAY_FILE == playType)
    {
        emit signalTimerStop();
        emit signalPlayStatus(player::FINISH);
        free_player();
    }
}

void FFmpegDecoder::slotSetPlayType(player::PlayType type)
{
    playType = type;
}

bool FFmpegDecoder::onReadFrame()
{
    if(!pFormatCtx || !packet || !pCodecCtx || !pFrameRGB || !pFrame)  return false;
    QFFmpegThreadMutexManager::instance()->lock();
    int ret = av_read_frame(pFormatCtx, packet);
    QFFmpegThreadMutexManager::instance()->unlock();
    if(ret >= 0)
    {
        if(isRecord)
        {
            AVPacket* pkt = av_packet_clone(packet);
            saveH264Stream(pkt);
        }else{
            if(pRecord)
            {
                qDebug()<<"finished record!";
                pRecord->Finished();
                QThread::msleep(10);
                delete pRecord;
                pRecord = nullptr;
                isRecord = false;
                emit signalRecordFinished();
            }
        }
        if (packet->stream_index == videoStream)
        {
            if(player::PLAY_FILE == playType)
            {
                int pts = packet->pts * av_q2d(pStream->time_base) * 1000;
                if(firstFramePts == 0)
                {
                    firstFramePts = pts;
                 }
                 // 发送播放进度
                emit signalPlayProgress(pts-firstFramePts);
            }
            //保存
            QFFmpegThreadMutexManager::instance()->lock();
            int frameFinished = 0;

             avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, packet);
            QFFmpegThreadMutexManager::instance()->unlock();
            if (frameFinished)
            {
 
                sws_scale(sws_ctx, (uint8_t const * const *)pFrame->data, pFrame->linesize, 0,
                          pCodecCtx->height, pFrameRGB->data, pFrameRGB->linesize);

                QImage img((uchar *)buffer,pCodecCtx->width,pCodecCtx->height,QImage::Format_RGB32);

                if(!img.isNull())
                {
                    emit sigShowFrame(img);
                }else{
                    qDebug()<<"get img error";
                }
            }
            else{
                qDebug()<<"no output frame";
            }
        }
        else
        {
            if(player::PLAY_FILE == playType)
                onReadFrame();
        }
        av_free_packet(packet);

    }
    else
    {

        qDebug()<<"play error..."<<playType;
        if(player::PLAY_FILE == playType)
        {
            emit signalTimerStop();
        }

        av_packet_unref(packet);
        emit signalPlayStatus(player::ERROR);
        free_player();
        return false;

    }
    return true;
}

void FFmpegDecoder::onPause()
{
    emit signalTimerStop();
}

void FFmpegDecoder::onStarting()
{
    emit signalTimerStart();
}

void FFmpegDecoder::onSetSpeed(double speed)
{
    onPause();

    if(pTimer)
    {
        pTimer->setInterval((1000.0/frameRate)/speed);
    }

    onStarting();
}

void FFmpegDecoder::onNextFrame()
{
    onPause();
    onReadFrame();
}

void FFmpegDecoder::onSeek(int pts)
{
    //    av_seek_frame().
    qDebug()<<"seek:"<<pts;
    int ret = av_seek_frame(pFormatCtx,-1,(int64_t)pts*1000 ,AVSEEK_FLAG_BACKWARD);

}



void FFmpegDecoder::StartReadFrame()
{

    emit signalPlayStatus(player::PLAYING);
    if(player::PLAY_FILE == playType)
    {
        //定时器的方式读流


        qDebug()<<"video duration:"<<pFormatCtx->duration;
        emit signalPlayProgress(0,pFormatCtx->duration/1000);

        double tmp = (double)pStream->avg_frame_rate.num/(double)pStream->avg_frame_rate.den;

        if(tmp >0)
        {
            frameRate = tmp;
        }
        qDebug()<<"frameRate:"<<frameRate;

        if(pTimer)
        {
            delete pTimer;
            pTimer= nullptr;
        }

        pTimer = new QTimer;
        connect(pTimer,SIGNAL(timeout()),this,SLOT(onReadFrame()),Qt::QueuedConnection);
        connect(this,SIGNAL(signalTimerStart()),pTimer,SLOT(start()),Qt::QueuedConnection);
        connect(this,SIGNAL(signalTimerStop()),pTimer,SLOT(stop()),Qt::QueuedConnection);

        pTimer->setTimerType(Qt::PreciseTimer);
        pTimer->setInterval(1000/frameRate);

        emit signalTimerStart();

    }else{
        // 迭代的方式读流

        ctrlParam = MediaTool::NOT;
        while(1)
        {
            if(!onReadFrame()) break;
            if(1 == videoControl())
            {

                break;
            }

        }
        qDebug()<<"play finish;";
        emit signalPlayStatus(player::FINISH);
        free_player();
    }
}
void FFmpegDecoder::free_player()
{

    if(pRecord)
    {
        pRecord->Finished();
        delete pRecord;
        pRecord = nullptr;
        isRecord = false;
    }
    qDebug()<<"free_player";
    QFFmpegThreadMutexManager::instance()->lock();
    if(pFrameRGB)
        av_frame_free(&pFrameRGB);
    if(pFrame)
        av_frame_free(&pFrame);
    if(packet)
        av_packet_free(&packet);
    if(pCodecCtx)
        avcodec_close(pCodecCtx);
    if(pCodecCtxOrg)
        avcodec_close(pCodecCtxOrg);
    if(pFormatCtx)
        avformat_close_input(&pFormatCtx);


    pFrameRGB = nullptr;
    pFrame = nullptr;
    packet = nullptr;
    pCodecCtx = nullptr;
    pCodecCtxOrg = nullptr;
    pFormatCtx = nullptr;
    QFFmpegThreadMutexManager::instance()->unlock();
    qDebug()<<"free_player finish";

}

void FFmpegDecoder::saveH264Stream(AVPacket* pkg)
{
    if(!pRecord)
    {
        pRecord = new Recording;
        qDebug()<<"start save video";
        if(!mkSavePath(recordFileName))
        {
            delete pRecord;
            pRecord = nullptr;
            return;
        }
        pRecord->Start(pFormatCtx,recordFileName);
    }
    pRecord->AddPacket(pkg);
}

bool FFmpegDecoder::mkSavePath(QString  path)
{
    QString dirPath = path.left(path.lastIndexOf("/"));
    QString realPath;
    if(dirPath.left(1) == ".")
    {
        QStringList lists = (QDir::currentPath()+"/"+dirPath).split("/");
        while(1)
        {
            for(int i=0;i<lists.size();i++)
            {
                if(lists.at(i) == ".." && i > 0)
                {
                    lists.removeAt(i);
                    lists.removeAt(i-1);
                }
            }
            bool flag = false;
            for(int i=0;i<lists.size();i++)
            {
                if(lists.at(i) == "..")
                    flag = true;
            }
            if(!flag)
                break;
        }
        for(int i=0;i<lists.size();i++)
        {
            if(i != lists.size()-1)
                realPath += lists.at(i)+"/";
            else
                realPath += lists.at(i);
        }
    }else{
        realPath = dirPath;
    }
    QDir dir(realPath);
    if(!dir.exists())
    {
        if(!dir.mkpath(realPath))
        {
            return false;
        }else
            return true;
    }
    return true;
}

int FFmpegDecoder::videoControl()
{
    int ret = 0;
    switch(ctrlParam){
    case MediaTool::NOT:
        ret = 0;
        break;
    case MediaTool::RECORD_START:
        ret = 0;
        break;
    case MediaTool::RECORD_STOP:
        ret = 0;
        break;
    case MediaTool::CLOSE:
        ret = 1;
        break;
    }
    ctrlParam = MediaTool::NOT;
    return ret;
}

 

  • 5
    点赞
  • 45
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
ffmpeg的硬件加速功能可以通过使用qsv(Quick Sync Video)实现。qsvIntel提供的一种硬件加速技术,它可以利用集成在Intel处理器中的GPU来加速视频解码和编码的过程。使用qsv可以大大提高ffmpeg处理视频的效率。 要启用ffmpegqsv硬件加速功能,可以按照以下步骤进行操作: 1. 首先,确保你的Intel处理器支持Quick Sync Video技术。你可以在Intel的官方网站上查找你的处理器型号,以确定它是否支持qsv。 2. 然后,你需要编译ffmpeg时启用qsv支持。在配置ffmpeg时,添加--enable-libmfx参数来启用libmfx库,这是与qsv配套的库。编译和安装ffmpeg后,你的ffmpeg就具备了qsv硬件加速的能力。 3. 在使用ffmpeg时,你需要使用适当的命令行参数来启用qsv硬件加速。例如,你可以使用-vcodec参数指定使用qsv进行视频编码,或使用-decoder参数指定使用qsv进行视频解码。具体的命令行参数可以根据你的需求和具体的视频处理任务进行调整。 总结来说,要搭建ffmpegqsv硬件加速调试环境,你需要确保你的处理器支持qsv技术,并在编译ffmpeg时启用qsv支持。然后,在使用ffmpeg时,使用适当的命令行参数来启用qsv硬件加速。通过这样的设置,你就可以利用qsv硬件加速来提高ffmpeg的视频处理效率了。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [FFmpeg硬件加速](https://blog.csdn.net/u012117034/article/details/123470108)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值