Skip to content

basic-tutorial-5가 Wayland 기본 환경에서 동작하지 않음 #32

Description

@gmgmgun

📌 요약

GStreamer 공식 튜토리얼 basic-tutorial-5.c가 Wayland 기본 환경에서 Gdk-CRITICAL assertion을 띄우고 GTK 창과 영상이 분리되어 출력됩니다.
원인은 GTK_WINDOW_XID()를 컴파일 타임 매크로로만 분기하고 런타임 백엔드 검사를 하지 않기 때문입니다.

🖥 환경

항목
장비 Raspberry Pi
OS Raspberry Pi OS (Debian 13 trixie), 2025-12-04 이미지
커널 6.12.75+rpt-rpi-v8 (aarch64)
컴포지터 labwc 0.9.2 (Wayland) — Pi OS 현재 기본
GTK 3.x (X11 + Wayland 백엔드 동시 빌드)
GStreamer 1.x
대상 소스 basic-tutorial-5.c

🔁 재현 절차

gcc basic-tutorial-5.c -o basic-tutorial-5 \
    `pkg-config --cflags --libs gtk+-3.0 gstreamer-1.0 gstreamer-video-1.0`
./basic-tutorial-5

기대 동작

GTK 창의 drawing area 안에 sintel_trailer-480p.webm 영상이 합성되어 재생.

실제 동작

(basic-tutorial-5:5860): Gdk-CRITICAL **: gdk_x11_window_get_xid:
    assertion 'GDK_IS_X11_WINDOW (window)' failed
  • GTK 창 내부는 검은 배경으로 남음
  • xvimagesink자체 창을 새로 띄워 영상을 그 바깥에 출력 → GUI와 영상이 분리

🔍 원인 분석

realize_cb()에서 윈도우 핸들 획득이 컴파일 타임 매크로로만 분기되어 있습니다:

#if defined (GDK_WINDOWING_X11)
  window_handle = GDK_WINDOW_XID (window);     // ← X11 전용 API
#endif

라즈베리파이 OS를 포함한 대부분의 배포판 GTK3는 X11과 Wayland 백엔드를 동시에 활성화해 빌드되어
GDK_WINDOWING_X11 매크로가 항상 정의됩니다. 하지만 런타임 세션이 Wayland(labwc)면 GdkWindow
Wayland 윈도우 객체이므로:

  1. GDK_WINDOW_XID() 내부의 GDK_IS_X11_WINDOW() 검사가 실패 → assertion 경고
  2. 반환된 핸들이 유효하지 않음 → gst_video_overlay_set_window_handle()에 쓰레기 값 전달
  3. xvimagesink가 자체 fallback 창을 생성 → GUI/영상 분리

튜토리얼 코드가 GTK3 X11을 전용으로 사용한다고 가정하고 작성된 것이 근본 원인입니다.

🩹 우회 방안

XWayland(X11 호환 레이어)로 강제:

GDK_BACKEND=x11 DISPLAY=:0 ./basic-tutorial-5
  • GDK_BACKEND=x11 : GTK가 X11 경로 선택 (튜토리얼 코드의 가정과 일치)
  • DISPLAY=:0 : 라즈베리파이 본체 모니터의 XWayland 인스턴스 지정

이 설정으로 실행 시 autovideosinkxvimagesink를 선택해 정상 합성됨을 확인.

🧪 파이프라인 분석 (부가 조사)

playbin이 런타임에 자동 조립하는 실제 파이프라인(덤프 기반):

GstPlayBin
├── GstURIDecodeBin
│   ├── GstSoupHTTPSrc
│   ├── GstTypeFindElement  (감지: video/webm)
│   └── GstDecodeBin
│       ├── GstMatroskaDemux
│       ├── GstMultiQueue
│       ├── GstVP8Dec       ← video decoder
│       └── GstVorbisDec    ← audio decoder
├── GstStreamSynchronizer
├── GstInputSelector × 2
└── GstPlaySink
    ├── (vbin) GstQueue → GstVideoConvert → GstVideoScale → GstXvImageSink
    └── (abin) GstQueue → GstAudioConvert → GstAudioResample → GstVolume → GstPulseSink

앱이 명시적으로 생성한 엘리먼트는 playbin 하나뿐이며, 나머지는 URI와 시스템
환경(XWayland/PulseAudio)에 따라 자동 autoplug됨.

📚 학습 포인트

  • 컴포지터/디스플레이 서버 개념: X11 → Wayland 전환, Xorg vs Wayland 컴포지터(labwc)의 역할
    차이
  • XWayland: wlroots 컴포지터의 lazy-start 호환 레이어. /tmp/.X11-unix/X0 소켓은 미리 떠 있지만
    XWayland 프로세스는 X11 클라이언트가 붙는 순간에만 spawn
  • GTK 백엔드 선택: GDK_BACKEND 환경변수로 런타임 강제 가능
  • GStreamer playbin: 단일 엘리먼트로 보이지만 내부에서 src→demux→decode→sink 체인을 자동 조립.
    직접 작성한 파이프라인(libcamerasrc ! videoconvert ! v4l2h264enc ! fakesink)과 개념은 동일하나
    포맷이 가변일 때 쓰는 고수준 헬퍼
  • GstVideoOverlay 인터페이스: GUI 창의 네이티브 핸들(XID/HWND/wl_surface)을 싱크에 전달해 외부
    윈도우 위에 합성하도록 지시
  • 컴파일 타임 vs 런타임 분기: 다중 백엔드를 동시 빌드하는 환경에서는 #if defined만으로는 부족하며 GDK_IS_*_WINDOW() 런타임 검사가 필수

✅ 체크리스트

  • 원인 재현 및 분석
  • 우회 방안 검증 (GDK_BACKEND=x11 DISPLAY=:0)
  • 실제 파이프라인 덤프로 구조 확인

해당 이슈는 Claude(LLM)의 도움을 받아 원인 분석과 초안 작성을 진행하였습니다.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions