音视频开发8. 使用ffmpeg 将pcm转码aac实践(C++)
Posted 编程圈子
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了音视频开发8. 使用ffmpeg 将pcm转码aac实践(C++)相关的知识,希望对你有一定的参考价值。
@[TOC](音视频开发8. 使用ffmpeg 将pcm转码aac实践(C++))
一、 准备环境
- CentOS 已安装ffmpeg库
- 本地使用vscode,安装 Remote developement 远程开发插件 。
- 实现目标: 将pcm文件转码aac格式文件。
PCM全称Pulse-Code Modulation,即脉冲调制编码。PCM音频数据是未经压缩的音频采样数据裸流,一般播放器无法直接播放。如果要播放PCM还需要描述其采样率、声道、采样格式、码率等信息。
本文示例需要准备一段PCM样本文件,本文使用的PCM:
- 采样率:44100
- 双声道
- 码率:64000
- 样本格式:float, planar
- 立体声
二、 项目结构
三、 audio.cpp 完整代码
1. 引用ffmpeg头
#include <stdio.h>
#include <iostream>
#ifdef __cplusplus
extern "C"
#endif
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/imgutils.h"
#include "libavutil/log.h"
#include "libswresample/swresample.h"
#ifdef __cplusplus
#endif
using namespace std;
2. 主体代码
int main()
av_register_all();
avcodec_register_all();
char inputfile[] = "input.pcm";
char outputfile[] = "audio.aac";
AVCodec* avCodec = avcodec_find_encoder(AV_CODEC_ID_AAC);
if(!avCodec)
cout << "avcodec_find_encoder failed" << endl;
return -1;
// 编码器上下文配置
AVCodecContext* avCodecContext = avcodec_alloc_context3(avCodec);
if(!avCodecContext)
cout << "avcodec_alloc_context3 failed" << endl;
return -1;
int ret=0;
// 采样率设置
avCodecContext->sample_rate = 44100;
// 双通道
avCodecContext->channels = 2;
// 32位样本格式
avCodecContext->sample_fmt = AV_SAMPLE_FMT_FLTP;
// 码率
avCodecContext->bit_rate = 64000;
// 声道类型
avCodecContext->channel_layout = AV_CH_LAYOUT_STEREO;
// 音频帧使用公共头部
avCodecContext->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
// 打开音频的编码器
ret = avcodec_open2(avCodecContext, avCodec,NULL);
if(ret<0)
cout << "avcodec_open2 failed" << endl;
return -1;
// 创建输出的上下文
AVFormatContext* avFormatContext = NULL;
// 初始化
avformat_alloc_output_context2(&avFormatContext, NULL, NULL, outputfile);
if(!avFormatContext)
cout << "avformat_alloc_output_context2 failed" << endl;
return -1;
cout << "创建音频流,然后打印输出流信息" << endl;
// 创建音频流
AVStream* st = avformat_new_stream(avFormatContext, NULL);
// 音频流参数设置
st->codecpar->codec_tag = 0;
// 编码器参数复制到音频流上,省得再设置一遍
avcodec_parameters_from_context(st->codecpar, avCodecContext);
// 第三个参数,0音频、1视频
av_dump_format(avFormatContext, 0, outputfile, 1);
cout << "打开输出文件 " << endl;
// 打开输出文件
ret = avio_open(&avFormatContext->pb, outputfile, AVIO_FLAG_WRITE);
if(ret<0)
cout << "avio_open failed" << endl;
return -1;
cout << "写文件头部,创建输出文件" << endl;
// 写头部信息,创建输出文件
avformat_write_header(avFormatContext, NULL);
cout << "重采样上下文" << endl;
// 重采样上下文
SwrContext* swrContext = NULL;
// 设置参数, 1. 上下文 2.输出声道布局 4.输出采样率, 5.输入声道布局 6.输入样本格式 7.输入采样率 8.配音 9.日志
swrContext = swr_alloc_set_opts(swrContext, avCodecContext->channel_layout, avCodecContext->sample_fmt, avCodecContext->sample_rate,
AV_CH_LAYOUT_STEREO, AV_SAMPLE_FMT_S16, 44100,
0, 0);
if(!swrContext)
cout << "swr_alloc_set_opts failed" << endl;
return -1;
// 初始化
ret = swr_init(swrContext);
if(ret<0)
cout << "swr_init failed" << endl;
return -1;
// 一帧帧读取 PCM 数据、重采样、编码、写入.1帧是1024个样本。
AVFrame* avFrame = av_frame_alloc();
// 样本格式 float4字节
avFrame->format = AV_SAMPLE_FMT_FLTP;
// 通道数
avFrame->channels=2;
// 通道布局
avFrame->channel_layout = AV_CH_LAYOUT_STEREO;
// 每一帧音频的样本数量
avFrame->nb_samples=1024;
ret = av_frame_get_buffer(avFrame, 0);
if(ret<0)
cout << "av_frame_get_buffer failed" << endl;
return -1;
// 准备缓存空间存放数据
int readSize = avFrame->nb_samples*2*2; //双声道,float
char* pcms = new char[readSize];
cout << "打开输入文件 每次读取(双声道,float):readsize=" << readSize << endl;
// 打开输入文件
FILE* fp = fopen(inputfile, "rb");
cout << "==========循环读入帧==========" << endl;
int sum=0;
int index=0;
for(;;)
// 存放编码后的数据
AVPacket pkt;
// 包初始化
av_init_packet(&pkt);
int len = fread(pcms,1,readSize, fp);
if(len <=0)
// 文件读完了
cout << "文件读取结束" << endl;
// 告诉解码器没有帧了
avcodec_send_frame(avCodecContext, NULL);
while(avcodec_receive_packet(avCodecContext, &pkt)!=AVERROR_EOF);
break;
else
index ++;
sum += len;
// 强制数据类型转换,重采样之前的数据
const uint8_t* data[1];
data[0] = (uint8_t*)pcms;
// 重采样 1.重采样上下文 2.重采样后的数据 3.样本数量不变 4.重采样之前的数据
len = swr_convert(swrContext, avFrame->data, avFrame->nb_samples, data, avFrame->nb_samples);
if(len <=0)
cout << "重采样输出异常" << len << endl;
break;
// ------------对重采样的数据重新编码--------
// 编码重采样后的数据
// 重采样的数据发送到编码线程
// 1. 编码器上下文 2.要发送的数据
ret = avcodec_send_frame(avCodecContext, avFrame);
if(ret < 0)
// 完成发送了
cout << "发编码器线程异常" << ret << endl;
continue;
// 接收编码和解码后的数据
// 取出编码后的数据,TODO: 这里要加循环多次读取以确保读取成功
ret = avcodec_receive_packet(avCodecContext, &pkt);
if(ret == -EAGAIN)
cout << "缓冲区未满 等待新数据" << endl;
else if(ret != 0)
cout << "取编码结果异常" << ret << endl;
continue;
// 指定是音频流。 1为视频流
pkt.stream_index = 0;
// 解码时间、显示时间置为0
pkt.dts = 0;
pkt.pts = 0;
// 编码后的写入ac文件 1.输出上下文
av_interleaved_write_frame(avFormatContext, &pkt);
if(index%50==0)
cout << "第" << index << "帧数据:" << len << "..." << "已处理 " << sum << " 字节" << endl ;
cout << endl << "处理总字节数:" << sum << endl;
cout << endl << "释放资源" <<endl;
// 释放
delete pcms;
pcms = NULL;
// 写入索引
av_write_trailer(avFormatContext);
fclose(fp);
cout << "关闭输出文件" << endl;
avio_close(avFormatContext->pb);
cout << "关闭编码器" << endl;
avcodec_close(avCodecContext);
cout << "清理编码器参数" << endl;
avcodec_free_context(&avCodecContext);
cout << "清理输出上下文" << endl;
avformat_free_context(avFormatContext);
return 0;
CMakeLists.txt
cmake_minimum_required(VERSION 3.17)
project(ffmpeg_demo)
# 设置ffmpeg依赖库及头文件所在目录,并存进指定变量
set(ffmpeg_libs_DIR /home/xundh/ffmpeg_sources/ffmpeg-4.2.2)
set(ffmpeg_headers_DIR /home/xundh/ffmpeg_sources/ffmpeg-4.2.2)
#对于find_package找不到的外部依赖库,可以用add_library添加
# SHARED表示添加的是动态库
# IMPORTED表示是引入已经存在的动态库
add_library( avcodec SHARED IMPORTED)
add_library( avfilter SHARED IMPORTED )
add_library( swresample SHARED IMPORTED )
add_library( swscale SHARED IMPORTED )
add_library( avformat SHARED IMPORTED )
add_library( avutil SHARED IMPORTED )
#指定所添加依赖库的导入路径
set_target_properties( avcodec PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libavcodec/libavcodec.so )
set_target_properties( avfilter PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libavfilter/libavfilter.so )
set_target_properties( swresample PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libswresample/libswresample.so )
set_target_properties( swscale PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libswscale/libswscale.so )
set_target_properties( avformat PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libavformat/libavformat.so )
set_target_properties( avutil PROPERTIES IMPORTED_LOCATION $ffmpeg_libs_DIR/libavutil/libavutil.so )
# 添加头文件路径到编译器的头文件搜索路径下,多个路径以空格分隔
include_directories( $ffmpeg_headers_DIR )
link_directories($ffmpeg_libs_DIR )
link_directories(/usr/lib)
set(CMAKE_CXX_STANDARD 14)
# add_executable(ffmpeg_demo main.cpp)
add_executable(ffmpeg_demo audio.cpp)
target_link_libraries($PROJECT_NAME avcodec avformat avutil swresample swscale swscale avfilter )
运行效果:
以上是关于音视频开发8. 使用ffmpeg 将pcm转码aac实践(C++)的主要内容,如果未能解决你的问题,请参考以下文章
音视频开发9. 使用ffmpeg 将pcm转码mp3实践(C++)