最简单的基于 FFmpeg 的音频编码器(PCM 编码为 AAC)

最简单的基于 FFmpeg 的音频编码器(PCM 编码为 AAC)

  • 最简单的基于 FFmpeg 的音频编码器(PCM 编码为 AAC)
    • 正文
    • 结果
    • 工程文件下载
    • 其他参考链接

最简单的基于 FFmpeg 的音频编码器(PCM 编码为 AAC)

参考雷霄骅博士的文章,链接:最简单的基于FFMPEG的音频编码器(PCM编码为AAC)

正文

本文介绍一个最简单的基于 FFmpeg 的音频编码器。

该编码器实现了 PCM 音频采样数据编码为 AAC 的压缩编码数据。

下面附一张使用 FFmpeg 编码音频的流程图。使用该流程,不仅可以编码 AAC 的音频,而且可以编码 MP3,MP2 等等各种 FFmpeg 支持的音频。图中蓝色背景的函数是实际输出数据的函数。浅绿色的函数是音频编码的函数。

请添加图片描述

简单介绍一下流程中各个函数的意义:

  1. av_register_all():注册 FFmpeg 所有编解码器。
  2. avformat_alloc_output_context2():初始化输出码流的 AVFormatContext。
  3. avio_open():打开输出文件。
  4. av_new_stream():创建输出码流的 AVStream。
  5. avcodec_find_encoder():查找编码器。
  6. avcodec_open2():打开编码器。
  7. avformat_write_header():写文件头(对于某些没有文件头的封装格式,不需要此函数。比如说 MPEG2TS)。
  8. avcodec_encode_audio2():编码音频。即将 AVFrame(存储 PCM 采样数据)编码为 AVPacket(存储 AAC,MP3 等格式的码流数据)。
  9. av_write_frame():将编码后的视频码流写入文件。
  10. av_write_trailer():写文件尾(对于某些没有文件头的封装格式,不需要此函数。比如说 MPEG2TS)。

另外,程序中的函数 flush_encoder():输入的像素数据读取完成后调用此函数。用于输出编码器中剩余的 AVPacket。

编码器代码十分简单,但是每一行代码都很重要。通过看本编码器的源代码,可以了解 FFmpeg 音频编码的流程。

本程序使用编译时间为 2014.5.6 的 FFmpeg 类库,下载链接:【免费】FFmpeg 库.zip。

开发平台为 VC2015。所有的配置都已经做好,只需要运行就可以了。

源代码:

