Graphics And Media/GStreamer

Getting started Gstreamer : GStreamer concepts

kahuz 2021. 5. 28. 19:26

Getting started GStremaer 는 freedesktop의 gstreamer 예제에 대해 번역 및 참고하여 개발해보았을때 필요하다고 느꼈던 내용 들을 아주 살짝 추가/제거하여 작성했습니다.  https://gstreamer.freedesktop.org/documentation/tutorials/basic

2) 사용자 정의 파이프라인 생성

목표

이전 예제는 파이프라인을 자동으로 빌드하는 방법을 보여주었습니다. 이제 각 엘레멘트 (src, sink)를 인스턴스화하고 연결하여 수동으로 파이프라인을 구축할 것입니다. 이 과정에서 다음 내용을 배울 수 있습니다.

  • GStreamer 엘레멘트는 무엇을 어떻게 만드는지.
  • 엘레멘트를 서로 연결하는 방법
  • 엘레멘트의 동작을 사용자 정의하는 방법
  • Bus에서 오류 상태를 관찰하고 GStreamer 메세지를 통해 정보를 추출하는 방법

예제

아래 그림은 엘레멘트를 포함한 GStreamer의 기본적인 pipeline블록 입니다. src엘레멘트 (데이터 생산자) 에서 sink엘레멘트 (데이터소비자) 로 다운 스트림 형태로 흐르면서 필터 엘레멘트를 통과하여 데이터를 처리합니다.

Fig 1. GStreamer pipeline

/* Create the elements */
source = gst_element_factory_make ("videotestsrc", "source");
sink = gst_element_factory_make ("autovideosink", "sink");

위 코드에서 볼 수 있듯이 get_element_factory_make()를 통해 새로운 엘레멘트를 생성할 수 있습니다.

GstElement *gst_element_factory_make (const gchar * factoryname, const gchar * name)

첫번째 매개변수로 주어진 엘레멘트의 유형으로 두번째 매개변수 이름을 가진 엘레멘트를 생성합니다. 만약 두번째 매개변수 name이 NULL이라면 GStreamer가 고유한 이름을 제공하게 됩니다.

본 예제에서는 videotestsrcautovideosink엘레멘트를 생성합니다.

videotestsrc는 테스트용 비디오 패턴을 생성하는 소스 엘레멘트입니다. 이 엘레멘트는 디버깅 목적 및 예제에 유용하며 일반적으로 실제 응용 프로그램에서 사용되지 않습니다.

autovideosink는 수신한 이미지를 창에 표시하는 sink 엘레멘트 입니다. 운영 체제에 따라 다양한 기능을 가진 여러 비디오 싱크 엘레멘트들이 있습니다. autovideosink는 자동으로 가장 좋은 것을 선택하고 인스턴스화하므로 세부 사항에 대해 걱정할 필요가 없습니다.

/* Create the empty pipeline */
pipeline = gst_pipeline_new ("test-pipeline");

위 코드는 새로운 파이프라인을 생성하는 코드입니다.

일반적으로 GStreamer의 모든 엘레멘트는 클럭킹 및 메세징 기능을 처리하기 때문에 파이프라인 내부에 포함되어야 사용가능합니다.

/* Build the pipeline */
gst_bin_add_many (GST_BIN (pipeline), source, sink, NULL);
if (gst_element_link (source, sink) != TRUE) {
  g_printerr ("Elements could not be linked.\n");
  gst_object_unref (pipeline);
  return -1;
}

위 코드는 파이프라인을 bin타입으로 정의하고 srcsink 엘레멘트를 적용하기 위해 gst_bin_add_many()를 호출하고 있습니다. bin 타입의 파이프라인은 다른 엘레멘트를 포함하는 데 사용되는 엘레멘트입니다. 따라서 빈에 적용되는 모든 방법은 파이프 라인에도 적용됩니다.

하지만 gst_bin_add_many()는 연결할 엘레멘트에 대한 내용을 정의할 뿐 연결을 시키는 함수는 아닙니다. 엘레멘트를 연결하기 위해서는 gst_element_link()를 호출하여야 합니다.

gboolean gst_element_link ( GstElement * src, GstElement * dest)

첫번째 매개변수 src는 파이프라인 흐름이 시작될 엘레멘트이고 두번째 매개변수 dest는 파이프라인의 흐름이 끝나는 곳에 위치하는 엘레멘트입니다. Fig 1. 파이프라인 블록에서 봤듯이 파이프라인에서 데이터는 src에서 sink로 흐르고 있습니다.

따라서 우리의 코드 또한 gst_element_link ( source, sink )로 정의됩니다.

만약 gst_element_link의 리턴 값이 FALSE라면 엘레멘트를 링크하는 데 실패한 것을 의미합니다.

/* Modify the source's properties */
g_object_set (source, "pattern", 0, NULL);

