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;
}
相关推荐
追随远方11 小时前
Android平台FFmpeg音视频开发深度指南
android·ffmpeg·音视频
charlie1145141912 天前
编译日志:关于编译opencv带有ffmpeg视频解码支持的若干办法
opencv·ffmpeg·音视频·imx6ull·移植教程
追随远方2 天前
Android平台FFmpeg视频解码全流程指南
android·ffmpeg·音视频
urhero3 天前
Python+ffmpeg 实现给视频添加字幕
ffmpeg·音视频·python编程·视频编辑·实用视频工具·添加字幕
追随远方3 天前
Android NDK版本迭代与FFmpeg交叉编译完全指南
android·ffmpeg
带鱼工作室3 天前
ffmpeg多媒体(音视频)处理常用命令
ffmpeg·音视频
批量小王子4 天前
2025-05-07-FFmpeg视频裁剪(尺寸调整,画面比例不变)
ffmpeg·音视频
人间花木5 天前
1. 视频基础知识
c++·ffmpeg·音视频·视频基础知识
追随远方6 天前
详解 FFMPEG 交叉编译 `FLAGS` 和 `INCLUDES` 的作用
android·ffmpeg·myeclipse·音频编解码
LDM>W<7 天前
Easy云盘总结篇-文件上传01
java·spring boot·学习·ffmpeg