音視頻解碼接口類 :IDecode
#include "XParameter.h"
#include "IObserver.h"
#include <list>
//解碼接口, 支持硬解碼
class IDecode : public IObserver {
public:
//打開解碼器
virtual bool open(XParameter parameter) = 0;
//future模式 發(fā)送數(shù)據(jù)到線程解碼
virtual bool sendPacket(XData pkt) = 0;
//從線程中獲取解碼結(jié)果, 并不會阻塞, 再次調(diào)用會復(fù)用上次空間, 線程不安全
virtual XData recvFrame() = 0;
//由主體notift的數(shù)據(jù) 達(dá)到最大隊(duì)列緩沖則阻塞
virtual void update(XData xData);
bool isAudio = false;
//最大的隊(duì)列緩沖
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 消費(fèi)者
XData xData = xDataList.front();
xDataList.pop_front();
//開始解碼
//發(fā)送數(shù)據(jù)到解碼線程, 音頻一個數(shù)據(jù)包可能解碼多個結(jié)果
if(sendPacket(xData)){
while(!isExit){
//獲取解碼數(shù)據(jù)
XData frame = recvFrame();
if (!frame.data){
break;
}
//發(fā)送數(shù)據(jù)給觀察者,解碼模塊得觀察者是音頻重采樣模塊IResample或視頻渲染模塊IVideoView
this->Notify(frame);
}
}
//因?yàn)檫@是從xDataList取出的?
xData.Drop();
xDataListMutex.unlock();
}
}
void IDecode::update(XData xData) {
if (xData.isAudio != isAudio){
return;
}
while(!isExit){
xDataListMutex.lock();
if (xDataList.size() < maxList){
//生產(chǎn)者
xDataList.push_back(xData);
xDataListMutex.unlock();
break;
}
xDataListMutex.unlock();
//休眠1毫秒防止消耗資源
XSleep(1);
}
}
音視頻解碼適配器類 :FFDecode
- 編碼器初始化函數(shù)
主要流程:1、查找解碼器;2、創(chuàng)建編碼器上下文臣嚣,并復(fù)制參數(shù);3剥哑、打開解碼器
bool FFDecode::open(XParameter parameter, bool isHard) {
//在重新打開時優(yōu)先釋放掉上次打開的資源硅则,
close();
if(nullptr == parameter.para){
return false;
}
//解碼器參數(shù)
AVCodecParameters *para = parameter.para;
//1.查找解碼器
//AVCodec是存儲編解碼器信息的結(jié)構(gòu)體
AVCodec *avc = avcodec_find_decoder(para->codec_id);
if (isHard){
//如果移植到其他平臺代碼要做調(diào)整
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.創(chuàng)建解碼器上下文, 并復(fù)制參數(shù)
std::unique_lock<std::mutex> lock{codecContextMutex};
//AVCodecContex結(jié)構(gòu)體除了包含解碼器之外還包含視音頻流相關(guān)的信息,如寬高株婴,比特率怎虫,聲道數(shù)等信息
codecContext = avcodec_alloc_context3(avc);
//參數(shù) AVCodecParameters為編解碼器的相關(guān)參數(shù)暑认,是從AVCodecContext分離出來,其結(jié)構(gòu)體中沒有函數(shù)
avcodec_parameters_to_context(codecContext, para);
//多線程解碼
codecContext->thread_count = 8;
//3.打開解碼器
//該函數(shù)用于初始化一個視音頻編解碼器的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;
}
- 發(fā)送AVPacket編碼數(shù)據(jù)包到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();
}
//再次調(diào)用frame會復(fù)用上次空間, 線程不安全
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){
//音頻幀大小 = 樣本字節(jié)數(shù) * 單通道樣本數(shù) * 通道數(shù)
xData.size = av_get_bytes_per_sample((AVSampleFormat)frame->format) * frame->nb_samples * frame->channels;
}
//遇到問題記錄, 這邊把xData.datas寫成xData.data導(dǎo)致uv數(shù)據(jù)沒有復(fù)制, 只有y, 有圖像運(yùn)行但是是綠色的
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模型 發(fā)送數(shù)據(jù)到線程解碼
virtual bool sendPacket(XData pkt);
//從線程中獲取解碼結(jié)果, 并不會阻塞, 再次調(diào)用會復(fù)用上次空間, 線程不安全
virtual XData recvFrame();
virtual void clear();
protected:
AVCodecContext *codecContext = 0;
AVFrame *frame = 0;
std::mutex codecContextMutex;
};
#endif //BOPLAY_FFDECODE_H