You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从FFmpeg libav获取decode_slice_header错误作为返回值?

H264解码丢包后libav无法恢复的问题处理

我在MCU上使用libav v5.1解码H264视频流,通过WebRTC+RTP协议在互联网传输数据包。网络拥塞丢包时,libav会输出以下错误:

[h264 @ 0x7f8bd97f80c0] Frame num change from 77 to 7
[h264 @ 0x7f8bd97f80c0] decode_slice_header error
[h264 @ 0x7f8bd97f80c0] Frame num change from 77 to 8
[h264 @ 0x7f8bd97f80c0] decode_slice_header error
and etc...

网络恢复后不再丢包,但错误持续存在,解码器无法恢复,导致客户端视频冻结。我知道需要发送NACK或PLI,但没法拦截这些错误触发操作。查libav源码发现它只记录错误,不返回错误码。我检查了AVFrame->decode_error_flags(始终为0)和avcodec_receive_frame的返回值(出现错误时一直是AVERROR(EAGAIN)),是不是漏了什么?

相关代码

解码器初始化代码

//codec initialization
ctx->decoder = avcodec_find_decoder(AV_CODEC_ID_H264);
ctx->av_ctx = avcodec_alloc_context3(ctx->decoder);

if (ctx->decoder->capabilities & AV_CODEC_CAP_TRUNCATED)
    ctx->av_ctx->flags |= AV_CODEC_FLAG_TRUNCATED;

ctx->av_ctx->flags2 |= AV_CODEC_FLAG2_CHUNKS;

avcodec_open2(ctx->av_ctx, ctx->decoder, NULL);

解码逻辑代码

//actual decoding
ret = avcodec_send_packet(ctx->av_ctx, ctx->pkt);
if (ret < 0) {
    printf("err send pkt to decoding: %d\n", ret);
    return NULL;
}

while (ret >= 0) {
    ret = avcodec_receive_frame(ctx->av_ctx, ctx->dec_frame);
    if (ctx->dec_frame->decode_error_flags) {
        //never executed
        printf("decode error flags %d\n", ctx->dec_frame->decode_error_flags);
        fflush(stdout);
    }
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
        //need more pkts
        return ctx->scaled_frame;
    } else if (ret < 0) {
        //never executed
        printf("err recv pkt from decoding: %d\n", ret);
        return NULL;
    }
}

问题分析与解决思路

  1. 捕获libav错误日志
    libav默认将这类解码错误输出到stderr,你可以通过自定义日志回调捕获这些信息。注册av_log_set_callback,在回调中解析日志内容,当匹配到Frame num change或decode_slice_header error时,触发NACK/PLI请求。示例:

    static void custom_av_log(void* ptr, int level, const char* fmt, va_list vl) {
        char log_buf[1024];
        vsnprintf(log_buf, sizeof(log_buf), fmt, vl);
        if (strstr(log_buf, "Frame num change") != NULL || strstr(log_buf, "decode_slice_header error") != NULL) {
            // 触发PLI请求的逻辑,比如向发送端发送RTCP PLI
            send_pli_request();
        }
        // 保留原日志输出(可选)
        av_log_default_callback(ptr, level, fmt, vl);
    }
    
    // 初始化时注册回调
    av_log_set_callback(custom_av_log);
    
  2. 调整解码器参数增强错误恢复

    • 开启AV_CODEC_FLAG_GLOBAL_HEADER,确保解码器能正确跟踪帧序列:
      ctx->av_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
      
    • 通过av_opt_set设置H264解码器的错误隐藏参数,提升恢复能力:
      av_opt_set(ctx->av_ctx->priv_data, "error_concealment", "3", 0);
      
    • 当检测到连续错误时,主动调用avcodec_flush_buffers重置解码器状态,之后等待关键帧(PLI请求后获取)恢复解码。
  3. 修复解码逻辑漏洞
    你的解码循环中,avcodec_receive_frame返回AVERROR(EAGAIN)时直接返回scaled_frame,这可能导致客户端拿到旧帧或无效帧。正确逻辑应该是:返回NULL或标记需要更多数据,避免无效帧流出。修改后的解码循环:

    while (ret >= 0) {
        ret = avcodec_receive_frame(ctx->av_ctx, ctx->dec_frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            // 需要更多数据包,不返回帧
            return NULL;
        } else if (ret < 0) {
            printf("err recv pkt from decoding: %d\n", ret);
            // 重置解码器
            avcodec_flush_buffers(ctx->av_ctx);
            return NULL;
        }
        // 解码成功,处理并返回帧
        process_frame(ctx->dec_frame);
        return ctx->scaled_frame;
    }
    
  4. WebRTC层自动处理丢包
    既然使用WebRTC,可以依赖其自身的丢包重传机制。检查WebRTC配置,开启NACK和PLI自动请求,比如设置RtpRtcpParameters中的相关参数,让WebRTC栈自动处理丢包恢复,减少libav层的手动干预。

内容的提问来源于stack exchange,提问作者frussian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 10:43:27