【FFmpeg】HW 解码器销毁时,资源回收顺序

cpp 复制代码
s32 CFFMpegVidDecoder::Destroy()
{
	//在FFmpeg中,这些资源的释放确实有隐含的依赖关系,需要按照正确的顺序进行。
	//正确顺序:frame → packet → context → codec
	if (m_ptAVframe)
	{
		av_frame_free(&m_ptAVframe);
		m_ptAVframe = NULL;
	}

	if (m_ptAVsw_frame)
	{
		av_frame_free(&m_ptAVsw_frame);
		m_ptAVsw_frame = NULL;
	}

	if(m_ptPacket)
	{
		av_packet_free(&m_ptPacket);
		m_ptPacket = NULL;
	}

	if (m_pAVdecoder_ctx)
	{
		avcodec_free_context(&m_pAVdecoder_ctx);
		m_pAVdecoder_ctx = NULL;
	}

	if(m_pthw_device_ctx)
	{
		av_buffer_unref(&m_pthw_device_ctx);
		m_pthw_device_ctx = NULL;
	}

	m_ptAVdecoder = NULL;

	m_bCreate = FALSE;
	return 0;
}

硬解时,销毁解码器时,这些资源的释放有先后顺序要求吗?

在硬解场景下,资源释放的顺序更加重要,因为涉及到硬件设备的依赖关系。你的当前顺序基本正确,但可以进一步优化。

硬解资源释放的正确顺序

cpp 复制代码
s32 CFFMpegVidDecoder::Destroy()
{
    // 第1步:先释放软件帧 (与硬件资源无关)
    if (m_ptAVframe)
    {
        av_frame_free(&m_ptAVframe);
        m_ptAVframe = NULL;
    }

    if (m_ptAVsw_frame)
    {
        av_frame_free(&m_ptAVsw_frame);
        m_ptAVsw_frame = NULL;
    }

    // 第2步:释放数据包 (与硬件资源无关)
    if(m_ptPacket)
    {
        av_packet_free(&m_ptPacket);
        m_ptPacket = NULL;
    }

    // 第3步:关键步骤 - 先解除硬件设备与解码器的关联
    if (m_pAVdecoder_ctx && m_pAVdecoder_ctx->hw_device_ctx)
    {
        // 重要:在释放解码器上下文前,先解除硬件设备关联
        av_buffer_unref(&m_pAVdecoder_ctx->hw_device_ctx);
    }

    // 第4步:释放解码器上下文 (此时已解除硬件关联)
    if (m_pAVdecoder_ctx)
    {
        avcodec_free_context(&m_pAVdecoder_ctx);
        m_pAVdecoder_ctx = NULL;
    }

    // 第5步:最后释放硬件设备上下文
    if(m_pthw_device_ctx)
    {
        av_buffer_unref(&m_pthw_device_ctx);
        m_pthw_device_ctx = NULL;
    }

    // 第6步:清理编解码器指针 (静态数据,无需释放)
    m_ptAVdecoder = NULL;

    m_bCreate = FALSE;
    return 0;
}

为什么需要这个顺序?

硬件解码的特殊依赖关系

复制代码
硬件设备上下文 (m_pthw_device_ctx)
        ↑
        | 被引用
解码器上下文 (m_pAVdecoder_ctx) 
        ↑
        | 可能引用
帧数据 (m_ptAVframe, m_ptAVsw_frame)

关键点说明

  1. 先释放帧数据:避免帧引用已释放的硬件资源
  2. 解除硬件关联:在释放解码器前,必须先解除硬件设备引用
  3. 最后释放硬件设备:确保没有解码器再引用硬件资源

更安全的完整实现

cpp 复制代码
s32 CFFMpegVidDecoder::Destroy()
{
    // 步骤1:保存到局部变量并清空成员,避免重复释放
    AVFrame* frame_to_free = m_ptAVframe;
    AVFrame* sw_frame_to_free = m_ptAVsw_frame;
    AVPacket* packet_to_free = m_ptPacket;
    AVCodecContext* context_to_free = m_pAVdecoder_ctx;
    AVBufferRef* hw_device_to_free = m_pthw_device_ctx;
    
    // 立即清空成员变量
    m_ptAVframe = NULL;
    m_ptAVsw_frame = NULL;
    m_ptPacket = NULL;
    m_pAVdecoder_ctx = NULL;
    m_pthw_device_ctx = NULL;
    m_ptAVdecoder = NULL;
    m_bCreate = FALSE;

    // 步骤2:按正确顺序释放资源
    if (frame_to_free) {
        av_frame_free(&frame_to_free);
    }
    
    if (sw_frame_to_free) {
        av_frame_free(&sw_frame_to_free);
    }
    
    if (packet_to_free) {
        av_packet_free(&packet_to_free);
    }
    
    // 关键:先解除硬件关联
    if (context_to_free && context_to_free->hw_device_ctx) {
        av_buffer_unref(&context_to_free->hw_device_ctx);
    }
    
    if (context_to_free) {
        avcodec_free_context(&context_to_free);
    }
    
    // 最后释放硬件设备
    if (hw_device_to_free) {
        av_buffer_unref(&hw_device_to_free);
    }

    return 0;
}

硬件解码资源依赖图

复制代码
m_ptAVframe ─────┐
                │ 可能包含硬件帧
m_ptAVsw_frame ─┼─→ m_pAVdecoder_ctx ───→ m_pthw_device_ctx
                │        ↑
m_ptPacket ─────┘        │
                   hw_device_ctx (内部引用)

总结

正确的硬解释放顺序

  1. ✅ 帧数据 (AVFrame)
  2. ✅ 数据包 (AVPacket)
  3. 关键:解除解码器与硬件的关联
  4. ✅ 解码器上下文 (AVCodecContext)
  5. ✅ 硬件设备上下文 (AVBufferRef)
  6. ✅ 编解码器指针 (置NULL)

你的原始代码缺少了第3步解除硬件关联,这可能导致资源泄漏或程序崩溃。

相关推荐
daidaidaiyu13 小时前
FFmpeg 关键的结构体
c++·ffmpeg
扶尔魔ocy1 天前
【QT window】ffmpeg实现录音功能之无损格式--PCM
ffmpeg·pcm
止礼1 天前
FFmpeg8.0.1 源代码的深入分析
ffmpeg
小曾同学.com1 天前
音视频中的“透传”与“DTS音频”
ffmpeg·音视频·透传·dts
vivo互联网技术1 天前
数字人动画云端渲染方案
前端·ffmpeg·puppeteer·web3d
止礼1 天前
FFmpeg8.0.1 编解码流程
ffmpeg
qs70161 天前
c直接调用FFmpeg命令无法执行问题
c语言·开发语言·ffmpeg
止礼1 天前
FFmpeg8.0.1 Mac环境 CMake本地调试配置
macos·ffmpeg
简鹿视频2 天前
视频转mp4格式具体作步骤
ffmpeg·php·音视频·实时音视频