从音频设备中读取音频数据
// AVFormatContext *s 上下文
// AVPacket *pkt 音视频包,到底是音频包、还是视频包,跟环境有关系,可以通过AVPacket做判断,到底是音频包、还是视频包
// res非0,读取失败,为0,成功,可以拿个while循环,来判断数据有没有读取完毕
int res = av_read_frame(<#AVFormatContext *s#>, <#AVPacket *pkt#>)
AVPacket参数
- data,音视频的具体数据
- size,data缓冲区的大小,
AVPacket相关API
//对AVPacket进行一次初始化,就是对除了data和size外,其他的参数进行的初始化
av_init_packet(<#AVPacket *pkt#>)
//释放AVPacket资源,减少一个引用计数,与上面的API,是一对
av_packet_unref(<#AVPacket *pkt#>)
init和unref 是成双出现的
//给AVPacket分配空间,
//做了2件事,1.分配空间 2. 调用av_init_packet
av_packet_alloc();
//做了2件事,1.先调用av_packet_unref ,2. 对av_packet_alloc创建的空间进行释放
av_packet_free(<#AVPacket **pkt#>)
alloc和free 是成双出现的
从设备中读取音频数据
- 增加头文件 #include "libavcodec/avcodec.h"
- 看下面代码
//每次重新分配地址
AVPacket pkt;
//初始化
av_init_packet(&pkt);
//默认为读取500次后退出
int count = 0;
while ((av_read_frame(fmt_ctx, &pkt) == 0)&& (count++ <= 500)) {
}
总的代码
这是目前testc.c文件中的代码
void haha()
{
av_log_set_level(AV_LOG_DEBUG);
av_log(NULL, AV_LOG_DEBUG, "Hello world\n");
//1. 注册所有设备
avdevice_register_all();
//2. 获取格式 设置采集方式
AVInputFormat * iformat = av_find_input_format("avfoundation");
AVFormatContext *fmt_ctx = NULL;
//音频设备 mac上 :0 从第一个音频设备获取声音 可以到ffmpeg官网查看devicename说明
char * devicename = ":0";
AVDictionary *options = NULL;
//3.打开设备
/*
AVFormatContext **ps 上下文,作用:前面调用的API和后面调用的API都依赖于上下文,对一个多媒体文件进行解码、编码都依赖于上下文,操作那个文件,只需要把这个文件的上下文传过去,就知道是对那个文件操作了 。这个上下文是avformat_open_input这个API返回的指针,我们只要定义这个指针就行 AVFormatContext *fmt_ctx;
const char *url: 可以是一个网络地址,也可以是本地的文件,
AVInputFormat *fmt
AVDictionary **options 打开设备的一些参数,一般解码的时候用到
*/
// 成功==0,不成功!= 0
int ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options);
if (ret < 0) {
/*
int enum 错误码
char errbuf 错误信息输出到哪
size_t errbuf_size
*/
char errors[1024];
av_strerror(ret, errors, 1024);
printf(stderr,"Failed to open audio device,[%d]%s\n",ret,errors);
return;
}
//每次重新分配地址
AVPacket pkt;
//初始化
av_init_packet(&pkt);
//默认为读取500次后退出
int count = 0;
while ((av_read_frame(fmt_ctx, &pkt) == 0)&& (count++ <= 500)) {
}
av_packet_unref(&pkt);
return;
};
优化代码
内存泄露
avformat_open_input
产生了一个上下文,需要关掉,掉下面API
avformat_close_input(<#AVFormatContext **s#>)
av_read_frame
使用完成之后,没有将对应的资源释放掉,随着录制时间越来越长,占用的内存越来越多,产生程序没有内存而崩掉的问题
AVPacket相关的API
av_init_packet
初始化AVPacket,使用默认值
av_packet_unref
释放AVPacket,减一个引用计数,在内部对AVPacket中的buffer(缓冲区的数据)进行释放
av_packet_alloc()
产生一个AVPacket空间,在堆段,
av_packet_free(<#AVPacket **pkt#>)
释放alloc分配的资
一般顺序是先掉av_packet_unref,释放掉AVPacket内部的资源,然后再掉av_packet_free释放掉对应的内存地址
优化后的代码
void record_audio()
{
av_log_set_level(AV_LOG_DEBUG);
//1. 注册所有设备
avdevice_register_all();
//2. 获取格式 设置采集方式
AVInputFormat * iformat = av_find_input_format("avfoundation");
AVFormatContext *fmt_ctx = NULL;
AVDictionary *options = NULL;
//每次重新分配地址
AVPacket pkt;
//默认为读取500次后退出
int count = 0;
//音频设备 mac上 :0 从第一个音频设备获取声音 可以到ffmpeg官网查看devicename说明
char * devicename = ":0";
//3.打开设备
/*
AVFormatContext **ps 上下文,作用:前面调用的API和后面调用的API都依赖于上下文,对一个多媒体文件进行解码、编码都依赖于上下文,操作那个文件,只需要把这个文件的上下文传过去,就知道是对那个文件操作了 。这个上下文是avformat_open_input这个API返回的指针,我们只要定义这个指针就行 AVFormatContext *fmt_ctx;
const char *url: 可以是一个网络地址,也可以是本地的文件,
AVInputFormat *fmt
AVDictionary **options 打开设备的一些参数,一般解码的时候用到
*/
// 成功==0,不成功!= 0
int ret = avformat_open_input(&fmt_ctx, devicename, iformat, &options);
if (ret < 0) {
/*
int enum 错误码
char errbuf 错误信息输出到哪
size_t errbuf_size
*/
char errors[1024];
av_strerror(ret, errors, 1024);
printf(stderr,"Failed to open audio device,[%d]%s\n",ret,errors);
return;
}
//初始化
av_init_packet(&pkt);
while ((av_read_frame(fmt_ctx, &pkt) == 0)&& (count++ <= 500)) {
//释放掉pkt
av_packet_unref(&pkt);
}
//释放上下文
avformat_close_input(&fmt_ctx);
av_log(NULL, AV_LOG_DEBUG, "Finished\n");
return;
};