반응형

Basic tutorial 1: Hello world!

 

#include <gst/gst.h>

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

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

	/* Build the pipeline */
	pipeline = gst_parse_launch("playbin uri=https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm", NULL);

	/* Start playing */
	gst_element_set_state(pipeline, GST_STATE_PLAYING);

	/* 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));
	// GST_MESSAGE_ERROR, GST_MESSAGE_EOS 는 GstMessageType 열거형의 멤버로, 각각 오류 메시지와 End Of Stream 메시지를 나타낸다.
	// GstMessageType으로 형변환 해야한다.
	// gst_bus_timed_pop_filtered 함수는 버스에서 지정된 유형의 메시지를 기다리고, 해당 메시지가 도착하면 반환한다.
	// GST_CLOCK_TIME_NONE는 무한 대기 시간을 의미한다. 따라서 이 코드는 오류나 EOS 메시지가 도착할 때까지 기다린다.

	/* See next tutorial for proper error message handling/parsing */
	if (GST_MESSAGE_TYPE(msg) == GST_MESSAGE_ERROR) {
		g_printerr("An error occurred! Re-run with the GST_DEBUG=*:WARN environment variable set for more details.\n");
	}

	/* Free resources */
	gst_message_unref(msg);
	gst_object_unref(bus);
	gst_element_set_state(pipeline, GST_STATE_NULL);
	gst_object_unref(pipeline);

	return 0;
}

 

playbin uri에 IP 카메라 스트리밍 주소(RTSP)를 넣어도 잘 작동하지만 파이프라인 최적화가 되지 않아 반응이 느리다.

pipeline = gst_parse_launch("playbin uri=rtsp://admin:admin@192.168.0.92:554/stream1", NULL);

 

 

Basic tutorial 12: Streaming

#include <gst/gst.h>
#include <string.h>

typedef struct _CustomData {
	gboolean is_live;
	GstElement* pipeline;
	GMainLoop* loop;
} CustomData;

static void cb_message(GstBus* bus, GstMessage* msg, CustomData* data) {
	switch (GST_MESSAGE_TYPE(msg)) {
	case GST_MESSAGE_ERROR: {
		GError* err = NULL;
		gchar* debug = NULL;

		gst_message_parse_error(msg, &err, &debug);
		g_print("Error: %s\n", err->message);
		g_error_free(err);
		g_free(debug);

		gst_element_set_state(data->pipeline, GST_STATE_READY);
		g_main_loop_quit(data->loop);
		break;
	} // case 내부에서 변수를 선언하고 초기화할 때는 중괄호로 감싸줘야 한다.

	case GST_MESSAGE_EOS:
		/* end-of-stream */
		gst_element_set_state(data->pipeline, GST_STATE_READY);
		g_main_loop_quit(data->loop);
		break;

	case GST_MESSAGE_BUFFERING: {
		gint percent = 0;

		/* If the stream is live, we do not care about buffering. */
		if (data->is_live)
			break;

		gst_message_parse_buffering(msg, &percent);
		g_print("Buffering (%3d%%)\r", percent);
		/* Wait until buffering is complete before start/resume playing */
		if (percent < 100)
			gst_element_set_state(data->pipeline, GST_STATE_PAUSED);
		else
			gst_element_set_state(data->pipeline, GST_STATE_PLAYING);
		break;
	} // case 내부에서 변수를 선언하고 초기화할 때는 중괄호로 감싸줘야 한다.

	case GST_MESSAGE_CLOCK_LOST:
		/* Get a new clock */
		gst_element_set_state(data->pipeline, GST_STATE_PAUSED);
		gst_element_set_state(data->pipeline, GST_STATE_PLAYING);
		break;
        // GST_MESSAGE_CLOCK_LOST: The current clock, as selected by the pipeline,
        // became unusable. The pipeline will select a new clock on the next PLAYING
        // state change.        
        // GStreamer에서 GST_MESSAGE_CLOCK_LOST 메시지는 파이프라인에서 현재 사용 중인
        // 클럭(Clock)을 더 이상 사용할 수 없게 되었을 때 발생한다. 이 메시지는 주로 다음과
        // 같은 상황에서 나타난다.
        // - 현재 클럭 제공자 유실: 파이프라인의 시간 기준(Master Clock)을 제공하던 요소
        // (예: 오디오 싱크, 오디오 소스 등)가 더 이상 클럭을 제공할 수 없을 때 발생.
        // - 새로운 소스 추가/변경: RTSP 스트림 추가 등 동적으로 파이프라인 소스가 변경되면서
        // 기존 클럭이 유효하지 않게 된 경우.
        // - 재생 상태 변경: 파이프라인이 재생(PLAYING) 상태에서 멈추거나 플러시(Flush)된 후
        // 다시 시작할 때 클럭을 재선택해야 하는 상황.

	default:
		/* Unhandled message */
		break;
	}
}

