QT:window 5.7.0版本 在FFmpeg的解压目录里 #这里是我所用到的库和 pro里的配置 #到这里配置就完成了 源码链接: https://github.com/autocatfuuustudy/note/tree/master/QT/ffmpeg AFFmepg.h [AFFmpeg类] 音视频操作功能封装 原本是用FFmpeg的avfilter模块来实现的,但是太卡了 【不是说FFmpeg 这功能做的不行 是因为我没找对方法吧。功能我也保留了 封装在【AFilter类】中 ================================================================= 实现思路: 我把QT播放音频的功能封装 和展示窗体封装再同一个文件了 【XAudio类】 关键代码
一、 环境搭建
1. 下载
FFmpeg: ffmpeg-20200522-38490cb-win32-dev
注意:这里下载 32位dev版本,要和编译器对应(我的mingw是32位的)2. 加载库
//avcodec-58.dll //avdevice-58.dll //avfilter-7.dll //avformat-58.dll //avutil-56.dll //potproc-55.dll //swresample-3.dll //swscale-5.dll INCLUDEPATH += $$PWD/include/ LIBS += -L$$PWD/lib -lavutil-56 -lavformat-58 -lavcodec-58 -lavdevice-58 -lavfilter-7 -lpostproc-55 -lswresample-3 -lswscale-5
二、实战演练
1. 功能介绍
实际效果图
2. 音视频操作流程图
操作流程:
打开音视频文件
读取音视频流
读取音视频的解码器
初始化音视频的缓存区
获取音视频的时长,时基
解码音视频流
读取解码后的音视频流数据
跳转音视频流
计算当前音视频帧的时间差
===========================================
在open()成功之后开始两个定时器分别执行
<1> readVideo() getFrame() [视频]
<2> readAudio() getAudio() [音频]
即可。#ifndef AFFMPEG_H #define AFFMPEG_H #ifdef __cplusplus extern "C"{ #include <libavcodec/avcodec.h> #include <libavformat/avformat.h> #include "libswresample/swresample.h" #include <libswscale/swscale.h> #include <libavutil/imgutils.h> #include <pthread.h> } #endif #include "afilter.h" class AFFmpeg { public: AFFmpeg(); ~AFFmpeg(); bool open(const char* filepath,bool isaudio = true);//打开文件 int readVideo(); //读取视频 int readAudio(); //读取音频 int seek(float pos); //跳转 AVFrame* getFrame(){ return pVideoFrameRGB;} //读取图像 int getAudio(char **buf); //读取音频 int getDuration(){ return duration;} //获取总时长 double getCDuration(){ return cduration;} //获取当前时长 int getFPS() { return fps.num;} //获取帧率 double getDeviation(); //获取当前视频和音频的时差 void setAFilterOpen() { isAFilteropen = !isAFilteropen; } //弹幕开关 private: //视频相关 AVFormatContext *pFormatContext; //文件内容相关信息 AVCodecContext *pVideoCodecContext;//编码信息 AVCodec *pVideoCodec; //解码器 AVFrame *pVideoFrame; //一帧视频 源 AVFrame *pVideoFrameRGB; //一帧视频 RGB格式 AVRational fps; //帧率 struct SwsContext *pSwsContext; //转换格式用的结构体 unsigned char *out_buffer; //数据流初始化用的 int videoindex; //视频流索引 AVPacket *pVCPacket; //当前视频解码包 AVRational Vrational; //视频时间基准 //音频相关 AVFormatContext *pAFormatContext; //文件内容相关信息 AVCodecContext *pAudioCodecContext;//编码信息 AVCodec *pAudioCodec; //解码器 AVFrame *pAudioFrame; //一帧音频 struct SwrContext *pSwrContext; //转换格式用的结构体 uint8_t *pAudioBuffer; //音频输出数据 int ABufLen; //pAudioBuffer的长度 int audioindex; //音频流索引 AVPacket *pACPacket; //当前音频解码包 AVRational Arational; //音频时间基准 //字幕相关 加弹幕不是很可靠 20条的时候CPU占用太高了 大概是方法不对吧 AFilter *pAFilter; //字幕类 AVFrame *pFilterFrame; //输出帧 bool isAFilteropen; //弹幕开关 int speed; //弹幕移动速度 //================================================== bool isopen; //打开标志 bool isstart; //开始解码标志 bool isaudio; //开启音频 int duration; //时长 double cduration; //当前时长 }; #endif // AFFMPEG_H
3. 弹幕功能
让我们来看看另外一种方法吧
PS: 无非就是一些增删查改的操作啦
PS:用double类型去更新太不准了 而且更新太频繁还是不要吧
PS: 滚动步进可以根据实际显示宽度自行计算
PS: n 值可以自行确定#ifndef DANMUKU_H #define DANMUKU_H #include <QFile> #include <QXmlStreamReader> #include <QMultiHash> struct DanMuData{ double index; //时间戳索引 int arg[4]; // int type; //弹幕类型 固定位置:滚动 0:1 // int time; //滚动弹幕实时位置 // int x; //X轴 // int y; //Y轴 QStringList list; // int color; //颜色 rgb值 // char text[256]; //内容 DanMuData *next; }; class DanMuKu { public: DanMuKu(); ~DanMuKu(); void open(); //打开文件 void init(DanMuData **data); //初始化结构体 void read(int timestamp); //读取数据 void insert(DanMuData *data); //插入数据 int insertFile(DanMuData data); //插入数据 void update(double time,int step=1); //更新数据 void del(double timestamp); //删除数据 void clear(); //清空数据 void show(); //打印数据 DanMuData* get(){ return head->next;} //获取数据 private: QFile *file; //弹幕文件 DanMuData *head; //数据 }; #endif // DANMUKU_H
4. 展示窗体
流程:
#一个在绘制界面
#一个在写入音频数据【QT 播放音频】#####
//记得在pro文件里加上 QT += multimedia class XAudio { public: explicit XAudio(); bool start(); //开启 void play(); //播放 void pause(); //暂停 bool write(const char *data,int len); //写入数据 int getfreebytes(){return pOutput->bytesFree();} //获取数据剩余空间大小 void setVolume(double volume){ pOutput->setVolume(volume);} //调节音量 double getVolume(){ return pOutput->volume();} //获取音量 ~XAudio(); private: QAudioOutput *pOutput; //输出 QAudioFormat fmt; //音频参数 QMutex mutex; QIODevice *pOut; bool isPlay; //启动标志 }; XAudio::XAudio() { fmt.setSampleRate(44100); //采样率 fmt.setSampleSize(16); //样本大小 fmt.setSampleType(QAudioFormat::UnSignedInt); //数据类型 fmt.setChannelCount(2); //通道数 fmt.setCodec("audio/pcm"); //解码格式 fmt.setByteOrder(QAudioFormat::LittleEndian); //端序 pOutput = new QAudioOutput(fmt); pOutput->setVolume(1); pOut = NULL; isPlay = false; start(); } XAudio::~XAudio() { delete pOutput; } bool XAudio::start() { if(!isPlay) { pOut = pOutput->start(); isPlay = true; return true; } return false; } void XAudio::play() { if(!pOut && !isPlay) { isPlay = true; mutex.lock(); pOutput->resume(); mutex.unlock(); } } void XAudio::pause() { if(!pOut && isPlay) { isPlay = false; mutex.lock(); pOutput->suspend(); mutex.unlock(); } }
【 绘制 弹幕 和 视频 】
void TestWidget::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); if(!img.isNull()) painter.drawImage(0,0,img.scaled(size())); if(isDanMu) DrawDanMu(&painter); } void TestWidget::DrawDanMu(QPainter *painter) { DanMuData *p = dMuKu->get(); if(!p) return; QPen pen; QFont f; f.setPixelSize(22); painter->setFont(f); while(1) { if(!p || p->index > CDuration) break; int color = p->list.at(0).toInt(NULL,16); pen.setColor(QColor(color & 0xFF,((color >> 8) & 0xFF),(color >> 16))); painter->setPen(pen); if(p->arg[0] == 0) { painter->drawText(p->arg[2],p->arg[3],p->list.at(1)); } else { //根据比例画弹幕 int X = (_DEM_ - p->arg[1]) * width() / _DEM_; //int X = p->arg[1] * width() / _DEM_; painter->drawText(X,p->arg[3],p->list.at(1)); } p = p->next; } }
【 读取音视频数据 】
void TestWidget::openvideo() { if(!isOpen) { m = new AFFmpeg; //if(m->open("D:/Picture/avi/test.mp4")) if(m->open(path.toLocal8Bit())) { dMuKu->open(); timer->start(1000/m->getFPS()); Atimer->start(10); isOpen = true; } else qDebug() << "can't open."; } } void TestWidget::videoshow() { if(isSPressed) return; int POs = m->readVideo(); if(POs > 0) { AVFrame *f = m->getFrame(); if(f) { img = QImage((uchar*)f->data[0],f->width,f->height,QImage::Format_RGB32); CDuration = m->getCDuration(); if((int)CDuration > timestamp - 1) { dMuKu->read((int)CDuration); dMuKu->del(CDuration - _DANMU_TIME_); timestamp++; } dMuKu->update(CDuration); int minter = m->getDuration() / 60; int sec = m->getDuration() % 60; int cminter = CDuration / 60; int csec = (int)CDuration % 60; QString strtime = QString("%1:%2/%3:%4").arg(cminter).arg(csec,2,10,QLatin1Char('0')).arg(minter).arg(sec,2,10,QLatin1Char('0')); Timelab->setText(strtime); //qDebug() << m->getCDuration() << m->getDuration(); CurTimeSlider->setValue(CDuration/m->getDuration() * 1000); update(); } } else if(POs == -2) { qDebug() << "stop"; timer->stop(); Atimer->stop(); isOpen = false; isPause = true; delete m; } } void TestWidget::audioplay() { if(isSPressed) return; char *buf = NULL; int len = m->getAudio(&buf); if(len >= 0 && pAudio->getfreebytes() >= len) { //int size = m->readAudio(); //qDebug() << len << freeByte << size; pAudio->write(buf,len); } }
5. 写在最后
本作品只是兴趣使然的产物,从零开始花费了大概一个月的时间,说做的多么牛逼那是没有的。但对于初学 者来说当作入门资料还是可以的。如果你想要入音视频这块行业,想要更多地了解,可以去看看雷霄骅的博客。 关于代码里有啥疑惑的可以留言【佛系回复】或者 联系本人QQ :673315140在项目过程中,遇到的问题还蛮多 的,但是并没记录下来,已经不知从何记起了【蛋疼(ˉ▽ˉ;)...】
本网页所有视频内容由 imoviebox边看边下-网页视频下载, iurlBox网页地址收藏管理器 下载并得到。
ImovieBox网页视频下载器 下载地址: ImovieBox网页视频下载器-最新版本下载
本文章由: imapbox邮箱云存储,邮箱网盘,ImageBox 图片批量下载器,网页图片批量下载专家,网页图片批量下载器,获取到文章图片,imoviebox网页视频批量下载器,下载视频内容,为您提供.
阅读和此文章类似的: 全球云计算