自带缓冲区
ffmpeg有自带缓存区,由于不对外暴露,无法很好的控制,经常出现播放花屏问题,当然我们可以修改源码,但通常做法是,忽略ffmpeg本身缓存区,自己实现缓冲队列,通常播放器底层会有两个队列解码队列(压缩数据) 渲染队列(非压缩数据)由于渲染队列通常有3帧左右的缓存就够了,所以当我们计算播放器的downloadSize的时候,通常说的是解码队列的数据总和,当解码队列为空的时候向上层抛开始缓冲消息,渲染器停止消费,为了播放器顺畅播放(减小卡顿率)当队列里有一定数据的时候抛出缓冲结束消息,渲染器再开始消费
AVFrame中buffer分配的两种方式
一、av_frame_get_buffer
int av_frame_get_buffer(AVFrame *frame, int align);
使用该接口分配到的数据空间,是可复用的,即内部有引用计数(reference),本次对frame data使用完成,可以解除引用,av_frame_unref(AVFrame *frame),调用后,引用计数减1,如果引用计数变为0,则释放data空间。
当然,也可以添加引用,接口:av_frame_ref(AVFrame *dst, const AVFrame *src)。
二、av_image_alloc
int av_image_alloc(uint8_t *pointers[4], int linesizes[4], int w, int h, enum AVPixelFormat pix_fmt, int align);
如上接口,和(1)不同在于,(1)只要输入frame指针即可,而本接口看不到frame的数据结构,相比(1),分配的级别更低,看注释中“The allocated image buffer has to be freed by using av_freep(&pointers[0])”,即,这种分配方式,不能用(2)中的解除引用来进行隐形释放了,需要自行调用释放才行。
注意使用过程中的坑:使用(1)进行分配后,调用ffmpeg某底层接口,结果,该接口对输入的frame做了一次解除引用,而外部调用并不知晓本次操作,从而导致可能的内存误释放,进而引起程序崩溃。
音频缓冲区——AVAudioFifo
AVAudioFifo是FFmpeg提供的一个先入先出的音频缓冲队列。主要要以下几个特点:
- 操作在样本级别而不是字节级别。
- 支持多通道的格式,不管是planar还是packed类型。
- 当写入一个已满的buffer时会自动重新分配内存。
注意:
- 音频头文件 libavutil/audio_fifo.h
主要函数
- av_audio_fifo_alloc(): 根据采样格式、通道数和样本个数创建一个AVAudioFifo。
- av_audio_fifo_realloc():根据新的样本个数为AVAudioFifo重新分配空间。
- av_audio_fifo_write(): 将数据写入AVAudioFifo。如果可用的空间小于传入nb_samples参数AVAudioFifo将自动重新分配空间。
- av_audio_fifo_size(): 获取当前AVAudioFifo中可供读取的样本数量。
- av_audio_fifo_read():从AVAudioFifo读取数据。
视频缓冲区
- 头文件:avfifo.h
一般步骤:
1、获得图像帧大小frame_size(av_image_get_buffer_size)
2、申请需要帧数的缓冲区(av_fifo_alloc_array)
3、进出缓冲区
相关函数
1、结构体
typedef struct AVFifoBuffer {
uint8_t *buffer;
uint8_t *rptr, *wptr, *end;
uint32_t rndx, wndx;
} AVFifoBuffer;
2、申请fifo
/*
*分配单个size大小的fifo,其内部调用av_fifo_alloc_array(size, 1),失败返回NULL
*/
AVFifoBuffer *av_fifo_alloc(unsigned int size);
/*
*分配nmemb个大小为size的空间,失败返回NULL
*/
AVFifoBuffer *av_fifo_alloc_array(size_t nmemb, size_t size);
3、释放fifo
/*
*释放fifo
*/
void av_fifo_free(AVFifoBuffer *f);
/*
*释放fifo,并且将f置为NULL
*/
void av_fifo_freep(AVFifoBuffer **f);
4、重置fifo
/*
*重置fifo
*/
void av_fifo_reset(AVFifoBuffer *f);
5、获取fifo内部数据大小
/*
*以字节数的方式返回当前在AVFifoBuffer中数据的大小,
*也就是你可以从当前AVFifoBuffer中读取数据的长度
*/
int av_fifo_size(const AVFifoBuffer *f);
6、获取fifo剩余容量
/*
*以字节数的方式返回当前AVFifoBuffer中剩余的容量,也就是你可以写入的内容的大小
*/
int av_fifo_space(const AVFifoBuffer *f);
7、读取fifo中的内容,(非丢弃方式,也就是不会将fifo中对应的数据擦除)
/*
*从当前AVFifoBuffer中的rptr += offset位置读取buf_size长度的数据到dest中;
*可以自定义读取函数,若没有自定义函数,则使用memcpy进行拷贝
*/
int av_fifo_generic_peek_at(AVFifoBuffer *f,
void *dest,
int offset,
int buf_size,
void (*func)(void*, void*, int));
/*
*从当前AVFifoBuffer中读取buf_size长度的数据到dest中;
*可以自定义读取函数
*/
int av_fifo_generic_peek(AVFifoBuffer *f,
void *dest,
int buf_size,
void (*func)(void*, void*, int));
8、读取fifo中的内容,(丢弃方式)
/*
*从AVFifoBuffe中读取buf_size长度的数据到dest中
*/
int av_fifo_generic_read(AVFifoBuffer *f,
void *dest,
int buf_size,
void (*func)(void*, void*, int));
9、写数据到fifo中
/*
*写size字节的数据到AVFifoBuffer中
*/
int av_fifo_generic_write(AVFifoBuffer *f,
void *src,
int size,
int (*func)(void*, void*, int));
10、重置fifo大小
int av_fifo_realloc2(AVFifoBuffer *f, unsigned int size);
11、增加fifo大小
int av_fifo_grow(AVFifoBuffer *f, unsigned int additional_space);
12、删除fifo中固定长度的内容
void av_fifo_drain(AVFifoBuffer *f, int size);