2025-03-06 ffmpeg提取SPS/PPS/SEI ( extradata )

一、需求

在某些情况下,可能需要直接使用H264/H265等原始数据流进行解码,比较常用的udp下的h264/h265。这时需要 av_parser_parse2 来组AVPacket,但对于视频的信息:宽高、格式等,可以根据 AVCodecParserContext 来获取,也可以直接提取sps/pps/sei这些原始数据extradata

本文讲如何从原始数据流(AVPacket)中找出 extradata 信息

二、ffmpeg5.0版本以下

在旧版本的ffmpeg中, AVCodecParserContext 内部有个 split 函数,可以直接返回 extradata 在数据流中的位置

cpp 复制代码
 AVCodecParserContext* m_parser    = nullptr;
 
 // 初始化 m_parser  
 // ........
 //
 
 // m_ctx : AVCodecContext*
 // pkt : AVPacket*
 // 其中第一个参数 m_ctx 可以不设置,直接设置为nullptr也可
 auto re = m_parser->parser->split(m_ctx, pkt->data, pkt->size);
 if (re > 0) {
    if (m_ctx->extradata_size <= 0 && m_ctx->extradata == nullptr) {
        //存放于解码器的上下文中,,在m_ctx释放的时候会自动释放
         m_ctx->extradata_size = re;
         m_ctx->extradata      = (uint8_t*) av_malloc(m_ctx->extradata_size
                                                        + AV_INPUT_BUFFER_PADDING_SIZE);
         memcpy(m_ctx->extradata, pkt->data, m_ctx->extradata_size);
         }
  }
 

三、ffmpeg5.0版本以上

ffmpeg5.0后 AVCodecParserContext 的 split 函数已删除

但可以使用 av_bsf_get_by_name("extract_extradata")

以下代码可供参考

cpp 复制代码
bool VideoStreamUdpH26X::setupExtraData(const AVCodecParserContext* parser,
AVCodecContext*             ctx,
AVPacket*                   pkt)
{
    bool need = false;
    // 检查输入参数及必要信息
    if (!parser || !pkt || !ctx)
        return need;
    if (parser->width <= 0 || parser->height <= 0)
        return need;

    // 获取 extract_extradata BSF
    const AVBitStreamFilter* bsf = av_bsf_get_by_name("extract_extradata");
    if (!bsf) {
        LOG_DEBUG() << "extract_extradata BSF not found";
        return need;
    }

    AVBSFContext* bsf_ctx = nullptr;
    auto          ret     = av_bsf_alloc(bsf, &bsf_ctx);
    if (ret < 0) {
        LOG_DEBUG() << "Failed to allocate BSF context, ret = " << ret;
        return need;
    }

    // 设置 BSF 的输入参数(使用 parser 的部分信息)
    bsf_ctx->par_in->codec_id   = (AVCodecID) parser->parser->codec_ids[0];
    bsf_ctx->par_in->codec_type = AVMEDIA_TYPE_VIDEO;
    bsf_ctx->par_in->width      = parser->width;
    bsf_ctx->par_in->height     = parser->height;

    ret = av_bsf_init(bsf_ctx);
    if (ret < 0) {
        LOG_DEBUG() << "Failed to initialize BSF context, ret = " << ret;
        av_bsf_free(&bsf_ctx);
        return need;
    }

    // 将包送入 BSF 提取 extradata
    ret = av_bsf_send_packet(bsf_ctx, pkt);
    if (ret < 0) {
        LOG_DEBUG() << "Failed to send packet to BSF, ret = " << ret << printError(ret);
        av_bsf_free(&bsf_ctx);
        return need;
    }
    // 从 BSF 中取出过滤后的包
    ret = av_bsf_receive_packet(bsf_ctx, pkt);
    if (ret < 0 && ret != AVERROR(EAGAIN) && ret != AVERROR_EOF) {
        LOG_DEBUG() << "Failed to receive packet from BSF, ret = " << ret << printError(ret);
        av_bsf_free(&bsf_ctx);
        return need;
    }

    size_t   extradata_size = 0;
    uint8_t* side_extradata = av_packet_get_side_data(pkt,
    AV_PKT_DATA_NEW_EXTRADATA,
    &extradata_size);
    if (side_extradata && extradata_size > 0) {
        // 更新 extradata
        if (ctx->extradata_size != extradata_size) {
            if (ctx->extradata) {
                av_freep(&ctx->extradata);
                ctx->extradata_size = 0;
            }

            ctx->extradata = (uint8_t*) av_malloc(extradata_size + AV_INPUT_BUFFER_PADDING_SIZE);
            if (!ctx->extradata) {
                LOG_DEBUG() << "Failed to allocate memory for extradata";
                av_bsf_free(&bsf_ctx);
                return need;
            }
            ctx->extradata_size = extradata_size;

            memcpy(ctx->extradata, side_extradata, ctx->extradata_size);
            LOG_DEBUG() << "Extracted extradata: "
                << QByteArray((char*) ctx->extradata, ctx->extradata_size).toHex();
        }

        need = true;
    }

    av_bsf_free(&bsf_ctx);
    return need;
}
相关推荐
StudyWinter8 小时前
FFmpeg-chapter7和chapter8-使用 FFmpeg 解码视频(原理篇和实站篇)
ffmpeg·音视频
T风呤8 小时前
ffmpeg windows 基本命令
windows·ffmpeg
firstime_tzjz8 小时前
windows下使用msys2编译ffmpeg
windows·ffmpeg
AJi11 小时前
FFmpeg学习(五):音视频数据转换
ffmpeg·音视频开发·视频编码
挣扎与觉醒中的技术人16 小时前
OpenCV视频解码全流程详解
人工智能·深度学习·opencv·计算机视觉·ffmpeg·音视频
曦月合一1 天前
SSM架构 +Nginx+FFmpeg实现rtsp流转hls流,在前端html上实现视频播放
nginx·架构·ffmpeg·摄像头·实时预览
挣扎与觉醒中的技术人2 天前
OpenCV视频解码性能优化十连击(实测帧率提升300%)
人工智能·opencv·ffmpeg·音视频·实时音视频·视频编解码·外包转型
晨同学03272 天前
rv1126交叉编译opencv+ffmpeg+x264
人工智能·opencv·ffmpeg
执念斩长河2 天前
ffmpeg-go学习笔记
学习·golang·ffmpeg