wayland(wl_shell) + egl + opengles 实例——gears

本文介绍了如何在Linux的Wayland窗口系统中,结合EGL和OpenGL ES 2.0实现类似glxgears的gles-gears程序。通过讲解源代码、编译和运行过程,展示了在Wayland环境下GPU渲染的实现方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

本文主要介绍如何在linux 下 wayland 窗口中,使用 egl + opengles 控制GPU 渲染 gears 并显示,即实现一个wayland 版本的gles-gears
软硬件环境:
硬件:PC
软件:ubuntu18.04 weston3.0 opengles2.0 egl1.4


一、ubuntu 上 opengl 版本的 glxgears

如下图所示,是在ubuntu 上执行 glxgears 的结果,会出现三个不同颜色的齿轮在不停的旋转,并会打印出帧率。
如果ubuntu 上没有安装 glxgears 程序, 可以通过 sudo apt-get install mesa-utils libgles2-mesa-dev 命令进行安装
在这里插入图片描述

  • glxgears 是一个简单的 OpenGL 示例程序,用于测试系统中 OpenGL 功能和性能
  • glxgears 只是一个简单的示例程序,并不是一个真正的性能测试工具。它主要用于检查 OpenGL
    驱动是否正确安装和运行,并提供一个简单的可视化效果。如果需要进行更详细的 OpenGL 性能测试,可以考虑使用专业的性能测试工具,例如 glmark2。
  • 运行 glxgears ,需要安装 Mesa 3D 图形库和相关的 OpenGL 开发库;

二、基于 wayland 窗口协议的 gles-gears

参考 Mesa Demos es2gears.c 实现基于 wayland 版本的 gears 总共有三个文件,分别是:egl_wayland_gears.c, matrix.c, matrix.h

1.egl_wayland_gears.c

使用 wayland (使用wl_shell 接口)+ egl + opengles2.0 实现和 glxgears 同样效果的 egl_wayland_gears.c 代码如下

#include <wayland-client.h>
#include <wayland-server.h>
#include <wayland-egl.h>
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <assert.h>
#include <unistd.h>
#include <time.h>
#include "matrix.h"

#define WIDTH 640
#define HEIGHT 480

static struct timespec start_time;

struct wl_display *display = NULL;
struct wl_compositor *compositor = NULL;
struct wl_shell *shell = NULL;
struct wl_registry *registry = NULL;

struct window {
   
	struct wl_surface *surface;
	struct wl_shell_surface *shell_surface;
	struct wl_egl_window *egl_window;
};

#define STRIPS_PER_TOOTH 7
#define VERTICES_PER_TOOTH 46
#define GEAR_VERTEX_STRIDE 6

/* Each vertex consist of GEAR_VERTEX_STRIDE GLfloat attributes */
typedef GLfloat GearVertex[GEAR_VERTEX_STRIDE];

/**
 * Struct representing a gear.
 */
struct gear {
   
	/** The array of vertices comprising the gear */
	GearVertex *vertices;
	/** The number of vertices comprising the gear */
	int nvertices;
	/** The Vertex Buffer Object holding the vertices in the graphics card */
	GLuint vbo;
};

/** The view rotation [x, y, z] */
static GLfloat view_rot[3] = {
    20.0, 30.0, 0.0 };
/** The gears */
static struct gear *gear1, *gear2, *gear3;
/** The current gear rotation angle */
static GLfloat angle = 0.0;
/** The location of the shader uniforms */
static GLuint ModelViewProjectionMatrix_location,
	      NormalMatrix_location,
	      LightSourcePosition_location,
	      MaterialColor_location;
/** The projection matrix */
static GLfloat ProjectionMatrix[16];

/** The direction of the directional light for the scene */
static const GLfloat LightSourcePosition[4] = {
    5.0, 5.0, 10.0, 1.0};


/**
 *Fills a gear vertex.
 */  
static GearVertex *
vert(GearVertex *v, GLfloat x, GLfloat y, GLfloat z, GLfloat n[3])
{
   
	v[0][0] = x;
	v[0][1] = y;
	v[0][2] = z;
	v[0][3] = n[0];
	v[0][4] = n[1];
	v[0][5] = n[2];

	return v + 1;
}

/**
 *Create a gear wheel.
 */
static struct gear *
create_gear(GLfloat inner_radius, GLfloat outer_radius, GLfloat width,
	GLint teeth, GLfloat tooth_depth)
{
   
	GLfloat r0, r1, r2;
	GLfloat da;
	GearVertex *v;
	struct gear *gear;
	double s[5], c[5];
	GLfloat normal[3];
	int cur_strip_start = 0;
	int i;

	/* Allocate memory for the gear */
	gear = malloc(sizeof *gear);
	if (gear == NULL)
		return NULL;

	/* Calculate the radii used in the gear */
	r0 = inner_radius;
	r1 = outer_radius - tooth_depth / 2.0;
	r2 = outer_radius + tooth_depth / 2.0;

	da = 2.0 * M_PI / teeth / 4.0;

	/* the first tooth doesn't need the first strip-restart sequence */
	assert(teeth > 0);
	gear->nvertices = VERTICES_PER_TOOTH +
		(VERTICES_PER_TOOTH + 2) * (teeth - 1);

	/* Allocate memory for the vertices */
	gear->vertices = calloc(gear->nvertices, sizeof(*gear->vertices));
	v = gear->vertices;

	for (i = 0; i < teeth; i++) {
   
		/* Calculate needed sin/cos for varius angles */
#if HAVE_SINCOS
		sincos(i * 2.0 * M_PI / teeth, &s[0], &c[0]);
		sincos(i * 2.0 * M_PI / teeth + da, &s[1], &c[1]);
		sincos(i * 2.0 * M_PI / teeth + da * 2, &s[2], &c[2]);
		sincos(i * 2.0 * M_PI / teeth + da * 3, &s[3], &c[3]);
		sincos(i * 2.0 * M_PI / teeth + da * 4, &s[4], &c[4]);
#else
		s[0] = sin(i * 2.0 * M_PI /teeth);
		c[0] = cos(i * 2.0 * M_PI /teeth);
		s[1] = sin(i * 2.0 * M_PI /teeth + da);
		c[1] = cos(i * 2.0 * M_PI /teeth + da);
		s[2] = sin(i * 2.0 * M_PI /teeth + da * 2);
		c[2] = cos(i * 2.0 * M_PI /teeth + da * 2);
		s[3] = sin(i * 2.0 * M_PI /teeth + da * 3);
		c[3] = cos(i * 2.0 * M_PI /teeth + da * 3);
		s[4] = sin(i * 2.0 * M_PI /teeth + da * 4);
		c[4] = cos(i * 2.0 * M_PI /teeth + da * 4);

#endif
		/* A set of macros for making the creation of the gears easier */
#define  GEAR_POINT(r, da) {
      (r) * c[(da)], (r) * s[(da)] }
#define  SET_NORMAL(x, y, z) do {
      \
	normal[0] = (x); normal[1] = (y); normal[2] = (z); \
} while(0)

#define  GEAR_VERT(v, point, sign) vert((v), p[(point)].x, p[(point)].y, (sign) * width * 0.5, normal)

#define START_STRIP do {
      \
	cur_strip_start = (v - gear->vertices); \
	if (cur_strip_start) \
	v += 2; \
} while(0);

		/* emit prev last vertex emit first vertex */