int main(int argc, char* argv[]) {
	GstElement* pipeline;
	GstBus* bus;
	GstStateChangeReturn ret;
	GMainLoop* main_loop;
	CustomData data;

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

	/* Initialize our data structure */
	memset(&data, 0, sizeof(data));

	/* Build the pipeline */
	pipeline = gst_parse_launch("playbin uri=https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm", NULL);
	//pipeline = gst_parse_launch("playbin uri=rtsp://admin:admin@192.168.0.92:554/stream1", NULL); // IP 카메라 스트림
	bus = gst_element_get_bus(pipeline);

	/* Start playing */
	ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
	if (ret == GST_STATE_CHANGE_FAILURE) {
		// GST_STATE_CHANGE_FAILURE (0) – the state change failed
		g_printerr("Unable to set the pipeline to the playing state.\n");
		gst_object_unref(pipeline);
		return -1;
	}
	else if (ret == GST_STATE_CHANGE_NO_PREROLL) {
		// GST_STATE_CHANGE_NO_PREROLL (3) – the state change succeeded but the element cannot produce data in GST_STATE_PAUSED.
		// This typically happens with live sources.
		// IP 카메라 스트림과 같이 라이브 스트림을 재생할 때는 이 상태가 반환될 수 있다.
		data.is_live = TRUE;
	}

	main_loop = g_main_loop_new(NULL, FALSE);
	data.loop = main_loop;
	data.pipeline = pipeline;

	gst_bus_add_signal_watch(bus);
	g_signal_connect(bus, "message", G_CALLBACK(cb_message), &data);

	g_main_loop_run(main_loop);

	/* Free resources */
	g_main_loop_unref(main_loop);
	gst_object_unref(bus);
	gst_element_set_state(pipeline, GST_STATE_NULL);
	gst_object_unref(pipeline);

	return 0;
}

 

 

 

 

 

 

 

 

 

반응형
Posted by J-sean
:
반응형

Application Development Manual - Your first application을 윈도우에서 컴파일하고 실행하면 아래와 같이 에러가 발생한다.

 

 

 

어떤 엘리먼트에서 에러가 발생했는지 정확히 파악하려면 GStreamer 디버깅 로그를 켜는 것이 좋습니다. Visual Studio에서 프로그램을 실행하기 전에 다음 환경 변수를 설정하세요.
- 환경 변수 설정: Visual Studio 프로젝트 속성 - Debugging - Environment 항목에 아래 내용 추가 GST_DEBUG=3 (또는 더 상세한 로그를 원하면 GST_DEBUG=4 지정)
이렇게 하면 콘솔 출력창이나 리눅스/윈도우 터미널에 에러를 발생시킨 конкрет한 원인(예: Not Negotiated, Wrong File Type 등)이 출력되어 디버깅에 큰 도움이 됩니다.

 

조금 더 자세한 내용이 표시된다.

Internal data stream error.

streaming stopped, reason not-linked (-1)

 