대부분의 GStreamer 엘레멘트에는 사용자 정의가 가능한 속성이 있습니다. 사용자 정의 속성이란 엘레멘트의 동작(쓰기 가능한 속성)을 변경하기 위해 수정하거나 엘레멘트의 내부 상태(읽기 가능한 속성)을 찾기 위해 요청할 수 있는 속성들을 의미합니다.

속성은 g_object_get ()으로 읽고 g_object_set ()로 작성합니다.

void g_object_set (gpointer object, const gchar *first_property_name, ...);

g_object_set ()은 속성 이름, 속성-값 쌍의 NULL로 끝나는 목록을 허용하므로 여러 속성을 한 번에 변경할 수 있습니다. 모든 GStreamer 엘레멘트는 속성 기능을 제공하는 특정한 종류의 GObject입니다. 위의 코드는 엘레멘트가 출력하는 테스트 비디오의 유형을 제어하는 videotestsrc의 "pattern" 속성을 변경합니다.

본 예제에서는 "pattern"의 값을 0으로 설정하고 있습니다. "pattern"값을 1,2 ... 로 바꿔보면 영상 출력이 달라지는 것을 확인할 수 있습니다.

요소가 노출하는 모든 속성의 이름과 가능한 값은 10) GStreamer 도구 에 설명 된 gst-inspect-1.0 도구를 사용하여 찾을 수 있습니다.

위 과정을 통해 전체 파이프라인을 구성 및 연결하고 설정했으며 다음 과정은 엘레멘트들의 오류를 검사하고 확인하는 과정을 다룹니다.

/* Start playing */
ret = gst_element_set_state (pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
  g_printerr ("Unable to set the pipeline to the playing state.\n");
  gst_object_unref (pipeline);
  return -1;
}

위 코드는 이전 예제에서 설명했던 것과 같이 생성된 pipeline 을 통해 영상을 재생하는 코드입니다.

생성된 pipeline으로 영상 재생이 불가능하다면 예외처리로 넘어가게 됩니다. 자세한 내용은 3) 동적 파이프라인 에서 다뤄집니다.

/* Wait until error or EOS */
bus = gst_element_get_bus (pipeline);
msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);

/* Parse message */
if (msg != NULL) {
  GError *err;
  gchar *debug_info;

  switch (GST_MESSAGE_TYPE (msg)) {
    case GST_MESSAGE_ERROR:
      gst_message_parse_error (msg, &err, &debug_info);
      g_printerr ("Error received from element %s: %s\n", GST_OBJECT_NAME (msg->src), err->message);
      g_printerr ("Debugging information: %s\n", debug_info ? debug_info : "none");
      g_clear_error (&err);
      g_free (debug_info);
      break;
    case GST_MESSAGE_EOS:
      g_print ("End-Of-Stream reached.\n");
      break;
    default:
      /* We should not reach here because we only asked for ERRORs and EOS */
      g_printerr ("Unexpected message received.\n");
      break;
  }
  gst_message_unref (msg);
}

gst_bus_timed_pop_filtered ()는 실행이 끝날 때까지 기다렸다가 이전에 무시했던 GstMessage를 반환합니다.

위 코드는 GStreamer가 오류 조건이나 EOS를 만났을 때 gst_bus_timed_pop_filtered ()에게 반환하도록 요청 했으므로 어떤 상황이 발생했는지 확인하고 화면에 메시지를 인쇄해야합니다 (애플리케이션이 더 복잡한 작업을 수행하려고 할 것입니다).

GstMessage는 거의 모든 종류의 정보를 전달할 수있습니다. 다행히 GStreamer는 각 메시지 종류에 대해 일련의 구문 분석 기능을 제공합니다.

이 경우 메시지에 오류 (GST_MESSAGE_TYPE () 매크로 사용)가 포함되어 있음을 알게되면 GLib GError 오류 구조와 디버깅에 유용한 문자열을 반환하는 gst_message_parse_error ()를 사용할 수 있습니다. 코드를 검토하여 메세지를 어떻게 사용하는지, 어떻게 해제되는지를 확인해보기 바랍니다.

freedesktop 튜토리얼에서는 GStreamer Bus에 대한 내용이 간략히 소개되고 있지만 지금 시점에서 이해하기 힘든 부분이므로 제외합니다. 추후 관련 내용이 나오거나 필요할 경우 추가할 예정입니다.

결론

본 예제에서는 다음과 같은 내용을 배울 수 있었습니다.

  • 엘레멘트를 만드는 방법 gst_element_factory_make()
  • 빈 파이프 라인을 만드는 방법 gst_pipeline_new()
  • 파이프라인에 엘레멘트를 추가하는 방법 gst_bin_add_many()
  • 엘레멘트를 서로 연결하는 방법 `gst_element_link()
  • 에러를 확인하고 메세지를 파싱하는 방법 GstMessage, gst_message_parse_error(), GST_MESSAGE_TYPE()

다음 예제에서는 동적 파이프라인을 생성하는 방법에 대해 공부할 것입니다.