您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
12. IPlayer完成Close清理函数编写并完成多次打开视频文件的测试~1
发布时间:2021-06-13 21:37:51编辑:雪饮阅读()
首先我們要在ffmpeg解碼之前先清理下,防止之前解碼殘留cpp/FFdecode.cpp:
{
#include <libavcodec/avcodec.h>
#include <libavcodec/jni.h>
}
#include "FFDecode.h"
#include "XLog.h"
void FFDecode::InitHard(void *vm)
{
av_jni_set_java_vm(vm,0);
}
void FFDecode::Close()
{
IDecode::Clear();
mux.lock();
pts = 0;
if(frame)
av_frame_free(&frame);
if(codec)
{
avcodec_close(codec);
avcodec_free_context(&codec);
}
mux.unlock();
}
bool FFDecode::Open(XParameter para,bool isHard)
{
Close();
if(!para.para) return false;
AVCodecParameters *p = para.para;
//1 查找解码器
AVCodec *cd = avcodec_find_decoder(p->codec_id);
if(isHard)
{
cd = avcodec_find_decoder_by_name("h264_mediacodec");
}
if(!cd)
{
XLOGE("avcodec_find_decoder %d failed! %d",p->codec_id,isHard);
return false;
}
XLOGI("avcodec_find_decoder success %d!",isHard);
mux.lock();
//2 创建解码上下文,并复制参数
codec = avcodec_alloc_context3(cd);
avcodec_parameters_to_context(codec,p);
codec->thread_count = 8;
//3 打开解码器
int re = avcodec_open2(codec,0,0);
if(re != 0)
{
mux.unlock();
char buf[1024] = {0};
av_strerror(re,buf,sizeof(buf)-1);
XLOGE("%s",buf);
return false;
}
if(codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
this->isAudio = false;
}
else
{
this->isAudio = true;
}
mux.unlock();
XLOGI("avcodec_open2 success!");
return true;
}
bool FFDecode::SendPacket(XData pkt)
{
if(pkt.size<=0 || !pkt.data)return false;
mux.lock();
if(!codec)
{
mux.unlock();
return false;
}
int re = avcodec_send_packet(codec,(AVPacket*)pkt.data);
mux.unlock();
if(re != 0)
{
return false;
}
return true;
}
//从线程中获取解码结果
XData FFDecode::RecvFrame()
{
mux.lock();
if(!codec)
{
mux.unlock();
return XData();
}
if(!frame)
{
frame = av_frame_alloc();
}
int re = avcodec_receive_frame(codec,frame);
if(re != 0)
{
mux.unlock();
return XData();
}
XData d;
d.data = (unsigned char *)frame;
if(codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
d.size = (frame->linesize[0] + frame->linesize[1] + frame->linesize[2])*frame->height;
d.width = frame->width;
d.height = frame->height;
}
else
{
//样本字节数 * 单通道样本数 * 通道数
d.size = av_get_bytes_per_sample((AVSampleFormat)frame->format)*frame->nb_samples*2;
}
d.format = frame->format;
//if(!isAudio)
// XLOGE("data format is %d",frame->format);
memcpy(d.datas,frame->data,sizeof(d.datas));
d.pts = frame->pts;
pts = d.pts;
mux.unlock();
return d;
}
而在cpp/IAudioPlay.h中藥聲明clear函數:
Cpp/IDecode.cpp中實現清理函數:
Cpp/IDecode.h中聲明清理函數:
Cpp/IPlayer.cpp中要實現close函數:
Cpp/IPlayer.h中要聲明close函數:
Cpp/IPlayerPorxy.cpp中也是實現close函數:
Cpp/IPlayerPorxy.h中也是聲明close函數:
最重要的還數cpp/IVideoView.h中聲明close函數:
然後cpp/SLAudioPlay.cpp中實現clear函數調用:
然後下面這個是運行在雷電4播放完成后的日志貓
#include "XLog.h"
void IAudioPlay::Clear()
{
framesMutex.lock();
while(!frames.empty())
{
frames.front().Drop();
frames.pop_front();
}
framesMutex.unlock();
}
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;
virtual void Close() = 0;
virtual void Clear();
//最大缓冲
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::Clear()
{
packsMutex.lock();
while(!packs.empty())
{
packs.front().Drop();
packs.pop_front();
}
pts = 0;
synPts = 0;
packsMutex.unlock();
}
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;
virtual void Close() = 0;
virtual void Clear();
//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 "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);
}
}
void IPlayer::Close()
{
mux.lock();
//2 先关闭主体线程,再清理观察者
//同步线程
XThread::Stop();
//解封装
if(demux)
demux->Stop();
//解码
if(vdecode)
vdecode->Stop();
if(adecode)
adecode->Stop();
//2 清理缓冲队列
if(vdecode)
vdecode->Clear();
if(adecode)
adecode->Clear();
if(audioPlay)
audioPlay->Clear();
//3 清理资源
if(audioPlay)
audioPlay->Close();
if(videoView)
videoView->Close();
if(vdecode)
vdecode->Close();
if(adecode)
adecode->Close();
if(demux)
demux->Close();
mux.unlock();
}
bool IPlayer::Open(const char *path)
{
Close();
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(vdecode)
vdecode->Start();
if(!demux || !demux->Start())
{
mux.unlock();
XLOGE("demux->Start failed!");
return false;
}
if(adecode)
adecode->Start();
if(audioPlay)
audioPlay->StartPlay(outPara);
XThread::Start();
mux.unlock();
return true;
}
void IPlayer::InitView(void *win)
{
if(videoView)
videoView->SetRender(win);
}
#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 void Close();
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
#include "FFPlayerBuilder.h"
void IPlayerPorxy::Close()
{
mux.lock();
if(player)
player->Close();
mux.unlock();
}
void IPlayerPorxy::Init(void *vm)
{
mux.lock();
if(vm)
{
FFPlayerBuilder::InitHard(vm);
}
if(!player)
player = FFPlayerBuilder::Get()->BuilderPlayer();
mux.unlock();
}
bool IPlayerPorxy::Open(const char *path)
{
bool re = false;
mux.lock();
if(player)
re = player->Open(path);
mux.unlock();
return re;
}
bool IPlayerPorxy::Start()
{
bool re = false;
mux.lock();
if(player)
re = player->Start();
mux.unlock();
return re;
}
void IPlayerPorxy::InitView(void *win)
{
mux.lock();
if(player)
player->InitView(win);
mux.unlock();
}
#define XPLAY_IPLAYERPORXY_H
#include "IPlayer.h"
#include <mutex>
class IPlayerPorxy: public IPlayer
{
public:
static IPlayerPorxy*Get()
{
static IPlayerPorxy px;
return &px;
}
void Init(void *vm = 0);
virtual bool Open(const char *path);
virtual void Close();
virtual bool Start();
virtual void InitView(void *win);
protected:
IPlayerPorxy(){}
IPlayer *player = 0;
std::mutex mux;
};
#endif //XPLAY_IPLAYERPORXY_H
#define XPLAY_IVIDEOVIEW_H
#include "XData.h"
#include "IObserver.h"
class IVideoView:public IObserver
{
public:
virtual void SetRender(void *win) = 0;
virtual void Render(XData data) = 0;
virtual void Update(XData data);
virtual void Close() = 0;
};
#endif //XPLAY_IVIDEOVIEW_H
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include "XLog.h"
static SLObjectItf engineSL = NULL;
static SLEngineItf eng = NULL;
static SLObjectItf mix = NULL;
static SLObjectItf player = NULL;
static SLPlayItf iplayer = NULL;
static SLAndroidSimpleBufferQueueItf pcmQue = NULL;
SLAudioPlay::SLAudioPlay()
{
buf = new unsigned char[1024*1024];
}
SLAudioPlay::~SLAudioPlay()
{
delete buf;
buf = 0;
}
static SLEngineItf CreateSL()
{
SLresult re;
SLEngineItf en;
re = slCreateEngine(&engineSL,0,0,0,0,0);
if(re != SL_RESULT_SUCCESS) return NULL;
re = (*engineSL)->Realize(engineSL,SL_BOOLEAN_FALSE);
if(re != SL_RESULT_SUCCESS) return NULL;
re = (*engineSL)->GetInterface(engineSL,SL_IID_ENGINE,&en);
if(re != SL_RESULT_SUCCESS) return NULL;
return en;
}
void SLAudioPlay::PlayCall(void *bufq)
{
if(!bufq)return;
SLAndroidSimpleBufferQueueItf bf = (SLAndroidSimpleBufferQueueItf)bufq;
//XLOGE("SLAudioPlay::PlayCall");
//阻塞
XData d = GetData();
if(d.size<=0)
{
XLOGE("GetData() size is 0");
return;
}
if(!buf)
return;
memcpy(buf,d.data,d.size);
mux.lock();
(*bf)->Enqueue(bf,buf,d.size);
mux.unlock();
d.Drop();
}
static void PcmCall(SLAndroidSimpleBufferQueueItf bf,void *contex)
{
SLAudioPlay *ap = (SLAudioPlay *)contex;
if(!ap)
{
XLOGE("PcmCall failed contex is null!");
return;
}
ap->PlayCall((void *)bf);
}
void SLAudioPlay::Close()
{
IAudioPlay::Clear();
mux.lock();
//停止播放
if(iplayer && (*iplayer))
{
(*iplayer)->SetPlayState(iplayer,SL_PLAYSTATE_STOPPED);
}
//清理播放队列
if(pcmQue && (*pcmQue))
{
(*pcmQue)->Clear(pcmQue);
}
//销毁player对象
if(player && (*player))
{
(*player)->Destroy(player);
}
//销毁混音器
if(mix && (*mix))
{
(*mix)->Destroy(mix);
}
//销毁播放引擎
if(engineSL && (*engineSL))
{
(*engineSL)->Destroy(engineSL);
}
mux.unlock();
}
bool SLAudioPlay::StartPlay(XParameter out)
{
Close();
mux.lock();
//1 创建引擎
eng = CreateSL();
if(eng)
{
XLOGI("CreateSL success! ");
}
else
{
mux.unlock();
XLOGE("CreateSL failed! ");
return false;
}
//2 创建混音器
SLresult re = 0;
re = (*eng)->CreateOutputMix(eng,&mix,0,0,0);
if(re !=SL_RESULT_SUCCESS )
{
mux.unlock();
XLOGE("SL_RESULT_SUCCESS failed!");
return false;
}
re = (*mix)->Realize(mix,SL_BOOLEAN_FALSE);
if(re !=SL_RESULT_SUCCESS )
{
mux.unlock();
XLOGE("(*mix)->Realize failed!");
return false;
}
SLDataLocator_OutputMix outmix = {SL_DATALOCATOR_OUTPUTMIX,mix};
SLDataSink audioSink= {&outmix,0};
//3 配置音频信息
//缓冲队列
SLDataLocator_AndroidSimpleBufferQueue que = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,10};
//音频格式
SLDataFormat_PCM pcm = {
SL_DATAFORMAT_PCM,
(SLuint32) out.channels,// 声道数
(SLuint32) out.sample_rate*1000,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,
SL_BYTEORDER_LITTLEENDIAN //字节序,小端
};
SLDataSource ds = {&que,&pcm};
//4 创建播放器
const SLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};
const SLboolean req[] = {SL_BOOLEAN_TRUE};
re = (*eng)->CreateAudioPlayer(eng,&player,&ds,&audioSink,sizeof(ids)/sizeof(SLInterfaceID),ids,req);
if(re !=SL_RESULT_SUCCESS )
{
mux.unlock();
XLOGE("CreateAudioPlayer failed!");
return false;
} else{
XLOGI("CreateAudioPlayer success!");
}
(*player)->Realize(player,SL_BOOLEAN_FALSE);
//获取player接口
re = (*player)->GetInterface(player,SL_IID_PLAY,&iplayer);
if(re !=SL_RESULT_SUCCESS )
{
mux.unlock();
XLOGE("GetInterface SL_IID_PLAY failed!");
return false;
}
re = (*player)->GetInterface(player,SL_IID_BUFFERQUEUE,&pcmQue);
if(re !=SL_RESULT_SUCCESS )
{
mux.unlock();
XLOGE("GetInterface SL_IID_BUFFERQUEUE failed!");
return false;
}
//设置回调函数,播放队列空调用
(*pcmQue)->RegisterCallback(pcmQue,PcmCall,this);
//设置为播放状态
(*iplayer)->SetPlayState(iplayer,SL_PLAYSTATE_PLAYING);
//启动队列回调
(*pcmQue)->Enqueue(pcmQue,"",1);
mux.unlock();
XLOGI("SLAudioPlay::StartPlay success!");
return true;
}
关键字词:IPlayer