#include <gst/gst.h>
#include <glib.h>

static gboolean bus_call(GstBus* bus, GstMessage* msg, gpointer data)
{
	GMainLoop* loop = (GMainLoop*)data;

	switch (GST_MESSAGE_TYPE(msg)) {
	case GST_MESSAGE_EOS:
		g_print("End of stream\n");
		g_main_loop_quit(loop);
		break;

	case GST_MESSAGE_ERROR: {
		gchar* debug;
		GError* error;

		gst_message_parse_error(msg, &error, &debug);
		g_free(debug);

		g_printerr("Error: %s\n", error->message);
		g_error_free(error);

		g_main_loop_quit(loop);
		break;
	}
	default:
		break;
	}

	return TRUE;
}

static void on_pad_added(GstElement* element, GstPad* pad, gpointer data)
{
	GstPad* sinkpad;
	GstElement* decoder = (GstElement*)data;

	// 새로 추가된 pad의 caps를 가져옴
	GstCaps* caps = gst_pad_get_current_caps(pad);
	if (!caps)
		caps = gst_pad_query_caps(pad, NULL);

	// caps의 구조체에서 이름을 가져옴
	const gchar* name = gst_structure_get_name(gst_caps_get_structure(caps, 0));
	g_print("New pad '%s' added with caps '%s'\n", GST_PAD_NAME(pad), name);

	// 오디오(vorbis) 스트림인지 확인
	if (g_str_has_prefix(name, "audio/x-vorbis")) {
		g_print("Dynamic pad created, linking demuxer/decoder\n");

		sinkpad = gst_element_get_static_pad(decoder, "sink");
		// 이미 연결되어 있는지 확인
		if (!gst_pad_is_linked(sinkpad)) {
			GstPadLinkReturn ret = gst_pad_link(pad, sinkpad);
			if (ret != GST_PAD_LINK_OK)
				g_printerr("Failed to link decoder. Error code: %d\n", ret);
			else
				g_print("Successfully linked demuxer and decoder.\n");
		}
		gst_object_unref(sinkpad);
	}
	else {
		g_print("Ignoring non-audio pad: %s\n", name);
	}
	gst_caps_unref(caps);
}

int main(int argc, char* argv[])
{
	GMainLoop* loop;
	GstElement* pipeline, * source, * demuxer, * decoder, * conv, * sink;
	GstBus* bus;
	guint bus_watch_id;

	/* Initialisation */
	gst_init(&argc, &argv);

	loop = g_main_loop_new(NULL, FALSE);

	/* Check input arguments */
	if (argc != 2) {
		g_printerr("Usage: %s <Ogg/Vorbis filename>\n", argv[0]);
		return -1;
	}

	/* Create gstreamer elements */
	pipeline = gst_pipeline_new("audio-player");
	source = gst_element_factory_make("filesrc", "file-source");
	demuxer = gst_element_factory_make("oggdemux", "ogg-demuxer");
	decoder = gst_element_factory_make("vorbisdec", "vorbis-decoder");
	conv = gst_element_factory_make("audioconvert", "converter");
	sink = gst_element_factory_make("autoaudiosink", "audio-output");
	//sink = gst_element_factory_make("directsoundsink", "audio-output");
	// Windows에서는 autoaudiosink 대신 directsoundsink 사용

	if (!pipeline || !source || !demuxer || !decoder || !conv || !sink) {
		g_printerr("One element could not be created. Exiting.\n");
		return -1;
	}

	/* Set up the pipeline */

	/* we set the input filename to the source element */
	g_object_set(G_OBJECT(source), "location", argv[1], NULL);

	/* we add a message handler */
	bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
	bus_watch_id = gst_bus_add_watch(bus, bus_call, loop);
	gst_object_unref(bus);

	/* we add all elements into the pipeline */
	/* file-source | ogg-demuxer | vorbis-decoder | converter | alsa-output */
	gst_bin_add_many(GST_BIN(pipeline), source, demuxer, decoder, conv, sink, NULL);

	/* we link the elements together */
	/* file-source -> ogg-demuxer ~> vorbis-decoder -> converter -> alsa-output */
	gst_element_link(source, demuxer);
	gst_element_link_many(decoder, conv, sink, NULL);
	g_signal_connect(demuxer, "pad-added", G_CALLBACK(on_pad_added), decoder);

	/* note that the demuxer will be linked to the decoder dynamically.
	   The reason is that Ogg may contain various streams (for example
	   audio and video). The source pad(s) will be created at run time,
	   by the demuxer when it detects the amount and nature of streams.
	   Therefore we connect a callback function which will be executed
	   when the "pad-added" is emitted.*/

	   /* Set the pipeline to "playing" state*/
	g_print("Now playing: %s\n", argv[1]);
	gst_element_set_state(pipeline, GST_STATE_PLAYING);

	/* Iterate */
	g_print("Running...\n");
	g_main_loop_run(loop);

	/* Out of the main loop, clean up nicely */
	g_print("Returned, stopping playback\n");
	gst_element_set_state(pipeline, GST_STATE_NULL);

	g_print("Deleting pipeline\n");
	gst_object_unref(GST_OBJECT(pipeline));
	g_source_remove(bus_watch_id);
	g_main_loop_unref(loop);

	return 0;
}

 

