[ffmpeg] 濾波格式協商

ffmpeg的中濾波器是以幀爲原料來進行濾波的,那麼天然地就會對幀的格式有所要求,能夠說若是濾波器不知道幀的格式,就沒法對幀進行處理。在進行視頻濾波時,濾波格式指的是視頻的像素格式;在進行音頻濾波時,濾波格式包括音頻採樣格式,採樣率以及通道數目。html

 

承擔協商任務的結構體AVFilterLink

濾波器可能只支持某一種幀格式,也有可能對全部的幀格式都支持,所以在執行濾波操做以前,有必要對濾波過程當中的各個濾波器所支持的格式進行協商。函數

相鄰的兩個濾波實例之間是由AVFilterLink來鏈接的,所以AVFilterLink也承擔了協商相鄰兩個濾波實例的濾波格式的任務。AVFilterLink的結構體中與濾波格式相關以下的變量以下:ui

struct AVFilterLink {
    /*format negotiation valuables*/
    uint64_t channel_layout;
    int sample_rate;
    int format;

    AVFilterFormats *in_formats;
    AVFilterFormats *out_formats;
    AVFilterFormats  *in_samplerates;
    AVFilterFormats *out_samplerates;
    struct AVFilterChannelLayouts  *in_channel_layouts;
    struct AVFilterChannelLayouts *out_channel_layouts;
}

其中,in_formats/in_samplerates/in_channel_layouts、out_formats/out_samplerates/out_channel_layouts分別爲當前Link輸入端濾波器支持的輸出濾波格式以及輸出端濾波器支持的輸入濾波格式,以Link的視點來講,分別就是可能的輸入(因此變量被命名爲in)格式以及可能的輸出格式(因此變量被命名爲out)。而format/sample_rate/channel_layout就是通過協商後得出惟一的格式。this

image

 

 

濾波格式協商

濾波格式的協商分爲如下幾個步驟:lua

  1. 設置全部AVFilterLink上的格式列表
  2. 若是AVFilterLink的輸入輸出格式列表中有相同的格式,則提取相同格式做爲新的輸入以及輸出格式
  3. 不然代表須要進行格式轉換
  4. 提取合併後的格式列表中的某個格式做爲最終格式

 

設置格式列表

濾波格式的協商是在AVFilterLink上執行的,所以濾波器須要向AVFilterLink代表本身所支持的輸入以及輸出格式,即由AVFilter來設置它周圍的AVFilterLink上的格式列表。這個設置的操做是由AVFilter內的query_formats函數來實現的。指針

image

query_formats是AVFilter中的一個回調函數,若是某個濾波器對輸入以及輸出格式有要求或者限制,則須要經過query_formats來設置輸入link的out_formats以及輸出link的in_formats。若是濾波器不實現query_formats,則代表該濾波器的輸入輸出默認支持全部格式。orm

    for (i = 0; i < graph->nb_filters; i++) {
        AVFilterContext *f = graph->filters[i];
        if (formats_declared(f))
            continue;
        if (f->filter->query_formats)
            ret = filter_query_formats(f);
        else
            ret = ff_default_query_formats(f);
        if (ret < 0 && ret != AVERROR(EAGAIN))
            return ret;
        /* note: EAGAIN could indicate a partial success, not counted yet */
        count_queried += ret >= 0;
    }

 

AVFilterLink的格式合併

在設置完graph上全部link的in_formats以及out_formats後,就須要提取每一個link上in_formats與out_formats的共同formats,咱們稱這一步驟爲合併(merge)。視頻

在討論怎麼merge formats以前,咱們須要先了解AVFilterFormats這一結構體。AVFilterFormats就是咱們前文所說的格式列表,定義以下:htm

struct AVFilterFormats {
    unsigned nb_formats;        ///< number of formats
    int *formats;               ///< list of media formats

    unsigned refcount;          ///< number of references to this list
    struct AVFilterFormats ***refs; ///< references to this list
};

nb_formats是列表中format的數目;formats是指向format列表的指針;refcount表明本列表被引用的次數;refs指向一個列表,該列表中存放的是引用了本列表的地方的地址。blog

image

這裏的合併,是要從AVFilterLink的in_formats以及out_formats中挑出相同的format,而後組合成新的一個AVFilterFormats。

image

在實際濾波過程當中,咱們須要經過名爲buffersrc的濾波器輸入幀,而buffersrc在進行初始化的時候須要指定惟一的幀格式,即位於整個濾波圖頭部的是一個format個數爲1的AVFilterFormats。而且,對於不少濾波器來講,所支持的輸入輸出格式是同樣的,所以位於濾波器輸入端link的out_formats與輸出端link的in_formats會指向同一個AVFilterFormats。出於這兩個緣由,在循環地對graph上的AVFilterLink進行merge的時候,很容易地就能使得整個graph上全部的link中的in_formats與out_formats都指向同一個AVFilterFormats,而且其中含有惟一一個format就是輸入幀的format。

