在Linux环境下使用ffmpeg将PCM音频数据编码成aac数据

  • 程序框图
    • 代码演示

程序框图

将pcm数据经过aac编码器编码成aac数据,我是将从设备上采集的数据经过重采样送入aac编码器进行编码,由于我的测试设备是Ubuntu 18.04虚拟机,采样率为44100hz,采样大小为有符号16位数,满足编码器的输入数据参数要求,所以也可以直接将从设备获取的音频数据送入编码器进行编码,无需经过重采样,但是下面的示例代码我是将从设备读取的音频数据经过重采样再送入编码器进行编码,但是我设置重采样的输入输出音频三元组没有发生变化,所以下面的代码,我重采样相当于采了个寂寞,颇有点脱裤子放屁的意思。在此说明防止大家阅读代码的时候产生误解。

代码演示


#include "ffmpeg_function.h"#define BUFFER 2048/*****************************函数名:SwrContext* init_swr(SwrContext*swr_ctx)函数作用:创建重采样上下文,和设置重采样参数函数参数:SwrContext*swr_ctx上下文指针函数返回值:SwrContext*swr_ctx上下文指针******************************/
static SwrContext* init_swr(SwrContext*swr_ctx)
{swr_ctx=swr_alloc_set_opts(NULL,                //ctx上下文AV_CH_LAYOUT_STEREO,        //输出channel布局AV_SAMPLE_FMT_S16,          //输出的采样格式44100,                      //输出的采样率AV_CH_LAYOUT_STEREO,        //输入的channel布局AV_SAMPLE_FMT_S16,          //输入的采样格式    AV_SAMPLE_FMT_FLT44100,                      //输入的采样率0,      NULL);if (NULL==swr_ctx)         //判对返回的上下文指针是否为空{printf("swr_ctx errpr\n");return NULL;}if (swr_init(swr_ctx)<0)   //初始化上下文{printf("swr_ctx init errpr\n");return NULL;}return swr_ctx;
}/*****************************函数名:static AVCodecContext *open_coder(void)函数作用:打开AAC编码器,并设置编码器的音频输入参数函数参数:无函数返回值:AVCodecContext * codec_ctx打开编码器的上下文指针******************************/
static AVCodecContext *open_coder(void)
{//查找编码器AVCodec *codec=avcodec_find_encoder_by_name("libfdk_aac");if (NULL==codec){printf("avcodec_find_decoder_by_name error\n");exit(-1);}//AVCodec *codec=avcodec_find_encoder(AV_CODEC_ID_AAC);//创建编码器上下文AVCodecContext * codec_ctx=avcodec_alloc_context3(codec);  if (NULL==codec_ctx){printf("avcodec_alloc_context3 error\n");exit(-1);}codec_ctx->sample_fmt=AV_SAMPLE_FMT_S16;     //输入音频的采样大小codec_ctx->channel_layout=AV_CH_LAYOUT_STEREO;  //输入音频的channel layoutcodec_ctx->channels=2;                       //输入音频的channel数codec_ctx->sample_rate=44100;                //输入音频的采样率codec_ctx->bit_rate=0;  //编码器码流大小AAC_LC:128K,AAC HE:64K,AAC HE V2:32K    默认的AAC为AAC_LC//如果使用了codec_ctx->profile选项选择对应的编码器,就要设置codec->bit_rate=0,只有在codec->bit_rate//设置成0之后ffmpeg才回去查找codec_ctx->profile这个选项codec_ctx->profile=FF_PROFILE_AAC_HE_V2;   //选择对应的编码器//打开编码器printf("error  test3\n");if(avcodec_open2(codec_ctx,codec,NULL)<0){printf("open aac error\n");return NULL;}else{return codec_ctx;}
}/*****************************函数名:static AVFrame* creat_frame(AVFrame*frame)函数作用:创建编码器的输入数据的缓冲区结构体函数参数:AVFrame*frame编码器输入数据结构体指针函数返回值:AVFrame*frame编码器输入数据结构体指针******************************/
static AVFrame* creat_frame(AVFrame*frame)
{frame=av_frame_alloc();  //分配编码器输入数据结构体AVFrame,在堆中分配if (NULL==frame){printf("av_frame_alloc error\n");return NULL;}frame->nb_samples=512;           //设置分配buffer大小,即为单通道一个音频帧的采样数frame->format=AV_SAMPLE_FMT_S16;  // 每个采样的大小frame->channel_layout=AV_CH_LAYOUT_STEREO;av_frame_get_buffer(frame,0);    //分配空间给AVFrame里面的存放音视频数据的buffer//实际大小为512*2*2=2048if (NULL==frame->buf[0]){printf("av_frame_get_buffer error\n");av_frame_free(frame);return NULL;}return frame;
}/*****************************函数名:static void encode_aac_file(AVCodecContext *codec_ctx,AVFrame *frame,AVPacket *new_packet,int file_fd)函数作用:将重采样的音频数据喂入编码器,并将编码后的输出数据保存在文件中。函数参数:AVCodecContext * codec_ctx 打开编码器的上下文指针AVFrame*frame  编码器输入编码数据结构体指针AVPacket *new_packet  编码器输出编码数据结构体指针int file_fd 保存文件的文件描述符函数返回值:无******************************/
static void encode_aac_file(AVCodecContext *codec_ctx,AVFrame *frame,AVPacket *new_packet,int file_fd)
{//将数据送入编码器int ret=0;ret=avcodec_send_frame(codec_ctx,frame);//如果ret>=0说明数据设置成功while (ret>=0)      //获取编码后的音频数据{ret=0;//获取编码后的数据,如果成功,需要重复获取,直到失败为止ret=avcodec_receive_packet(codec_ctx,new_packet);if (ret<0){//编码器没有数据了,需要退出向编码器喂数据if (ret==AVERROR(EAGAIN)||AVERROR_EOF) return;else  //编码器出错程序退出{printf("error encoding audio frame\n");exit(-1);}}write(file_fd,new_packet->data,new_packet->size);}
}
/*****************************函数名:static AVFormatContext* open_dev(void)函数作用:打开音频输入设备函数参数:无函数返回值:AVFormatContext *fmt_ctx  打开音频设备的上下文指针******************************/
static AVFormatContext* open_dev(void)
{char errors[1024]={0};//ctxAVFormatContext *fmt_ctx=NULL;AVDictionary *option =NULL;//mic addresschar *devicename="hw:0";//注册音频设备avdevice_register_all();//获取音频格式AVInputFormat *iformat=av_find_input_format("alsa");//打开设备int ret=avformat_open_input(&fmt_ctx,devicename,iformat,&option);if (ret<0){av_strerror(ret,errors,1024);return NULL;}return fmt_ctx;
}/*****************************函数名:static uint8_t* packet_data_buffer(AVPacket *pkt,uint8_t *buf)函数作用:对从av_read_frame()函数读取到的音频数据进行缓冲,数据太小无法重采样函数参数:无函数返回值:AVFormatContext *fmt_ctx  打开音频设备的上下文指针******************************/
static uint8_t* packet_data_buffer(AVPacket *pkt,uint8_t *buf)
{static int data_num=0;if (data_num < (BUFFER-64)) {for (int i = 0; i < pkt->size; ++i){buf[i + data_num] = pkt->data[i];}data_num +=pkt->size;return NULL;}else{//把最后一次判断未进if中存放的一包数据放入临时缓冲区bufferData中for (int i = 0; i < pkt->size; ++i){buf[i + data_num] = pkt->data[i];}data_num += pkt->size;data_num = 0;return buf;}
}
/*****************************函数名:static uint8_t* packet_data_buffer(AVPacket *pkt,uint8_t *buf)函数作用:录制音频数据并编码保存成acc格式的音频文件函数参数:无函数返回值:无******************************/
void ffmpeg_record_aac(void)
{int file_fd=0; //存放音频数据的文件描述符int count =0;int ret=0;    uint8_t **src_data=NULL; //存放输入数据int src_linesize=0;      //存放输入数据的大小uint8_t **dts_data=NULL; //存放输出数据int dts_linesize=0;      //存放输出数据的大小SwrContext*swr_ctx=NULL; //创建上下文指针uint8_t *buf=(uint8_t*)malloc(BUFFER*sizeof(uint8_t)); //音频数据帧缓冲区file_fd=open("./voice.aac",O_CREAT|O_RDWR,0666);AVFormatContext* fmt_ctx=open_dev();  //打开设备AVPacket pkt;AVPacket *pkt_pointer=NULL;av_init_packet(&pkt);AVCodecContext * codec_ctx=open_coder();  //打开编码器if (NULL==codec_ctx){return;}AVFrame*frame=NULL;frame=creat_frame(frame);  //设置编码器输入参数,并创建输入缓冲区if (NULL==frame){return;}AVPacket *new_packet=NULL;new_packet=av_packet_alloc(); //分配编码后的数据空间if (NULL==new_packet){printf("av_packet_alloc from new_packet\n");}swr_ctx=init_swr(swr_ctx);  //创建重采样上下文//创建输入缓冲区av_samples_alloc_array_and_samples(&src_data,           //输入缓冲区地址&src_linesize,       //缓冲区的大小2,                  //通道数512,                //通道采样个数   2048/2=1024/2=512AV_SAMPLE_FMT_S16,  //采样格式0);                 //创建输出缓冲区av_samples_alloc_array_and_samples(&dts_data,           //输出缓冲区地址&dts_linesize,       //缓冲区的大小2,                  //通道数512,                //通道采样个数AV_SAMPLE_FMT_S16,  //采样格式0);       while (ret=av_read_frame(fmt_ctx,&pkt)==0&&count++<30000){printf("packet size is %d(%p),count=%d\n",pkt.size,pkt.data,count);if (NULL!=packet_data_buffer(&pkt,buf))   //判断数据是否缓冲足够,如果足够就进行重采样,并将重采样的数据送入编码器{memcpy(src_data[0],buf, BUFFER);// 重采样swr_convert(swr_ctx,                        // 重采样上下文dts_data,                       // 输出缓冲区512,                            // 输出每个通道的采样数(const uint8_t **)src_data,     // 输入缓冲区512);                           // 输入每个通道的采样数memcpy((void*)frame->data[0],dts_data[0],BUFFER);encode_aac_file(codec_ctx,frame,new_packet,file_fd);}}encode_aac_file(codec_ctx,NULL,new_packet,file_fd);  //已经没有音频数据喂给编码器了,让编码器将最后一点数据输出,保存到文件av_packet_unref(&pkt); //解引用音频包//释放源的缓冲区if (src_data) av_freep(&src_data[0]);av_free(src_data);//释放目的的缓冲区if (dts_data)  av_freep(&dts_data[0]);av_free(dts_data);//释放重采样的上下文swr_free(&swr_ctx);close(file_fd);avformat_close_input(&fmt_ctx);av_log_set_level(AV_LOG_DEBUG);
}

在主函数直接调用void ffmpeg_record_aac(void)函数即可完成对音频的录制并编码保存成aac格式的音频数据。

在Linux环境下使用ffmpeg将PCM音频数据编码成aac数据相关推荐

  1. windows下使用Qt播放PCM音频文件(通过QAudioOutput和QIODevice)

    在博主之前的博文<windows下使用FFmpeg生成PCM音频文件并播放(通过命令的方式)>(链接https://blog.csdn.net/u014552102/article/det ...

  2. ffmpeg编译gb28181_RTSP/GB28181协议/海康SDK/Ehome协议视频上云网关EasyCVR视频平台在linux环境下ffmpeg源码单步调试环境搭建...

    目前TSINGSEE青犀视频研发的视频上云服务平台EasyCVR已经可集成海康EHome私有协议,并且在前文中我也跟大家讲过EHome协议的配置和调用流程,有兴趣的可以阅读一下:配置及协议介绍.Eho ...

  3. PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)...

    源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...

  4. NDK实践(一)在linux环境下编译ffmpe

    系列文章目录: NDK实践(一)在linux环境下编译ffmpe NDK实践(二)将编译的ffmpeg静态库集成到Android工程 最近开始接触学习ndk开发,本着边学习边实践的原则,计划完成视频编 ...

  5. ubuntu linux 环境下的程序打包

    有时为了打包自己的编译好的程序,如ffmpeg到其它机子上运行,需要打包 参考博主文章ubuntu linux 环境下的QT程序打包_王者之路001的博客-CSDN博客后做了些修改,完成以下的打包脚本 ...

  6. Linux环境下Arm端源码编译OpenCV+ncnn目标检测模型实例运行调试完整实践记录

    今天需要在嵌入式设备端运行C的程序,里面有依赖OpenCV的部分,这就需要编译安装好OpenCV才行,这个对于我来说还是比较陌生的,我很少用C,所以这里面也没少折腾,一路上遇上了很多的报错,这里我将完 ...

  7. Linux 环境下使用 OpenCV 显示图片

    Linux 环境下使用 OpenCV 显示图片 一.下载虚拟机 二.下载镜像文件(我用的 ubuntu) 三.安装虚拟机 四.打开虚拟机 五.安装 opencv 依赖包 六.下载 OpenCV 七.解 ...

  8. linux svn更换数据仓库,Linux环境下SVN数据仓库迁移

    上一篇文章介绍了Windows环境下SVN数据的备份与还原,这篇文章介绍下Linux环境下数据迁移. 一准备工作 1安装环境 1 centOS7 2可上网 2软件需求 1 winSCP 2 PuTTy ...

  9. Linux环境下的网络编程

    本文介绍了在Linux环境下的socket编程常用函数用法及socket编程的一般规则和客户/服务器模型的编程应注意的事项和常遇问题的解决方法,并举了具体代  码实例.要理解本文所谈的技术问题需要读者 ...

最新文章

  1. vue 不是内部或外部命令,也不是可运行的程序 或批处理文件
  2. SoJpt Boot 2.3-3.8 发布,Spring Boot 使用 Jfinal 特性极速开发
  3. Java 类加载机制详解
  4. 本周不容错过的的9篇NLP论文 | PaperDaily #21
  5. 20000W的电灯泡,真的是叼炸天
  6. windows查看dll库接口函数
  7. 关于maven面试的哪些事儿~
  8. python字符串相关习题
  9. 7-5 日期问题面向对象设计(聚合二) (40 分)
  10. JavaMail---简介
  11. 写入多个表_制作属于自己的教学工作表
  12. python编写一个程序、计算字符串中子串出现的次数_急求。。。C语言实现,计算字符串中子串出现的次数,就是先输入一个字符串,再输入一个上面字符串中存在...
  13. 如何访问SSH公钥?
  14. 线性代数【二】:矩阵的概念与计算
  15. AT24C02/04/08 地址理解
  16. 怎样快速将图片dpi修改为300?如何调整照片分辨率?
  17. PS:将一个图片变成圆形
  18. 赛特斯艰难上市,“软件定义通信”的路并不好走
  19. 网络语言上多个C是什么意思,今年流行的网络用语,个个都很有意思,你知道几个呢?...
  20. 2017找工作的经历,给求职小伙伴的一些建议

热门文章

  1. 可用的交换空间为 0 MB
  2. SQL 简介以及MySQL的优点
  3. 【Java基础】语法基础
  4. pygame小项目 ~ 3 :Python完成简易飞机大战
  5. 《PyQt5高级编程实战》学会使用视图委托
  6. 增设区域分销商:APC渠道变革拒绝“扁平化”
  7. 二叉树no与n2关系数学证明
  8. python量化实战 顾比倒数线_龙腾四海:顾比倒数线+顾比均线
  9. 最火10款经典游戏项目合集 让你轻松拿下
  10. 海康硬盘录像机无法通过rtsp协议连接到EasyNVR的Web页面如何处理?