既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
XDemuxThread;解封装读包到对应解码器管理类的pkts的list中
XVideoThread;视频解码器进行取包解码显示
XAudioThread;音频解码器进行取包解码重采样播放
因为音视频的解码处理线程很多操作都是相同的则可以重新构建一下封装一个XDecodeThread基类,将共同操作提取到基类
XDemuxThread
成员有;
XDemux \*demux = 0;
XVideoThread \*vt = 0;
XAudioThread \*at = 0;
成员函数;
//将XDemux 、XVideoThread 、XAudioThread 都调用open操作
virtual bool Open(const char \*url, IVideoCall \*call);
//创建XDemux 、XVideoThread 、XAudioThread对象 并启动这三个线程run函数
virtual void Start();
//关闭线程等待清理 关闭vt at 并delete
virtual void Close();
//清理缓冲资源 demux vt at 三个都调用clear函数进行清理
virtual void Clear();
//设置状态暂停 以及 at vt都调用pause函数
void SetPause(bool isPause);
bool isPause = false;
//清理资源 调用都调用pause 解封装移动到seek位置 只读视频流 进行显示
virtual void Seek(double pos);
//音视频同步、读取包分别存放到at、vt管理的队列中去
void run();
XDecodeThread
成员
XDcode \*decode = 0;
std::list <AVPacket \*> packts;
成员函数
//产生
virtual void Push(AVPacket \*pkt);
//取出一帧数据并入栈,没有贼返回NULL
virtual AVPacket \*Pop();
//清理队列
virtual void Clear();
//关闭清理 要将XDcode关闭
virtual void Close();
XAudioThread
成员
XAudioPlay \*ap = 0; 音频播放的类
XResample \*res = 0; 重采样的类
成员函数
//decode ap res都调用Open操作
virtual bool Open(AVCodecParameters \*para,int sampleRate, int channels);
//停止线程 清理资源 ap res都要close
virtual void Close();
//音频播放有两部分缓冲 要重载Clear ap部分也要clear
virtual void Clear();
//读取包 解码 重采样 IO播放
void run();
//暂停操作 并调用ap的pause
void SetPause(bool isPause);
bool isPause = false;
//创建对象ap 、res
XAudioThread::XAudioThread()
XVideoThread
成员
IVideoCall \*call = NULL; //视频显示的类
成员函数
//创建窗口 decode打开
virtual bool Open(AVCodecParameters \*para,IVideoCall \*call,int width,int height);
//解码pts,如果接收到的解码数据pts大于seekPts return true 并显示
virtual bool RepaintPts(AVPacket \*Pkt, long long seekPts);
//音视频同步 取包 解码 显示
void run();
//设置视频处理暂停步骤
void SetPause(bool isPause);
bool isPause = false;
3、视频显示类设计 添加抽象接口进行封装
抽象出来一个接口给XVideoThread使用 在XVideoThread播放的时候使用接口类,而真正显示的类继承接口类传入进来,进行隔离封装,在视频编解码部分是不需要知道显示部分的,不管他用什么技术显示都不用管,我里面只管调用接口类的几个函数进行调用显示,至于内部具体什么操作这边是不需要管的进行了隔离。
//创建接口,则以后用其他显示的时候 videoThread这个类是不用变的
class IVideoCall
{
public:
virtual void Init(int width, int height) = 0;
virtual void Repaint(AVFrame \*frame) = 0;
};
XVideoWidget
使用qt的OPenGL进行显示
class XVideoWidget : public QOpenGLWidget, protected QOpenGLFunctions, public IVideoCall
则后面需要继承IVideoCall这个接口类,并实现接口中的方法
并且调用这两个接口可以完成视频的显示
4、音频播放类设计 工程模式进行封装
提供一个抽象类并实现一个Get的静态方法用来返回子类的实例化对象的,这样操作主要是将真正的播放类进行隔离与编解码这边没有任何关系,因为这边都看不到这个类。
class XAudioPlay
{
public:
//工程模式
static XAudioPlay \*Get();
....
}
XAudioPlay \*XAudioPlay::Get()
{
static CXAudiaoPlay play;//返回具体静态类
return &play;
}
使用qt的QAudioFormat进行音频播放的
class CXAudiaoPlay : public XAudioPlay
{
//重载里面的方法
};
5、界面相关处理
实现了双击放大还原、滑动条的拖动与点击位置播放、尺寸大小的变化
滑动条随视频播放移动是采用重载定时器事件的方式进行处理的
main函数中启动startTimer(40);
//重载定时器函数的方法 滑动条进行显示
void Widget::timerEvent(QTimerEvent \*e)
{
//如果滑动条按下的时候就不在计时
if(isSliderPress) return ;
long long total = dt.totalMs;
//播放的pts
if(total > 0)
{
double pos = (double)dt.pts/(double)total;
int v = ui->playPos->maximum() \* pos;
ui->playPos->setValue(v);
}
}
三、具体逻辑
只有static XDemuxThread dt;这个类与界面类进行交互的
在main函数中也是一开始就启动了dt.Start();线程
1、线程逻辑
void XDemuxThread::Start()
demux = new XDemux();
vt = new XVideoThread();
at = new XAudioThread();
QThread::start();
XDemuxThread::run()
//如果暂停则取消下面的解封装读pkt操作
//音视频同步
AVPacket \*pkt = demux->Read();
at->Push(pkt); or vt->Push(pkt);
vt->start();
//暂停状态
//音频同步
AVPacket \*pkt = Pop();
bool ret = decode->Send(pkt);
AVFrame \*frame = decode->Recv();
call->Repaint(frame);
at->start();
//处理暂停情况
AVPacket \*pkt = Pop();
bool ret = decode->Send(pkt);
AVFrame \*frame = decode->Recv();
int size = res->Resample(frame, pcm);
ap->Write(pcm,size);
2、播放流程
//保证XDemuxThread::run() -》 AVPacket *pkt = demux->Read();能取到包 那么线程就能运行进行播放了
void Widget::on\_pushButton\_clicked()
dt.Open(name.toLocal8Bit(), ui->vedio)
bool ret = demux->Open(url);
int re = avformat\_open\_input(&ic,url,0,&opts);
avformat\_find\_stream\_info(ic, 0);
保存各个参数
ret = vt->Open(demux->CopyVPara(), call, demux->width, demux->height);
call->Init(width, height);
decode->Open(para)
AVCodec \*vcodec = avcodec\_find\_decoder(para->codec_id);
codec = avcodec\_alloc\_context3(vcodec);
avcodec\_parameters\_to\_context(codec, para);
int re = avcodec\_open2(codec, 0, 0);
ret = at->Open(demux->CopyAPara(), demux->sampleRate, demux->channels);
res->Open(para, false)
actx = swr\_alloc\_set\_opts()
swr\_init(actx);
ap->Open()
output = new QAudioOutput(fmt);
io = output->start();
decode->Open(para)
AVCodec \*vcodec = avcodec\_find\_decoder(para->codec_id);
codec = avcodec\_alloc\_context3(vcodec);
avcodec\_parameters\_to\_context(codec, para);
int re = avcodec\_open2(codec, 0, 0);
3、音视频同步
首先要知道音视频同步是有三种策略的
参考时钟同步;音视频都根据第三方时钟来进行同步,就是以外部时钟为参考对象,将音频和视频同步到此时间 ,这个实现较为复杂。但是因为人对声音的变化的敏感度比对视觉变化的敏感度大得多,所以如果使用这种方式频繁去调整音频的的话可能会产生一些沙沙或刺耳的杂音,这是很影响用户体验的。但也不是说这种方式很不好,只是不太适用于单个音视频的同步而已,但是对于多路音视频的同步则比较适用了。
**音频同步视频;**以视频时间为基准,判断音频快了还是慢了,从而调整音频的播放速度,其实是一个动态的追赶与等待的过程。但是前面说了人体对于声音的变化太敏感,而对于画面的变化不太敏感,而这个方案也是需要频繁调整音频的,所以这种方案并没有太好的效果。
**视频同步音频;**以音频时间为基准,判断视频是快了还是慢了,从而调整视频的播放速度。因为人体对于画面的变化不太敏感,所以在同步的过程如果视频轻微等待或者丢掉一些画面帧也不太影响观看体验。因而这个方案最合适。
虽然视频同步音频这个方案最合适并且实现也简单,但是同样也存在问题的,如没有音频的时候 音频失帧的时候。
在代码中如何实现同步,需要获取音视频两边的时间,并且音频需要把时间传出来,视频根据传出的音频时间来进行等待或者调节
1、首先确定在xdemuxThread类中进行同步
2、再就是要把音频的时间传出来,音频读取是有缓冲的,则要以解码的时间为准传出来pts(统一转为ms,便于比较),并且这里解码的时间也不一定就是播放的时间,因为XAudioPlay是以qt的QAudioOutput他同样也有部分的缓冲的,因此都要考虑到,
则音频传出的时间应该是当前解码时间pts减去QAudioOutput缓冲中未播放的时间
//在XAudioPlay中实现 返回QAudioOutput缓冲中还没有播放的时间 毫秒单位
virtual long long GetNoPlayMs()
{
mux.lock();
if(!output)
{
mux.unlock();
return 0;
}
**收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。**


**[如果你需要这些资料,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618679757)**
**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**
**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**
友。**
[外链图片转存中...(img-7dYihK84-1715872435898)]
[外链图片转存中...(img-NOLOvTUd-1715872435898)]
**[如果你需要这些资料,可以戳这里获取](https://bbs.youkuaiyun.com/topics/618679757)**
**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人**
**都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**