项目下载地址:https://download.csdn.net/download/qq_42503022/12000297
FFmpeg+qt实现的播放器解析(一)
FFmpeg+qt实现的播放器解析(二)
FFmpeg+qt实现的播放器解析(三)
FFmpeg+qt实现的播放器解析(四)
这一张主要说明如何把视音频分开来解码的,先说一下他们的基类XDecodeThread
这里使用了消息队列的方式来存储解封装的包,使用list 实现存储 主要实现了 push()入栈 ,pop()出栈,两个方法
头文件:
#pragma once
///解码和显示视频
struct AVPacket;
class XDecode;
#include <list>
#include <mutex>
#include <QThread>
class XDecodeThread:public QThread
{
public:
XDecodeThread();
virtual ~XDecodeThread();
virtual void Push(AVPacket *pkt);
//清理队列
virtual void Clear();
//清理资源,停止线程
virtual void Close();
//取出一帧数据,并出栈,如果没有返回NULL
virtual AVPacket *Pop();
//最大队列
int maxList = 100;
bool isExit = false;
XDecode *decode = 0;
protected:
std::list <AVPacket *> packs;
std::mutex mux;
};
源文件:
#include "XDecodeThread.h"
#include "XDecode.h"
//清理资源,停止线程
void XDecodeThread::Close()
{
Clear();
//等待线程退出
isExit = true;
wait();
decode->Close();
mux.lock();
delete decode;
decode = NULL;
mux.unlock();
}
void XDecodeThread::Clear()
{
mux.lock();
decode->Clear();
while (!packs.empty())
{
AVPacket *pkt = packs.front();
XFreePacket(&pkt);
packs.pop_front();
}
mux.unlock();
}
//取出一帧数据,并出栈,如果没有返回NULL
AVPacket *XDecodeThread::Pop()
{
mux.lock();
if (packs.empty())
{
mux.unlock();
return NULL;
}
AVPacket *pkt = packs.front();
packs.pop_front();
mux.unlock();
return pkt;
}
void XDecodeThread::Push(AVPacket *pkt)
{
if (!pkt)return;
//阻塞
while (!isExit)
{
mux.lock();
if (packs.size() < maxList)
{
packs.push_back(pkt);
mux.unlock();
break;
}
mux.unlock();
msleep(1);
}
}
XDecodeThread::XDecodeThread()
{
//打开解码器
if (!decode) decode = new XDecode();
}
XDecodeThread::~XDecodeThread()
{ //等待线程退出
isExit = true;
wait();
}
XVideoThread 类视频解码线程 继承 XDecodeThread
头文件:
#pragma once
///解码和显示视频
struct AVPacket;
struct AVCodecParameters;
class XDecode;
#include <list>
#include <mutex>
#include <QThread>
#include "IVideoCall.h"
#include "XDecodeThread.h"
class XVideoThread:public XDecodeThread
{
public:
//解码pts,如果接收到的解码数据pts >= seekpts return true 并且显示画面
virtual bool RepaintPts(AVPacket *pkt, long long seekpts);
//打开,不管成功与否都清理
virtual bool Open(AVCodecParameters *para,IVideoCall *call,int width,int height);
void run();
XVideoThread();
virtual ~XVideoThread();
//同步时间,由外部传入
long long synpts = 0;
// long long seeks = 0;
void SetPause(bool isPause);
bool isPause = false;
protected:
IVideoCall *call = 0;
std::mutex vmux;
};
源文件:
#include "XVideoThread.h"
#include "XDecode.h"
#include <iostream>
extern "C"
{
#include<libavcodec/avcodec.h>
}
using namespace std;
//????????????????????????????????????
bool XVideoThread::Open(AVCodecParameters *para, IVideoCall *call,int width,int height)
{
if (!para)return false;
Clear();
vmux.lock();
synpts = 0;
//????????????????????????
this->call = call;
if (call)
{
call->Init(width, height);
}
vmux.unlock();
int re = true;
if (!decode->Open(para))
{
cout << "audio XDecode open failed!" << endl;
re = false;
}
cout << "XAudioThread::Open :" << re << endl;
return re;
}
void XVideoThread::SetPause(bool isPause)
{
vmux.lock();
this->isPause = isPause;
vmux.unlock();
}
void XVideoThread::run()
{
while (!isExit)
{
vmux.lock();
if (this->isPause)
{
vmux.unlock();
msleep(5);
continue;
}
cout << "synpts = " << synpts << " dpts =" << decode->pts << endl;
//视音频同步
if (synpts >0 && synpts < decode->pts)
{
vmux.unlock();
msleep(1);
continue;
}
AVPacket *pkt = Pop();
bool re = decode->Send(pkt);
if (!re)
{
vmux.unlock();
msleep(5);
continue;
}
//接收frame
while (!isExit)
{
AVFrame * frame = decode->Recv();
if (!frame)break;
//显示视频
if (call)
{
call->Repaint(frame);
}
}
vmux.unlock();
}
}
//????????pts????????????????????????????????????????pts >= seekpts return true ??????????????????????
bool XVideoThread::RepaintPts(AVPacket *pkt, long long seekpts)
{
vmux.lock();
bool re = decode->Send(pkt);
if (!re)
{
vmux.unlock();
return true; //??????????????????????
}
AVFrame *frame = decode->Recv();
if (!frame)
{
vmux.unlock();
return false;
}
//????????λ????
if (decode->pts >= seekpts)
{
if(call)
call->Repaint(frame);
vmux.unlock();
return true;
}
XFreeFrame(&frame);
vmux.unlock();
return false;
}
XAudioThread 类音频解码线程同样继承XDecodeThread
头文件:
#pragma once
#include <QThread>
#include <mutex>
#include <list>
struct AVCodecParameters;
class XAudioPlay;
class XResample;
#include "XDecodeThread.h"
class XAudioThread:public XDecodeThread
{
public:
//当前音频播放的pts
long long pts = 0;
//打开,不管成功与否都清理
virtual bool Open(AVCodecParameters *para,int sampleRate,int channels);
//停止线程,清理资源
virtual void Close();
virtual void Clear();
void run();
XAudioThread();
virtual ~XAudioThread();
void SetPause(bool isPause);
bool isPause = false;
double audio_clock; ///音频时钟
protected:
std::mutex amux;
XAudioPlay *ap = 0;
XResample *res = 0;
};
源文件
#include "XAudioThread.h"
#include "XDecode.h"
#include "XAudioPlay.h"
#include "XResample.h"
#include <iostream>
extern "C"
{
#include<libavcodec/avcodec.h>
}
using namespace std;
void XAudioThread::Clear()
{
XDecodeThread::Clear();
mux.lock();
if (ap) ap->Clear();
mux.unlock();
}
//停止线程,清理资源
void XAudioThread::Close()
{
XDecodeThread::Close();
if (res)
{
res->Close();
amux.lock();
delete res;
res = NULL;
amux.unlock();
}
if (ap)
{
ap->Close();
amux.lock();
ap = NULL;
amux.unlock();
}
}
bool XAudioThread::Open(AVCodecParameters *para,int sampleRate, int channels)
{
if (!para)return false;
Clear();
amux.lock();
pts = 0;
bool re = true;
if (!res->Open(para, false))
{
cout << "XResample open failed!" << endl;
re = false;
}
ap->sampleRate = sampleRate;
ap->channels = channels;
if (!ap->Open())
{
re = false;
cout << "XAudioPlay open failed!" << endl;
}
if (!decode->Open(para))
{
cout << "audio XDecode open failed!" << endl;
re = false;
}
amux.unlock();
cout << "XAudioThread::Open :" << re << endl;
return re;
}
void XAudioThread::SetPause(bool isPause)
{
//amux.lock();
this->isPause = isPause;
if (ap)
ap->SetPause(isPause);
//amux.unlock();
}
void XAudioThread::run()
{
unsigned char *pcm = new unsigned char[1024 * 10];
while (!isExit)
{
amux.lock();
if (isPause)
{
amux.unlock();
msleep(5);
continue;
}
//没有数据
//if (packs.empty() || !decode || !res || !ap)
//{
// mux.unlock();
// msleep(1);
// continue;
//}
//AVPacket *pkt = packs.front();
//packs.pop_front();
AVPacket *pkt = Pop();
if ( pkt !=nullptr && pkt->pts != AV_NOPTS_VALUE)
{
audio_clock = pkt->pts;
}
bool re = decode->Send(pkt);
if (!re)
{
amux.unlock();
msleep(1);
continue;
}
//一次send 多次recv
while (!isExit)
{
AVFrame * frame = decode->Recv();
if (!frame) break;
//减去缓冲中未播放的时间
pts = decode->pts - ap->GetNoPlayMs();
cout << "audio pts = " << pts << endl;
//重采样
int size = res->Resample(frame, pcm);
//播放音频
while (!isExit)
{
if (size <= 0)break;
//缓冲未播完,空间不够
int nn= ap->GetFree();
if (ap->GetFree() < size || isPause)
{
msleep(5);
continue;
}
ap->Write(pcm, size);
break;
}
}
amux.unlock();
}
delete pcm;
}
XAudioThread::XAudioThread()
{
if (!res) res = new XResample();
if (!ap) ap = XAudioPlay::Get();
}
XAudioThread::~XAudioThread()
{
//等待线程退出
isExit = true;
wait();
}
XDemuxThread 类 对XDemux,XVideoThread,XAudioThread,的调用
头文件:
#pragma once
#include <QThread>
#include "IVideoCall.h"
#include <mutex>
class XDemux;
class XVideoThread;
class XAudioThread;
class XDemuxThread:public QThread
{
public:
//创建对象并打开
virtual bool Open(const char *url, IVideoCall *call);
//启动所有线程
virtual void Start();
//关闭线程清理资源
virtual void Close();
virtual void Clear();
virtual void Seek(double pos);
//获取播放时长
QString PlayTime();
void run();
//获取视频总时长
int GetTotalMs();
XDemuxThread();
virtual ~XDemuxThread();
public:
bool isExit = false;
long long pts = 0;
long long totalMs = 0;
void SetPause(bool isPause);
bool isPause = false;
XDemux *demux = 0;
protected:
std::mutex mux;
XVideoThread *vt = 0;
XAudioThread *at = 0;
};
源文件:
#include "XDemuxThread.h"
#include "XDemux.h"
#include "XVideoThread.h"
#include "XAudioThread.h"
#include <iostream>
extern "C"
{
#include <libavformat/avformat.h>
}
#include "XDecode.h"
using namespace std;
void XDemuxThread::Clear()
{
mux.lock();
if (demux)demux->Clear();
if (vt) vt->Clear();
if (at) at->Clear();
mux.unlock();
}
QString XDemuxThread::PlayTime()
{
mux.lock();
if(demux !=nullptr && demux->ic != nullptr && demux->ic->streams[demux->videoStream] != nullptr)
{
double d=av_q2d(demux->ic->streams[demux->audioStream]->time_base);
double pts1 = at->audio_clock*d;
int hour =(int)pts1/60/60%60;
int min = (int)pts1/60%60;
int sec = (int)pts1%60;
char buf[1024] = { 0 };
sprintf(buf, "%02d:%02d:%02d",hour, min, sec);
mux.unlock();
return (QString)buf;
}
mux.unlock();
return nullptr;
}
void XDemuxThread::Seek(double pos)
{
//清理缓存
Clear();
mux.lock();
bool status = this->isPause;
mux.unlock();
//暂停
SetPause(true);
mux.lock();
if (demux)
demux->Seek(pos);
//实际要显示的位置pts
long long seekPts = pos*demux->totalMs;
while (!isExit)
{
AVPacket *pkt = demux->ReadVideo();
if (!pkt) break;
//如果解码到seekPts
if (vt->RepaintPts(pkt, seekPts))
{
this->pts = seekPts;
break;
}
}
mux.unlock();
//seek是非暂停状态
if(!status)
SetPause(false);
}
void XDemuxThread::SetPause(bool isPause)
{
mux.lock();
this->isPause = isPause;
if (at) at->SetPause(isPause);
if (vt) vt->SetPause(isPause);
mux.unlock();
}
void XDemuxThread::run()
{
while (!isExit)
{
mux.lock();
if (isPause)
{
mux.unlock();
msleep(5);
continue;
}
if (!demux)
{
mux.unlock();
msleep(5);
continue;
}
//音视频同步
if (vt && at)
{
pts = at->pts;
vt->synpts = at->pts;
}
AVPacket *pkt = demux->Read();
if (!pkt)
{
mux.unlock();
msleep(5);
continue;
}
//判断数据是音频
if (demux->IsAudio(pkt))
{
//while (at->IsFull())
{
// vt->synpts = at->pts;
}
if(at)at->Push(pkt);
}
else //视频
{
//while (vt->IsFull())
//{
// vt->synpts = at->pts;
//}
if (vt)vt->Push(pkt);
}
mux.unlock();
msleep(1);
}
}
bool XDemuxThread::Open(const char *url, IVideoCall *call)
{
if (url == 0 || url[0] == '\0')
return false;
mux.lock();
if (!demux) demux = new XDemux();
if (!vt) vt = new XVideoThread();
if (!at) at = new XAudioThread();
//打开解封装
bool re = demux->Open(url);
if (!re)
{
mux.unlock();
cout << "demux->Open(url) failed!" << endl;
return false;
}
//打开视频解码器和处理线程
if (!vt->Open(demux->CopyVPara(), call, demux->width, demux->height))
{
re = false;
cout << "vt->Open failed!" << endl;
}
//打开音频解码器和处理线程
if (!at->Open(demux->CopyAPara(), demux->sampleRate, demux->channels))
{
re = false;
cout << "at->Open failed!" << endl;
}
totalMs = demux->totalMs;
mux.unlock();
cout << "XDemuxThread::Open " << re << endl;
return re;
}
//关闭线程清理资源
void XDemuxThread::Close()
{
isExit = true;
wait();
if (vt) vt->Close();
if (at) at->Close();
mux.lock();
delete vt;
delete at;
vt = NULL;
at = NULL;
mux.unlock();
}
//启动所有线程
void XDemuxThread::Start()
{
mux.lock();
if (!demux) demux = new XDemux();
if (!vt) vt = new XVideoThread();
if (!at) at = new XAudioThread();
//启动当前线程
QThread::start();
if (vt)vt->start();
if (at)at->start();
mux.unlock();
}
int XDemuxThread::GetTotalMs()
{
return demux->totalMs;
}
XDemuxThread::XDemuxThread()
{
}
XDemuxThread::~XDemuxThread()
{
isExit = true;
wait();
}
FFmpeg+qt实现的播放器解析(一)
FFmpeg+qt实现的播放器解析(二)
FFmpeg+qt实现的播放器解析(三)
FFmpeg+qt实现的播放器解析(四)