Pad negotiation
Gstremaer에서 개발하는데 잘 모르고 개발이 잘 안되는 가장 큰 부분이 Pad Negotiation이다. 대충 Printf로 찍어보며 개발이 안된다. 원리를 이해를 해야 할거 같아서 페이지에서 정리할 겸 번역하며 정리를 해보았다.
Negotiation
Basic Rules
These simple rules must be followed:
- downstream suggests formats
- upstream decides on format
4 Queris/events used in negotiation
GST_QUERY_CAPS
: get possible formats
GST_QUERY_ACCEPT_CAPS
: check if format is possible
GST_EVENT_CAPS
: configure format (downstream)
GST_EVENT_RECONFIGURE
: inform upstream of possibly new caps
Query
Pad query는 GstCaps로 이용하며 재귀적으로 동작할 수 있음
filter
(in) GST_TYPE_CAPS
(default NULL): - a GstCaps
to filter the results against
caps
(out) GST_TYPE_CAPS
(default NULL): - the result caps
Pad는 peer pad에 주어진 caps가 지원되는지 물어볼 수 잇따. 이것은 ACCEPT_CAPS query를 통해 이루어진다. 이때 caps는 반드시 fixed여야 한다. ACCEPT_CAPS는 Recursive로 동작하지 않는다. 단순히 accept되어지면 TRUE를 return한다.
caps
(in) GST_TYPE_CAPS
: - a GstCaps
to check, must be fixed
result
(out) G_TYPE_BOOLEAN
(default FALSE): - TRUE if the caps are accepted
Event
mediaformat 이 negotiated 상태가 되면 peer element는 CAPS event로 notified된다. 이떄 caps는 반드시 fixed여야 한다.
• caps
GST_TYPE_CAPS
: - the negotiated GstCaps
, must be fixed
Operation
Gstreamer는 두가지 scheduling mode를 지원하며 이것은 push mode와 pull mode이다 두가지는 목표가 다른 서로 다른 메커니즘으로 동작한다.
Push-mode negotiation
Push-mode negotiation은 element가 buffer를 push하고 format을 결정하기를 원할때 이루어지며, 이는 downstream negotiation으로 불리는데 이는 upstream element는 downstream element에 대하여 format을 결정하기 때문이고 이것은 일반적인 케이스이다.
Negotiation은 또한 downstream element가 upstream element로 부터 다른 data format을 받기를 원할때도 이루어진다.
Basic negotiation은 아래와 같다.
- GstCaps는 buffer의 contents에 대한 describe의 event를 push하기 이전에 참조 카운트를 한다.
- Element는 이어질 Buffer의 processing이전에 CAPS event로서 새로운 format을 전달받기 위하여 스스로 reconfigure되어야 한다. 만약 data type이 not acceptable일 경우, element는 event 를 거절(refuse)해야 한다. Element는 chain function에서 buffer 유입에 대해서 GST_FLOW_NOT_NEGOTIATED를 return해야 한다.
- Downstream element는 upstream 에 대해서 RECONFIGURE event를 보냄으로, stream format이 변경에 대해 요청을 할 수 있다. Upstream element은 RECONFIGURE event를 수신하면 new format에 대해서 다시 negotitation을 수행할 것이다.
아래는 source pad의 negotiation을 시작하는 일반적인 Flow이다.
src sink
| |
| querycaps? |
|---------------->|
| caps |
select caps |< - - - - - - - -|
from the | |
candidates | |
| |-.
| accepts? | |
type A |---------------->| | optional
| yes | |
|< - - - - - - - -| |
| |-'
| send_event() |
send CAPS |---------------->| Receive type A, reconfigure to
event A | | process type A.
| |
| push |
push buffer |---------------->| Process buffer of type A
| |
아래는 구현에 대한 Pseudo code 예제이다.
[element wants to create a buffer]
if not format
# see what we can do
ourcaps = gst_pad_query_caps (srcpad)
# see what the peer can do filtered against our caps
candidates = gst_pad_peer_query_caps (srcpad, ourcaps)
foreach candidate in candidates
# make sure the caps is fixed
fixedcaps = gst_pad_fixate_caps (srcpad, candidate)
# see if the peer accepts it
if gst_pad_peer_accept_caps (srcpad, fixedcaps)
# store the caps as the negotiated caps, this will
# call the setcaps function on the pad
gst_pad_push_event (srcpad, gst_event_new_caps (fixedcaps))
break
endif
done
endif
Negotiate allocator/buffer pool with the ALLOCATION query
buffer = gst_buffer_new_allocate (NULL, size, 0);
# fill buffer and push
아래는 sink pad의 renegotiation을 시작하는 일반적인 Flow이다.
src sink
| |
| accepts? |
|<----------------| type B
| yes |
|- - - - - - - - >|-.
| | | suggest B caps next
| |<'
| |
| push_event() |
mark .-|<----------------| send RECONFIGURE event
renegotiate| | |
'>| |
| querycaps() |
renegotiate |---------------->|
| suggest B |
|< - - - - - - - -|
| |
| send_event() |
send CAPS |---------------->| Receive type B, reconfigure to
event B | | process type B.
| |
| push |
push buffer |---------------->| Process buffer of type B
| |
Pull-mode negotiation
Pull mode Pipeline 은 push mode와 negotiation 요구사항이 다름. Push mode는 아래의 두가지 Use cases에 최적화 되어 있음
- Media file Playback에 대해서 어떤 정보가 전달되어야 하는 고나점에서의 decoder, demuxer의 관점
- Live source recording에 대해서 source element 다음에 capsfilter를 추가하는데 익숙함, 그러므로 caps information flow는 Pipeline의 sink들을 향해서 source의 잠재적인 caps를 통해 user에 의해 결정됨.
그에 반해 Pull mode는 아래의 전형적인 use case들이 있음
- RTP와 같은 손실이 있는 Source를 재생할 떄에 Latency에 대한 추가 정보로 품질을 향상 시킬 수가 있음.
- 오디오 합성에서, 오디오 API는 필요한 샘플 수만 생성하도록 조정되며, 일반적으로 하드웨어 인터럽트에 의해 구동되어 DMA 버퍼나 Jack[0] 포트 버퍼를 채우는 방식으로 작동함.
- 저지연 효과 처리를 위해, 필터는 데이터를 링 버퍼에서 싱크로 전송할 때 적용되어야 하며, 미리 적용되지 않아야 함. 예를 들어,
alsasink
의 내부 링버퍼 스레드를 푸시 모드에서 사용하는 대신, wavsrc ! volume ! alsasink
와 같은 구성 대신 wavsrc ! audioringbuffer ! volume ! alsasink
로 사운드 카드 쓰기 스레드 내에 볼륨을 배치하는 방식이 있음.
Pull mode에서 문제는 sink가 gst_pad_pull_range()
를 통해 몇 바이트를 가져와야 하는지 알기 위해 format을 알아야 한다는 점이 있음. 이는 데이터를 가져오기 전에 sink가 negotiation을 시작하여 format을 결정해야 함을 의미함.
RTP 및 저지연 재생은 일반적인 재생과 유사하게 정보가 downstream으로 흐름.
오디오 합성에서는 정보가 가장 많은 부분이 sink 쪽에 있으며, 이때 caps는 완전히 지정되지 않고 사용자가 샘플 속도 등을 선택해야 합니다. 이 과정은 gstreamer 외부에서나 capsfilter
를 통해 수행될 수 있음.
싱크가 소스의 입력을 필요로 하는 경우 협상 단계가 필요하며, 풀 모드의 저지연 특성상 풀링 스레드 내에서 협상을 피해야 함.
Pull thread는 보통 PAUSED
에서 PLAYING
으로 상태가 변경될 때 시작되므로, 이 상태 변경 전에 negotiation을 완료해야 함.
따라서 caps negotiation은 SCHEDULING
Query가 성공한 후, pulling thread가 시작되기 전에 수행해야 함.
Machanism
Sink는 SCHEDULING query를 통해 Upstream element들이 Pull based scheduing을 지원하는지 확인함.
Sink는 자신의 sink pad와 연결된 src pad에서 gst_pad_query_caps()
의 결과를 교차하여 Negotiation 과정을 시작합니다. 간단한 Passthrough의 경우, 피어 패드의 caps through는 모든 sink pad에 대해 get_allowed_caps()
를 호출한 결과의 교집합을 반환해야 합니다. 이를 통해 sink element는 Pipeline 전체의 기능을 파악합니다.
필요한 경우 sink element는 결과 caps를 고정하여 flow caps를 생성합니다. 이 시점부터 sink pad의 caps query는 이 fixed caps만 반환하며, Upstream element들은 이 형식만 생성할 수 있게 됩니다.
sink element가 sink pad에 caps를 설정하지 못했다면, 버스에 오류 메시지를 게시하여 Negotiation이 불가능했음을 알립니다.
Negotiation이 성공하면, sink pad와 모든 내부적으로 연결된 Upstream Pad가 Pull mode로 활성화됩니다. 일반적으로 이 작업은 Downstream element에서 Negotiation을 trigger하여, 이제 Sink pad의 최종 Fixed caps와 Negotiation하게 됩니다.
이 단계가 완료된 후, Sink element는 상태 변경 함수에서 ASYNC를 반환합니다. 첫 번째 버퍼가 Sink에 도착하면 상태가 PAUSED로 전환됩니다. 이는 Sink에서 ASYNC 반환 값을 기대하는 application에 일관된 API를 제공하기 위해 필요하며, Polling thread의 Context 외부에서 나머지 negotiation을 수행할 수 있도록 합니다."
Pattern
Negotiation에서 3가지 패턴을 구분할 수 있음:
- 고정(Fixed): 출력 형식을 선택할 수 없음.
- Stream에 인코딩된 caps
- 비디오/오디오 decoder가 일반적으로 사용
gst_pad_use_fixed_caps()
를 사용
- 변환(Transform): CAPS가 수정되지 않음(passthrough)
- 요소 속성에 따라 CAPS 변환 가능
- 고정된 caps가 고정된 caps로 변환
- 예:
videobox
- 동적(Dynamic): 출력 형식을 선택할 수 있음
- 변환 요소
- Downstream caps에 따라 다르며, CAPS 쿼리 필요
- 일반적으로 동일한 변환을 선호
- 고정된 caps가 고정되지 않은 caps로 변환 가능
출처
https://gstreamer.freedesktop.org/documentation/additional/design/negotiation.html?gi-language=c