因为解码获取AVPacket需要耗费一定的时间,为了达到更好地播放效果
(流畅度),需要把解码出来的AVPacket先缓存到队列中,播放时直接
从队里里面取。
1、队列
一种先进先出的数据结构
1、头文件
#include “queue”
2、创建队列
std::queue<T> queue;
3、入队
queue.push(t);
4、出队
T t = queue.front();//获取队头
queue.pop();
1.1、AVPacket队列封装
1.1.1、入队:
putAvpacket(AVPacket *avPacket)
{
//加锁
pthread_mutex_lock(&mutexPacket);
//入队
queuePacket.push(avPacket);
//发送消息给消费者
pthread_cond_signal(&condPacket);
//解锁
pthread_mutex_unlock(&mutexPacket);
}
1.1.2、出队
getAvpacket(AVPacket *avPacket)
{
pthread_mutex_lock(&mutexPacket);
while(playStatus != NULL && !playStatus->exit)
{
if(queuePacket.size() > 0)
{
AVPacket *pkt = queuePacket.front();
if(av_packet_ref(avPacket, pkt) == 0) //把pkt的内存数据拷贝到avPacket内存中
{
queuePacket.pop();
}
av_packet_free(&pkt);
av_free(pkt);
pkt = NULL;
break;
} else{
pthread_cond_wait(&condPacket, &mutexPacket);
}
}
pthread_mutex_unlock(&mutexPacket);
}
2、
创建队列的C++类-JfQueue
JfQueue.h
class JfQueue {
public:
std::queue<AVPacket *> queuePacket;//存储AVPacket的队列
pthread_mutex_t mutexPacket;//线程锁
pthread_cond_t condPacket;//消息
JfPlayStatus *jfPlayStatus = NULL; //播放状态
public:
JfQueue(JfPlayStatus *jfPlayStatus);
~JfQueue();
int putAVPacket(AVPacket *avPacket);//将AVPacket放进队列中
int getAVPacket(AVPacket *avPacket);//从队列中取出AVPacket
int getQueueSize();
};
JfPlayStatus 是一个判断是否退出的全局都要用到的类
JfPlayStatus.h
class JfPlayStatus {
public:
bool exit;
public:
JfPlayStatus();
};
JfPlayStatus.cpp
JfPlayStatus::JfPlayStatus() {
exit = false;
}
主要实现:JfQueue.cpp
- 先在构造函数中初始化mutex和cond,然后在析构函数中回收;
- 完成入队出队操作,在这个过程中创建一个全局变量的类JfPlayStatus,控制是否退出
- 在JfAudio中创建一个JfQueue指针;
JfQueue::JfQueue(JfPlayStatus *jfPlayStatus) {
this->jfPlayStatus = jfPlayStatus;
pthread_mutex_init(&mutexPacket,NULL);
pthread_cond_init(&condPacket,NULL);
}
JfQueue::~JfQueue() {
pthread_mutex_destroy(&mutexPacket);
pthread_cond_destroy(&condPacket);
}
int JfQueue::putAVPacket(AVPacket *avPacket) {
pthread_mutex_lock(&mutexPacket);
queuePacket.push(avPacket);
if (LOG_DEBUG){
LOGD("放入一个AVPacket到队列中,个数为 == %d",queuePacket.size());
}
pthread_cond_signal(&condPacket);//入队完之后发一个信号
pthread_mutex_unlock(&mutexPacket);
return 0;
}
int JfQueue::getAVPacket(AVPacket *packet) {
pthread_mutex_lock(&mutexPacket);
while (jfPlayStatus != NULL && !jfPlayStatus->exit){
if (queuePacket.size() > 0){
AVPacket *avPacket = queuePacket.front();//取出来
if (av_packet_ref(packet,avPacket) == 0){//把pkt的内存数据拷贝到avPacket内存中,只是拷贝了引用
queuePacket.pop();
}
av_packet_free(&avPacket);//AVPacket中的第一个参数,就是引用,减到0才真正释放
av_free(avPacket);
avPacket = NULL;
if (LOG_DEBUG){
LOGD("从队列中取出一个AVPacket,还剩下%d个",queuePacket.size());
}
break;
} else {
pthread_cond_wait(&condPacket,&mutexPacket);
}
}
pthread_mutex_unlock(&mutexPacket);
return 0;
}
int JfQueue::getQueueSize() {
int size = 0;
pthread_mutex_lock(&mutexPacket);
size = queuePacket.size();
pthread_mutex_unlock(&mutexPacket);
return size;
}
改JfFFmpeg.cpp
void JfFFmpeg::start() {
if (audio == NULL) {
if (LOG_DEBUG){
LOGE("AUDIO == NULL");
}
}
int count;
while (1) {
AVPacket *avPacket = av_packet_alloc();
if (av_read_frame(pAFmtCtx,avPacket) == 0) {
if (avPacket->stream_index == audio->streamIndex){
count++;
if (LOG_DEBUG) {
LOGD("解码第%d帧",count);
}
audio->queue->putAVPacket(avPacket);
} else {
av_packet_free(&avPacket);
av_free(avPacket);
avPacket = NULL;
}
} else {
av_packet_free(&avPacket);
av_free(avPacket);
avPacket = NULL;
break;
}
}
while (audio->queue->getQueueSize() > 0){
AVPacket *avPacket = av_packet_alloc();
audio->queue->getAVPacket(avPacket);
av_packet_free(&avPacket);
av_free(avPacket);
avPacket = NULL;
}
if (LOG_DEBUG){
LOGD("解码完成");
}
}
源码地址:https://github.com/Xiaoben336/SuperAudioPlayer.git:pktQueue分支
网友评论