FFmpeg C++ 教程:入门指南与实例 – wiki基地

FFmpeg C++ 教程:入门指南与实例

FFmpeg 是一个开源的跨平台多媒体框架,可以用于录制、转换以及流化音频和视频。它包含了 libavcodec、libavformat、libavutil 等核心库,提供了强大的多媒体处理能力。虽然 FFmpeg 本身是用 C 语言编写的,但它提供了 C API,因此我们可以使用 C++ 来调用 FFmpeg 库,从而开发强大的多媒体应用。

本文将为您提供一个详细的 FFmpeg C++ 入门指南,包括 FFmpeg 的安装配置、核心库的介绍、C++ 集成的步骤、以及一些实用的示例代码,帮助您快速上手 FFmpeg C++ 开发。

一、FFmpeg 的安装与配置

在开始使用 FFmpeg C++ 之前,我们需要先安装 FFmpeg 及其开发库。不同的操作系统安装方式有所不同,以下分别介绍 Windows、Linux 和 macOS 下的安装方法:

1. Windows:

  • 下载 FFmpeg: 前往 https://ffmpeg.org/download.html 下载适用于 Windows 的 FFmpeg 预编译版本。建议选择 Windows builds from gyan.devBtbN builds 提供的版本,因为它们通常包含最新功能和 bug 修复。

  • 解压并配置环境变量: 将下载的压缩包解压到您选择的目录,例如 C:\ffmpeg。然后,将 C:\ffmpeg\bin 添加到系统的 Path 环境变量中。这样,您就可以在命令行中直接使用 ffmpeg 命令。

  • 安装开发库: 某些 FFmpeg 预编译版本可能不包含开发库文件 (例如 *.lib*.h)。 如果是这种情况,您需要下载一个包含开发库的版本,或者自行编译 FFmpeg。 常用的方法是使用 vcpkg 或 MSYS2 构建 FFmpeg。

    • 使用 vcpkg:

      1. 安装 vcpkg:按照 vcpkg 官方文档 (https://vcpkg.io/en/getting-started) 安装 vcpkg。
      2. 使用 vcpkg 安装 FFmpeg:在 vcpkg 的命令行中执行 vcpkg install ffmpeg。 您还可以指定平台,例如 vcpkg install ffmpeg:x64-windows
    • 使用 MSYS2:

      1. 安装 MSYS2:前往 https://www.msys2.org/ 下载并安装 MSYS2。
      2. 更新 MSYS2:打开 MSYS2 终端,执行 pacman -Syupacman -Su
      3. 安装 FFmpeg 开发库:执行 pacman -S mingw-w64-x86_64-ffmpeg (对于 64 位系统) 或 pacman -S mingw-w64-i686-ffmpeg (对于 32 位系统)。

2. Linux (Ubuntu/Debian):

  • 使用 apt 安装 FFmpeg:打开终端,执行以下命令:

    bash
    sudo apt update
    sudo apt install ffmpeg libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libavdevice-dev libavfilter-dev libswresample-dev

    这些命令分别安装 FFmpeg 命令行工具和 FFmpeg 的开发库。

3. macOS:

  • 使用 Homebrew 安装 FFmpeg: 如果您的系统已经安装了 Homebrew,可以使用以下命令安装 FFmpeg:

    bash
    brew install ffmpeg

    Homebrew 会自动安装 FFmpeg 及其依赖的库。

二、FFmpeg 核心库介绍

FFmpeg 框架包含多个核心库,它们各自负责不同的多媒体处理任务。以下是一些常用的库及其功能:

  • libavformat: 负责处理多媒体文件的封装格式,例如 MP4、AVI、MKV 等。它可以读取和写入不同格式的文件,并提供访问文件 metadata (例如码率、时长、编解码器等) 的接口。

  • libavcodec: 负责音视频的编解码。它支持多种常见的编解码器,例如 H.264、H.265、AAC、MP3 等。 libavcodec 提供了解码和编码音视频数据的接口。

  • libavutil: 提供一些通用的工具函数,例如内存分配、错误处理、日志输出、数学运算等。 libavutil 是其他 FFmpeg 库的基础。

  • libswscale: 负责图像的缩放和像素格式转换。它可以将图像从一种分辨率缩放到另一种分辨率,并将图像从一种像素格式转换为另一种像素格式。

  • libavdevice: 负责访问音视频输入设备,例如摄像头和麦克风。 它可以捕获来自设备的音视频数据。

  • libavfilter: 提供音视频滤镜功能,例如裁剪、缩放、旋转、添加水印等。 libavfilter 允许您对音视频数据进行各种处理。

  • libswresample: 负责音频的重采样和格式转换。 它可以将音频从一种采样率转换为另一种采样率,并将音频从一种通道布局转换为另一种通道布局。

三、C++ 集成 FFmpeg 的步骤

在 C++ 中使用 FFmpeg,我们需要包含相应的头文件,链接相应的库文件,并按照 FFmpeg 的 API 调用相关函数。以下是一个简单的 C++ 集成 FFmpeg 的步骤:

  1. 创建 C++ 项目: 创建一个 C++ 项目,例如使用 Visual Studio、CMake、或 Xcode。

  2. 包含头文件: 在您的 C++ 代码中包含 FFmpeg 的头文件。通常,您需要包含以下头文件:

    “`c++

    include

    include

    include

    “`

  3. 链接库文件: 在您的项目配置中链接 FFmpeg 的库文件。具体取决于您的编译器和构建系统。

    • Visual Studio: 在项目属性中,选择 “链接器” -> “输入” -> “附加依赖项”,添加以下库文件:

      avformat.lib
      avcodec.lib
      avutil.lib
      swscale.lib
      avdevice.lib
      avfilter.lib
      swresample.lib

      确保库文件路径已添加到 “链接器” -> “常规” -> “附加库目录”。

    • CMake:CMakeLists.txt 文件中添加以下代码:

      cmake
      find_package(FFmpeg REQUIRED COMPONENTS avformat avcodec avutil swscale avdevice avfilter swresample)
      include_directories(${FFmpeg_INCLUDE_DIRS})
      target_link_libraries(your_target ${FFmpeg_LIBRARIES})

    • Makefile: 在您的 Makefile 中添加 -lavformat -lavcodec -lavutil -lswscale -lavdevice -lavfilter -lswresample 等链接选项。

  4. 初始化 FFmpeg: 在使用 FFmpeg 之前,通常需要调用 av_register_all() 函数来注册所有的编解码器和封装格式。 但是,从 FFmpeg 5.0 开始,av_register_all() 已经废弃,不再需要调用。 应该使用更细粒度的注册函数,例如 avformat_network_init()。 如果使用网络协议,需要调用这个函数。

  5. 使用 FFmpeg API: 现在您可以开始使用 FFmpeg 的 API 来处理多媒体数据了。

四、实用示例代码

以下是一些实用的 FFmpeg C++ 示例代码,演示了如何使用 FFmpeg 进行一些常见的操作:

1. 获取视频文件信息:

“`c++

include

include

int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << “Usage: ” << argv[0] << ” ” << std::endl;
return 1;
}

const char *input_file = argv[1];

AVFormatContext *format_context = nullptr;

// 打开输入文件
int ret = avformat_open_input(&format_context, input_file, nullptr, nullptr);
if (ret < 0) {
std::cerr << “Could not open input file: ” << av_err2str(ret) << std::endl;
return 1;
}

// 读取文件信息
ret = avformat_find_stream_info(format_context, nullptr);
if (ret < 0) {
std::cerr << “Could not find stream information: ” << av_err2str(ret) << std::endl;
avformat_close_input(&format_context);
return 1;
}

// 打印文件信息
av_dump_format(format_context, 0, input_file, 0);

// 释放资源
avformat_close_input(&format_context);

return 0;
}
“`

这段代码打开一个视频文件,读取其信息,并将信息打印到控制台。 它使用了 avformat_open_input() 打开文件,avformat_find_stream_info() 读取文件信息,以及 av_dump_format() 打印文件信息。

2. 解码视频帧:

“`c++

include

include

include

include

include

int main(int argc, char *argv[]) {
if (argc < 2) {
std::cerr << “Usage: ” << argv[0] << ” ” << std::endl;
return 1;
}

const char *input_file = argv[1];

AVFormatContext *format_context = nullptr;
AVCodecContext *codec_context = nullptr;
AVCodec *codec = nullptr;
AVFrame *frame = nullptr;
AVPacket *packet = nullptr;
SwsContext *sws_context = nullptr;

int video_stream_index = -1;

// 打开输入文件
int ret = avformat_open_input(&format_context, input_file, nullptr, nullptr);
if (ret < 0) {
    std::cerr << "Could not open input file: " << av_err2str(ret) << std::endl;
    return 1;
}

// 读取文件信息
ret = avformat_find_stream_info(format_context, nullptr);
if (ret < 0) {
    std::cerr << "Could not find stream information: " << av_err2str(ret) << std::endl;
    avformat_close_input(&format_context);
    return 1;
}

// 查找视频流
for (int i = 0; i < format_context->nb_streams; ++i) {
    if (format_context->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
        video_stream_index = i;
        break;
    }
}

if (video_stream_index == -1) {
    std::cerr << "Could not find video stream" << std::endl;
    avformat_close_input(&format_context);
    return 1;
}

// 获取解码器
codec = avcodec_find_decoder(format_context->streams[video_stream_index]->codecpar->codec_id);
if (!codec) {
    std::cerr << "Could not find decoder" << std::endl;
    avformat_close_input(&format_context);
    return 1;
}

// 创建解码器上下文
codec_context = avcodec_alloc_context3(codec);
if (!codec_context) {
    std::cerr << "Could not allocate codec context" << std::endl;
    avformat_close_input(&format_context);
    return 1;
}

// 将流中的参数复制到解码器上下文
ret = avcodec_parameters_to_context(codec_context, format_context->streams[video_stream_index]->codecpar);
if (ret < 0) {
    std::cerr << "Could not copy codec parameters to context: " << av_err2str(ret) << std::endl;
    avformat_close_input(&format_context);
    avcodec_free_context(&codec_context);
    return 1;
}

// 打开解码器
ret = avcodec_open2(codec_context, codec, nullptr);
if (ret < 0) {
    std::cerr << "Could not open codec: " << av_err2str(ret) << std::endl;
    avformat_close_input(&format_context);
    avcodec_free_context(&codec_context);
    return 1;
}

// 分配帧和包
frame = av_frame_alloc();
if (!frame) {
    std::cerr << "Could not allocate frame" << std::endl;
    avformat_close_input(&format_context);
    avcodec_free_context(&codec_context);
    return 1;
}

packet = av_packet_alloc();
if (!packet) {
    std::cerr << "Could not allocate packet" << std::endl;
    avformat_close_input(&format_context);
    avcodec_free_context(&codec_context);
    av_frame_free(&frame);
    return 1;
}

// 初始化 swscale 上下文 (用于像素格式转换)
sws_context = sws_getContext(
    codec_context->width, codec_context->height, codec_context->pix_fmt,
    codec_context->width, codec_context->height, AV_PIX_FMT_RGB24,
    SWS_BILINEAR, nullptr, nullptr, nullptr
);
if (!sws_context) {
    std::cerr << "Could not initialize swscale context" << std::endl;
    avformat_close_input(&format_context);
    avcodec_free_context(&codec_context);
    av_frame_free(&frame);
    av_packet_free(&packet);
    return 1;
}


// 读取和解码帧
while (av_read_frame(format_context, packet) >= 0) {
    if (packet->stream_index == video_stream_index) {
        ret = avcodec_send_packet(codec_context, packet);
        if (ret < 0) {
            std::cerr << "Error sending packet for decoding: " << av_err2str(ret) << std::endl;
            break;
        }

        while (ret >= 0) {
            ret = avcodec_receive_frame(codec_context, frame);
            if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
                break;
            } else if (ret < 0) {
                std::cerr << "Error during decoding: " << av_err2str(ret) << std::endl;
                break;
            }

            // 成功解码一帧
            std::cout << "Frame decoded, width: " << frame->width << ", height: " << frame->height << std::endl;

            // 进行像素格式转换(这里转换为 RGB24)
            AVFrame *rgb_frame = av_frame_alloc();
            if (!rgb_frame) {
                std::cerr << "Could not allocate RGB frame" << std::endl;
                break;
            }

            rgb_frame->width = codec_context->width;
            rgb_frame->height = codec_context->height;
            rgb_frame->format = AV_PIX_FMT_RGB24;
            ret = av_image_alloc(rgb_frame->data, rgb_frame->linesize, rgb_frame->width, rgb_frame->height, (AVPixelFormat)rgb_frame->format, 32);
            if (ret < 0) {
                std::cerr << "Could not allocate raw picture buffer: " << av_err2str(ret) << std::endl;
                av_frame_free(&rgb_frame);
                break;
            }

            sws_scale(sws_context, frame->data, frame->linesize, 0, codec_context->height, rgb_frame->data, rgb_frame->linesize);


            //  处理解码后的帧数据 (例如显示图像或保存到文件)
            // ...
            av_frame_free(&rgb_frame);

        }
    }
    av_packet_unref(packet); // 释放 packet
}

// 释放资源
sws_freeContext(sws_context);
av_packet_free(&packet);
av_frame_free(&frame);
avcodec_close(codec_context);
avcodec_free_context(&codec_context);
avformat_close_input(&format_context);

return 0;

}
“`

这段代码解码一个视频文件,读取每一帧,并将其像素格式转换为 RGB24。它使用了 avformat_open_input() 打开文件,avformat_find_stream_info() 读取文件信息, avcodec_find_decoder() 查找解码器, avcodec_alloc_context3()分配解码器上下文,avcodec_open2() 打开解码器, av_read_frame() 读取帧,avcodec_send_packet() 发送包进行解码, avcodec_receive_frame()接收解码后的帧, sws_getContext()初始化 swscale 上下文, sws_scale() 进行像素格式转换。最后,它释放所有分配的资源。

五、总结与建议

本文为您提供了一个 FFmpeg C++ 入门指南,涵盖了 FFmpeg 的安装配置、核心库的介绍、C++ 集成的步骤、以及一些实用的示例代码。希望这些内容能够帮助您快速上手 FFmpeg C++ 开发。

一些建议:

  • 阅读官方文档: FFmpeg 的官方文档是学习 FFmpeg 的最佳资源。

  • 阅读示例代码: FFmpeg 的源代码中包含大量的示例代码,可以参考这些示例代码学习 FFmpeg 的用法.

  • 使用调试工具: 使用调试工具可以帮助您更好地理解 FFmpeg 的运行原理.

  • 参与社区讨论: 加入 FFmpeg 的社区,与其他开发者交流经验,可以帮助您解决遇到的问题。

FFmpeg 是一个功能强大的多媒体框架,掌握 FFmpeg C++ 开发,可以帮助您开发出各种强大的多媒体应用。祝您学习愉快!

发表评论

您的邮箱地址不会被公开。 必填项已用 * 标注

滚动至顶部