MP4转AAC转换器
代码模块结构
1. 核心类结构
MP4ToAACConverter
├── 私有成员变量
│ ├── inputFormatContext // 输入格式上下文
│ ├── outputFormatContext // 输出格式上下文
│ ├── inputCodecContext // 输入编解码器上下文
│ ├── outputCodecContext // 输出编解码器上下文
│ ├── resampleContext // 重采样上下文
│ ├── audioFifo // 音频FIFO缓冲区
│ └── audioStreamIndex // 音频流索引
├── 公共方法
│ ├── 构造函数/析构函数
│ ├── openInputFile() // 打开输入文件
│ ├── openOutputFile() // 创建输出文件
│ ├── initResamplerAndFifo()// 初始化重采样和FIFO
│ ├── convert() // 主转换流程
│ ├── resampleAndEncode() // 重采样和编码
│ ├── encodeFromFifo() // 从FIFO编码
│ ├── encodeFrame() // 编码单帧
│ ├── flushEncoder() // 刷新编码器
│ ├── cleanup() // 清理资源
│ └── convertMP4ToAAC() // 公共转换接口
2. 文件结构
project/
├── main.cpp // 主程序入口
├── mp4_to_aac_converter.h // 转换器头文件
├── mp4_to_aac_converter.cpp // 转换器实现
└── CMakeLists.txt // 构建配置
详细代码模块
模块1: 头文件和类定义
cpp
// mp4_to_aac_converter.h
#ifndef MP4_TO_AAC_CONVERTER_H
#define MP4_TO_AAC_CONVERTER_H
#include <string>
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavutil/opt.h>
#include <libswresample/swresample.h>
#include <libavutil/audio_fifo.h>
}
class MP4ToAACConverter {
private:
// FFmpeg上下文指针
AVFormatContext* inputFormatContext;
AVFormatContext* outputFormatContext;
AVCodecContext* inputCodecContext;
AVCodecContext* outputCodecContext;
SwrContext* resampleContext;
AVAudioFifo* audioFifo;
int audioStreamIndex;
// 内部方法
bool openInputFile(const std::string& inputFilename);
bool openOutputFile(const std::string& outputFilename);
bool initResamplerAndFifo();
bool convert();
bool resampleAndEncode(AVFrame* inputFrame);
bool encodeFromFifo();
bool encodeFrame(AVFrame* frame);
bool flushEncoder();
void cleanup();
public:
MP4ToAACConverter();
~MP4ToAACConverter();
bool convertMP4ToAAC(const std::string& inputFile, const std::string& outputFile);
};
#endif
模块2: 构造函数和资源管理
cpp
// mp4_to_aac_converter.cpp - 部分1
#include "mp4_to_aac_converter.h"
#include <iostream>
MP4ToAACConverter::MP4ToAACConverter() :
inputFormatContext(nullptr),
outputFormatContext(nullptr),
inputCodecContext(nullptr),
outputCodecContext(nullptr),
resampleContext(nullptr),
audioFifo(nullptr),
audioStreamIndex(-1) {
// 注册所有编解码器
avformat_network_init();
}
MP4ToAACConverter::~MP4ToAACConverter() {
cleanup();
}
void MP4ToAACConverter::cleanup() {
if (resampleContext) swr_free(&resampleContext);
if (audioFifo) av_audio_fifo_free(audioFifo);
if (inputCodecContext) avcodec_free_context(&inputCodecContext);
if (outputCodecContext) avcodec_free_context(&outputCodecContext);
if (inputFormatContext) avformat_close_input(&inputFormatContext);
if (outputFormatContext) {
if (!(outputFormatContext->oformat->flags & AVFMT_NOFILE)) {
avio_closep(&outputFormatContext->pb);
}
avformat_free_context(outputFormatContext);
}
}
模块3: 文件操作模块
cpp
// mp4_to_aac_converter.cpp - 部分2
bool MP4ToAACConverter::openInputFile(const std::string& inputFilename) {
// 打开输入文件
if (avformat_open_input(&inputFormatContext, inputFilename.c_str(), nullptr, nullptr) < 0) {
std::cerr << "无法打开输入文件: " << inputFilename << std::endl;
return false;
}
// 获取流信息
if (avformat_find_stream_info(inputFormatContext, nullptr) < 0) {
std::cerr << "无法获取流信息" << std::endl;
return false;
}
// 查找音频流
audioStreamIndex = av_find_best_stream(inputFormatContext, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
if (audioStreamIndex < 0) {
std::cerr << "未找到音频流" << std::endl;
return false;
}
// 设置解码器...
return true;
}
bool MP4ToAACConverter::openOutputFile(const std::string& outputFilename) {
// 创建输出格式上下文
if (avformat_alloc_output_context2(&outputFormatContext, nullptr, nullptr, outputFilename.c_str()) < 0) {
std::cerr << "无法创建输出格式上下文" << std::endl;
return false;
}
// 设置编码器和输出流...
return true;
}
模块4: 音频处理模块
cpp
// mp4_to_aac_converter.cpp - 部分3
bool MP4ToAACConverter::initResamplerAndFifo() {
// 初始化重采样器
int ret = swr_alloc_set_opts2(
&resampleContext,
&outputCodecContext->ch_layout,
outputCodecContext->sample_fmt,
outputCodecContext->sample_rate,
&inputCodecContext->ch_layout,
inputCodecContext->sample_fmt,
inputCodecContext->sample_rate,
0, nullptr
);
if (ret < 0 || swr_init(resampleContext) < 0) {
std::cerr << "无法初始化重采样器" << std::endl;
return false;
}
// 初始化音频FIFO
audioFifo = av_audio_fifo_alloc(outputCodecContext->sample_fmt,
outputCodecContext->ch_layout.nb_channels,
1024 * 10);
return audioFifo != nullptr;
}
模块5: 主转换流程模块
cpp
// mp4_to_aac_converter.cpp - 部分4
bool MP4ToAACConverter::convert() {
AVPacket* packet = av_packet_alloc();
AVFrame* inputFrame = av_frame_alloc();
// 读取并处理数据包
while (av_read_frame(inputFormatContext, packet) >= 0) {
if (packet->stream_index == audioStreamIndex) {
// 解码和处理音频帧
if (avcodec_send_packet(inputCodecContext, packet) >= 0) {
while (avcodec_receive_frame(inputCodecContext, inputFrame) >= 0) {
resampleAndEncode(inputFrame);
av_frame_unref(inputFrame);
}
}
}
av_packet_unref(packet);
}
// 刷新处理...
return true;
}
bool MP4ToAACConverter::convertMP4ToAAC(const std::string& inputFile, const std::string& outputFile) {
return openInputFile(inputFile) &&
openOutputFile(outputFile) &&
initResamplerAndFifo() &&
convert();
}
模块6: 主程序入口
cpp
// main.cpp
#include "mp4_to_aac_converter.h"
#include <iostream>
int main(int argc, char* argv[]) {
if (argc != 3) {
std::cout << "用法: " << argv[0] << " <input.mp4> <output.aac>" << std::endl;
return 1;
}
MP4ToAACConverter converter;
if (converter.convertMP4ToAAC(argv[1], argv[2])) {
std::cout << "转换成功!" << std::endl;
return 0;
} else {
std::cerr << "转换失败!" << std::endl;
return 1;
}
}
代码流程图
是 否 是 否 否 是 开始转换 打开输入文件 查找音频流 初始化解码器 创建输出文件 初始化编码器 初始化重采样器 初始化音频FIFO 开始主转换循环 读取数据包 是音频流? 发送到解码器 接收解码帧 重采样处理 写入FIFO FIFO足够数据? 编码帧 写入输出文件 文件结束? 刷新编码器 写入文件尾 清理资源 转换完成
详细处理流程图
解码流程
读取数据包 → 发送到解码器 → 接收解码帧 → 重采样 → 写入FIFO
编码流程
从FIFO读取 → 创建编码帧 → 发送到编码器 → 接收数据包 → 写入文件
学习文档
1. FFmpeg核心概念
关键数据结构:
AVFormatContext: 格式上下文,管理输入输出格式AVCodecContext: 编解码器上下文,管理编解码参数AVPacket: 压缩数据包AVFrame: 未压缩数据帧SwrContext: 音频重采样上下文
2. 音频处理流程详解
步骤1: 输入处理
cpp
avformat_open_input() // 打开输入文件
avformat_find_stream_info() // 获取流信息
av_find_best_stream() // 查找最佳音频流
avcodec_find_decoder() // 查找解码器
avcodec_alloc_context3() // 分配解码器上下文
avcodec_parameters_to_context() // 复制参数
avcodec_open2() // 打开解码器
步骤2: 输出设置
cpp
avformat_alloc_output_context2() // 创建输出上下文
avcodec_find_encoder() // 查找编码器(AAC)
avcodec_alloc_context3() // 分配编码器上下文
// 设置编码参数: 采样率、声道、位率等
avcodec_open2() // 打开编码器
avformat_new_stream() // 创建输出流
avio_open() // 打开输出文件
avformat_write_header() // 写入文件头
步骤3: 音频处理链
cpp
av_read_frame() // 读取数据包
avcodec_send_packet() // 发送到解码器
avcodec_receive_frame() // 接收解码帧
swr_convert() // 重采样
av_audio_fifo_write() // 写入FIFO
av_audio_fifo_read() // 从FIFO读取
avcodec_send_frame() // 发送到编码器
avcodec_receive_packet() // 接收编码包
av_interleaved_write_frame() // 写入文件
3. 错误处理模式
cpp
// 标准错误检查模式
if (av_function_call() < 0) {
std::cerr << "错误描述" << std::endl;
return false;
}
// 资源分配检查
AVType* ptr = av_alloc_function();
if (!ptr) {
std::cerr << "分配失败" << std::endl;
return false;
}
4. 资源管理原则
RAII模式:
- 构造函数中初始化
- 析构函数中清理
- 使用智能指针或手动管理FFmpeg资源
清理顺序:
- 释放重采样器和FIFO
- 关闭编解码器上下文
- 关闭格式上下文
- 释放数据包和帧
5. 性能优化要点
缓冲区管理:
- 使用FIFO平衡解码和编码速度差异
- 合理设置FIFO大小避免内存浪费
- 及时释放不再使用的帧和数据包
参数调优:
- 选择合适的采样率和位率
- 根据输入特性调整重采样参数
- 使用编码器预设平衡质量和速度
6. 常见问题解决
编译问题:
- 确保FFmpeg开发库正确安装
- 检查头文件和库文件路径
- 使用正确的链接器标志
运行时问题:
- 检查输入文件格式支持
- 验证输出文件路径权限
- 处理内存分配失败情况