Basic tutorial 2: GStreamer concepts

Basic tutorial 2: GStreamer concepts

// basic-tutorial-2.c
#include <gst/gst.h>

int main(int argc, char* argv[])
{
    GstElement* pipeline, * source, * sink;
    GstBus* bus;
    GstMessage* msg;
    GstStateChangeReturn ret;

    /* Initialize GStreamer */
    gst_init(&argc, &argv);

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

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

    if (!pipeline || !source || !sink) {
        g_printerr("Not all elements could be created.\n");
        return -1;
    }

    /* 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;
    }

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

    /* 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;
    }

    /* Wait until error or EOS */
    bus = gst_element_get_bus(pipeline);
    msg =
        gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE,
            (GstMessageType)(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);
    }

    /* Free resources */
    gst_object_unref(bus);
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);
    return 0;
}

Walkthrough


해당 GStreamer의 pipeline은 데이터가 source에서 sink로 다운스트림으로 흐르면서 filter를 통과하는 블록입니다.

/* Initialize GStreamer */
gst_init (&argc, &argv);

Basic tutorial 1과 동일한 방법으로 GStreamer 초기화를 진행합니다.

Element creation

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

gst_element_factory_make()함수는 새 요소를 생성합니다.

매개변수:
factoryname
생성할 요소의 유형

name ([ nullable ])
새 요소의 이름 또는 고유한 이름을 자동으로 생성하려면 NULL입력

위 예시에서는 videotestsrcautovideosink 의 두 가지 요소를 생성하고, 필터요소는 없습니다.

videotestsrc는 테스트 비디오 패턴을 생성하는 source요소(데이터 생성)입니다.

autovideosinksink요소(데이터 소비)로, 수신한 이미지를 창에 표시합니다. 운영 체제에 따라 다양한 기능을 가진 여러 비디오 싱크가 있습니다. autovideosink는 가장 좋은 것을 자동으로 선택하고 인스턴스화하므로 세부 사항에 대해 걱정할 필요가 없으며 코드가 플랫폼에 더 독립적입니다.

Pipeline creation

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

GStreamer의 모든 요소는 일반적으로 일부 클럭 및 메시징 기능을 처리하기 때문에 사용하기 전에 파이프라인 내부에 포함되어야 합니다. gst_pipeline_new() 를 사용하여 파이프라인을 생성합니다.

/* 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;
}

pipeline은 다른 요소를 포함하는 데 사용되는 요소 인 bin 의 특정 유형입니다. 따라서 bin에 적용되는 모든 방법은 pipeline에도 적용됩니다.

예제의 경우, pipeline에 요소를 추가하기 위해 gst_bin_add_many()를 호출하며, NULL 로 끝나는 추가할 요소 목록을 받습니다. 개별 요소는 gst_bin_add()를 사용하여 추가할 수 있습니다.

gst_bin_add_many()
NULL 로 끝나는 요소 목록을 저장소에 추가합니다 . 이 함수는 목록의 각 구성원에 대해 gst_bin_add 를 호출하는 것과 같습니다. 각 gst_bin_add 의 반환 값 은 무시됩니다.

gst_bin_add()
빈에 주어진 요소를 추가합니다. 요소의 부모를 설정하므로 요소의 소유권을 가져옵니다. 요소는 하나의 저장소에만 추가할 수 있습니다.

추가된 요소들을 서로 연결 시키기 위해 gst_element_link()함수를 사용합니다.

매개변수:
src ([transfer: none])
소스 패드를 포함 하는 GstElement

dest ([transfer: none])
목적지 패드를 포함하는 GstElement

첫 번째 매개변수 source두 번째 매개변수 목적지데이터의 흐름(source->sink)에 따라 링크가 설정되어야 함으로 순서가 중요합니다.

동일한 저장소에 있는 요소들끼리만 연결 가능하기때문에 시도전에 pipeline에 추가하는 것(gst_bin_add_many()/gst_bin_add())을 잊지 말아야 합니다.

Properties

GStreamer 요소는 모두 속성 기능 을 제공하는 엔티티 인 GObject의 특정 종류입니다.

대부분의 GStreamer요소에는 사용자 정의 가능한 속성이 있습니다. 동작을 변경하는 속성(쓰기 속성), 내부 상태를 조회하는 속성(읽기 속성)이 있습니다.

g_object_get(): 속성 읽기
g_object_set(): 속성 쓰기

g_object_set()은 NULL 로 끝나는 속성-이름, 속성-값 쌍 목록을 허용하므로 여러 속성을 한 번에 변경할 수 있습니다.

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

위 예시는 요소 출력의 테스트 비디오 유형을 제어하는 videotestsrc의 "pattern"속성을 변경합니다.

Error checking

/* 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;
}

상태 변경은 섬세한 프로세스이므로 gst_element_set_state ()를 호출 한 뒤, 반환 값에 오류가 있는지 확인합니다.

/* 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_element_get_bus()는 실행이 끝날 때까지 기다렸다가 GStreamer가 errorEOS를 만났을 때 GstMessage를 반환하도록 합니다.

GstMessage는 거의 모든 종류의 정보를 전달할 수 있는 다양한 구조입니다.
만약 GST_MESSAGE_TYPE으로 error가 포함되어 있음을 발견하면, GError 오류 구조와 디버깅에 유용한 문자열을 반환하는 gst_message_parse_error()를 사용합니다.

The GStreamer bus

요소에 의해 생성된 GstMessage를 애플리케이션 스레드에 순서대로 전달하는 역할을 하는 객체입니다. 미디어의 실제 스트리밍은 애플리케이션이 아닌 다른 스레드에서 수행되므로 GStreamer bus의 역할이 중요합니다.

message는 gst_bus_timed_pop_filtered()를 사용하여 동기적으로 또는 비동기적(신호)으로 버스에서 추출할 수 있습니다. 애플리케이션은 오류 및 기타 재생 관련 문제에 대한 알림을 받기 위해 항상 버스를 주시해야 합니다.

Exercise

파이프라인의 소스와 싱크 사이에 비디오 필터 요소 추가하기.
(플랫폼 및 사용 가능한 플러그인에 따라 싱크가 필터의 생성을 이해하기 못하기 때문에 "negotiation" 오류가 발생할 수 있습니다. 이 경우, 필터 뒤에 videoconvert요소를 추가하세요.)

#include <gst/gst.h>

int main(int argc, char* argv[])
{
    GstElement* pipeline, * source, * filter, * convert, * sink;
    GstBus* bus;
    GstMessage* msg;
    GstStateChangeReturn ret;

    /* Initialize GStreamer */
    gst_init(&argc, &argv);

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

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

    if (!pipeline || !source || !filter || !sink) {
        g_printerr("Not all elements could be created.\n");
        return -1;
    }

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

    if (gst_element_link(filter, convert) != TRUE) {
        g_printerr("Elements could not be linked.\n");
        gst_object_unref(pipeline);
        return -1;
    }

    if (gst_element_link(convert, sink) != TRUE) {
        g_printerr("Elements could not be linked.\n");
        gst_object_unref(pipeline);
        return -1;
    }

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

    /* 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;
    }

    /* Wait until error or EOS */
    bus = gst_element_get_bus(pipeline);
    msg =
        gst_bus_timed_pop_filtered(bus, GST_CLOCK_TIME_NONE,
            (GstMessageType)(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);
    }

    /* Free resources */
    gst_object_unref(bus);
    gst_element_set_state(pipeline, GST_STATE_NULL);
    gst_object_unref(pipeline);
    return 0;
}

좋은 웹페이지 즐겨찾기