您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
5. 统一换算pts为毫秒使用IPlayer完成音视频同步播放~1
发布时间:2021-06-13 12:10:55编辑:雪饮阅读()
為實現音視頻同步需要轉換pts,則cpp/FFDemux.cpp:
#include "XLog.h"
extern "C"{
#include <libavformat/avformat.h>
}
//分数转为浮点数
static double r2d(AVRational r)
{
return r.num == 0 || r.den == 0 ?0.:(double) r.num/(double)r.den;
}
//打开文件,或者流媒体 rmtp http rtsp
bool FFDemux::Open(const char *url)
{
XLOGI("Open file %s begin",url);
int re = avformat_open_input(&ic,url,0,0);
if(re != 0 )
{
char buf[1024] = {0};
av_strerror(re,buf,sizeof(buf));
XLOGE("FFDemux open %s failed!",url);
return false;
}
XLOGI("FFDemux open %s success!",url);
//读取文件信息
re = avformat_find_stream_info(ic,0);
if(re != 0 )
{
char buf[1024] = {0};
av_strerror(re,buf,sizeof(buf));
XLOGE("avformat_find_stream_info %s failed!",url);
return false;
}
this->totalMs = ic->duration/(AV_TIME_BASE/1000);
XLOGI("total ms = %d!",totalMs);
GetVPara();
GetAPara();
return true;
}
//获取视频参数
XParameter FFDemux::GetVPara()
{
if (!ic) {
XLOGE("GetVPara failed! ic is NULL!");
return XParameter();
}
//获取了视频流索引
int re = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, 0, 0);
if (re < 0) {
XLOGE("av_find_best_stream failed!");
return XParameter();
}
videoStream = re;
XParameter para;
para.para = ic->streams[re]->codecpar;
return para;
}
//获取音频参数
XParameter FFDemux::GetAPara()
{
if (!ic) {
XLOGE("GetVPara failed! ic is NULL!");
return XParameter();
}
//获取了音频流索引
int re = av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1, 0, 0);
if (re < 0) {
XLOGE("av_find_best_stream failed!");
return XParameter();
}
audioStream = re;
XParameter para;
para.para = ic->streams[re]->codecpar;
para.channels = ic->streams[re]->codecpar->channels;
para.sample_rate = ic->streams[re]->codecpar->sample_rate;
return para;
}
//读取一帧数据,数据由调用者清理
XData FFDemux::Read()
{
if(!ic)return XData();
XData d;
AVPacket *pkt = av_packet_alloc();
int re = av_read_frame(ic,pkt);
if(re != 0)
{
av_packet_free(&pkt);
return XData();
}
//XLOGI("pack size is %d ptss %lld",pkt->size,pkt->pts);
d.data = (unsigned char*)pkt;
d.size = pkt->size;
if(pkt->stream_index == audioStream)
{
d.isAudio = true;
}
else if(pkt->stream_index == videoStream)
{
d.isAudio = false;
}
else
{
av_packet_free(&pkt);
return XData();
}
//转换pts
pkt->pts = pkt->pts * (1000*r2d(ic->streams[pkt->stream_index]->time_base));
pkt->dts = pkt->dts * (1000*r2d(ic->streams[pkt->stream_index]->time_base));
d.pts = (int)pkt->pts;
//XLOGE("demux pts %d",d.pts);
return d;
}
FFDemux::FFDemux()
{
static bool isFirst = true;
if(isFirst)
{
isFirst = false;
//注册所有封装器
av_register_all();
//注册所有的解码器
avcodec_register_all();
//初始化网络
avformat_network_init();
XLOGI("register ffmpeg!");
}
}
那麽其聲明的地方也是同樣的,cpp/IAudioPlay.h:
然後需要在cpp/IDecode.cpp中判斷音視頻同步:
所以要有聲明同步時間,cpp/IDecode.h:
獲取到的音頻的pts要同步告訴視頻,cpp/IPlayer.cpp:
#include "XLog.h"
XData IAudioPlay::GetData()
{
XData d;
while(!isExit)
{
framesMutex.lock();
if(!frames.empty())
{
//有数据返回
d = frames.front();
frames.pop_front();
framesMutex.unlock();
pts = d.pts;
return d;
}
framesMutex.unlock();
XSleep(1);
}
//未获取数据
return d;
}
void IAudioPlay::Update(XData data)
{
//XLOGE("IAudioPlay::Update %d",data.pts);
//压入缓冲队列
if(data.size<=0|| !data.data) return;
while(!isExit)
{
framesMutex.lock();
if(frames.size() > maxFrame)
{
framesMutex.unlock();
XSleep(1);
continue;
}
frames.push_back(data);
framesMutex.unlock();
break;
}
}
#define XPLAY_IAUDIOPLAY_H
#include <list>
#include "IObserver.h"
#include "XParameter.h"
class IAudioPlay: public IObserver
{
public:
//缓冲满后阻塞
virtual void Update(XData data);
//获取缓冲数据,如没有则阻塞
virtual XData GetData();
virtual bool StartPlay(XParameter out) = 0;
//最大缓冲
int maxFrame = 100;
int pts = 0;
protected:
std::list <XData> frames;
std::mutex framesMutex;
};
#endif //XPLAY_IAUDIOPLAY_H
#include "XLog.h"
//由主体notify的数据
void IDecode::Update(XData pkt)
{
if(pkt.isAudio != isAudio)
{
return;
}
while (!isExit)
{
packsMutex.lock();
//阻塞
if(packs.size() < maxList)
{
//生产者
packs.push_back(pkt);
packsMutex.unlock();
break;
}
packsMutex.unlock();
XSleep(1);
}
}
void IDecode::Main()
{
while(!isExit)
{
packsMutex.lock();
//判断音视频同步
if(!isAudio && synPts > 0)
{
if(synPts < pts)
{
packsMutex.unlock();
XSleep(1);
continue;
}
}
if(packs.empty())
{
packsMutex.unlock();
XSleep(1);
continue;
}
//取出packet 消费者
XData pack = packs.front();
packs.pop_front();
//发送数据到解码线程,一个数据包,可能解码多个结果
if(this->SendPacket(pack))
{
while(!isExit)
{
//获取解码数据
XData frame = RecvFrame();
if(!frame.data) break;
//XLOGE("RecvFrame %d",frame.size);
pts = frame.pts;
//发送数据给观察者
this->Notify(frame);
}
}
pack.Drop();
packsMutex.unlock();
}
}
#define XPLAY_IDECODE_H
#include "XParameter.h"
#include "IObserver.h"
#include <list>
//解码接口,支持硬解码
class IDecode:public IObserver
{
public:
//打开解码器
virtual bool Open(XParameter para,bool isHard=false) = 0;
//future模型 发送数据到线程解码
virtual bool SendPacket(XData pkt) = 0;
//从线程中获取解码结果 再次调用会复用上次空间,线程不安全
virtual XData RecvFrame() = 0;
//由主体notify的数据 阻塞
virtual void Update(XData pkt);
bool isAudio = false;
//最大的队列缓冲
int maxList = 100;
//同步时间,再次打开文件要清理
int synPts = 0;
int pts = 0;
protected:
virtual void Main();
//读取缓冲
std::list<XData> packs;
std::mutex packsMutex;
};
#endif //XPLAY_IDECODE_H #include "IPlayer.h"
#include "IDemux.h"
#include "IDecode.h"
#include "IAudioPlay.h"
#include "IVideoView.h"
#include "IResample.h"
#include "XLog.h"
IPlayer *IPlayer::Get(unsigned char index)
{
static IPlayer p[256];
return &p[index];
}
void IPlayer::Main()
{
while (!isExit)
{
mux.lock();
if(!audioPlay|| !vdecode)
{
mux.unlock();
XSleep(2);
continue;
}
//同步
//获取音频的pts 告诉视频
int apts = audioPlay->pts;
XLOGE("apts = %d",apts);
vdecode->synPts = apts;
mux.unlock();
XSleep(2);
}
}
bool IPlayer::Open(const char *path)
{
mux.lock();
//解封装
if(!demux || !demux->Open(path))
{
mux.unlock();
XLOGE("demux->Open %s failed!",path);
return false;
}
//解码 解码可能不需要,如果是解封之后就是原始数据
if(!vdecode || !vdecode->Open(demux->GetVPara(),isHardDecode))
{
XLOGE("vdecode->Open %s failed!",path);
//return false;
}
if(!adecode || !adecode->Open(demux->GetAPara()))
{
XLOGE("adecode->Open %s failed!",path);
//return false;
}
//重采样 有可能不需要,解码后或者解封后可能是直接能播放的数据
if(outPara.sample_rate <= 0)
outPara = demux->GetAPara();
if(!resample || !resample->Open(demux->GetAPara(),outPara))
{
XLOGE("resample->Open %s failed!",path);
}
mux.unlock();
return true;
}
bool IPlayer::Start()
{
mux.lock();
if(!demux || !demux->Start())
{
mux.unlock();
XLOGE("demux->Start failed!");
return false;
}
if(adecode)
adecode->Start();
if(audioPlay)
audioPlay->StartPlay(outPara);
if(vdecode)
vdecode->Start();
XThread::Start();
mux.unlock();
return true;
}
void IPlayer::InitView(void *win)
{
if(videoView)
videoView->SetRender(win);
}同樣需要聲明,cpp/IPlayer.h:
#ifndef XPLAY_IPLAYER_H
#define XPLAY_IPLAYER_H
#include <mutex>
#include "XThread.h"
#include "XParameter.h"
class IDemux;
class IAudioPlay;
class IVideoView;
class IResample;
class IDecode;
class IPlayer : public XThread
{
public:
static IPlayer *Get(unsigned char index=0);
virtual bool Open(const char *path);
virtual bool Start();
virtual void InitView(void *win);
//是否视频硬解码
bool isHardDecode = true;
//音频输出参数配置
XParameter outPara;
IDemux *demux = 0;
IDecode *vdecode = 0;
IDecode *adecode = 0;
IResample *resample = 0;
IVideoView *videoView = 0;
IAudioPlay *audioPlay = 0;
protected:
//用作音视频同步
void Main();
std::mutex mux;
IPlayer(){};
};
#endif //XPLAY_IPLAYER_H既然用到了pts,那麽結構體中又怎麽能少了聲明,cpp/XData.h:
#ifndef XPLAY_XDATA_H
#define XPLAY_XDATA_H
enum XDataType
{
AVPACKET_TYPE = 0,
UCHAR_TYPE = 1
};
struct XData
{
int type = 0;
int pts = 0;
unsigned char *data = 0;
unsigned char *datas[8] = {0};
int size = 0;
bool isAudio = false;
int width = 0;
int height = 0;
int format = 0;
bool Alloc(int size,const char *data=0);
void Drop();
};
#endif //XPLAY_XDATA_H看到這個結果就很欣慰了
关键字词:pts
相关文章
-
无相关信息