image

如上圖就是filter的輸入與輸出端支持一樣格式,所以會把輸入link的out_formats以及輸出link的in_formats指向同一AVFilterFormats。不過若是filter兩端支持不一樣的格式,則表示該filter內可能對幀的格式進行了轉換。

 

自動格式轉換

若是AVFilterLink的in_formats與out_formats中不含有相同format,就代表須要進行格式轉換。出現這種狀況的時候ffmpeg會在該link上插入一個用於格式轉換的濾波器,進行視頻像素格式轉換的濾波器名爲scale,進行音頻採樣格式轉換的濾波器名爲aresample。

if (convert_needed) {
    switch (link->type) {
    case AVMEDIA_TYPE_VIDEO:
        filter = avfilter_get_by_name("scale");
        avfilter_graph_create_filter(&convert, filter,
                                        inst_name, graph->scale_sws_opts, NULL,
                                        graph);
        break;
    case AVMEDIA_TYPE_AUDIO:
        filter = avfilter_get_by_name("aresample");
        avfilter_graph_create_filter(&convert, filter,
                                            inst_name, graph->aresample_swr_opts,
                                            NULL, graph);
        break;
    default:
        return AVERROR(EINVAL);
    }

    if ((ret = avfilter_insert_filter(link, convert, 0, 0)) < 0)
        return ret;

    if ((ret = filter_query_formats(convert)) < 0)
        return ret;
    if (!ff_merge_formats( inlink->in_formats,  inlink->out_formats,  inlink->type) ||
        !ff_merge_formats(outlink->in_formats, outlink->out_formats, outlink->type))
        ret = AVERROR(ENOSYS);

插入格式轉換濾波器包含如下步驟:

  1. 插入格式轉換濾波器的函數avfilter_insert_filter會把原來的link的出口鏈接到格式轉換濾波器的入口上,而後用新的link鏈接格式轉換濾波器的出口以及源link原來的目標端口。最後還會把源link的out_formats移給新link的out_formats。
  2. filter_query_formats則會調用格式轉換濾波器的query_formats函數來設置其兩端的out_formats以及in_formats,因爲此時咱們通常不會爲其設置任何參數,所以此時格式轉換濾波器兩端的out_formats以及in_formats會支持全部格式。
  3. 最後的ff_merge_formats把涉及到的這兩個link上的格式進行merge,這樣就使得格式轉換濾波器兩端的out_formats以及in_formats設定完畢。

image

 

在獲得格式轉換濾波器兩邊的兩個link的最終格式後,會經過調用格式轉換濾波器的config_props函數來進行轉換的初始化,初始化時的輸入參數就是這兩個link的最終格式。

static int config_output(AVFilterLink *outlink)
{
    AVFilterContext *ctx = outlink->src;
    AVFilterLink *inlink = ctx->inputs[0];

    aresample->swr = swr_alloc_set_opts(aresample->swr,
                                        outlink->channel_layout, outlink->format, outlink->sample_rate,
                                        inlink->channel_layout, inlink->format, inlink->sample_rate,
                                        0, ctx);
}

 

 

提取最終格式

通過前面的流程,已經能保證link上的in_formats與out_formats是merge過的了,下面處理同一個濾波器的輸入以及輸出link之間的格式問題。

儘管咱們前面說過,對於內部不會進行格式轉換的濾波器,一般其輸入輸出會支持相同的格式,所以輸入link的out_formats與輸出link的in_formats通常來講都是指向同一個AVFilterFormats,不過濾波器多種多樣,也有可能會出現明明能夠不用格式轉換,輸入link的out_formats卻與輸出link的in_formats卻指向不一樣AVFilterFormats。爲了防止濾波器作沒必要要的格式轉換,有如下處理方式:一旦發現輸入link的out_formats當中只有惟一一個格式,而且輸出link的in_formats當中包含該格式,則會把該格式移動到in_formats[0],並把格式數目設置爲1。

image

 

在選取link的最終格式時,通常來講link的格式列表中只有一個格式,所以會直接選則這一個格式。

static int pick_format(AVFilterLink *link, AVFilterLink *ref)
{
    link->in_formats->nb_formats = 1;
    link->format = link->in_formats->formats[0];
}

 

不過也存在特殊狀況:若是濾波器支持不一樣於源格式的多種輸出格式,可是用戶並無指定具體的輸出格式,那麼應該根據源格式從當前列表中選擇最優的輸出格式。

image

相關文章
相關標籤/搜索