ffmpeg使用avformat_close_input()函数释放结构体时崩溃的问题

先看一下我调试时,发现程序崩溃的代码位置

//这是我的程序释放流上下文时的操作
if(m_pAvFormatContext)
{
    //释放视频解码器上下文
    if(m_iVideoStreamIndex >= 0)
    avcodec_free_context(&m_pVideoDecodeContext);    //此处是发生崩溃的根本原因

    //释放音频解码器上下文
    if(m_iAudioStreamIndex >= 0)
    avcodec_free_context(&m_pAudioDecodeContext);    //此处是发生崩溃的根本原因

    //释放文件流上下文(发生崩溃)
    avformat_close_input(&m_pAvFormatContext);
    avformat_free_context(m_pAvFormatContext);
    m_pAvFormatContext = YNULL;
}

1. 在一次释放文件流上下文的调试中,发现调用avformat_close_input函数时程序崩溃退出。查看函数内部

功能

  • Close an opened input AVFormatContext.(关闭打开的输入AVFormatContext)
  • Free it and all its contents and set s to NULL.(释放它和它的所有内容,并设置为NULL)
void avformat_close_input(AVFormatContext **ps)
{
   AVFormatContext *s;
   AVIOContext *pb;

   if (!ps || !*ps)
       return;

   s  = *ps;
   pb = s->pb;

   if ((s->iformat && strcmp(s->iformat->name, "image2") && s->iformat->flags & AVFMT_NOFILE) ||
       (s->flags & AVFMT_FLAG_CUSTOM_IO))
       pb = NULL;

   if (s->iformat)
       if (s->iformat->read_close)
           s->iformat->read_close(s);

   avformat_free_context(s);

   *ps = NULL;

   avio_close(pb);
}

2. 经过排查,发现是在内部调用avformat_free_context()函数时崩溃了, 查看avformat_free_context源码

void avformat_free_context(AVFormatContext *s)
{
   FFFormatContext *si;

   if (!s)
       return;
   si = ffformatcontext(s);

   if (s->oformat && s->oformat->deinit && si->initialized)
       s->oformat->deinit(s);

   av_opt_free(s);
   if (s->iformat && s->iformat->priv_class && s->priv_data)
       av_opt_free(s->priv_data);
   if (s->oformat && s->oformat->priv_class && s->priv_data)
       av_opt_free(s->priv_data);

   for (unsigned i = 0; i < s->nb_streams; i++)
       ff_free_stream(&s->streams[i]);
   s->nb_streams = 0;

   for (unsigned i = 0; i < s->nb_programs; i++) {
       av_dict_free(&s->programs[i]->metadata);
       av_freep(&s->programs[i]->stream_index);
       av_freep(&s->programs[i]);
   }
   s->nb_programs = 0;

   av_freep(&s->programs);
   av_freep(&s->priv_data);
   while (s->nb_chapters--) {
       av_dict_free(&s->chapters[s->nb_chapters]->metadata);
       av_freep(&s->chapters[s->nb_chapters]);
   }
   av_freep(&s->chapters);
   av_dict_free(&s->metadata);
   av_dict_free(&si->id3v2_meta);
   av_packet_free(&si->pkt);
   av_packet_free(&si->parse_pkt);
   av_freep(&s->streams);
   ff_flush_packet_queue(s);
   av_freep(&s->url);
   av_free(s);
}

初步发现是在avformat_free_context内部的av_packet_free(&si->parse_pkt)处发生崩溃错误。为什么在释放这个参数时会崩溃?由于这些函数内部的参数太多且不好理解,我没有深究下去

3. 百度"调用avformat_free_context崩溃",发现一篇博主文章 [ 关于avformat_free_context释放流上下文的一些问题 ]里面说到一句话:

//下面是释放上下文的步骤顺序
sws_freeContext(pSwsContext);               
av_frame_free(&pAVFrame);
avcodec_close(pAVCodecContext);
avformat_close_input(&pAVFormatContext);
  • 释放顺序不能错,如果想关闭一个取流地址不能单独调用avformat_close_input(&pAVFormatContext);

  • 因为调用avformat_close_input释放文件流上下文时,如果里面的一些结构体在之前没有被提前释放掉,会导致程序崩溃。

4. 打开代码调试,查看调用顺序,经过一番查找后,发现没有调用顺序没有问题。但是有一个地方是跟上面代码不同的地方,是释放解码器上下文时,我使用到新的API接口avcodec_free_context而不是avcodec_close,于是尝试换成avcodec_close重新运行,发现没有问题了!

//这是我的程序释放流上下文时的操作
if(m_pAvFormatContext)
{
    //释放视频解码器上下文
    if(m_iVideoStreamIndex >= 0)
    avcodec_close(&m_pVideoDecodeContext);    //修改此处则可以正常运行

    //释放音频解码器上下文
    if(m_iAudioStreamIndex >= 0)
    avcodec_close(&m_pAudioDecodeContext);    //修改此处则可以正常运行

    //释放文件流上下文
    avformat_close_input(&m_pAvFormatContext);
    avformat_free_context(m_pAvFormatContext);
    m_pAvFormatContext = YNULL;
}

5. 去ffmpeg官网查看函数解析

AVCodecContext* avcodec_alloc_context3 (const AVCodec * codec)

功能

  • 分配一个AVCodecContext,并将其字段设置为默认值。
  • 结果由avcodec_free_context()释放

参数

  • 如果非null,分配私有数据并初始化给定编解码器的默认值。使用不同的编解码器调用avcodec_open2()是非法的。
  • 如果为NULL,那么特定于编解码器的默认值将不会被初始化,这可能会导致默认设置不理想(这对于编码器(例如libx264)尤为重要)。

返回值

  • 一个用默认值填充的AVCodecContext,失败时返回NULL。
//通过传入的音频、视频编码器名字查找编码器
AVCodec * codec = avcodec_find_decoder(AV_CODEC_ID_H264);
if (!codec)
    exit(1);

//分配音频、视频编码器上下文,并初始化
AVCodecContext * context = avcodec_alloc_context3(codec);
if (avcodec_open2(context, codec, YNULL) < 0)
    exit(1);

//释放音频、视频解码器上下文
avcodec_free_context(c);

avcodec_open2函数介绍:
int avcodec_open2(AVCodecContext *avctx, const AVCodec *codec, AVDictionary **options);

功能

  • 初始化AVCodecContext以使用给定的AVCodec
  • 在使用此函数之前,必须为上下文分配avcodec_alloc_context3()

6. 我使用另外一种方式去分配解码器上下文,则使用avcodec_close()释放解码器上下文

//分配视频解码器上下文
AVCodecContext * pVideoDecodeContext = YNULL;
pVideoDecodeContext = m_pAvFormatContext->streams[m_iVideoStreamIndex]->codec;

//分配音频解码器上下文
AVCodecContext * pAudioDecodeContext = YNULL;
pAudioDecodeContext= m_pAvFormatContext->streams[m_iAudioStreamIndex]->codec;

//初始化解码器
AVCodec * pVideoCodec = YNULL;
pVideoCodec = avcodec_find_decoder_by_name("h264_rkmpp_dec");   //arm的显示卡

avcodec_open2(m_pVideoDecodeContext, pVideoCodec, YNULL)

//释放音频、视频解码器
avcodec_close(m_pVideoDecodeContext);

综上所述,是因为不正常释放解码器上下文导致的崩溃。

posted @ 2022-12-19 09:49  程序员没有头发  阅读(1358)  评论(0编辑  收藏  举报