读取代码在下面,下面是代码讲解

ret = avformat_find_stream_info(g_capture_ctx, NULL);

  • 用于解析输入流(这里是摄像头)的详细信息

  • 函数会填充 g_capture_ctx->streams 数组,每个元素代表一个媒体流(视频、音频等)

  • 对于摄像头这类实时设备,它会协商获取实际支持的参数(分辨率、帧率等)

videoStreamId = av_find_best_stream(g_capture_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, NULL);

  • 用于从多个流中找到最合适的指定类型的流

  • 对于摄像头,通常只有一个视频流,但该函数可以兼容有多个流的情况(如带麦克风的摄像头)

  • 用这个函数可以跨设备兼容

// 计算帧率(将AVRational格式转换为double)

double fps = av_q2d(video_stream->avg_frame_rate);

AVPacket的初始化和使用:

// 分配AVPacket用于存储采集到的视频数据,api函数

AVPacket* captured_packet = av_packet_alloc();// 等同于new AVPacket并初始化

// 读取一帧视频数据,api函数

auto ret = av_read_frame(g_capture_ctx, captured_packet);

// 将采集到的YUV帧写入文件,自定义函数

write_yuv_frame_to_file(captured_packet);

// 释放AVPacket的资源(但不释放指针本身)

av_packet_unref(captured_packet);

#include "include_ffmpeg.h"
#include <string>
#include <iostream>
#include <fstream>
using namespace std;

// 全局变量:视频采集上下文,用于管理整个视频采集过程
AVFormatContext* g_capture_ctx = nullptr;
// 全局变量:视频流对象,存储视频流的相关信息
AVStream* video_stream = nullptr;

/**
 * 创建DirectShow视频采集上下文,初始化摄像头采集
 * @param video_device_name 视频设备名称(如摄像头名称)
 */
void create_capture_dshow_context(const string& video_device_name)
{
    // 分配AVFormatContext上下文,用于管理媒体文件或流
    g_capture_ctx = avformat_alloc_context();
    
    // 查找DirectShow输入格式(Windows平台的视频采集接口)
    const AVInputFormat* inputFormat = av_find_input_format("dshow");
    cout << "capture : " << inputFormat->long_name << endl;

    // 创建并设置采集参数选项
    AVDictionary* options = nullptr;
    // 设置视频分辨率为1280x720
    av_dict_set(&options, "video_size", "1280x720", 0);
    // 设置像素格式为YUYV422(一种常见的YUV格式)
    av_dict_set(&options, "pixel_format", "yuyv422", 0);

    // 打开输入设备(摄像头)
    // 注意:dshow设备名需要以"video="为前缀
    int ret = avformat_open_input(&g_capture_ctx
        , string("video=" + video_device_name).c_str()
        , inputFormat, &options);
    check_error("avformat_open_input", ret);  // 检查函数调用是否成功

    // 查找流信息,获取视频流的详细参数
    ret = avformat_find_stream_info(g_capture_ctx, NULL);
    check_error("avformat_find_stream_info", ret);  // 检查函数调用是否成功

    // 查找最佳视频流(从所有流中找出视频类型的流)
    int videoStreamId = -1;
    videoStreamId = av_find_best_stream(g_capture_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, NULL);

    // 获取找到的视频流
    video_stream = g_capture_ctx->streams[videoStreamId];
    // 获取视频流的编解码器参数
    AVCodecParameters* codec_par = video_stream->codecpar;
    // 获取像素格式
    AVPixelFormat pixel_format = (AVPixelFormat)codec_par->format;

    // 计算帧率(将AVRational格式转换为double)
    double fps = av_q2d(video_stream->avg_frame_rate);
    
    // 输出采集到的视频信息
    cout << "capture format:" << endl
        << " capture fps = " << fps << endl  // 帧率
        <<" capture codec_id = " << avcodec_get_name( codec_par->codec_id) << endl  // 编解码器ID
        << " w=" << video_stream->codecpar->width  // 宽度
        << ", h=" << video_stream->codecpar->height << endl  // 高度
        << " capture format="<< av_get_pix_fmt_name(pixel_format)  // 像素格式
        << endl;
}

/**
 * 将YUV帧数据写入文件
 * @param captured_packet 包含YUV数据的数据包
 */
void write_yuv_frame_to_file(AVPacket* captured_packet)
{
    cout << "write_yuv_frame_to_file" << endl;
    // 输出数据包大小
    cout << "packet size=" << captured_packet->size << endl;
    
    // 打开文件流,准备写入YUV数据
    ofstream fout("captured_yuyv422.yuv");
    // 将数据包中的数据写入文件
    // reinterpret_cast用于将void*转换为const char*,适配ofstream的write方法
    fout.write(reinterpret_cast<const char*>(captured_packet->data), captured_packet->size);

    // 对于1280x720分辨率的YUYV422格式,每帧大小计算:
    // YUV422格式每个像素占用2字节(YUV420是1.5字节)
    // 1280*720*2 = 1,843,200字节
}

int main(void)
{
    // 注册所有的FFmpeg设备(必须在使用设备前调用)
    avdevice_register_all();
    
    // 显示系统中所有可用的视频和音频设备
    // 运行此代码后,应从输出列表中选择一个视频设备名称用于后续采集
    show_dshow_device();

    // 视频设备名称(需从show_dshow_device()的输出中复制)
    string video_device_name = "Integrated Camera";// 例如:"集成摄像头"或"Integrated Camera"
    // 创建视频采集上下文,初始化摄像头
    create_capture_dshow_context(video_device_name);

    // 分配AVPacket用于存储采集到的视频数据
    AVPacket* captured_packet = av_packet_alloc();// 等同于new AVPacket并初始化
    
    // 读取一帧视频数据
    auto ret = av_read_frame(g_capture_ctx, captured_packet);

    // 将采集到的YUV帧写入文件
    write_yuv_frame_to_file(captured_packet);
    
    // 释放AVPacket的资源(但不释放指针本身)
    av_packet_unref(captured_packet);

    // 关闭输入设备,释放采集上下文
    avformat_close_input(&g_capture_ctx);
    
    return 0;
}

开源链接

https://gitee.com/cxx888/ffmpeg-capture-camera-1-yuv-frame-answer

Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