最简单的基于 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类…

MongoDB聚合:$replaceWith

r e p l a c e W i t h ‘ 可以将输入文档替换为指定的文档。该操作可以替换输入文档的所有字段&#xff0c;包括 ‘ i d ‘ 字段。使用 ‘ replaceWith可以将输入文档替换为指定的文档。该操作可以替换输入文档的所有字段&#xff0c;包括_id字段。使用 replaceWith‘可以将输…

nginx添加lua模块

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

Python和Java的区别(不断更新)

主要通过几个方面区分Python和Java&#xff0c;让大家有一个对比&#xff1a; 语言类型 Java是一种静态类型、编译型语言。 Python是一种动态类型、解释型语言&#xff0c;注重简洁和灵活的语法。 语法 在Java中&#xff0c;变量需要显式地声明&#xff0c;指定其类型。例如&am…

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;…

H5/CSS 笔试面试考题(31-40)

简述CSS 样式&#xff0c;边距&#xff1a; 10px 20px 40px 30px &#xff1b;哪一个是底边距 ( ) A&#xff1a;10px B&#xff1a;20px C&#xff1a;40px D&#xff1a;30px 面试通过率&#xff1a;45.0% 推荐指数&#xff1a; ★★★ 试题难度&#xff1a; 初级 试题类型&a…

JavaScript的原型与继承

原型 原型 prototype&#xff0c;我们所创建的每一个实例&#xff0c;解析器都会向这个函数中添加一个prototype&#xff0c;属性&#xff0c;这个属性会对应这个一个对象&#xff0c;这个对象就是原型对象&#xff08;显式原型&#xff09;&#xff0c;原型对象就相当于一个公…

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

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

YOLOv5改进 | 融合改进篇 | 华为VanillaNet + BiFPN突破涨点极限

一、本文介绍 本文给大家带来的改进机制是华为VanillaNet主干配合BiFPN实现融合涨点,这个主干是一种注重极简主义和效率的神经网络我也将其进行了实验, 其中的BiFPN不用介绍了从其发布到现在一直是比较热门的改进机制,其主要思想是通过多层级的特征金字塔和双向信息传递来提…

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

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

企业IT故障应急响应:四大关键控制点的精细管理

面对不断复杂的生产环境&#xff0c;如何围绕“故障发现、故障响应、故障定位、故障恢复”四个关键环节&#xff0c;进行多方面统筹建设&#xff0c;从而达到增加TBF和缩短TTR的目标&#xff1f; TBF&#xff08;无故障时长&#xff09;和TTR&#xff08;故障修复时长&#xf…

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

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

Bert与ChatGPT

1. Bert模型 BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;是一种预训练语言表示的方法&#xff0c;由Google AI在2018年提出。它标志着自然语言处理&#xff08;NLP&#xff09;领域的一个重大进步&#xff0c;因为它能够理解单词在…

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

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

第69讲后端登录逻辑实现

Admin实体&#xff1a; TableName("t_admin") Data public class Admin {TableId(type IdType.AUTO)private Integer id; // 编号private String userName; // 用户名private String password; // 密码TableField(select false)private String newPassword; // 新…

labelimg 在pycharm下载使用

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

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

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