ffmpeg编程接口--音频解码
文章描述: 本文主要描述了通过ffmpeg接口对音频进行编解码的例程,及主要函数详细说明。
操作系统: Debian8
/*
* 功能描述: 打开指定音频文件,转换成PCM数据格式,并保存至文件中
* 支持多种格式文件输入mp3,m4a等
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libavcodec/avcodec.h> // 解码器
#include <libavformat/avformat.h> // 封装格式
#include <libswresample/swresample.h> // 重采样格式转换
int main(int argc, char **argv)
{
// 1.注册组件 -----------------------------------------------------
av_register_all();
// 封装格式上下文
AVFormatContext *pFormatCtx = avformat_alloc_context();
// 2.打开输入音频文件 ---------------------------------------------
if (avformat_open_input(&pFormatCtx, argv[1], NULL, NULL) != 0)
{
printf("打开输入音频文件失败.\r\n");
return -1;
}
// 3.获取音频信息 -------------------------------------------------
if (avformat_find_stream_info(pFormatCtx, NULL) < 0)
{
printf("获取音频信息失败.\r\n");
return -2;
}
// 音频解码: 找到对应的AVStream所在的pFormatCtx->streams的索引位置
int i;
int audio_stream_idx = -1;
for (i = 0; i < pFormatCtx->nb_streams; i++)
{
// 根据类型判断是否是音频流
if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO)
{
printf("查找到音频流\r\n");
audio_stream_idx = i;
}
// 根据类型判断是否是视频流
else if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
printf("查找到视频流\r\n");
}
else
{
printf("未知的编码类型: %d\r\n", pFormatCtx->streams[i]->codec->codec_type);
}
}
// 4.获取解码器 ---------------------------------------------------
// 通过stream获取解码器上下文
AVCodecContext *pCodeCtx = pFormatCtx->streams[audio_stream_idx]->codec;
// 通过编解码id获取解码器
AVCodec *pCodec = avcodec_find_decoder(pCodeCtx->codec_id);
if (pCodec == NULL)
{
printf("未找到对应解码器.\r\n");
return -3;
}
// 5.打开解码器 ---------------------------------------------------
if (avcodec_open2(pCodeCtx, pCodec, NULL) < 0)
{
printf("解码器无法打开");
return -4;
}
// 申请数据内存:编码数据(原始数据)
AVPacket *packet = av_malloc(sizeof(AVPacket));
// 申请数据内存:解码数据
AVFrame *frame = av_frame_alloc();
// 设置解码器选项--------------------------------------------------
SwrContext *swrCtx = swr_alloc();
// 输出的采样格式 16bit PCM
enum AVSampleFormat out_sample_fmt = AV_SAMPLE_FMT_S16;
// 输出的采样率
int out_sample_rate = 44100;
// 输出的声道布局
uint64_t out_ch_layout = AV_CH_LAYOUT_STEREO; // (立体声)
swr_alloc_set_opts(swrCtx,
out_ch_layout, // 输出的声道布局
out_sample_fmt, // 输出的采样格式 16bit PCM
out_sample_rate, // 输出的采样率
pCodeCtx->channel_layout, // 输入的声道布局
pCodeCtx->sample_fmt, // 输入的采样格式
pCodeCtx->sample_rate, // 输入的采样率
0, // 日志偏移 log_offset
NULL); // 日志指针 log_ctx
swr_init(swrCtx); // 初始化解码器
// ----------------------------------------------------------------
// 通过声道布局获取声道数
int out_channel_nb = av_get_channel_layout_nb_channels(out_ch_layout);
int ret;
int got_frame;
// 申请单个采样点PCM数据所需要的内存(2*2*44100)
uint8_t *out_buffer = (uint8_t *)av_malloc(2 * 2 * 44100);
if (out_buffer == NULL)
{
exit(1);
}
FILE *fp_pcm = fopen("out.pcm", "wb");
// 6.一帧一帧读取压缩的音频数据AVPacket
while (av_read_frame(pFormatCtx, packet) >= 0)
{
if (packet->stream_index == audio_stream_idx)
{
// 解码:AVPacket->AVFrame
ret = avcodec_decode_audio4(pCodeCtx, frame, &got_frame, packet);
if (ret < 0)
{
printf("解码出错.\r\n");
}
// 获取有效帧
if (got_frame)
{
// 由于解码器输出的数据与PCM播放的格式不一致,所以需要转换一下
swr_convert(swrCtx,
&out_buffer, // 输出数据缓冲区
2 * 2 * 44100, // 输出缓冲区大小
(const uint8_t **)frame->data, // 输入数据缓冲区
frame->nb_samples); // 每通道的采样数(帧数)
// 通过帧数,音频格式,通道数计算每次转换后输出的字节数
int out_buffer_size = av_samples_get_buffer_size(NULL, // 传出值,与返回值相同
out_channel_nb, // 通道数
frame->nb_samples, // 采样数(帧数)
out_sample_fmt, // 音频格式
1); // 对齐模式(1 - 紧凑,不对齐)
// 转码后的数据写入文件(也可直接写入PCM进行播放)
fwrite(out_buffer, 1, out_buffer_size, fp_pcm);
}
}
av_free_packet(packet);
}
// 释放动态申请的内存
av_frame_free(&frame);
av_free(out_buffer);
swr_free(&swrCtx);
fclose(fp_pcm); // 关闭文件
avcodec_close(pCodeCtx); // 关闭解码器
avformat_close_input(&pFormatCtx); // 关闭输入文件
return 0;
}
编译选项:
-lavformat -lavcodec -lavutil -lswresample
转换后的文件测试:
由于没有文件头来指定音频格式,所以需要-f选项指定音频格式:
$ aplay -f cd out.pcm
数据结构及函数说明:
/*
* 功能描述: 打开文件,解析文件格式
* 返 回 值: 0 - 成功,负值 - 失败,发生错误
*/
int avformat_open_input(AVFormatContext **ps, // 封装句柄
const char *url, // 文件路径
AVInputFormat *fmt, // 强制指定格式,一般为NULL
AVDictionary **options); // 一般设置为NULL
linux应用程序--磁盘管理
相关头文件:
#include <sys/statfs.h>
结构体说明:
struct statfs {
long f_type; // 文件系统类型
long f_bsize; // 传输块大小
long f_blocks; // 文件系统数据块总数
long f_bfree; // 可用块数
long f_bavail; // 非超级用户可获取的块数
long f_files; // 文件结点总数
long f_ffree; // 可用文件结点数
fsid_t f_fsid; // 文件系统标识
long f_namelen; // 文件名的最大长度
long f_frsize; // 碎片大小
long f_flags;
long f_spare[4];
};
函数原型:
/*
* 功能描述: 获取文件系统的相关参数
* 参数说明: path - 文件系统路径(挂载点)
* info - 参数结构体指针(传出)
* 返 回 值: 0 - 成功, 其值 - 失败
*/
int statfs(const char *path, struct statfs *info);
int fstatfs(int fd, struct statfs *info);
clang-format详细说明
简要说明: 本文主要描述了clang-format应用说明,基于Vs Code测试。
设置Visual Code中应用
在工程根目录下新建.clang-format文件,内容如下:
# 表示注释,但貌似不支持中文
# 基于模板样式 LLVM,Google,Visual Studio,Mozillat,WebKit等
BasedOnStyle: LLVM
# 缩进字节数
IndentWidth: 2
# case标签是否缩进
IndentCaseLabels: true
# Never表示空格代替制表符
UseTab: Never
# 制表符宽度
TabWidth: 2
# 每行限制字节数,超出此值后换行,0表示不限制
ColumnLimit: 0
# 最大的连续空行数
MaxEmptyLinesToKeep: 2
# 指针的*的挨着哪边(区分大小写)
PointerAlignment: Right
# 中括号两边添加空格 []
SpacesInSquareBrackets: false
# 小括号两边添加空格 ()
SpacesInParentheses : false
# 等号两边的空格
SpaceBeforeAssignmentOperators: true
# 注释对齐
AlignTrailingComments: true
# 多行声明语句对齐(不选)
AlignConsecutiveDeclarations: false
# 连续的赋值语句以 = 为中心对齐
AlignConsecutiveAssignments: true
大括号换行模式
BreakBeforeBraces: Allman
Attach 总是将大括号与上下文连在一起。
Linux 像Attach一样, 但是在一个方法、命名空间或一个类定义的大括号之前换行
Mozilla 像Attach一样, 但是在一个枚举、方法或记录定义前换行。
Stroustrup 像Attach一样,但是在方法定义、catch、和else前换行
WebKit 像Attach一样, 但是在方法前换行。
Allman 总是在大括号之前换行。
GNU 总是在括号前中断,并添加一个额外的级别的缩进到控件语句的括号中,而不是类、函数或其他定义的括号中。
Custom 在“BraceWrapping”里配置每一个单独的大括号。
BreakBeforeBraces: Custom
BraceWrapping:
# 注意这里的缩进,否则vscode格式化时报错
AfterClass: false
AfterFunction: true # 函数换行(一般为true)
AfterControlStatement: true # 控制块(if-else/while/switch等)
AfterStruct: true # 结构体(不包括typedef)
AfterUnion: false # 联合体
AfterEnum: true # 枚举
AfterNamespace: false # 命名空间
AfterObjCDeclaration: false
BeforeCatch: false # catch与前边的花括号是否换行
BeforeElse: true # else与前边的花括号是否换行
IndentBraces: false # 缩进换行的大括号(一般为false)
# 结构体数组赋值时用的到,具体描述待查
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
比较好看的配置:
BasedOnStyle: LLVM
IndentWidth: 2
IndentCaseLabels: true
UseTab: Never
TabWidth: 2
ColumnLimit: 0
MaxEmptyLinesToKeep: 1
PointerAlignment: Right
SpacesInSquareBrackets: false
SpacesInParentheses : false
SpaceBeforeAssignmentOperators: true
AlignTrailingComments: true
AlignConsecutiveDeclarations: false
AlignConsecutiveAssignments: true
BreakBeforeBraces: Custom
BraceWrapping:
AfterClass: false
AfterFunction: true
AfterControlStatement: true
AfterStruct: true
AfterEnum: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterUnion: false
BeforeCatch: false
BeforeElse: true
IndentBraces: false
参考资料:
https://www.cnblogs.com/PaulpauL/p/5929753.html
https://www.jianshu.com/p/346f439d230c
https://www.jianshu.com/p/c2dd26fe6f78
https://blog.csdn.net/u013187057/article/details/85273775
vscode使用笔记
设置-->用户 指当前用户的配置,适用于所有工程,配置文件位于 ${用户目录}\AppData\Roaming\Code\User\settings.json;
设置-->工作区 指当工程的配置,优先级会覆盖用户设置,配置文件位于工程目录下的.vscode/setting.json;
在工程中排除目录或文件:
"files.exclude": {
"**/.git": true,
"**/.DS_Store": true,
"**/*.meta": true,
"library/": true,
"local/": true,
"temp/": true
}
其他设置项:
// 打开时猜测文件编码
"files.autoGuessEncoding": true,
// 编辑器字体设置
"editor.fontFamily": "Monaco, Consolas, 'Courier New', monospace",
添加至右键菜单:
新建menu.reg文件输入以下内容:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\*\shell\VSCode]
@="Open with Code"
"Icon"="D:\\Program Files\\Microsoft VS Code\\Code.exe"
[HKEY_CLASSES_ROOT\*\shell\VSCode\command]
@="\"D:\\Program Files\\Microsoft VS Code\\Code.exe\" \"%1\""
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\shell\VSCode]
@="Open with Code"
"Icon"="D:\\Program Files\\Microsoft VS Code\\Code.exe"
[HKEY_CLASSES_ROOT\Directory\shell\VSCode\command]
@="\"D:\\Program Files\\Microsoft VS Code\\Code.exe\" \"%V\""
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\Directory\Background\shell\VSCode]
@="Open with Code"
"Icon"="D:\\Program Files\\Microsoft VS Code\\Code.exe"
[HKEY_CLASSES_ROOT\Directory\Background\shell\VSCode\command]
@="\"D:\\Program Files\\Microsoft VS Code\\Code.exe\" \"%V\""
保存并双击执行导入注册表即可。
注意事项:
- 路径名称要和实际一致。
- 第一项表示任意文件右键菜单,第二项表示在目录上右键菜单,第三项表示在空白处右键(一般不用),具体说明请参考注册表相关章节。
Keil Assistant插件: 可用于编译,下载Keil工程
1.在商店中搜索Keil Assistant插件并安装。
2.在扩展设置(Extension Settings)中设置Keil软件的路径,(C51与MKD可分别设置)。
3.安装完成后,左侧资源管理器栏会出现KEIL UVISION PORJECT子菜单,即可打开keil工程,进行编译下载。