您当前的位置: 首页 > 学无止境 > 心得笔记 网站首页心得笔记
22. 通过手动解码帧数据完成了IPlayer控制视频播放进度的功能~1
发布时间:2021-06-14 19:13:54编辑:雪饮阅读()
這次改動不大主要是涉及cpp/FFdecode.cpp:
{
#include "../../../include/libavcodec/avcodec.h"
#include "../../../include/libavcodec/jni.h"
#include "../../../include/libavutil/frame.h"
}
#include "FFDecode.h"
#include "XLog.h"
void FFDecode::InitHard(void *vm)
{
av_jni_set_java_vm(vm,0);
}
void FFDecode::Clear()
{
IDecode::Clear();
mux.lock();
if(codec)
avcodec_flush_buffers(codec);
mux.unlock();
}
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/FFdecode.h中要聲明清理函數:
改動最大的當然還是cpp/IPlayer.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::Clear()
{
IDecode::Clear();
mux.lock();
if(codec)
avcodec_flush_buffers(codec);
mux.unlock();
}
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;
}
#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();
virtual void Clear();
//future模型 发送数据到线程解码
virtual bool SendPacket(XData pkt);
//从线程中获取解码结果,再次调用会复用上次空间,线程不安全
virtual XData RecvFrame();
protected:
AVCodecContext *codec = 0;
AVFrame *frame = 0;
std::mutex mux;
};
#endif //XPLAY_FFDECODE_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);
}
}
void IPlayer::Close()
{
mux.lock();
//2 先关闭主体线程,再清理观察者
//同步线程
XThread::Stop();
//解封装
if(demux)
demux->Stop();
//解码
if(vdecode)
vdecode->Stop();
if(adecode)
adecode->Stop();
if(audioPlay)
audioPlay->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();
}
double IPlayer::PlayPos()
{
double pos = 0.0;
mux.lock();
int total = 0;
if(demux)
total = demux->totalMs;
if(total>0)
{
if(vdecode)
{
pos = (double)vdecode->pts/(double)total;
}
}
mux.unlock();
return pos;
}
void IPlayer::SetPause(bool isP)
{
mux.lock();
XThread::SetPause(isP);
if(demux)
demux->SetPause(isP);
if(vdecode)
vdecode->SetPause(isP);
if(adecode)
adecode->SetPause(isP);
if(audioPlay)
audioPlay->SetPause(isP);
mux.unlock();
}
bool IPlayer::Seek(double pos)
{
bool re = false;
if(!demux) return false;
//暂停所有线程
SetPause(true);
mux.lock();
//清理缓冲
//2 清理缓冲队列
if(vdecode)
vdecode->Clear(); //清理缓冲队列,清理ffmpeg的缓冲
if(adecode)
adecode->Clear();
if(audioPlay)
audioPlay->Clear();
re = demux->Seek(pos); //seek跳转到关键帧
if(!vdecode)
{
mux.unlock();
SetPause(false);
return re;
}
//解码到实际需要显示的帧
int seekPts = pos*demux->totalMs;
while(!isExit)
{
XData pkt = demux->Read();
if(pkt.size<=0)break;
if(pkt.isAudio)
{
if(pkt.pts < seekPts)
{
pkt.Drop();
continue;
}
//写入缓冲队列
demux->Notify(pkt);
continue;
}
//解码需要显示的帧之前的数据
vdecode->SendPacket(pkt);
pkt.Drop();
XData data = vdecode->RecvFrame();
if(data.size <=0)
{
continue;
}
if(data.pts >= seekPts)
{
//vdecode->Notify(data);
break;
}
}
mux.unlock();
SetPause(false);
return re;
}
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->Close();
videoView->SetRender(win);
}
}
关键字词:IPlayer