音频播放器—读取未解码数据以及队列操作

------------------------------------全系列文章目录------------------------------------

读取音频文件中未解码数据包
  • av_read_frame(AVFormatContext *s, AVPacket *pkt) :此函数返回文件中存储的内容,它会将文件中存储的内容拆分为帧,并为每个调用返回一个帧。

  • av_packet_unref(AVPacket *pkt):此函数用于擦拭数据包, 即取消引用数据包引用的缓冲区并将剩余的数据包字段重置为其默认值。

  • url_feof(AVIOContext *s):当文件结束时返回非零,否则返回零。

模仿ffplay中的队列操作
  • ffplay中的队列操作主要是使用了AVFifoBuffer这个结构体,可知该结构体就是使用读写游标控制的一块缓冲区。

    typedef struct AVFifoBuffer {
    uint8_t *buffer;
    uint8_t *rptr, *wptr, *end;
    uint32_t rndx, wndx;
    } AVFifoBuffer;
    
  • 定义如下数据结构

    /*未解码数据包队列*/
    class PacketQueue {
    public:
    	PacketQueue();
    	~PacketQueue();
    	int packet_queue_put(AVPacket *pkt);	//将AVPacket压入队列
    	int packet_queue_get(AVPacket *pkt);	//将AVPacket从队列取出
    	void packet_queue_flush();				//清空队列
    private:
    	AVFifoBuffer	*pkt_list;				//packet FIFO		
        int				nb_packets;				//packet 数量
    	SDL_mutex		*mutex;					//互斥锁
        SDL_cond		*cond;					//状态量
    	int				size;					//大小
    };
    
  • 相关函数

    SDL_mutex* SDL_CreateMutex(void);  			//创建一个互斥锁
    void SDL_DestroyMutex(SDL_mutex * mutex);	//释放一个互斥锁
    int SDL_LockMutex(SDL_mutex * mutex);		//给互斥锁上锁
    int SDL_UnlockMutex(SDL_mutex * mutex);		//给互斥锁解锁
    
    SDL_cond* SDL_CreateCond(void);				//创建一个条件变量
    void SDL_DestroyCond(SDL_cond * cond);		//释放一个条件变量
    int SDL_CondSignal(SDL_cond * cond);		//释放一个条件变量
    int SDL_CondWait(SDL_cond * cond, SDL_mutex * mutex);	//等待条件变量cond,暂时释放mutex
    
    AVFifoBuffer *av_fifo_alloc(unsigned int size);		//创建一个AVFifoBuffer,参数size为初始buf大小
    void av_fifo_free(AVFifoBuffer *f);					//释放一个AVFifoBuffer
    int av_fifo_space(const AVFifoBuffer *f);			//返回AVFifoBuffer的缓冲区空间大小
    /*给AVFifoBuffer的缓冲区空间增加additional_space大小的空间*/
    int av_fifo_grow(AVFifoBuffer *f, unsigned int additional_space);
    
    /*从AVFifoBuffer *f中读取一段数据,数据大小为buf_size,存放在dest中,func为用户提供的拷贝函数(NULL使用默认拷贝函数)*/
    int av_fifo_generic_read(AVFifoBuffer *f, void *dest, int buf_size, void (*func)(void*, void*, int));
    /*向AVFifoBuffer *f中写入一段数据,数据大小为size,数据源在src中,func为用户提供的拷贝函数(NULL使用默认拷贝函数)*/
    int av_fifo_generic_write(AVFifoBuffer *f, void *src, int size, int (*func)(void*, void*, int));
    
    /*将src内容移动到dst,同时复位src*/
    void av_packet_move_ref(AVPacket *dst, AVPacket *src);
    
  • 构造和析构操作

    PacketQueue::PacketQueue()
    {
    	size		= 0;
    	nb_packets	= 0;
    	pkt_list	= av_fifo_alloc(sizeof(AVPacket));		//创建一个AVFifoBuffer,初始buf大小为AVPacket
    	mutex		= SDL_CreateMutex();	
    	cond		= SDL_CreateCond();		
    	if (!pkt_list || !mutex || !cond)
    		printf("PacketQueue init error! \n");
    }
    
    PacketQueue::~PacketQueue()
    {
    	packet_queue_flush();
        av_fifo_free(pkt_list);
        SDL_DestroyMutex(mutex);
        SDL_DestroyCond(cond);
    }
    
  • 将AVPacket压入缓冲区操作

    int PacketQueue::packet_queue_put(AVPacket *pkt)
    {
    	AVPacket *pkt1;
    
    	pkt1 = (AVPacket*)av_malloc(sizeof(AVPacket));
        if (!pkt1) {
            av_packet_unref(pkt);
            return -1;
        }
    
    	memset(pkt1, 0, sizeof(AVPacket));
    	av_packet_move_ref(pkt1, pkt);
    
        SDL_LockMutex(mutex);
    
    	if (av_fifo_space(pkt_list) < sizeof(AVPacket)) {
            if (av_fifo_grow(pkt_list, sizeof(AVPacket)) < 0) {
                av_free(&pkt1);
    			return -1;
    		}
        }
    	av_fifo_generic_write(pkt_list, pkt1, sizeof(AVPacket), NULL);
    	nb_packets ++;
    	size += pkt1->size;
    	
        SDL_CondSignal(cond);
        SDL_UnlockMutex(mutex);
    
        return 0;
    }
    
  • 将AVPacket从缓冲区读出操作

    int PacketQueue::packet_queue_get(AVPacket *pkt)
    {
    	int ret = -1;
    	AVPacket *pkt1 = (AVPacket*)av_malloc(sizeof(AVPacket));
    	if (!pkt1)
    		return -1;
    
        SDL_LockMutex(mutex);
    
    	if (av_fifo_size(pkt_list) < sizeof(AVPacket))
            SDL_CondWait(cond, mutex);
        
        if (av_fifo_size(pkt_list) >= sizeof(AVPacket)) {
         	av_fifo_generic_read(pkt_list, pkt1, sizeof(AVPacket), NULL);
            nb_packets --;
            size -= pkt1->size;
            av_packet_move_ref(pkt, pkt1);
            av_free(pkt1);
            ret = 1;   
        }
    
        SDL_UnlockMutex(mutex);
    
        return ret;
    }
    
  • 清空AVFifoBuffer操作

    void PacketQueue::packet_queue_flush()
    {
    	AVPacket *pkt1 = (AVPacket*)av_malloc(sizeof(AVPacket));
    	if (!pkt1)
    		return;
    
        SDL_LockMutex(mutex);
        while (av_fifo_size(pkt_list) >= sizeof(AVPacket)) {
            av_fifo_generic_read(pkt_list, pkt1, sizeof(AVPacket), NULL);
            av_packet_unref(pkt1);
        }
        nb_packets = 0;
        size = 0;
        av_free(pkt1);
        SDL_UnlockMutex(mutex);
    }
    
