FFmpeg filter 개발

2023. 9. 20. 11:59Multimedia

    목차
반응형

1. FFmpeg filtrer ?

FFmpeg의 filter들은 libavfilter library를 통해 제공됩니다.

하나의 filter는

  • 여러 input들을 가질 수 있으며,
  • 여러 output들을 가질 수 있습니다.

fitler graph

simple filtergraph

  • simple filtergraph: 하나의 input과 output을 지닌 filter들로 구성된 filter graph

사용 방법
-filter
-vf는 video filter
-af는 audio filter를 의미

complex filtergraph

여러 입력과 출력을 지닌 filter로 구성된 filter graph 입니다.
-filter_complex option을 사용합니다.
-lavfi 는 -filter_complex와 같은 의미 입니다.

overlay example

ffmpeg -i input -i logo -filter_complex 'overlay=10:main_h-overlay_h-10' output

filter usage ex.

다음과 같은 graph가 있다고 해 보겠습니다. (출처 https://ffmpeg.org/ffmpeg-filters.html)

                [main]
input --> split ---------------------> overlay --> output
            |                             ^
            |[tmp]                  [flip]|
            +-----> crop --> vflip -------+

input을 두 개의 stream으로 split 합니다. 이후 하나는 crop filter로 보내고 vflip을 수행합니다. 다른 하나는 바로 overlay로 전달됩니다.
위와 같이 filter를 연결해서 실행하려면 다음과 같이 ffmpeg을 실행하면 됩니다.

ffmpeg -i INPUT -vf "split [main][tmp]; [tmp] crop=iw:ih/2:0:0, vflip [flip]; [main][flip] overlay=0:H/2" OUTPUT
  • filter들은 comma로 구분되는 chain 형태로 연결됩니다.
  • 또한 교유한 linear chain들은 semicolon으로 구분됩니다.
  • 위 예에서는 split filter가 2개의 output을 만들고 각각을 [main]과 [tmp]로 label을 지정합니다.
  • split의 두 번째 output은 [tmp]로 label이 지정되며, 이는 crop filter로 들어가서 처리됩니다.
  • 일 부 filter는 여러 parameter들을 받을 수 있으며, 이는 = sign 이후에 이름으로 지정되며 각각의 parameter들은 colon으로 구분됩니다.

filter 구현 필요 함수

name desc.
init() filter의 초기화 과정을 수행합니다.
uninit() filter의 해제 과정을 수행합니다.
query_formats() filter가 지원하는 input의 format등의 정보를 전달합니다. 이는 filter negitiation에 사용됩니다.
config_props() filter 연결 시 filter의 property를 설정합니다.
filter_frame() push 모드로 동작할 시 data가 전달되는 함수 입니다.

2. 파일 추가 및 build 방법

libavfilter 하위에 filter 파일을 추가하면 됩니다. test filter를 추가해 보겠습니다.
우선 libavfilter 밑에 vf_test.c를 추가합니다.

이후 이를 build 하기 위해 libavfilter/Makefile을 열고 추가한 file이 빌드 될 수 있도록 다음과 같이 추가합니다.

JS-$(CONFIG_TEST_FILTER)                += vf_test.o

filter가 framework에서 사용될 수 있도록 Filter의 context에 대한 변수를 extern으로 선언합니다.
이는 filter의 entry로서 사용됩니다.

libavfilter/allfilters.c

extern const AVFilter ff_vf_test;

allfilters.c 파일은 configure script에서 parsing 되며, 이를통해 C와 build system을 위해 필요한 변수들을 정의합니다.
다음의 결과문들이 생성됩니다.

  • ffbuild/config.mak 파일에 다음의 line이 추가됩니다.
    • CONFIG_TEST_FILTER=yes
    • 이는 Makefile에서 사용됩니다.
  • config.h 파일에 다음의 정의가 추가됩니다.
    • #define CONFIG_TEST_FILTER 1
    • 이는 filter의 등록 시 사용됩니다.

./configure 수행 후 make 하여 빌드할 수 있으며, 빌드된 결과물은 libavfilter.a (혹은 .so) 파일로 packaging 됩니다.

3. filter 내용 추가

FFmpeg/doc/writing_filters.txt를 참고하여 filter를 작성할 수 있습니다.
위 document에서는 foobar filter를 작성하는 방법에 대해 설명합니다.
이 filter는 하나의 frame을 입력으로 받은 뒤 pixel을 변경합니다.

filter code 구조

libavfilter/vf_test.c를 열고 다음의 내용들을 추가합니다.

Copyright

우선 copyright를 추가합니다. 보통의 filter들은 모두 LGPL license를 따릅니다.

Doxy

Doxygen comment를 추가하고자 하는 경우 추가합니다.

Context

TestContext의 정의 부분입니다. user option같은 것을 전역의 정보로 담고자 하는데 사용됩니다.

Options

User가 사용 가능한 option을 지정합니다.
ex.
-vf test=mode=colormix:high=0.4:low=0.1

이름, 설명, offset, type, 기본값, 치ㅗ소값, 최대값, flags
보통은 위와같은 pattern을 따릅니다.

Class

AVFILTER_DEFINE_CLASS(test)
이는 고유한 test_class를 정의합니다.

Filter definitioin

file의 마지막 부분에서 test_inputs를 발견할 수 있습니다. test_options와 AVFilter ff_vf_test이며, 여기에 description을 update 해야 합니다.

Callbacks

libavfilter/avfilter.h에 callback들에 대한 설명이 있습니다.

  • init()
    • pre-compute data, buffer를 할당하고 초기화 하는 곳
    • 이니 user option들은 초기화 되어 있어야 함
    • 어떤 input을 받을지는 모르는 상태라 user option을 sanitize하는데 사용됨
  • uninit()
    • 할당한 모든 것을 해제하는 곳
  • query_formats()
    • init이후에 호출되어 format negotiation을 수행하는데 사용됨
    • input과 output pixel format을 지정
    • pixel format을 변경하지 않을 시 ff_set_common_formats()를 호출하면 됨
  • config_props()
    • 필수 callback은 아님
    • input과 output을 위한 callback
    • input config_props()에서는 query_formats() 이후 어떤 pixel format을 선택할지 지정
  • filter_frame()
    • 전달받은 frame을 처리하는 곳
    • input link에서 filter context를 얻을 수 있음
      • AVFilterContext *ctx = inlink->dst;
    • filter의 internal state context
      • TestContext *test = ctx->priv;
    • ouptut link
      • 여기로 farme을 전달
      • AVFilter *outlink = ctx->outputs[0];
      • [0]은 첫 번째 output을 선택한 경우임
    • 단순한 pass-through filter인 경우 다음과 같이 처리
      • return ff_filter_frame(outlink, in)

주요 parameters
frame->data[]
frame->linesize[]

    <-------------- linesize ------------------------>
    +-------------------------------+----------------+ ^
    |                               |                | |
    |                               |                | |
    |           picture             |    padding     | | height
    |                               |                | |
    |                               |                | |
    +-------------------------------+----------------+ v
    <----------- width ------------->

pixel format 변경 시 새롭게 buffer를 할당하여 처리해야 합니다.

    AVFrame *out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
    if (!out) {
        av_frame_free(&in);
        return AVERROR(ENOMEM);
    }
    av_frame_copy_props(out, in);

    // out->data[...] = test(in->data[...])

    av_frame_free(&in);
    return ff_filter_frame(outlink, out);

in-place 처리 시에는 다음과 같이 input buffer를 그대로 사용합니다.

    av_frame_make_writable(in);
    // in->data[...] = foobar(in->data[...])
    return ff_filter_frame(outlink, in);
    int direct = 0;
    AVFrame *out;

    if (av_frame_is_writable(in)) {
        direct = 1;
        out = in;
    } else {
        out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
        if (!out) {
            av_frame_free(&in);
            return AVERROR(ENOMEM);
        }
        av_frame_copy_props(out, in);
    }

    // out->data[...] = foobar(in->data[...])

    if (!direct)
        av_frame_free(&in);
    return ff_filter_frame(outlink, out);

Timeline

AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC

filter가 이전의 context frame을 저장할 필요가 없을 때 혹은 입력에 대해 이전 상태를 고려하지 않고 처리하는 경우에 사용합니다.

etc.

filter 개발을 마무리 하는 단계에서 해야 하는 다음과 같은 일들이 있습니다.

  • documentation
  • FATE test
  • Changelog에 entry 추가
  • libavfilter/version.h 수정 (LIBAVFILTER_VERSION_MINOR를 +1
    • LIBAVFILTER_VERSION_MICRO를 100으로 초기화

이렇게 작업이 완료된 후, 기여를 하고 싶다면, commit 하고 patch를 만들어 mailing-list에 전달 (to ffmpeg-devel)

  • name:

4. graph2dot

graph2dot은 filtergraph description을 parsing 하며 연관ㄷ왼 extual representation을 dot 언어를 써서 표현합니다.

graph2dot -h

echo GRAPH_DESCRIPTION |
tools/graph2dot -o graph.tmp &&
dot -Tpng graph.tmp -o graph.png &&
display graph.png

반응형

'Multimedia' 카테고리의 다른 글

동영상 컨텐츠 종류 (UCC, RMC 등)  (0) 2023.09.26
FFmpeg Side data 설정  (0) 2023.09.21
CQ vs. CRF  (0) 2023.09.01
JPEG 파일 분석 tool jpeginfo  (0) 2023.08.21
color  (0) 2023.07.24