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

來源:互聯網
上載者:User

FFmpeg原始碼簡單分析:avformat_close_input(),
本文簡單分析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);

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

最簡單的基於FFMPEG+SDL的視頻播放器 ver2 (採用SDL2.0)


函數呼叫歷程圖

函數的調用關係如所示。


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()可以參考文章:

FFmpeg原始碼簡單分析:記憶體的分配和釋放


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()函數分析完畢。


雷霄驊
leixiaohua1020@126.com
http://blog.csdn.net/leixiaohua1020

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.