1. 程式人生 > >FFmpeg原始碼簡單分析:avformat_close_input()

FFmpeg原始碼簡單分析:avformat_close_input()

=====================================================

FFmpeg的庫函式原始碼分析文章列表:

【架構圖】

【通用】

【解碼】

【編碼】

【其它】

【指令碼】

【H.264】

=====================================================


本文簡單分析FFmpeg的avformat_close_input()函式。該函式用於關閉一個AVFormatContext,一般情況下是和avformat_open_input()成對使用的。


avformat_close_input()的宣告位於libavformat\avformat.h,如下所示。
/**
 * Close an opened input AVFormatContext. Free it and all its contents
 * and set *s to NULL.
 */
void avformat_close_input(AVFormatContext **s);

該函式最典型的例子可以參考:


函式呼叫關係圖

函式的呼叫關係如下圖所示。


avformat_close_input()

下面看一下avformat_close_input()的原始碼,位於libavformat\utils.c檔案中。
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;

    flush_packet_queue(s);

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

    avformat_free_context(s);

    *ps = NULL;

    avio_close(pb);
}

從原始碼中可以看出,avformat_close_input()主要做了以下幾步工作:
(1)呼叫AVInputFormat的read_close()方法關閉輸入流
(2)呼叫avformat_free_context()釋放AVFormatContext
(3)呼叫avio_close()關閉並且釋放AVIOContext
下面我們分別來看上述幾個步驟。

AVInputFormat-> read_close()

AVInputFormat的read_close()是一個函式指標,指向關閉輸入流的函式。不同的AVInputFormat包含有不同的read_close()方法。例如,FLV格式對應的AVInputFormat的定義如下。
AVInputFormat ff_flv_demuxer = {
    .name           = "flv",
    .long_name      = NULL_IF_CONFIG_SMALL("FLV (Flash Video)"),
    .priv_data_size = sizeof(FLVContext),
    .read_probe     = flv_probe,
    .read_header    = flv_read_header,
    .read_packet    = flv_read_packet,
    .read_seek      = flv_read_seek,
    .read_close     = flv_read_close,
    .extensions     = "flv",
    .priv_class     = &flv_class,
};

從ff_flv_demuxer的定義中可以看出,read_close()指向的函式是flv_read_close()。我們可以看一下flv_read_close()的定義,如下所示。
static int flv_read_close(AVFormatContext *s)
{
    int i;
    FLVContext *flv = s->priv_data;
    for (i=0; i<FLV_STREAM_TYPE_NB; i++)
        av_freep(&flv->new_extradata[i]);
    return 0;
}

從flv_read_close()的定義可以看出,該函式釋放了FLVContext中的new_extradata陣列中每個元素指向的記憶體。

avformat_free_context()

avformat_free_context()是一個FFmpeg的API函式,用於釋放一個AVFormatContext。在這裡要注意搞清楚avformat_free_context()和avformat_close_input()之間的區別與聯絡。

有關avformat_free_context()可以參考文章:


avio_close()

avio_close()是一個FFmpeg的API函式,用於關閉和釋放AVIOContext。它的宣告位於libavformat\avio.h,如下所示。

/**
 * Close the resource accessed by the AVIOContext s and free it.
 * This function can only be used if s was opened by avio_open().
 *
 * The internal buffer is automatically flushed before closing the
 * resource.
 *
 * @return 0 on success, an AVERROR < 0 on error.
 * @see avio_closep
 */
int avio_close(AVIOContext *s);

avio_close()的定義位於libavformat\aviobuf.c,如下所示。
int avio_close(AVIOContext *s)
{
    URLContext *h;

    if (!s)
        return 0;

    avio_flush(s);
    h = s->opaque;
    av_freep(&s->buffer);
    if (s->write_flag)
        av_log(s, AV_LOG_DEBUG, "Statistics: %d seeks, %d writeouts\n", s->seek_count, s->writeout_count);
    else
        av_log(s, AV_LOG_DEBUG, "Statistics: %"PRId64" bytes read, %d seeks\n", s->bytes_read, s->seek_count);
    av_free(s);
    return ffurl_close(h);
}

從原始碼可以看出,avio_close()按照順序做了以下幾個步驟:
(1)呼叫avio_flush()強制清除快取中的資料
(2)呼叫av_freep()釋放掉AVIOContext種的buffer
(3)呼叫av_free()釋放掉AVIOContext結構體
(4)呼叫ffurl_close()關閉並且釋放掉URLContext

下面按照順序分別看看avio_flush()和ffurl_close()這兩個函式。

