【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步解除硬件关联,这可能导致资源泄漏或程序崩溃。

相关推荐
FlightYe1 天前
视界原理之3D视频(四):裸眼、眼镜和头显
android·linux·c++·3d·ffmpeg·音视频
Android系统攻城狮2 天前
Linux FFmpeg深度解析之avcodec_default_get_buffer2调用流程与实战(二十)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
Android系统攻城狮2 天前
Linux FFmpeg深度解析之av_parser_init调用流程与实战(二十一)
linux·运维·服务器·ffmpeg·音视频进阶·ffmpeg音视频
Android系统攻城狮2 天前
Linux FFmpeg深度解析之avcodec_flush_buffers调用流程与实战(十九)
linux·运维·服务器·ffmpeg·车载音视频·ffmpeg音视频进阶
重生之我是萧炎3 天前
ffmpeg官方下载安装教程(提供安装包)
ffmpeg
zslefour3 天前
使用ffmpeg遍历获取音频文件相关信息
ffmpeg
Android系统攻城狮3 天前
Linux FFmpeg深度解析之avcodec_parameters_from_context调用流程与实战(十六)
linux·运维·ffmpeg·音视频进阶·ffmpeg音视频·车载音视频进阶
Android系统攻城狮3 天前
Linux FFmpeg深度解析之avcodec_alloc_context3调用流程与实战(十四)
linux·运维·ffmpeg·车载音视频·音视频进阶·ffmpeg音视频进阶
菜鸟小芯4 天前
【Quectel Pi M2 实战】Day3 USB 摄像头图像采集:拍照、录像与踩坑记录(V4L2 + FFmpeg)
ffmpeg·嵌入式
Android系统攻城狮5 天前
Linux FFmpeg深度解析之av_dump_format调用流程与实战(十一)
linux·运维·服务器·ffmpeg·1024程序员节·音视频进阶