FFmpeg C++ AI视觉开发核心手册 (整合版)适用场景:视频流接入、AI模型预处理(抽帧/缩放/格式转换)、高性能算法集成。

一、 环境配置与头文件规范

在 C++ 项目中,必须通过 extern "C" 屏蔽 C++ 的符号修饰,否则编译器无法正确链接 FFmpeg 的 C 函数库。

1. 核心头文件及其职责

C++

复制代码
extern "C" {
    #include <libavformat/avformat.h> // 【解封装】负责打开文件、读取网络流(RTSP/RTMP)
    #include <libavcodec/avcodec.h>   // 【编解码】负责把 H.264/H.265 数据解压成像素
    #include <libswscale/swscale.h>   // 【图像转换】负责 YUV 转 RGB,以及画面缩放
    #include <libavutil/imgutils.h>   // 【图像工具】负责内存分配、计算图像大小
    #include <libavutil/log.h>        // 【日志系统】负责调试和排错
}

二、 调试排错:日志系统 (必用)

FFmpeg 内部运行极其复杂,不打印日志就相当于盲人摸象。在 main() 函数开头调用即可设置级别。

1. 设置示例

C++

复制代码
av_log_set_level(AV_LOG_DEBUG); // 开发阶段必选,能看到详细的网络握手和解码细节

2. 日志级别速查表

级别 用途 备注
AV_LOG_ERROR 发生崩溃或无法继续的错误 如:解码器打开失败
AV_LOG_WARNING 异常但不致命 如:视频流丢包
AV_LOG_INFO 打印流信息 默认级别,显示宽、高、码率
AV_LOG_DEBUG 调试首选 显示底层交互细节,排查网络流必选

三、 内存中的"四大金刚" (核心数据结构)