avio_flush()

avio_flush()是一個FFmpeg的API函式,宣告位於libavformat\avio.h,如下所示。
/**
 * Force flushing of buffered data.
 *
 * For write streams, force the buffered data to be immediately written to the output,
 * without to wait to fill the internal buffer.
 *
 * For read streams, discard all currently buffered data, and advance the
 * reported file position to that of the underlying stream. This does not
 * read new data, and does not perform any seeks.
 */
void avio_flush(AVIOContext *s);

avio_flush()的定義位於libavformat\aviobuf.c,如下所示。
void avio_flush(AVIOContext *s)
{
    flush_buffer(s);
    s->must_flush = 0;
}

可以看出avio_flush()簡單呼叫了flush_buffer()函式。我們看一下flush_buffer()的定義。
static void flush_buffer(AVIOContext *s)
{
    if (s->write_flag && s->buf_ptr > s->buffer) {
        writeout(s, s->buffer, s->buf_ptr - s->buffer);
        if (s->update_checksum) {
            s->checksum     = s->update_checksum(s->checksum, s->checksum_ptr,
                                                 s->buf_ptr - s->checksum_ptr);
            s->checksum_ptr = s->buffer;
        }
    }
    s->buf_ptr = s->buffer;
    if (!s->write_flag)
        s->buf_end = s->buffer;
}

從flush_buffer()定義我們可以看出,該函式將當前快取指標buf_ptr的位置重新設定到快取buffer的首部,然後根據AVIOContext對應的流是否可寫分別做不同的處理。如果AVIOContext對應的流是隻讀的(write_flag取值為0),就將快取的尾部buf_end設定到快取首部位置;如果AVIOContext對應的流如果是可寫的(write_flag取值非0),則會呼叫writeout()函式輸出快取中剩餘的資料。
在這裡我們看一下writeout()函式的定義,如下所示。
static void writeout(AVIOContext *s, const uint8_t *data, int len)
{
    if (s->write_packet && !s->error) {
        int ret = s->write_packet(s->opaque, (uint8_t *)data, len);
        if (ret < 0) {
            s->error = ret;
        }
    }
    s->writeout_count ++;
    s->pos += len;
}

從定義可以看出,writeout()呼叫了AVIOContext的write_packet()方法。根據此前文章《FFmpeg原始碼簡單分析:avio_open2()》中的分析我們可以瞭解到,AVIOContext的write_packet()實際指向了ffurl_write()函式,而ffurl_write()經過retry_transfer_wrapper()函式最終呼叫了URLProtocol的url_write()函式。url_write()是一個函式指標,不同的URLProtocol的url_write()指向不同的函式。

例如,file(檔案)對應的URLProtocol的定義位於libavformat\file.c,如下所示。

URLProtocol ff_file_protocol = {
    .name                = "file",
    .url_open            = file_open,
    .url_read            = file_read,
    .url_write           = file_write,
    .url_seek            = file_seek,
    .url_close           = file_close,
    .url_get_file_handle = file_get_handle,
    .url_check           = file_check,
    .priv_data_size      = sizeof(FileContext),
    .priv_data_class     = &file_class,
};

可以看出ff_file_protocol中的url_write()指向的是file_write()函式。我們繼續看一下file_write()的原始碼,如下所示。
static int file_write(URLContext *h, const unsigned char *buf, int size)
{
    FileContext *c = h->priv_data;
    int r;
    size = FFMIN(size, c->blocksize);
    r = write(c->fd, buf, size);
    return (-1 == r)?AVERROR(errno):r;
}

從原始碼中可以看出file_write()呼叫了系統的write()方法向檔案中寫資料(很多人可能對write()函式很陌生,可以簡單理解為它等同於fwrite())。


ffurl_close()和ffurl_closep()

ffurl_close()和ffurl_closep()是FFmpeg內部的兩個函式,它們的宣告位於libavformat\url.h,如下所示。
/**
 * Close the resource accessed by the URLContext h, and free the
 * memory used by it. Also set the URLContext pointer to NULL.
 *
 * @return a negative value if an error condition occurred, 0
 * otherwise
 */
int ffurl_closep(URLContext **h);
int ffurl_close(URLContext *h);

其實這兩個函式是等同的。ffurl_close()的定義位於libavformat\avio.c,如下所示。
int ffurl_close(URLContext *h)
{
    return ffurl_closep(&h);
}