더 자세한 내용을 알기 위해 on_pad_added()를 위와 같이 수정하고 Debugging - Environment에 추가했던 GST_DEBUG=3을 삭제하고(출력 내용을 간결하게 하기 위해) 실행해 보자.

 

Error code: -4

■ 에러 코드별 의미 및 해결법:
 -1 (GST_PAD_LINK_WRONG_HIERARCHY): 요소(element)들이 서로 같은 파이프라인 버스(Bin) 안에 있지 않을 때. (현재 코드상엔 잘 들어있으므로 제외)
 -2 (GST_PAD_LINK_WAS_LINKED) / -6 (GST_PAD_LINK_REFUSED): 이미 연결되었거나 디코더 측에서 패드 연결을 거부한 경우입니다.
 -4 (GST_PAD_LINK_NOFORMAT): 가장 빈번하게 발생합니다. 디코더와 컨버터, 혹은 컨버터와 오디오 싱크 간에 데이터를 주고받을 포맷 협상(Negotiation)에 실패한 것입니다. 이 경우 다음을 점검해야 합니다.
 ▶오디오 재생 장치 문제: 윈도우 환경에서 autoaudiosink가 기본 오디오 장치를 잘 잡지 못하는 경우가 있습니다. main 함수에서 autoaudiosink를 directsoundsink 또는 wasapisink 로 교체해 보세요.
 ▶Vorbis 플러그인 누락: 개발 환경의 GStreamer 버전에 gst-plugins-base 또는 Ogg 관련 플러그인이 누락되어 디코더가 정상 동작하지 않을 수 있습니다.

 

/* Create gstreamer elements */
pipeline = gst_pipeline_new("audio-player");
source = gst_element_factory_make("filesrc", "file-source");
demuxer = gst_element_factory_make("oggdemux", "ogg-demuxer");
decoder = gst_element_factory_make("vorbisdec", "vorbis-decoder");
conv = gst_element_factory_make("audioconvert", "converter");
//sink = gst_element_factory_make("autoaudiosink", "audio-output");
sink = gst_element_factory_make("directsoundsink", "audio-output");
// Windows에서는 autoaudiosink 대신 directsoundsink 사용

 

main()에서 sink만 위와 같이 수정한다.

 

다시 실행하면 잘 플레이된다.

 

플레이가 끝나면 End of stream 이벤트가 발생하고 종료된다.

 

반응형
Posted by J-sean
:

보호되어 있는 글입니다.
내용을 보시려면 비밀번호를 입력하세요.