// Simplest FFmpeg Audio Encoder.cpp : 定义控制台应用程序的入口点。
///**
* 最简单的基于 FFmpeg 的音频编码器
* Simplest FFmpeg Audio Encoder
*
* 源程序:
* 雷霄骅 Lei Xiaohua
* leixiaohua1020@126.com
* 中国传媒大学/数字电视技术
* Communication University of China / Digital TV Technology
* http://blog.csdn.net/leixiaohua1020
*
* 修改:
* 刘文晨 Liu Wenchen
* 812288728@qq.com
* 电子科技大学/电子信息
* University of Electronic Science and Technology of China / Electronic and Information Science
* https://blog.csdn.net/ProgramNovice
*
* 本程序实现了音频 PCM 采样数据编码为压缩码流(MP3,WMA,AAC 等)。
* 是最简单的 FFmpeg 音频编码方面的教程。
* 通过学习本例子可以了解 FFmpeg 的编码流程。
*
* This software encode PCM data to AAC bitstream.
* It's the simplest audio encoding software based on FFmpeg.
* Suitable for beginner of FFmpeg
*
*/#include "stdafx.h"#include <stdio.h>// 解决报错:fopen() 函数不安全
#pragma warning(disable:4996)// 解决报错:无法解析的外部符号 __imp__fprintf,该符号在函数 _ShowError 中被引用
#pragma comment(lib, "legacy_stdio_definitions.lib")
extern "C"
{// 解决报错:无法解析的外部符号 __imp____iob_func,该符号在函数 _ShowError 中被引用FILE __iob_func[3] = { *stdin, *stdout, *stderr };
}#define __STDC_CONSTANT_MACROS#ifdef _WIN32
// Windows
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
};
#else
// Linux...
#ifdef __cplusplus
extern "C"
{
#endif
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#ifdef __cplusplus
};
#endif
#endifint flush_encoder(AVFormatContext *fmt_ctx, unsigned int stream_index)
{int ret;int got_frame;AVPacket enc_pkt;if (!(fmt_ctx->streams[stream_index]->codec->codec->capabilities &CODEC_CAP_DELAY))return 0;while (1){enc_pkt.data = NULL;enc_pkt.size = 0;av_init_packet(&enc_pkt);ret = avcodec_encode_audio2(fmt_ctx->streams[stream_index]->codec, &enc_pkt,NULL, &got_frame);av_frame_free(NULL);if (ret < 0)break;if (!got_frame){ret = 0;break;}printf("Flush Encoder: Succeed to encode 1 frame!\tsize:%5d.\n", enc_pkt.size);// mux encoded frameret = av_write_frame(fmt_ctx, &enc_pkt);if (ret < 0){break;}}return ret;
}int main(int argc, char* argv[])
{AVFormatContext* pFormatCtx;AVOutputFormat* fmt;AVStream* audio_stream;AVCodecContext* pCodecCtx;AVCodec* pCodec;uint8_t* frame_buf;AVFrame* pFrame;AVPacket pkt;int got_frame = 0;int size = 0;int ret = 0;FILE *fp_in = fopen("tdjm.pcm", "rb"); // 输入 PCM 文件int framenum = 1000; // Audio frame numberconst char* out_file = "tdjm.aac"; // 输出 URLint i;av_register_all();// Method 1// pFormatCtx = avformat_alloc_context();// fmt = av_guess_format(NULL, out_file, NULL);// pFormatCtx->oformat = fmt;// Method 2 (More simple)avformat_alloc_output_context2(&pFormatCtx, NULL, NULL, out_file);fmt = pFormatCtx->oformat;// Open Output URLif (avio_open(&pFormatCtx->pb, out_file, AVIO_FLAG_READ_WRITE) < 0){printf("Can't open output file.\n");return -1;}audio_stream = avformat_new_stream(pFormatCtx, 0);if (audio_stream == NULL){printf("Can't create audio stream.\n");return -1;}pCodecCtx = audio_stream->codec;pCodecCtx->codec_id = fmt->audio_codec;pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;pCodecCtx->sample_fmt = AV_SAMPLE_FMT_S16;pCodecCtx->sample_rate = 44100;pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);pCodecCtx->bit_rate = 64000;//pCodec = avcodec_find_encoder(fmt->audio_codec);//if (!pCodec)//{//	printf("Can't find encoder.\n");//	return -1;//}//pCodecCtx = audio_stream->codec;//pCodecCtx->codec_id = fmt->audio_codec;//pCodecCtx->codec_type = AVMEDIA_TYPE_AUDIO;//pCodecCtx->sample_fmt = pCodec->sample_fmts[0];//pCodecCtx->sample_rate = 44100;//pCodecCtx->channel_layout = AV_CH_LAYOUT_STEREO;//pCodecCtx->channels = av_get_channel_layout_nb_channels(pCodecCtx->channel_layout);//pCodecCtx->bit_rate = 64000;//pCodecCtx->profile = FF_PROFILE_AAC_MAIN;//pCodecCtx->strict_std_compliance = FF_COMPLIANCE_EXPERIMENTAL;// Print some informationav_dump_format(pFormatCtx, 0, out_file, 1);pCodec = avcodec_find_encoder(pCodecCtx->codec_id);if (!pCodec){printf("Can not find encoder!\n");return -1;}if (avcodec_open2(pCodecCtx, pCodec, NULL) < 0){printf("Failed to open encoder.\n");return -1;}pFrame = av_frame_alloc();pFrame->nb_samples = pCodecCtx->frame_size;pFrame->format = pCodecCtx->sample_fmt;// 计算缓存区域大小size = av_samples_get_buffer_size(NULL, pCodecCtx->channels, pCodecCtx->frame_size, pCodecCtx->sample_fmt, 1);// 分配缓存frame_buf = (uint8_t *)av_malloc(size);if (!frame_buf){printf("Can't malloc frame buffer.\n");return -1;}avcodec_fill_audio_frame(pFrame, pCodecCtx->channels, pCodecCtx->sample_fmt,(const uint8_t*)frame_buf, size, 1);// Write Headeravformat_write_header(pFormatCtx, NULL);av_new_packet(&pkt, size);for (i = 0; i < framenum; i++){// Read PCMif (fread(frame_buf, sizeof(char), size, fp_in) <= 0){printf("Failed to read raw data! \n");return -1;}else if (feof(fp_in)){break;}pFrame->data[0] = frame_buf;  // PCM DatapFrame->pts = i * 100;got_frame = 0;// Encoderet = avcodec_encode_audio2(pCodecCtx, &pkt, pFrame, &got_frame);if (ret < 0){printf("Failed to encode!\n");return -1;}if (got_frame == 1){printf("Succeed to encode 1 frame! \tsize:%5d\n", pkt.size);pkt.stream_index = audio_stream->index;ret = av_write_frame(pFormatCtx, &pkt);av_free_packet(&pkt);}}// Flush Encoderret = flush_encoder(pFormatCtx, 0);if (ret < 0){printf("Flushing encoder failed.\n");return -1;}// Write Trailerav_write_trailer(pFormatCtx);printf("Encode Successful.\n");// Cleanif (audio_stream){avcodec_close(audio_stream->codec);av_free(pFrame);av_free(frame_buf);}avio_close(pFormatCtx->pb);avformat_free_context(pFormatCtx);fclose(fp_in);return 0;
}

结果

输入 pcm 文件:

在这里插入图片描述

采样率 64000,双声道,位深 16bit。

在 Adobe Audition 2020 中查看波形:

在这里插入图片描述

运行程序,得到采样率为 44100HZ 的输出文件 tdjm.aac。

用 MediaInfo 查看:

在这里插入图片描述

播放该 aac 文件,能听出来是周杰伦的《她的睫毛》。

注:pcm 也能播放,因为采样率是 64000HZ,比正常速度的 44100HZ 快,所以听着有些奇怪。

工程文件下载

GitHub:UestcXiye / Simplest-FFmpeg-Audio-Encoder

CSDN:Simplest FFmpeg Audio Encoder.zip

其他参考链接

  1. 新版ffmpeg编码AAC注意事项
  2. FFmpeg 音频编码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/677704.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Android SystemConfig相关

SystemConfig在哪里初始化 它声明在PackageManagerService类的静态方法main()中。在该方法中间定义Injector类对象时&#xff0c;作为它的构造参数。它是调用的SystemConfig.getInstance()实现初始化&#xff0c;之后能通过Injector类对象的getSystemConfig()得到SystemConfig类…

nginx添加lua模块

目录 已安装了nginx&#xff0c;后追加lua模块nginx 重新编译知识参考&#xff1a; 从零安装一、首先需要安装必要的库&#xff08;pcre、zlib、openssl&#xff09;二、安装LUA环境及相关库 &#xff08;LuaJIT、ngx_devel_kit、lua-nginx-module&#xff09;注意&#xff1a;…

STM32——OLED(2)

目录 一、OLED显示屏介绍 引脚说明&#xff1a; 二、OLED驱动 1. 基本认识 2. OLED 驱动原理 及过程 三、SSD1306工作时序 (8080时序&#xff09; 1. 8080并口读/写过程 2. SSD1306工作时序 (8080时序) 四、屏幕显示 1. GRAM 补&#xff1a; 2. 画点原理 3. 显示字…

一周学会Django5 Python Web开发-Django5创建项目(用PyCharm工具)

锋哥原创的Python Web开发 Django5视频教程&#xff1a; 2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili2024版 Django5 Python web开发 视频教程(无废话版) 玩命更新中~共计11条视频&#xff0c;包括&#xff1a;2024版 Django5 Python we…

【C++从0到王者】第四十一站:特殊类的设计

文章目录 一、设计一个类&#xff0c;不能被拷贝1.C98方法2.C11方法 二、设计一个类&#xff0c;只能在堆上创建对象1.析构函数私有化2.构造函数私有化 三、请设计一个类&#xff0c;只能在栈上创建对象四、设计一个类不能被继承1.C98方式2.C11方式 五、设计一个类&#xff0c;…

【JavaScript 漫游】【013】Date 对象知识点摘录

文章简介 本文为【JavaScript 漫游】专栏的第 013 篇文章&#xff0c;记录了 JS 语言中 Date 对象的重要知识点。 普通函数的用法构造函数的用法日期的运算静态方法&#xff0c;包括&#xff1a;Date.now()、Date.parse() 和 Date.UTC()实例方法&#xff0c;包括&#xff1a;…

156基于Matlab的光纤陀螺随机噪声和信号

基于Matlab的光纤陀螺随机噪声和信号&#xff0c;利用固定步长和可调步长的LMS自适应滤波、最小二乘法、滑动均值三种方法进行降噪处理&#xff0c;最后用阿兰方差评价降噪效果。程序已调通&#xff0c;可直接运行。 156 信号处理 自适应滤波 降噪效果评估 (xiaohongshu.com)

[office] Excel自带的编辑函数求和方法 #其他#媒体

Excel自带的编辑函数求和方法 今天小编为大家分享Excel自带的编辑函数求和方法&#xff0c;方法很简单的&#xff0c;对于不是很熟悉excel表格的朋友可以参考一下&#xff0c;希望能对大家有所帮助 很多同学以及上班族需要大量使用Excel这款表格编辑器&#xff0c;当表格中有大…

论文笔记:相似感知的多模态假新闻检测

整理了RecSys2020 Progressive Layered Extraction : A Novel Multi-Task Learning Model for Personalized Recommendations&#xff09;论文的阅读笔记 背景模型实验 论文地址&#xff1a;SAFE 背景 在此之前&#xff0c;对利用新闻文章中文本信息和视觉信息之间的关系(相似…

CSP-202009-1-称检测点查询

CSP-202009-1-称检测点查询 解题思路 本题的时间复杂度貌似没有限制&#xff0c;直接暴力枚举就能知识盲点&#xff1a;sort()函数-升序排序 #include <algorithm>给名为dis&#xff0c;长度为n的数组排序sort(new_dis, new_dis n); #include <iostream> #inc…

labelimg 在pycharm下载使用

labelimg 使用数据标注工具 labelimg 制作数据集 在pycharm中搜索labelimg 选择版本安装 labelimg install 使用数据标注工具制作数据集 启动 带参数启动 1、cmd cd到指定目录 2、带参数启动标注工具 左侧可以选择切换为需要的数据格式 一些快捷键 和自动保存&#xff0c…

Stable Diffusion 模型下载:RealCartoon-Realistic - V13

文章目录 模型介绍生成案例案例一案例二案例三案例四案例五案例六案例七案例八案例九案例十下载地址模型介绍 该检查点是 RealCartoon3D 检查点的一个分支。这个目标是在背景和人物中产生更“真实”的外观。我试图避免这个模型中更多的动漫、卡通和“完美”外观。这是一个肯

hexo 博客搭建以及踩雷总结

搭建时的坑 文章置顶 安装一下这个依赖 npm install hexo-generator-topindex --save然后再文章的上面设置 top: number&#xff0c;数字越大&#xff0c;权重越大&#xff0c;也就是越靠顶部 hexo 每次推送 nginx 都访问不到 宝塔自带的 nginx 的 config 里默认的角色是 …

easyx 随机火花生成器 视觉盛宴

作品介绍&#xff1a; 在数字化艺术的世界里&#xff0c;我们经常寻求模拟自然现象的方式&#xff0c;为观众带来沉浸式的体验。本作品“随机火花生成器”就是一个尝试&#xff0c;通过编程模拟了火花的随机生成和消散过程。 在这段代码中&#xff0c;我们使用了EasyX图形库&…

前端JavaScript篇之如何获得对象非原型链上的属性?

目录 如何获得对象非原型链上的属性&#xff1f; 如何获得对象非原型链上的属性&#xff1f; 要获取对象上非原型链上的属性&#xff0c;可以使用 hasOwnProperty() 方法。这个方法是 JavaScript 内置的对象方法&#xff0c;用于检查一个对象是否包含指定名称的属性&#xff0…

【论文阅读笔记】InstantID : Zero-shot Identity-Preserving Generation in Seconds

InstantID:秒级零样本身份保持生成 理解摘要Introduction贡献 Related WorkText-to-image Diffusion ModelsSubject-driven Image GenerationID Preserving Image Generation Method实验定性实验消融实验与先前方法的对比富有创意的更多任务新视角合成身份插值多身份区域控制合…

Linux操作系统基础(六):Linux常见命令(一)

文章目录 Linux常见命令 一、命令结构 二、ls命令 三、cd命令 四、mkdir命令 五、touch命令 六、rm命令 七、cp命令 八、mv命令 九、cat命令 十、more命令 Linux常见命令 一、命令结构 command [-options] [parameter]说明: command : 命令名, 相应功能的英文单词…

Dubbo源码一:【Dubbo与Spring整合】

正常在项目中&#xff0c;我们都是在Spring环境下使用Dubbo&#xff0c;所以我们这里就在Spring的环境下看看Dubbo是如何运作的 入口 在源码下载下来之后&#xff0c;有一个dubbo-demo目录&#xff0c;里面有一个基于spring注解的子目录dubbo-demo-annotation, 里面有一个生产…

【PyQt】10 QLineEdit

文章目录 前言一、回显模式&#xff08;EchoMode&#xff09;1.1 四种回显模式1.2 代码展示运行结果 二、校验器2.1 代码2.2 运行结果 三、通过掩码限制输入3.1 代码3.2 运行结果 总结 前言 1、QLineEdit 可以输入单行文字 2、回显模式 3、校验器 4、掩码输入 一、回显模式&am…

k8s-资源限制与监控 15

资源限制 上传实验所需镜像 Kubernetes采用request和limit两种限制类型来对资源进行分配。 request(资源需求)&#xff1a;即运行Pod的节点必须满足运行Pod的最基本需求才能 运行Pod。 limit(资源限额)&#xff1a;即运行Pod期间&#xff0c;可能内存使用量会增加&#xff0…