MP4转AAC转换器C++

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资源

清理顺序:

  1. 释放重采样器和FIFO
  2. 关闭编解码器上下文
  3. 关闭格式上下文
  4. 释放数据包和帧

5. 性能优化要点

缓冲区管理:

  • 使用FIFO平衡解码和编码速度差异
  • 合理设置FIFO大小避免内存浪费
  • 及时释放不再使用的帧和数据包

参数调优:

  • 选择合适的采样率和位率
  • 根据输入特性调整重采样参数
  • 使用编码器预设平衡质量和速度

6. 常见问题解决

编译问题:

  • 确保FFmpeg开发库正确安装
  • 检查头文件和库文件路径
  • 使用正确的链接器标志

运行时问题:

  • 检查输入文件格式支持
  • 验证输出文件路径权限
  • 处理内存分配失败情况
相关推荐
2301_8079973835 分钟前
代码随想录-day47
数据结构·c++·算法·leetcode
李日灐37 分钟前
手搓简单 string 库:了解C++ 字符串底层
开发语言·c++
Elias不吃糖1 小时前
LeetCode每日一练(3)
c++·算法·leetcode
别动哪条鱼1 小时前
FFmpeg 核心数据结构关系图
数据结构·ffmpeg
小龙报1 小时前
《算法通关指南数据结构和算法篇(2)--- 链表专题》
c语言·数据结构·c++·算法·链表·学习方法·visual studio
mjhcsp1 小时前
C++ 动态规划(Dynamic Programming)详解:从理论到实战
c++·动态规划·1024程序员节
随意起个昵称2 小时前
【二分】洛谷P2920,P2985做题小记
c++·算法
大模型实验室Lab4AI2 小时前
从帧到世界:面向世界模型的长视频生成
音视频
望眼欲穿的程序猿2 小时前
Win系统Vscode+CoNan+Cmake实现调试与构建
c语言·c++·后端