音视频解码接口类 :IDecode
#include "XParameter.h"
#include "IObserver.h"
#include <list>
//解码接口, 支持硬解码
class IDecode : public IObserver {
public:
//打开解码器
virtual bool open(XParameter parameter) = 0;
//future模式 发送数据到线程解码
virtual bool sendPacket(XData pkt) = 0;
//从线程中获取解码结果, 并不会阻塞, 再次调用会复用上次空间, 线程不安全
virtual XData recvFrame() = 0;
//由主体notift的数据 达到最大队列缓冲则阻塞
virtual void update(XData xData);
bool isAudio = false;
//最大的队列缓冲
int maxList = 100;
protected:
virtual void main();
//读取缓冲
std::list<XData> xDataList;
std::mutex xDataListMutex;
};
#endif //BOPLAY_IDECODE_H
#include "IDecode.h"
#include "XLog.h"
void IDecode::main() {
while(!isExit){
xDataListMutex.lock();
if (xDataList.empty()){
xDataListMutex.unlock();
XSleep(1);
continue;
}
//取出packet 消费者
XData xData = xDataList.front();
xDataList.pop_front();
//开始解码
//发送数据到解码线程, 音频一个数据包可能解码多个结果
if(sendPacket(xData)){
while(!isExit){
//获取解码数据
XData frame = recvFrame();
if (!frame.data){
break;
}
//发送数据给观察者,解码模块得观察者是音频重采样模块IResample或视频渲染模块IVideoView
this->Notify(frame);
}
}
//因为这是从xDataList取出的?
xData.Drop();
xDataListMutex.unlock();
}
}
void IDecode::update(XData xData) {
if (xData.isAudio != isAudio){
return;
}
while(!isExit){
xDataListMutex.lock();
if (xDataList.size() < maxList){
//生产者
xDataList.push_back(xData);
xDataListMutex.unlock();
break;
}
xDataListMutex.unlock();
//休眠1毫秒防止消耗资源
XSleep(1);
}
}
音视频解码适配器类 :FFDecode
- 编码器初始化函数
主要流程:1、查找解码器;2、创建编码器上下文,并复制参数;3、打开解码器
bool FFDecode::open(XParameter parameter, bool isHard) {
//在重新打开时优先释放掉上次打开的资源,
close();
if(nullptr == parameter.para){
return false;
}
//解码器参数
AVCodecParameters *para = parameter.para;
//1.查找解码器
//AVCodec是存储编解码器信息的结构体
AVCodec *avc = avcodec_find_decoder(para->codec_id);
if (isHard){
//如果移植到其他平台代码要做调整
avc = avcodec_find_decoder_by_name("h264_mediacodec");
}
if(!avc){
XLOGI("avcodec_find_decoder %d failed! %d", para->codec_id, isHard);
}
XLOGI("avcodec_find_decoder %d successfully! %d", para->codec_id, isHard);
//2.创建解码器上下文, 并复制参数
std::unique_lock<std::mutex> lock{codecContextMutex};
//AVCodecContex结构体除了包含解码器之外还包含视音频流相关的信息,如宽高,比特率,声道数等信息
codecContext = avcodec_alloc_context3(avc);
//参数 AVCodecParameters为编解码器的相关参数,是从AVCodecContext分离出来,其结构体中没有函数
avcodec_parameters_to_context(codecContext, para);
//多线程解码
codecContext->thread_count = 8;
//3.打开解码器
//该函数用于初始化一个视音频编解码器的AVCodecContext
int re = avcodec_open2(codecContext, nullptr, nullptr);
if (re != 0){
char buf[1024] = {0};
av_strerror(re, buf, sizeof(buf) - 1);
XLOGE("%s", buf);
return false;
}
XLOGI("avcodec_open2 successfully!");
if (AVMEDIA_TYPE_VIDEO == codecContext->codec_type){
this->isAudio = false;
} else if (AVMEDIA_TYPE_AUDIO == codecContext->codec_type){
this->isAudio = true;
}
return true;
}
- 发送AVPacket编码数据包到ffmpeg的解码线程
bool FFDecode::sendPacket(XData xData) {
//临时变量
if (xData.size <= 0 || !xData.data){
return false;
}
//codecContext类成员, 多线程访问的变量
std::unique_lock<std::mutex> lock(codecContextMutex);
if (!codecContext){
return false;
}
int re = avcodec_send_packet(codecContext, (AVPacket *)xData.data);
return re == 0;
}
XData FFDecode::recvFrame() {
std::unique_lock<std::mutex> lock(codecContextMutex);
if (!codecContext){
return XData{};
}
if (!frame){
frame = av_frame_alloc();
}
//再次调用frame会复用上次空间, 线程不安全
int re = avcodec_receive_frame(codecContext, frame);
if (re != 0){
return XData{};
}
XData xData;
xData.data = (unsigned char*)frame;
if (AVMEDIA_TYPE_VIDEO == codecContext->codec_type){
//视频帧大小,帧宽,帧高
xData.size = (frame->linesize[0] + frame->linesize[1] + frame->linesize[2]) * frame->height;
xData.width = frame->width;
xData.height = frame->height;
}else if (AVMEDIA_TYPE_AUDIO == codecContext->codec_type){
//音频帧大小 = 样本字节数 * 单通道样本数 * 通道数
xData.size = av_get_bytes_per_sample((AVSampleFormat)frame->format) * frame->nb_samples * frame->channels;
}
//遇到问题记录, 这边把xData.datas写成xData.data导致uv数据没有复制, 只有y, 有图像运行但是是绿色的
xData.format = frame->format;
memcpy(xData.datas, frame->data, sizeof(xData.datas));
xData.pts = (int)frame->pts;
pts = xData.pts;
return xData;
}
//硬解码初始化
//https://blog.csdn.net/zhangpengzp/article/details/88943867
void FFDecode::initHard(void *vm) {
av_jni_set_java_vm(vm, nullptr);
}
void FFDecode::close() {
//清空AVPacket缓冲
IDecode::clear();
codecContextMutex.lock();
pts = 0;
if (frame){
av_frame_free(&frame);
}
if (codecContext){
avcodec_close(codecContext);
avcodec_free_context(&codecContext);
}
codecContextMutex.unlock();
}
void FFDecode::clear() {
IDecode::clear();
mux.lock();
//丢掉解码器中缓存帧
if (codecContext){
avcodec_flush_buffers(codecContext);
}
mux.unlock();
}
#ifndef BOPLAY_FFDECODE_H
#define BOPLAY_FFDECODE_H
#include "IDecode.h"
struct AVCodecContext;
struct AVFrame;
class FFDecode : public IDecode {
public:
static void initHard(void *vm);
virtual bool open(XParameter parameter, bool isHard = false);
virtual void close();
//future模型 发送数据到线程解码
virtual bool sendPacket(XData pkt);
//从线程中获取解码结果, 并不会阻塞, 再次调用会复用上次空间, 线程不安全
virtual XData recvFrame();
virtual void clear();
protected:
AVCodecContext *codecContext = 0;
AVFrame *frame = 0;
std::mutex codecContextMutex;
};
#endif //BOPLAY_FFDECODE_H