Caps negotiation은 element 간에 처리할 수 있는 미디어 형식 (GstCaps)을 찾는 행위입니다. GStreamer에서 이 프로세스는 대부분의 경우 전체 파이프라인에 대한 최적의 솔루션을 찾을 수 있습니다. 이 섹션에서는 이것이 어떻게 작동하는지 설명합니다.
Caps negotiation basics
GStreamer에서 미디어 형식 negotiation은 항상 다음과 같은 간단한 규칙을 따릅니다.
- 다운스트림 element는 sinkpad에서 형식을 제안하고 sinkpad에서 수행된 CAPS 쿼리 결과에 suggestion을 배치합니다. Implementing a CAPS query function도 참조하세요.
- 업스트림 element는 형식을 결정합니다. CAPS 이벤트와 함께 source pad에서 선택한 미디어 형식을 다운스트림으로 전송합니다. 다운스트림 element는 sinkpad에서 CAPS 이벤트의 미디어 유형을 처리하도록 재구성합니다.
- 다운스트림 element는 RECONFIGURE 이벤트를 업스트림으로 전송하여 새 형식을 제안하고 싶다는 것을 알릴 수 있습니다. RECONFIGURE 이벤트는 업스트림 element에 negotiation 단계를 다시 시작하도록 지시하기만 합니다. RECONFIGURE 이벤트를 전송한 element가 이제 다른 형식을 제안하고 있기 때문에 파이프라인의 형식이 변경될 수 있습니다.
CAPS 및 RECONFIGURE 이벤트와 CAPS 쿼리 외에도 특정 caps가 element에 의해 허용될 수 있는지 빠르게 확인하는 ACCEPT_CAPS 쿼리가 있습니다.
모든 negotiation은 이러한 간단한 규칙을 따릅니다. 몇 가지 일반적인 사용 사례와 negotiation이 어떻게 이루어지는지 살펴보겠습니다.
Caps negotiation use cases
다음에서는 push-mode 스케줄링에 대한 몇 가지 사용 사례를 살펴보겠습니다. Pull-mode 스케줄링 negotiation 단계는 Pull-mode Caps negotiation에서 논의되며 실제로는 우리가 보게 될 것과 비슷합니다.
Sink pad는 형식만 제안하고 source pad는 형식을 결정해야 하므로 가장 복잡한 작업은 source pad에서 수행됩니다. Source pad에 대한 3개의 caps negotiation 사용 사례를 아래와 같이 볼 수 있습니다.
- Fixed negotiation: Element는 하나의 형식만 출력할 수 있습니다. Fixed negotiation을 참조하세요.
- Transform negotiation: Element의 입력 및 출력 형식 간에는 일반적으로 일부 element 속성을 기반으로 하는 (fixed) transform이 있습니다. Element가 생성하는 caps는 업스트림 caps에 따라 달라지고 element가 허용할 수 있는 caps는 다운스트림 caps에 따라 달라집니다. Transform negotiation을 참조하세요.
- Dynamic negotiation: Element는 여러 형식을 출력할 수 있습니다. Dynamic negotiation을 참조하세요.
Fixed negotiation
이 경우 source pad는 고정된 형식만 생성할 수 있습니다. 일반적으로 이 형식은 미디어 내부에 인코딩됩니다. 다운스트림 element는 다른 형식을 요청할 수 없으며 source pad가 재협상하는 유일한 방법은 element 자체가 caps를 변경하기로 결정할 때입니다.
Fixed caps를 구현할 수 있는 element (source pad에서)는 일반적으로 재협상할 수 없는 모든 element입니다. 예를 들어 다음과 같습니다.
- Typefinder: 발견된 유형이 실제 데이터 스트림의 일부이므로 재협상할 수 없습니다. Typefinder는 바이트 스트림을 보고 유형을 파악하고 caps와 함께 CAPS 이벤트를 보낸 다음 유형의 버퍼를 push 합니다.
- 거의 모든 demuxer: 기본 데이터 스트림은 파일 헤더에 정의되어 있어 재협상할 수 없습니다.
- 일부 decoder: 형식이 데이터 스트림에 내장되어 있고 peercaps의 일부가 아니며 decoder 자체도 재구성할 수 없습니다.
- 고정된 형식을 생성하는 일부 소스.
gst_pad_use_fixed_caps()는 fixed caps가 있는 source pad에서 사용됩니다. Pad가 negotiation 되지 않는 한 기본 CAPS 쿼리는 pad 템플릿에 제시된 cpas를 반환합니다. Pad가 협상되는 즉시 CAPS 쿼리는 협상된 caps (그리고 그 외 아무것도 반환하지 않음)를 반환합니다. 이는 fixed caps source pad에 대한 관련 코드 조각입니다.
[..]
pad = gst_pad_new_from_static_template (..);
gst_pad_use_fixed_caps (pad);
[..]
그런 다음 gst_pad_set_caps()를 호출하여 pad에 fixed caps를 설정할 수 있습니다.
[..]
caps = gst_caps_new_simple ("audio/x-raw",
"format", G_TYPE_STRING, GST_AUDIO_NE(F32),
"rate", G_TYPE_INT, <samplerate>,
"channels", G_TYPE_INT, <num-channels>, NULL);
if (!gst_pad_set_caps (pad, caps)) {
GST_ELEMENT_ERROR (element, CORE, NEGOTIATION, (NULL),
("Some debug information here"));
return GST_FLOW_ERROR;
}
[..]
이러한 유형의 element는 입력 형식과 출력 형식 사이에 관계가 없으며, 입력 caps에는 출력 caps을 생성하는 데 필요한 정보가 포함되지 않습니다.
형식에 대해 구성해야 하는 다른 모든 element는 전체 caps negotiation을 구현해야 하며, 이는 다음 몇 섹션에서 설명합니다.
Transform negotiation
이 negotiation 기술에서는 element 입력 caps와 출력 caps 사이에 fixed transform이 있습니다. 이 transform은 element 속성으로 매개변수화할 수 있지만 스트림의 내용으로 매개변수화할 수는 없습니다 (해당 사용 사례에 대한 Fixed negotiation 참조).
Element가 허용할 수 있는 caps는 (fixed transformation) 다운스트림 caps에 따라 달라집니다. Element가 생성할 수 있는 caps는 업스트림 caps (의 fixed transformation)에 따라 달라집니다.
이 유형의 element는 일반적으로 CAPS 이벤트를 수신할 때 sink pad의 _event() 함수에서 source pad에 caps를 설정할 수 있습니다. 즉, caps transformation function은 fixed caps를 다른 fixed caps로 변환합니다. Element의 예는 다음과 같습니다.
- Videobox. 객체 속성에 따라 비디오 프레임 주위에 구성 가능한 테두리를 추가합니다.
- Identity elements. 데이터 형식을 변경하지 않고 콘텐츠만 변경하는 모든 element. 비디오 및 오디오 효과가 그 예입니다. 다른 예로는 스트림을 검사하는 element가 있습니다.
- mulawdec 및 mulawenc와 같이 출력 형식이 입력 형식에 의해 정의되는 일부 decoders 및 encoders. 이러한 decoder에는 일반적으로 스트림의 콘텐츠를 정의하는 헤더가 없습니다. 일반적으로 conversion element와 더 비슷합니다.
아래는 일반적인 transform element의 negotiation 단계의 예입니다. Sink pad CAPS 이벤트 핸들러에서 source pad의 caps를 계산하고 설정합니다.
[...]
static gboolean
gst_my_filter_setcaps (GstMyFilter *filter,
GstCaps *caps)
{
GstStructure *structure;
int rate, channels;
gboolean ret;
GstCaps *outcaps;
structure = gst_caps_get_structure (caps, 0);
ret = gst_structure_get_int (structure, "rate", &rate);
ret = ret && gst_structure_get_int (structure, "channels", &channels);
if (!ret)
return FALSE;
outcaps = gst_caps_new_simple ("audio/x-raw",
"format", G_TYPE_STRING, GST_AUDIO_NE(S16),
"rate", G_TYPE_INT, rate,
"channels", G_TYPE_INT, channels, NULL);
ret = gst_pad_set_caps (filter->srcpad, outcaps);
gst_caps_unref (outcaps);
return ret;
}
static gboolean
gst_my_filter_sink_event (GstPad *pad,
GstObject *parent,
GstEvent *event)
{
gboolean ret;
GstMyFilter *filter = GST_MY_FILTER (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
ret = gst_my_filter_setcaps (filter, caps);
break;
}
default:
ret = gst_pad_event_default (pad, parent, event);
break;
}
return ret;
}
[...]
Dynamic negotiation
마지막 negotiation 방법은 가장 복잡하고 강력한 dynamic negotiation입니다.
Transform negotiation의 transform negotiation과 마찬가지로 dynamic negotiation은 다운스트림/업스트림 caps에 대한 transformation을 수행합니다. Transform negotiation과 달리 이 transform은 fixed caps를 unfixed caps로 변환합니다. 즉, sink pad 입력 caps는 unfixed (multiple) 형식으로 변환될 수 있습니다. Source pad는 모든 가능성 중에서 형식을 선택해야 합니다. 일반적으로 생성하는 데 가장 적은 노력이 필요한 형식을 선택하고 싶어하지만 반드시 그럴 필요는 없습니다. 형식 선택은 다운스트림에서 허용할 수 있는 caps에 따라 달라야 합니다 (Implementing a CAPS query function에서 QUERY_CAPS 함수 참조).
일반적인 흐름은 다음과 같습니다.
- Element의 sink pad에서 caps를 수신합니다.
- Element가 passthrough 모드에서 작동하기를 선호하는 경우 ACCEPT_CAPS 쿼리를 사용하여 다운스트림이 caps를 허용하는지 확인합니다. 그렇다면 negotiation을 완료하고 passthrough 모드에서 작동할 수 있습니다.
- Source pad의 가능한 caps를 계산합니다.
- 다운스트림 peer pad에서 가능한 caps 목록을 쿼리합니다.
- 다운스트림 목록에서 변환할 수 있는 첫 번째 caps를 선택하고 이를 출력 caps로 설정합니다. Fixed caps를 구성하려면 caps를 일부 합리적인 기본값으로 고정해야 할 수도 있습니다.
이러한 유형의 element의 예는 다음과 같습니다.
- Videoconvert, audioconvert, audioresample, videoscale 등의 converter elements
- Audiotestsrc, videotestsrc, v4l2src, pulsesrc 등의 source elements
입력 및 출력 샘플 속도가 동일할 필요가 없는, 샘플 속도 간을 변환할 수 있는 element의 예를 살펴보겠습니다.
static gboolean
gst_my_filter_setcaps (GstMyFilter *filter,
GstCaps *caps)
{
if (gst_pad_set_caps (filter->srcpad, caps)) {
filter->passthrough = TRUE;
} else {
GstCaps *othercaps, *newcaps;
GstStructure *s = gst_caps_get_structure (caps, 0), *others;
/* no passthrough, setup internal conversion */
gst_structure_get_int (s, "channels", &filter->channels);
othercaps = gst_pad_get_allowed_caps (filter->srcpad);
others = gst_caps_get_structure (othercaps, 0);
gst_structure_set (others,
"channels", G_TYPE_INT, filter->channels, NULL);
/* now, the samplerate value can optionally have multiple values, so
* we "fixate" it, which means that one fixed value is chosen */
newcaps = gst_caps_copy_nth (othercaps, 0);
gst_caps_unref (othercaps);
gst_pad_fixate_caps (filter->srcpad, newcaps);
if (!gst_pad_set_caps (filter->srcpad, newcaps))
return FALSE;
/* we are now set up, configure internally */
filter->passthrough = FALSE;
gst_structure_get_int (s, "rate", &filter->from_samplerate);
others = gst_caps_get_structure (newcaps, 0);
gst_structure_get_int (others, "rate", &filter->to_samplerate);
}
return TRUE;
}
static gboolean
gst_my_filter_sink_event (GstPad *pad,
GstObject *parent,
GstEvent *event)
{
gboolean ret;
GstMyFilter *filter = GST_MY_FILTER (parent);
switch (GST_EVENT_TYPE (event)) {
case GST_EVENT_CAPS:
{
GstCaps *caps;
gst_event_parse_caps (event, &caps);
ret = gst_my_filter_setcaps (filter, caps);
break;
}
default:
ret = gst_pad_event_default (pad, parent, event);
break;
}
return ret;
}
static GstFlowReturn
gst_my_filter_chain (GstPad *pad,
GstObject *parent,
GstBuffer *buf)
{
GstMyFilter *filter = GST_MY_FILTER (parent);
GstBuffer *out;
/* push on if in passthrough mode */
if (filter->passthrough)
return gst_pad_push (filter->srcpad, buf);
/* convert, push */
out = gst_my_filter_convert (filter, buf);
gst_buffer_unref (buf);
return gst_pad_push (filter->srcpad, out);
}
Upstream caps (re)negotiation
업스트림 negotiation의 주요 용도는 이미 negotiation 된 파이프라인 (일부)을 새 형식으로 re negotiation 하는 것입니다. 몇 가지 실제적인 예로는 비디오 창 크기가 변경되어 다른 비디오 크기를 선택하거나 비디오 출력 자체가 재조정할 수 없거나 또는 오디오 채널 구성이 변경된 것이 있습니다.
업스트림 caps renegotiation은 GST_EVENT_RECONFIGURE 이벤트를 업스트림으로 보내 요청합니다. 아이디어는 업스트림 element에 허용된 caps에 대한 새 쿼리를 수행한 다음 새 caps를 선택하여 caps 재구성하도록 지시하는 것입니다. RECONFIGURE 이벤트를 보내는 element는 GST_QUERY_CAPS 쿼리 함수에서 새 선호 caps를 반환하여 새 caps 선택에 영향을 미칩니다. RECONFIGURE 이벤트는 이동하는 모든 pad에서 GST_PAD_FLAG_NEED_RECONFIGURE를 설정합니다.
여기서 다른 element는 실제로 다른 책임을 진다는 점에 유의하는 것이 중요합니다.
- 새로운 형식을 업스트림으로 제안하려는 element는 먼저 ACCEPT_CAPS 쿼리를 사용하여 새로운 caps가 업스트림에서 허용되는지 확인해야 합니다. 그런 다음 RECONFIGURE 이벤트를 보내고 새로운 선호 형식으로 CAPS 쿼리에 응답할 준비를 합니다. renegotiation 할 수 있는 (또는 하려는) 업스트림 element가 없는 경우 element는 현재 구성된 형식을 처리해야 합니다.
- Transform negotiation에 따라 transform negotiation에서 작동하는 element는 RECONFIGURE 이벤트를 업스트림으로 전달합니다. 이러한 element는 단순히 업스트림 caps를 기반으로 fixed transform을 수행하므로 이벤트를 업스트림으로 보내서 새로운 형식을 선택할 수 있도록 해야 합니다.
- Fixed negotiation (Fixed negotiation)에서 작동하는 element는 RECONFIGURE 이벤트를 삭제합니다. 이러한 element는 재구성할 수 없으며 출력 caps는 업스트림 caps에 의존하지 않으므로 이벤트를 삭제할 수 있습니다.
- Source pad에서 재구성할 수 있는 element (Dynamic negotiation에서 dynamic negotiation을 구현하는 source pad)는 gst_pad_check_reconfigure()로 NEED_RECONFIGURE 플래그를 확인해야 하며, 함수가 TRUE를 반환하면 renegotiation을 시작해야 합니다.
Implementing a CAPS query function
Peer element가 이 pad가 어떤 형식을 지원하는지, 그리고 어떤 우선순위를 가지고 있는지 알고 싶을 때 GST_QUERY_CAPS 쿼리 유형의 _query() 함수가 호출됩니다. 반환 값은 peer element가 다운스트림 또는 업스트림의 제한 사항을 고려하여 이 element가 지원하는 모든 형식이어야 하며, 우선순위 순서대로 정렬되어야 하며, 그리고 우선순위가 가장 높은 것이 먼저 나와야 합니다.
static gboolean
gst_my_filter_query (GstPad *pad, GstObject * parent, GstQuery * query)
{
gboolean ret;
GstMyFilter *filter = GST_MY_FILTER (parent);
switch (GST_QUERY_TYPE (query)) {
case GST_QUERY_CAPS
{
GstPad *otherpad;
GstCaps *temp, *caps, *filt, *tcaps;
gint i;
otherpad = (pad == filter->srcpad) ? filter->sinkpad :
filter->srcpad;
caps = gst_pad_get_allowed_caps (otherpad);
gst_query_parse_caps (query, &filt);
/* We support *any* samplerate, indifferent from the samplerate
* supported by the linked elements on both sides. */
for (i = 0; i < gst_caps_get_size (caps); i++) {
GstStructure *structure = gst_caps_get_structure (caps, i);
gst_structure_remove_field (structure, "rate");
}
/* make sure we only return results that intersect our
* padtemplate */
tcaps = gst_pad_get_pad_template_caps (pad);
if (tcaps) {
temp = gst_caps_intersect (caps, tcaps);
gst_caps_unref (caps);
gst_caps_unref (tcaps);
caps = temp;
}
/* filter against the query filter when needed */
if (filt) {
temp = gst_caps_intersect (caps, filt);
gst_caps_unref (caps);
caps = temp;
}
gst_query_set_caps_result (query, caps);
gst_caps_unref (caps);
ret = TRUE;
break;
}
default:
ret = gst_pad_query_default (pad, parent, query);
break;
}
return ret;
}
Pull-mode Caps negotiation
WRITEME, pull-mode negotiation의 메커니즘은 아직 완전히 이해되지 않았습니다. (??? 이거 뭐임????)
이 장을 읽고 얻은 모든 지식을 사용하면 올바른 caps negotiation을 수행하는 element를 작성할 수 있어야 합니다. 의심스러운 경우 git 저장소에서 동일한 유형의 다른 element를 살펴보고 원하는 작업을 수행하는 방법을 알아보세요.
원문: Caps negotiation
Caps negotiation
Caps negotiation Caps negotiation is the act of finding a media format (GstCaps) between elements that they can handle. This process in GStreamer can in most cases find an optimal solution for the complete pipeline. In this section we explain how this work
gstreamer.freedesktop.org
'IT와 개발 > GStreamer Study' 카테고리의 다른 글
Different scheduling modes (1) | 2024.11.01 |
---|---|
Request and Sometimes pads (6) | 2024.10.18 |
Signals (1) | 2024.10.11 |
Building a Test Application (7) | 2024.10.11 |
Adding Properties (1) | 2024.10.04 |