AI 开发本质上就是在这四个关键结构体之间搬运和转换数据:

  1. AVFormatContext:集装箱。包含了视频的所有流信息。

  2. AVCodecContext:加工厂。负责具体的编解码逻辑。

  3. AVPacket :压缩包。存放 H.264/H.265 数据,AI 看不懂

  4. AVFrame :原始帧。存放 YUV 或 RGB 像素,AI 推理的直接原材料

  5. H.264/H.265:是视频的"压缩包"。

  6. 解码(Decoding) :就是把这个压缩包(AVPacket)解压成 AI 认识的像素图片(AVFrame


四、 实战项目:视频抽帧与格式转换 (C++完整代码)

逻辑流程

打开视频 -> 找到视频流 ->初始化解码器 ->准备图像格式转换器 (YUV420P -> RGB24)->为 RGB 帧手动分配内存空间->循环读取 AVPacket-> 解码成 AVFrame (YUV) ->转换成 RGB-> AI 处理/保存。

C++

复制代码
#include <iostream>

extern "C" {
    #include <libavformat/avformat.h>
    #include <libavcodec/avcodec.h>
    #include <libswscale/swscale.h>
    #include <libavutil/imgutils.h>
}

int main() {
    // 设置日志级别
    av_log_set_level(AV_LOG_INFO);
    const char* url = "test.mp4"; // 支持本地文件或 RTSP 地址

    // 1. 打开流并获取信息
    AVFormatContext* fmt_ctx = avformat_alloc_context();//1. 申请内存并初始化默认值
    if (avformat_open_input(&fmt_ctx, url, NULL, NULL) < 0) {
        av_log(NULL, AV_LOG_ERROR, "无法打开输入流\n");
        return -1;
    }
    avformat_find_stream_info(fmt_ctx, NULL);
// 第二步:判断能不能读懂里面的内容(格式对不对?有没有坏帧?)

    // 2. 查找视频流索引与解码器
//如果你打开一个电影,nb_streams 可能是 3(1路视频 + 1路中文音频 + 1路英文字幕)一共三个流。
    int v_idx = -1;
    for (int i = 0; i < fmt_ctx->nb_streams; i++) {
        if (fmt_ctx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            //查看流类型,找到视频流, v_idx存放的是视频流编号
            v_idx = i; break;
        }
    }
    
    AVCodecParameters* c_par = fmt_ctx->streams[v_idx]->codecpar;//拿到编码参数
//里面有什么:这个视频是用什么编码的(H.264?)、宽高是多少、像素格式(YUV420P?)等。
    const AVCodec* codec = avcodec_find_decoder(c_par->codec_id);//寻找解码器
    AVCodecContext* c_ctx = avcodec_alloc_context3(codec);//创建解码器上下文,为解码
//过程开辟一块内存空间
    avcodec_parameters_to_context(c_ctx, c_par);//同步参数,让解码器知道视频的宽、高、码率
    avcodec_open2(c_ctx, codec, NULL);//正式打开解码器

    // 3. 准备图像格式转换器 (YUV420P -> RGB24)
    // AI 推理通常需要 RGB 格式
    SwsContext* sws_ctx = sws_getContext(
        c_ctx->width, c_ctx->height, c_ctx->pix_fmt,   // 原图宽高及格式
        c_ctx->width, c_ctx->height, AV_PIX_FMT_RGB24, // 目标宽高及格式
        SWS_BILINEAR, NULL, NULL, NULL
    );

    // 4. 准备存放数据的容器(内存分配)
    AVPacket* pkt = av_packet_alloc();
    AVFrame* frame = av_frame_alloc();     // 接收解码后的原始 YUV
    AVFrame* frame_rgb = av_frame_alloc(); // 接收转换后的目标 RGB
    
    // 为 RGB 帧手动分配内存空间
    int num_bytes = av_image_get_buffer_size(AV_PIX_FMT_RGB24, c_ctx->width, c_ctx->height, 1);//计算出存放一帧图片到底需要多少字节
    uint8_t* buffer = (uint8_t*)av_malloc(num_bytes);
//把刚才申请的 buffer 的地址,"填"到 frame_rgb 结构体的 data 指针数组里。
//从此以后,你往 frame_rgb->data 里写数据,实际上就是写到了 buffer 这块内存里。
    av_image_fill_arrays(frame_rgb->data, frame_rgb->linesize, buffer, 
                         AV_PIX_FMT_RGB24, c_ctx->width, c_ctx->height, 1);
//frame_rgb->linesize写完一行后,要跳过多少字节才能开始写下一行。

    // 5. 解码主循环
    int count = 0;
    while (av_read_frame(fmt_ctx, pkt) >= 0 && count < 10) {
        if (pkt->stream_index == v_idx) {
            // 发送压缩包到解码器
            if (avcodec_send_packet(c_ctx, pkt) == 0) {
                // 接收解码后的帧
                while (avcodec_receive_frame(c_ctx, frame) == 0) {
                    // 【关键步骤】执行像素格式转换:YUV -> RGB
                    sws_scale(sws_ctx, frame->data, frame->linesize, 0, c_ctx->height, 
                              frame_rgb->data, frame_rgb->linesize);
//0 (起始行):表示从输入图像的 第 0 行(最顶部)开始处理
//c_ctx->height (扫描行数):表示本次处理 连续的多少行。在这里填入视频的总高度,意思就是"处理完整的一帧"。                    
                    // 🎯 此时 frame_rgb->data[0] 存储的就是 RGB 裸数据
                    // 可以直接传给 OpenCV Mat 或 TensorRT 推理引擎
                    av_log(NULL, AV_LOG_INFO, "已处理第 %d 帧视频\n", ++count);
                }
            }
        }
        av_packet_unref(pkt); // 必须释放 AVPacket,防止内存泄漏
    }

    // 6. 资源清理
    av_free(buffer);
    av_frame_free(&frame);
    av_frame_free(&frame_rgb);
    av_packet_free(&pkt);
    avcodec_free_context(&c_ctx);
    avformat_close_input(&fmt_ctx);
    sws_freeContext(sws_ctx);

    return 0;
}

五、 编译与链接命令 (Linux/WSL)

在终端编译时,必须明确指定 FFmpeg 的安装路径并链接相关库。

Bash

复制代码
g++ main.cpp -o ai_vision_demo \
    -I/usr/local/ffmpeg/include \
    -L/usr/local/ffmpeg/lib \
    -lavformat -lavcodec -lavutil -lswscale \
    -Wl,-rpath=/usr/local/ffmpeg/lib

💡 学习建议与 AI 集成笔记

  1. 目标检测集成 :在 sws_scale 之后,frame_rgb->data[0] 实际上就是一个一维数组。如果你使用 OpenCV,可以用 cv::Mat(height, width, CV_8UC3, frame_rgb->data[0]) 直接封装。

  2. 画面缩放 :如果你需要将 1080P 缩放到 640x640 喂给 YOLO,只需修改 sws_getContext 里的目标宽高参数即可。

  3. RTSP 稳定性 :如果是接入网络摄像头,建议增加 AVDictionary 参数设置 rtsp_transporttcp 以防止花屏。

cpp 复制代码
// 1. 定义一个配置字典指针
AVDictionary* options = NULL;

// 2. 设置 RTSP 传输协议为 TCP (默认通常是 UDP)
// 参数解释:"rtsp_transport" 是键,"tcp" 是值
av_dict_set(&options, "rtsp_transport", "tcp", 0);

// 3. (可选) 设置超时时间,防止摄像头掉线导致程序永久卡死
// 单位是微秒,这里设置 5 秒超时
av_dict_set(&options, "stimeout", "5000000", 0);

// 4. 打开输入流,注意第四个参数传入了 &options
// avformat_open_input 内部会读取这些设置
if (avformat_open_input(&fmt_ctx, url, NULL, &options) < 0) {
    av_log(NULL, AV_LOG_ERROR, "无法打开网络流\n");
    return -1;
}

// 5. 关键:打开后记得释放 dictionary
// 即使打开失败了,也要调用这个来防内存泄漏
av_dict_free(&options);

相关推荐
huakoh2 小时前
电脑用了好几年,找个文件像走迷宫——我用 AI Agent 清理了 D 盘
人工智能
两万五千个小时2 小时前
Claude Code 源码:普通工具实现 Read / Write / Edit / TodoWrite
人工智能·程序员·架构
伟贤AI之路2 小时前
为什么AI里的公式复制到Word格式会乱?
人工智能·word·latex
TechubNews2 小时前
CLARITY法案立法窗口开启|BTC回踩50日EMA确认:监管确定性+技术共振+AI算力革命三重定价
人工智能
深度学习lover2 小时前
<数据集>yolo微藻识别<目标检测>
人工智能·python·yolo·目标检测·计算机视觉·微藻识别
TechMix2 小时前
【经验总结】最近AiCoding的一些感受
人工智能·ai编程
A.A呐2 小时前
【C++第二十八章】单例模式
c++·单例模式
冰西瓜6002 小时前
深度学习的数学原理(二十七)—— 掩码注意力
人工智能·深度学习