借助C++ STL queue来实现队列操作
#include <queue>

AVPacket *pkt = (AVPacket*)av_malloc(sizeof(AVPacket));
queue<AVPacket*> pktq;		//声明一个队列,元素为AVPacket
pktq.push(pkt);				//压入一个元素至队列尾
AVPacket *pkt1 = q.front();	//获取队列首元素
pktq.pop();					//删除队列中的第一个元素
示例代码如下所示:
class AudioCtrl {
public:
  class PacketQueue	pktq;
  ......
private:
  ......
}
/*媒体控制*/
class MediaCtrl {
public:
	AVFormatContext		*fmt_ctx;
    class AudioCtrl		ac;
    AVPacket			avp;
	int get_audio_index() { return audio_index; }
	............
private:
	int					audio_index, video_index;
    ............
};

int main(int argc, char* argv[])
{
    .............
	/*读取未解码的帧数据*/
	audio_index = media->get_audio_index();
	while (1) {
		ret = av_read_frame(media->fmt_ctx, &media->avp);					/*读取未解码的一帧数据*/
		if (ret >= 0) {
			if (media->avp.stream_index == audio_index) {					/*音频帧*/
				media->ac.pktq.packet_queue_put(&media->avp);						/*将未解码数据压入queue中*/
			}
			while (media->ac.pktq.get_nb_pkt() > 200)								/*防止未解码帧数据过多*/
  				SDL_Delay(1000);
  			av_packet_unref(&media->avp);											/*擦除数据包*/
		} else {
			if ( ret == AVERROR_EOF && url_feof(media->fmt_ctx->pb) ) {				/*文件结尾*/
				break;
			}
			SDL_Delay(1000);			/*非文件结尾,暂停一段时间继续读取*/
		}
	}
    .............
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值