V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
yoyoluck
V2EX  ›  FFmpeg

intel qsv ffmpeg 编码问题

  •  
  •   yoyoluck · 3 小时 45 分钟前 · 210 次点击

    有朋友搞过 ffmpeg qsv 硬件编码吗? 最近有一个任务,需要将音视频经过 ffmpeg 库编码成 h264/h265 之后送到海康的硬盘录像机 目前遇到问题是 使用 libx264 软编码的数据通过 rtsp 送到海康录像机能正常显示 通过 intel 的 qsv 硬件编码的 h264/h265 等送到海康录像机只能显示出关键帧,其他 P 帧无法显示(没有 B 帧),通过英伟达显卡硬件编码的 h264/h265 送到海康能正常显示。 以上所有编码后的数据都是通过 rtsp 服务推送的,所有的在 vlc 等播放器上都能正常显示。 目前大部分都是核显,所以想解决 qsv 硬件编码问题 下面是部分初始化代码,有偿协助

        // 查找编码器(支持硬件编码)
        video_codec_ = FindBestEncoder(config_.use_hardware_encoding);
        // 检查是否使用了硬件编码器
        video_using_hardware_ = (strcmp(video_codec_->name, "libx264") != 0 && strcmp(video_codec_->name, "libx265") != 0);
        // 创建编码器上下文
        video_codec_ctx_ = avcodec_alloc_context3(video_codec_);
        // 设置编码参数
        video_codec_ctx_->width = config_.width;
        video_codec_ctx_->height = config_.height;
        video_codec_ctx_->time_base.num = 1;
        video_codec_ctx_->time_base.den = config_.fps;
        video_codec_ctx_->framerate.num = config_.fps;
        video_codec_ctx_->framerate.den = 1;
        video_codec_ctx_->bit_rate = config_.bitrate;
        video_codec_ctx_->gop_size = config_.fps * 3; // 每 3 秒一个关键帧
        video_codec_ctx_->has_b_frames = 0;
        video_codec_ctx_->max_b_frames = 0;
        video_codec_ctx_->refs = 1;
    
        // 根据编码器类型设置像素格式
        if (video_using_hardware_)
        {
            // 硬件编码器使用 NV12 格式( QSV 、NVENC 、AMF 都支持)
            video_codec_ctx_->pix_fmt = AV_PIX_FMT_NV12;
        }
        else
        {
            // 软件编码器使用 YUV420P
            video_codec_ctx_->pix_fmt = AV_PIX_FMT_YUV420P;
        }
    
        video_codec_ctx_->flags = AV_CODEC_FLAG_LOW_DELAY | AV_CODEC_FLAG_GLOBAL_HEADER;
        video_codec_ctx_->rc_buffer_size = config_.bitrate; // 缓冲区大小等于码率
        video_codec_ctx_->rc_max_rate = config_.bitrate; // 最大码率
        video_codec_ctx_->rc_min_rate = config_.bitrate/5; // 最小码率
    
        if (!video_using_hardware_)
        {
            // 软件编码器特定参数
            video_codec_ctx_->thread_count = 4;
            if (strcmp(video_codec_->name, "libx264") == 0)
            {
                // libx264 特定选项
                if (!config_.preset.isEmpty())
                {
                    av_opt_set(video_codec_ctx_->priv_data, "preset", config_.preset.toStdString().c_str(), 0);
                }
    
                if (!config_.tune.isEmpty())
                {
                    av_opt_set(video_codec_ctx_->priv_data, "tune", config_.tune.toStdString().c_str(), 0);
                }
    
                if (!config_.profile.isEmpty())
                {
                    av_opt_set(video_codec_ctx_->priv_data, "profile", config_.profile.toStdString().c_str(), 0);
                }
    
                av_opt_set(video_codec_ctx_->priv_data, "x264opts", "bframes=0:rc-lookahead=0:scenecut=0", 0);
            }
            else if (strcmp(video_codec_->name, "libx265") == 0)
            {
                // libx265 特定选项
                if (!config_.preset.isEmpty())
                {
                    av_opt_set(video_codec_ctx_->priv_data, "preset", config_.preset.toStdString().c_str(), 0);
                }
    
                if (!config_.tune.isEmpty())
                {
                    av_opt_set(video_codec_ctx_->priv_data, "tune", config_.tune.toStdString().c_str(), 0);
                }
    
                // H.265 的 profile: main, main10, main-intra 等
                if (!config_.profile.isEmpty())
                {
                    QString profile_to_use = config_.profile;
    
                    // 检查 profile 是否与 H.265 兼容
                    // H.264 的 profile (baseline, high) 不能用于 H.265
                    if (profile_to_use == "baseline" || profile_to_use == "high")
                    {
                        profile_to_use = "main";  // H.265 的默认 profile
                        LOG(WARNING) << "H.265 不支持 profile \"" << config_.profile.toStdString()
                                     << "\", 已自动调整为 \"main\"";
                    }
    
                    av_opt_set(video_codec_ctx_->priv_data, "profile", profile_to_use.toStdString().c_str(), 0);
                }
    
                // H.265 低延迟参数
                av_opt_set(video_codec_ctx_->priv_data, "x265-params",
                           "bframes=0:rc-lookahead=0:scenecut=0:aq-mode=0", 0);
            }
        }
        else
        {
            // 硬件编码器特定参数
            const char* codec_name = video_codec_->name;
    
            if (strcmp(codec_name, "h264_qsv") == 0 || strcmp(codec_name, "hevc_qsv") == 0)
            {
                // Intel QSV 编码器参数
                // QSV 的 preset 值: veryfast, faster, fast, medium, slow, slower, veryslow
                av_opt_set(video_codec_ctx_->priv_data, "preset", "veryfast", 0);
                av_opt_set(video_codec_ctx_->priv_data, "async_depth", "1", 0); // 低延迟
                av_opt_set(video_codec_ctx_->priv_data, "look_ahead", "0", 0);  // 关闭前瞻
    
                // H.264 专用:添加严格的兼容性参数以解决海康录像机播放问题
                if (strcmp(codec_name, "h264_qsv") == 0)
                {
                    // 强制使用 baseline profile (如果配置中指定了)
                    if (!config_.profile.isEmpty())
                    {
                        av_opt_set(video_codec_ctx_->priv_data, "profile", config_.profile.toStdString().c_str(), 0);
                    }
    
                    // 强制 CAVLC 熵编码( baseline profile 必需,H.264 标准)
                    // QSV 默认可能使用 CABAC ,这会导致不兼容 baseline profile
                    av_opt_set(video_codec_ctx_->priv_data, "cavlc", "1", 0);
    
                    // 设置 H.264 level ( 40 = level 4.0 ,支持 1080p@30fps )
                    // 与 NVENC 保持一致,提高兼容性
                    av_opt_set(video_codec_ctx_->priv_data, "level", "40", 0);
    
                    // 禁用 B 帧策略(确保真正不使用 B 帧)
                    av_opt_set(video_codec_ctx_->priv_data, "b_strategy", "0", 0);
    
                    // 禁用自适应 I 帧和 B 帧(提高 GOP 结构的一致性)
                    av_opt_set(video_codec_ctx_->priv_data, "adaptive_i", "0", 0);
                    av_opt_set(video_codec_ctx_->priv_data, "adaptive_b", "0", 0);
    
                    // 严格的 GOP 结构(不允许动态调整关键帧位置)
                    av_opt_set(video_codec_ctx_->priv_data, "strict_gop", "1", 0);
    
                    // 低延迟模式(类似 NVENC 的 zerolatency )
                    av_opt_set(video_codec_ctx_->priv_data, "low_delay_brc", "1", 0);
    
                    LOG(INFO) << "Intel QSV H.264 编码器:已启用海康录像机兼容模式( baseline+CAVLC+level4.0 )";
                }
            }
            else if (strcmp(codec_name, "h264_nvenc") == 0 || strcmp(codec_name, "hevc_nvenc") == 0)
            {
                // NVIDIA NVENC 编码器参数
                // NVENC 的 preset: default, slow, medium, fast, hp, hq, bd, ll, llhq, llhp, lossless, losslesshp
                av_opt_set(video_codec_ctx_->priv_data, "preset", "llhp", 0);  // 低延迟高性能
                av_opt_set(video_codec_ctx_->priv_data, "zerolatency", "1", 0);
                av_opt_set(video_codec_ctx_->priv_data, "delay", "0", 0);
            }
            else if (strcmp(codec_name, "h264_amf") == 0 || strcmp(codec_name, "hevc_amf") == 0)
            {
                // AMD AMF 编码器参数
                av_opt_set(video_codec_ctx_->priv_data, "usage", "lowlatency", 0);
                av_opt_set(video_codec_ctx_->priv_data, "quality", "speed", 0);
            }
            else if (strcmp(codec_name, "h264_mf") == 0)
            {
                // Windows Media Foundation 编码器参数
                av_opt_set(video_codec_ctx_->priv_data, "rate_control", "cbr", 0);
            }
    
            // NVENC 的 profile 设置( QSV 已在上面单独设置)
            if (!config_.profile.isEmpty() && strcmp(codec_name, "h264_nvenc") == 0)
            {
                // NVENC 支持标准 H.264 profiles: baseline, main, high
                av_opt_set(video_codec_ctx_->priv_data, "profile", config_.profile.toStdString().c_str(), 0);
            }
        }
    
        // 初始化硬件加速上下文(如果使用硬件编码)
        if (!InitializeHardwareContext())
        {
            LOG(ERROR) << "硬件加速上下文初始化失败";
            return false;
        }
    
        // 打开编码器
        int ret = avcodec_open2(video_codec_ctx_, video_codec_, nullptr);
        if (ret < 0)
        {
            char errbuf[AV_ERROR_MAX_STRING_SIZE];
            av_strerror(ret, errbuf, sizeof(errbuf));
            LOG(ERROR) << "无法打开编码器: " << errbuf;
            return false;
        }
    
    2 条回复    2025-12-17 18:06:32 +08:00
    okkk
        1
    okkk  
       3 小时 26 分钟前
    yoyoluck
        2
    yoyoluck  
    OP
       3 小时 22 分钟前
    @okkk 谢谢你的回答,应该不是这几个问题导致的。能播放和不能播放的流数据我都分析过,基本上都一样,目前发现 P 帧中某几个参数不一致。
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   Solana   ·   3017 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 13:29 · PVG 21:29 · LAX 05:29 · JFK 08:29
    ♥ Do have faith in what you're doing.