FFmpeg+qt实现的播放器解析(三)

项目下载地址: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实现的播放器解析(四)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值