<think>嗯,用户的问题是关于如何在Linux系统下使用EGLWayland、OpenGL和GStreamer来实现将OpenGL渲染的图像通过GStreamer传输到远端播放,并且要求使用共享上下文的方式。这看起来涉及到多个技术的整合,需要仔细分析每个组件的作用和它们之间的协作方式。 首先,我需要确认各个组件的基本功能和它们如何交互。EGL是用于管理OpenGL上下文和窗口系统(如Wayland)之间的接口。Wayland作为显示服务器协议,替代了传统的X11,负责处理窗口管理和显示。OpenGL负责图形渲染,而GStreamer则处理多媒体数据的流传输,比如编码和网络传输。 用户提到要使用共享上下文的方式,这意味着OpenGL和GStreamer需要共享同一个EGL上下文,这样在渲染后的图像数据可以直接被GStreamer访问,而不需要额外的拷贝,提高效率。这可能涉及到EGLImage和DMABUF的使用,以便在不同的组件之间共享纹理数据。 接下来,我需要考虑如何将OpenGL渲染的结果传递给GStreamer的管道。GStreamer的appsrc是一个常用的源插件,用于将应用程序生成的数据推送到管道中。结合共享上下文,可能需要使用如waylandsink或自定义的插件来处理Wayland的显示,但用户的需求是传输到远端,所以可能需要编码(如H.264)和网络传输(如RTP)。 代码结构方面,大概需要以下几个步骤: 1. 初始化WaylandEGL,创建OpenGL上下文。 2. 创建共享的EGL上下文或资源,使得GStreamer可以访问OpenGL渲染的图像。 3. 设置GStreamer管道,包含appsrc、编码器、网络传输插件等。 4. 在OpenGL渲染循环中,将渲染后的帧导出到GStreamer的管道中进行处理和发送。 需要注意的是,共享上下文可能需要使用EGL的扩展功能,如EGL_KHR_image_base和EGL_WL_create_wayland_buffer_from_image,以便将OpenGL纹理转换为Wayland的缓冲区,再通过GStreamer处理。同时,确保数据格式(如DMA-BUF)的正确性,以便GStreamer能够正确解析。 可能遇到的挑战包括内存管理和同步问题,确保在OpenGL渲染完成后,GStreamer能够安全地访问这些数据而不会出现竞争条件。此外,编码器的配置和网络传输的设置也需要正确,以确保远端播放的流畅性和低延迟。 需要验证各个组件的兼容性,例如GStreamer是否支持Wayland的显示服务器,以及是否安装了必要的插件(如vaapi、x264等)。在代码实现中,可能需要处理错误检查和资源释放,以避免内存泄漏。 最后,测试整个流程,确保图像能够正确渲染并通过GStreamer传输到远端设备播放。可能需要使用工具如GStreamer的命令行工具(gst-launch-1.0)来验证接收端的配置是否正确。</think>好的!我将分步骤为您解释如何使用EGL+Wayland+OpenGL+GStreamer实现共享上下文的视频传输方案。以下是关键技术点说明和示例代码框架: --- ### **核心架构说明** 1. **EGL**:管理OpenGL与窗口系统(Wayland)的接口 2. **Wayland**:作为显示服务器协议 3. **OpenGL**:负责图形渲染 4. **GStreamer**:通过共享上下文获取OpenGL渲染结果并进行流传输 --- ### **关键代码结构示例(C语言)** ```c #include <wayland-client.h> #include <EGL/egl.h> #include <gst/gst.h> #include <GLES3/gl3.h> // Wayland全局对象 struct wl_display *display; struct wl_compositor *compositor; struct wl_surface *surface; // EGL相关对象 EGLDisplay egl_display; EGLContext egl_context; EGLSurface egl_surface; // GStreamer管道 GstElement *pipeline; GstElement *appsrc; // 共享资源 EGLImageKHR egl_image; GLuint texture_id; //------------------------------------------ // Wayland+EGL初始化 //------------------------------------------ void init_wayland_egl() { // 1. 连接Wayland服务器 display = wl_display_connect(NULL); // 2. 获取compositor接口 struct wl_registry *registry = wl_display_get_registry(display); wl_registry_add_listener(registry, &registry_listener, NULL); wl_display_roundtrip(display); // 3. 初始化EGL egl_display = eglGetDisplay(display); eglInitialize(egl_display, NULL, NULL); // 4. 配置EGL上下文 const EGLint attribs[] = { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; EGLConfig config; eglChooseConfig(egl_display, attribs, &config, 1, &config_count); // 5. 创建共享上下文 egl_context = eglCreateContext(egl_display, config, EGL_NO_CONTEXT, NULL); // 6. 创建Wayland窗口表面 surface = wl_compositor_create_surface(compositor); struct wl_egl_window *egl_window = wl_egl_window_create(surface, 1280, 720); egl_surface = eglCreateWindowSurface(egl_display, config, egl_window, NULL); } //------------------------------------------ // GStreamer初始化(共享上下文) //------------------------------------------ void init_gstreamer() { gst_init(NULL, NULL); // 创建管道(示例:H.264编码+RTP传输) pipeline = gst_parse_launch( "appsrc name=gl_src ! videoconvert ! x264enc ! rtph264pay ! udpsink host=127.0.0.1 port=5000", NULL ); appsrc = gst_bin_get_by_name(GST_BIN(pipeline), "gl_src"); // 配置共享内存传输 g_object_set(appsrc, "caps", gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "RGBA", "width", G_TYPE_INT, 1280, "height", G_TYPE_INT, 720, "framerate", GST_TYPE_FRACTION, 30, 1, NULL), "is-live", TRUE, NULL); } //------------------------------------------ // OpenGL渲染循环 //------------------------------------------ void render_frame() { eglMakeCurrent(egl_display, egl_surface, egl_surface, egl_context); // 1. OpenGL渲染操作 glClear(GL_COLOR_BUFFER_BIT); // ... 实际渲染代码 // 2. 创建EGLImage共享数据 EGLint attribs[] = { EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE }; egl_image = eglCreateImageKHR( egl_display, egl_context, EGL_GL_TEXTURE_2D_KHR, (EGLClientBuffer)(uintptr_t)texture_id, attribs ); // 3. 通过GStreamer传输 GstBuffer *buffer = gst_buffer_new_wrapped_full( GST_MEMORY_FLAG_READONLY, (gpointer)(uintptr_t)egl_image, // 共享内存指针 1280*720*4, // 数据大小 0, 1280*720*4, // 偏移量和尺寸 NULL, NULL ); gst_app_src_push_buffer(GST_APP_SRC(appsrc), buffer); // 4. 交换缓冲区 eglSwapBuffers(egl_display, egl_surface); } //------------------------------------------ // 主函数 //------------------------------------------ int main() { init_wayland_egl(); init_gstreamer(); gst_element_set_state(pipeline, GST_STATE_PLAYING); while (1) { render_frame(); usleep(33333); // ~30fps } return 0; } ``` --- ### **关键实现细节** 1. **共享上下文**:通过`eglCreateImageKHR`创建EGLImage共享纹理 2. **内存传递**:使用`gst_buffer_new_wrapped_full`将EGLImage包装为GStreamer缓冲区 3. **格式匹配**:确保OpenGL纹理格式与GStreamer的`video/x-raw`格式对齐 4. **同步机制**:需要添加帧同步控制,避免缓冲区竞争 --- ### **编译依赖** ```bash # 编译命令示例 gcc main.c -o demo \ $(pkg-config --cflags --libs wayland-client egl glesv2) \ $(pkg-config --cflags --libs gstreamer-1.0 gstreamer-app-1.0) ``` --- ### **潜在问题优化** 1. **DMA-BUF支持**:现代系统建议使用`DMABuf`进行零拷贝传输 2. **EGL扩展检查**:运行时需要验证`EGL_KHR_image_base`扩展是否可用 3. **线程安全**:渲染线程与GStreamer推送线程需要同步 需要根据实际运行环境调整具体参数,建议使用GStreamer调试工具(如`GST_DEBUG=3`)排查问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值