可見ffurl_close()呼叫了ffurl_closep()。
ffurl_closep()的定義如下所示。
int ffurl_closep(URLContext **hh)
{
    URLContext *h= *hh;
    int ret = 0;
    if (!h)
        return 0;     /* can happen when ffurl_open fails */

    if (h->is_connected && h->prot->url_close)
        ret = h->prot->url_close(h);
#if CONFIG_NETWORK
    if (h->prot->flags & URL_PROTOCOL_FLAG_NETWORK)
        ff_network_close();
#endif
    if (h->prot->priv_data_size) {
        if (h->prot->priv_data_class)
            av_opt_free(h->priv_data);
        av_freep(&h->priv_data);
    }
    av_freep(hh);
    return ret;
}

從ffurl_closep()的定義可以看出,它主要做了兩步工作:
(1)呼叫URLProtocol的url_close()
(2)呼叫av_freep()釋放URLContext結構體
其中URLProtocol的url_close()是一個函式指標,其指向的函式與具體的URLProtocol有關,這裡我們還是看一下file(檔案)對應的URLProtocol,如下所示。
URLProtocol ff_file_protocol = {
    .name                = "file",
    .url_open            = file_open,
    .url_read            = file_read,
    .url_write           = file_write,
    .url_seek            = file_seek,
    .url_close           = file_close,
    .url_get_file_handle = file_get_handle,
    .url_check           = file_check,
    .priv_data_size      = sizeof(FileContext),
    .priv_data_class     = &file_class,
};

從ff_file_protocol中可以看出,url_close()指向file_close()函式。我們再看一下file_close()的定義,如下所示。
static int file_close(URLContext *h)
{
    FileContext *c = h->priv_data;
    return close(c->fd);
}

可見file_close()最終呼叫了系統函式close()關閉了檔案指標(不熟悉close()的可以簡單把它理解為fclose())。

至此avio_close()函式分析完畢。

雷霄驊
[email protected]
http://blog.csdn.net/leixiaohua1020

相關推薦

FFmpeg原始碼簡單分析avformat_close_input()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

ffmpeg 原始碼簡單分析 av_read_frame()

此前寫了好幾篇ffmpeg原始碼分析文章,列表如下: ============================ ffmpeg中的av_read_frame()的作用是讀取碼流中的音訊若干幀或者視訊一幀。例如,解碼視訊的時

ffmpeg 原始碼簡單分析 av_register_all()

此前寫了好幾篇ffmpeg原始碼分析文章,列表如下: ============================ 前一陣子看了一下ffmpeg的原始碼,並且做了一些註釋,在此貼出來以作備忘。 本文分析一下ffmpeg註冊

FFmpeg原始碼簡單分析makefile

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析結構體成員管理系統-AVClass

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析avcodec_open2()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析av_write_frame()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析常見結構體的初始化和銷燬(AVFormatContext,AVFrame等)

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析記憶體的分配和釋放(av_malloc()、av_free()等)

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析avcodec_encode_video()

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg原始碼簡單分析configure

=====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】================================

FFmpeg的H.264解碼器原始碼簡單分析概述

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

FFmpeg的HEVC解碼器原始碼簡單分析CTU解碼(CTU Decode)部分-TU

=====================================================HEVC原始碼分析文章列表:【解碼 -libavcodec HEVC 解碼器】==============================================

FFmpeg的H.264解碼器原始碼簡單分析解碼器主幹部分

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

FFmpeg的H.264解碼器原始碼簡單分析熵解碼(Entropy Decoding)部分

=====================================================H.264原始碼分析文章列表:【編碼 - x264】【解碼 - libavcodec H.264 解碼器】================================

H264編碼器5( x264原始碼簡單分析x264_slice_write() 與H264 編碼簡介)

  x264原始碼簡單分析:x264_slice_write() 來自:https://blog.csdn.net/leixiaohua1020/article/details/45536607     H264 編碼簡介 https://blo

H264編碼器4( x264原始碼簡單分析概述)

來自:https://blog.csdn.net/leixiaohua1020/article/details/45536607   ===================================================== H.264原始碼分析文章列表:

FFmpeg原始碼簡單分析 avio open2

                =====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】=================================

FFmpeg原始碼簡單分析 avcodec encode video

                =====================================================FFmpeg的庫函式原始碼分析文章列表:【架構圖】【通用】【解碼】【編碼】【其它】【指令碼】【H.264】=================================

FFmpeg源代碼簡單分析常見結構體的初始化和銷毀(AVFormatContext,AVFrame等)

new init _array border 代碼 alloc ecc .com VC 結構體 初始化 銷毀 AVFormatContext avformat_alloc_context() avfo