简介

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-demuxersmake -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(); // 注册支持的文件格式及对应的codecavformat_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 streamvideoStream = -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的 decoderQFFmpegThreadMutexManager::instance()->lock();
//    pCodec = avcodec_find_decoder(pCodecCtxOrg->codec_id);//h264_qsv h264_dxva2pCodec = 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,// rtspRECORD_START,     //开始录制RECORD_STOP,      //结束录制SCREENSHOT,       //截图拍照CLOSE,            //关闭//filePLAY,              //播放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();elsereturn 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(); // 注册支持的文件格式及对应的codecavformat_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 streamvideoStream = -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的 decoderQFFmpegThreadMutexManager::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)+"/";elserealPath += lists.at(i);}}else{realPath = dirPath;}QDir dir(realPath);if(!dir.exists()){if(!dir.mkpath(realPath)){return false;}elsereturn 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;
}

FFmpeg 开启QSV硬解加速相关推荐

  1. Ubuntu18使用FFMPEG实现QSV硬解

    前言 由于项目需要,需要在一块I7-8850H上进行H264解码成YUV并显示的功能.由于系统是Ubuntu18,故打算使用QT+FFMPEG来实现.先前的一路软解发现CPU占用率去到了20%以上,我 ...

  2. Ubuntu18.04 编译FFmpeg 支持 QSV 硬编解码

    在Linux下,由于FFmpeg软解码CPU占用过高,所以打算使用h264_qsv硬解码,本文并非原创,主要参考以下文章,在此记录下编译过程,以便日后使用. 参考文章: 视频和视频帧:FFMPEG+I ...

  3. 群晖918 docker命令行方式安装jellyfin并开启GPU硬解的方法

    问题 jellyfin是一个开源的视频管理平台,比群晖自带的video station功能更强,但是直接通过918的docker界面直接安装的jellyfin,由于没有进行device的映射,因此实际 ...

  4. ubuntu18.0.4编译ffmpeg开启qsv硬件编解码

    文章目录 前言 正文 1. libmfx / iHD 路线 前置安装 编译安装ffmpeg 2. VAAPI / i965 路线 前言 参考官方文档:http://trac.ffmpeg.org/wi ...

  5. 【干货】关于软解(ffmpeg)和硬解(MediaCodec、MediaPlayer)以及底层(OpenMax)的那点事

    现在各种视频软件上都有硬解软解这两个选择,但它们有什么区别呢?用哪个好呢?今天就跟随小编一起了解了解吧. 首先,了解下播放视频的基本流程: 解封装:就是将输入的封装格式的数据,分离成为音频流压缩编码数 ...

  6. ffmpeg解码的软解及硬解(cuda和qsv)使用方法

    对ffmpeg不是很熟悉,在使用的过程中遇到了很多坑,总结下,避免以后再遇到类似情况 版本兼容问题: 本次使用的ffmpeg版本是4.2,解码的调用方式为: int32_t iRet = -1;// ...

  7. 【视频编码】软解与硬解

    视频解码分为软解和硬解. 软解,即软件解码:即通过软件让CPU来对视频进行解码处理: 硬解,即硬件解码:是将原来全部交由CPU来处理的视频数据的部分交由GPU来做. 所谓"软解"就 ...

  8. 关于在线flash视频硬解

    现在视频网站的码率越来越高,用chrome(ver32.0+)内置的flash player(ver12.0)播放1080p,纯软解非常的慢.很多人尝试用gpu硬解加速视频播放,但是对何为开启gpu硬 ...

  9. ffmepg实践系列之--硬解接口实现

    闲话 知道ffmpeg很久了,可是一直没有深入研究.最近在研究一款显卡的ffmpeg下的硬解,因此想记录下自己研究所得.关于ffmpeg的基本知识,推荐雷神博客,感谢雷神.废话少说,开始填坑. 思路 ...

最新文章

  1. spring+mybatis整合读取不了配置文件
  2. 受软银收购利好影响 ARM股价大涨近50%
  3. MySQL 19个规则数据库设计总结
  4. 【渝粤题库】陕西师范大学200431综合英语(一)作业(高起专、高起本)
  5. windows部署tomcat服务自动启动,同时解决服务无法启动的问题
  6. MPLS ××× 基本实验测试
  7. Linux基础-1使用命令帮助
  8. 如何用Java和Kotlin实现高性能桌面条形码扫描
  9. ONOS 南向抽象层分析
  10. 百度地图点击触发事件介绍
  11. 国际短信平台怎么找?
  12. wav文件隐写:Deepsound+TIFF图片PS处理( AntCTF x D^3CTF 2022 misc BadW3ter)
  13. 高德地图看各省分界线_高德地图API生成地图(含有各个省份边界线)
  14. VC/MFC得到电脑的默认打印机、设置默认打印机、遍历电脑打印机
  15. 京东区块链白皮书摘要
  16. RACV2022观点集锦 | 视觉基础模型
  17. 10分钟入门Pandas(添加一些个人见解)
  18. Typora添加参考文献
  19. centos mysql 5.2.3 编译安装_在CentOS上编译安装MySQL 5.7.13步骤详解
  20. 【置顶】图灵近期出版和即将出版的新书

热门文章

  1. 学习计算机一年的学费,电脑高手难培养,计算机学费多少一年?
  2. 【从入门到精通系列】-- MySQL(持续更新中……)
  3. 深圳金证科技股份有限公司(介绍,准备进和有意向进去的请进来看一看,保证不后悔)...
  4. UI设计/GUI开发-入门界面设计
  5. flowable 候选人候选组同时使用
  6. 【贪心】电视节目安排
  7. 故宫发布原创儿童绘本 这套书说了啥?
  8. 泛微全程数字化售后管理方案:统一信息渠道,提高服务效率
  9. Ubunto20.04安装MySQL并修改root用户密码(Linux安装mysql root用户无法登陆)
  10. 课设项目之——教学辅助系统(学生考试监考系统)