您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
7. FFdecode和FFResample线程安全处理和Close清理函数编写~1
发布时间:2021-06-13 15:06:59编辑:雪饮阅读()
綫程安全除了要處理pts自身外,還要處理close清理的時候,則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()
{
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/FFResample.cpp重采樣中同樣需要實現close清理時與綫程安全的兼容:
那對於其聲明cpp/FFResample.h:
那對於cpp/IDecode.h中則要聲明close的虛函數:
那麽對於cpp/IDemux.h中也是同樣的:
在cpp/IResample.cpp重采樣接口中則可以進行優化了:
重采樣接口的頭文件同樣需要聲明哈:cpp/IResample.h:
對於視頻視圖接口cpp/IVideoView.cpp這裏順德去除點冗餘的代碼:
那麽現在對於入口cpp/native-lib.cpp來説就可以更加精簡一步了:
看著還行吧
#define XPLAY_FFDECODE_H
#include "XParameter.h"
#include "IDecode.h"
struct AVCodecContext;
struct AVFrame;
class FFDecode:public IDecode
{
public:
static void InitHard(void *vm);
virtual bool Open(XParameter para,bool isHard=false);
virtual void Close();
//future模型 发送数据到线程解码
virtual bool SendPacket(XData pkt);
//从线程中获取解码结果,再次调用会复用上次空间,线程不安全
virtual XData RecvFrame();
protected:
AVCodecContext *codec = 0;
AVFrame *frame = 0;
std::mutex mux;
};
#endif //XPLAY_FFDECODE_H
{
#include <libswresample/swresample.h>
}
#include "XLog.h"
#include <libavcodec/avcodec.h>
#include "FFResample.h"
void FFResample::Close()
{
mux.lock();
if(actx)
{
swr_free(&actx);
}
mux.unlock();
}
bool FFResample::Open(XParameter in,XParameter out)
{
Close();
mux.lock();
//音频重采样上下文初始化
actx = swr_alloc();
actx = swr_alloc_set_opts(actx,
av_get_default_channel_layout(out.channels),
AV_SAMPLE_FMT_S16,out.sample_rate,
av_get_default_channel_layout(in.para->channels),
(AVSampleFormat)in.para->format,in.para->sample_rate,
0,0 );
int re = swr_init(actx);
if(re != 0)
{
mux.unlock();
XLOGE("swr_init failed!");
return false;
}
else
{
XLOGI("swr_init success!");
}
outChannels = in.para->channels;
outFormat = AV_SAMPLE_FMT_S16;
mux.unlock();
return true;
}
XData FFResample::Resample(XData indata)
{
if(indata.size<=0 || !indata.data) return XData();
mux.lock();
if(!actx)
{
mux.unlock();
return XData();
}
//XLOGE("indata pts is %d",indata.pts);
AVFrame *frame = (AVFrame *)indata.data;
//输出空间的分配
XData out;
int outsize = outChannels * frame->nb_samples * av_get_bytes_per_sample((AVSampleFormat)outFormat);
if(outsize <=0)return XData();
out.Alloc(outsize);
uint8_t *outArr[2] = {0};
outArr[0] = out.data;
int len = swr_convert(actx,outArr,frame->nb_samples,(const uint8_t **)frame->data,frame->nb_samples);
if(len<=0)
{
mux.unlock();
out.Drop();
return XData();
}
out.pts = indata.pts;
mux.unlock();
//XLOGE("swr_convert success = %d",len);
return out;
}
#define XPLAY_FFRESAMPLE_H
#include "IResample.h"
struct SwrContext;
class FFResample: public IResample
{
public:
virtual bool Open(XParameter in,XParameter out=XParameter());
virtual void Close();
virtual XData Resample(XData indata);
protected:
SwrContext *actx = 0;
std::mutex mux;
};
#endif //XPLAY_FFRESAMPLE_H
#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;
//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
#define XPLAY_IDEMUX_H
#include "XData.h"
#include "XThread.h"
#include "IObserver.h"
#include "XParameter.h"
//解封装接口
class IDemux: public IObserver {
public:
//打开文件,或者流媒体 rmtp http rtsp
virtual bool Open(const char *url) = 0;
virtual void Close() = 0;
//获取视频参数
virtual XParameter GetVPara() = 0;
//获取音频参数
virtual XParameter GetAPara() = 0;
//读取一帧数据,数据由调用者清理
virtual XData Read() = 0;
//总时长(毫秒)
int totalMs = 0;
protected:
virtual void Main();
};
#endif //XPLAY_IDEMUX_H
#include "XLog.h"
void IResample::Update(XData data)
{
XData d = this->Resample(data);
//XLOGE("this->Resample(data) %d",d.pts);
if(d.size > 0)
{
this->Notify(d);
}
}
#define XPLAY_IRESAMPLE_H
#include "XParameter.h"
#include "IObserver.h"
class IResample: public IObserver
{
public:
virtual bool Open(XParameter in,XParameter out=XParameter()) = 0;
virtual XData Resample(XData indata) = 0;
virtual void Close() = 0;
virtual void Update(XData data);
int outChannels = 2;
int outFormat = 1;
};
#endif //XPLAY_IRESAMPLE_H
#include "XLog.h"
void IVideoView::Update(XData data)
{
//("IVideoView->Update(data) %d",data.pts);
this->Render(data);
}
#include <string>
#include <android/native_window_jni.h>
#include "XLog.h"
#include "IPlayerPorxy.h"
extern "C"
JNIEXPORT
jint JNI_OnLoad(JavaVM *vm,void *res)
{
IPlayerPorxy::Get()->Init(vm);
IPlayerPorxy::Get()->Open("/sdcard/v1080.mp4");
IPlayerPorxy::Get()->Start();
return JNI_VERSION_1_4;
}
extern "C"
JNIEXPORT void JNICALL
Java_com_example_xplay_XPlay_InitView(JNIEnv *env, jobject instance, jobject surface) {
// TODO
ANativeWindow *win = ANativeWindow_fromSurface(env,surface);
IPlayerPorxy::Get()->InitView(win);
}
extern "C"
JNIEXPORT jstring
JNICALL
Java_com_example_xplay_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
关键字词:FFdecode