基于下面代码,如果不能定义自己的surface id,能否获取waylandsink 自动生成的surface id,来完成下面程序(注意:GStreamer 1.20.7)
#include <gst/gst.h>
#include <gst/video/videooverlay.h>
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <libgen.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <wayland-client.h>
#include <wayland-util.h>
#include <glib.h>
#include <glib-object.h>
#include <ilm/ivi-application-client-protocol.h>
// -------------------------- wl_display 的 boxed 类型定义 --------------------------
static gpointer wl_display_boxed_copy(const gpointer src) {
return (gpointer)src; // 仅传递指针引用
}
static void wl_display_boxed_free(gpointer data) {
// 空实现:由 Wayland 库管理释放
}
GType wl_display_get_type(void) {
static GType type = 0;
if (G_UNLIKELY(type == 0)) {
type = g_boxed_type_register_static(
"WlDisplay",
wl_display_boxed_copy,
wl_display_boxed_free
);
}
return type;
}
#define WL_TYPE_DISPLAY (wl_display_get_type())
// GStreamer Wayland 上下文常量
#define GST_CONTEXT_TYPE_WAYLAND_DISPLAY "wayland-display"
// -------------------------- 配置常量 --------------------------
#define LAYER_ID 300
#define SCREEN_ID 0
#define SCREEN_WIDTH 1920
#define SCREEN_HEIGHT 720
#define SURFACE_WAIT_US 1000000
#define RENDER_DELAY_US 500000
#define IVI_SURFACE_ID 2000100
#define IVI_WAYLAND_VERSION 4
#define DUMMY_BUFFER_SIZE 4
// -------------------------- 全局变量 --------------------------
static struct wl_display *wl_display = NULL;
static struct wl_registry *wl_registry = NULL;
static struct wl_compositor *wl_compositor = NULL;
static struct wl_shm *wl_shm = NULL;
static struct ivi_application *ivi_app = NULL;
static struct ivi_surface *ivi_surface = NULL;
static struct wl_surface *wayland_surface = NULL;
static struct wl_buffer *dummy_buffer = NULL;
static guint32 videosink_surface_id = 0;
static gboolean layer_initialized = FALSE;
// -------------------------- Wayland 注册回调 --------------------------
static void registry_handle_global(void *data, struct wl_registry *registry,
uint32_t name, const char *interface, uint32_t version) {
if (strcmp(interface, "wl_compositor") == 0) {
if (version < IVI_WAYLAND_VERSION) {
fprintf(stderr, "Warning: wl_compositor version %u < %u\n", version, IVI_WAYLAND_VERSION);
}
wl_compositor = wl_registry_bind(registry, name, &wl_compositor_interface, IVI_WAYLAND_VERSION);
printf("Debug: Bound wl_compositor (name: %u)\n", name);
}
else if (strcmp(interface, "ivi_application") == 0) {
ivi_app = wl_registry_bind(registry, name, &ivi_application_interface, 1);
printf("Debug: Bound ivi_application (name: %u)\n", name);
}
else if (strcmp(interface, "wl_shm") == 0) {
wl_shm = wl_registry_bind(registry, name, &wl_shm_interface, 1);
printf("Debug: Bound wl_shm (name: %u)\n", name);
}
}
static const struct wl_registry_listener registry_listener = {
.global = registry_handle_global,
};
// -------------------------- 创建 dummy buffer --------------------------
static struct wl_buffer* create_valid_dummy_buffer() {
if (!wl_shm) {
fprintf(stderr, "Error: wl_shm not bound (cannot create buffer)\n");
return NULL;
}
char shm_name[32];
snprintf(shm_name, sizeof(shm_name), "/wl_shm_dummy_%d", getpid());
int fd = shm_open(shm_name, O_CREAT | O_RDWR, 0600);
if (fd < 0) {
fprintf(stderr, "Error: shm_open failed (errno: %d)\n", errno);
return NULL;
}
shm_unlink(shm_name);
if (ftruncate(fd, DUMMY_BUFFER_SIZE) < 0) {
fprintf(stderr, "Error: ftruncate failed (errno: %d)\n", errno);
close(fd);
return NULL;
}
void *data = mmap(NULL, DUMMY_BUFFER_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (data == MAP_FAILED) {
fprintf(stderr, "Error: mmap failed (errno: %d)\n", errno);
close(fd);
return NULL;
}
memset(data, 0, DUMMY_BUFFER_SIZE);
struct wl_shm_pool *pool = wl_shm_create_pool(wl_shm, fd, DUMMY_BUFFER_SIZE);
if (!pool) {
fprintf(stderr, "Error: wl_shm_create_pool failed\n");
munmap(data, DUMMY_BUFFER_SIZE);
close(fd);
return NULL;
}
struct wl_buffer *buffer = wl_shm_pool_create_buffer(
pool, 0, 1, 1, 4, WL_SHM_FORMAT_ARGB8888
);
wl_shm_pool_destroy(pool);
munmap(data, DUMMY_BUFFER_SIZE);
close(fd);
if (!buffer) {
fprintf(stderr, "Error: Failed to create wl_buffer\n");
return NULL;
}
printf("Debug: Created valid wl_buffer (ptr: %p)\n", buffer);
return buffer;
}
// -------------------------- 路径拼接工具 --------------------------
char* safe_path_join(const char *dir, const char *file) {
if (!dir || !file) return NULL;
size_t dir_len = strlen(dir);
size_t file_len = strlen(file);
char *result = malloc(dir_len + file_len + 2);
if (!result) return NULL;
sprintf(result, "%s%s%s", dir, (dir_len > 0 && dir[dir_len-1] != '/') ? "/" : "", file);
return result;
}
// -------------------------- 执行 LayerManager 命令 --------------------------
gboolean exec_layermanager_cmd(const char *cmd, const char *error_msg) {
printf("Executing LayerManager command: %s\n", cmd);
int ret = system(cmd);
if (ret != 0) {
fprintf(stderr, "Error: %s (return code: %d)\n", error_msg, WEXITSTATUS(ret));
return FALSE;
}
return TRUE;
}
// -------------------------- 检查 Surface 是否存在 --------------------------
gboolean check_surface_exist(guint32 surface_id) {
char cmd[256];
char buf[1024] = {0};
FILE *fp = NULL;
snprintf(cmd, sizeof(cmd), "LayerManagerControl get surfaces | grep 'Surface %u'", surface_id);
fp = popen(cmd, "r");
if (!fp) {
fprintf(stderr, "Error: Failed to check surface existence\n");
return FALSE;
}
gboolean exist = (fgets(buf, sizeof(buf), fp) != NULL);
if (exist) {
printf("Debug: IVI Surface %u confirmed\n", surface_id);
} else {
fprintf(stderr, "Debug: IVI Surface %u not found\n", surface_id);
}
pclose(fp);
return exist;
}
// -------------------------- 配置图层和 Surface 绑定 --------------------------
gboolean setup_layer_and_surface(guint32 surface_id) {
if (surface_id == 0) {
fprintf(stderr, "Error: Invalid IVI Surface ID (0)\n");
return FALSE;
}
int retry = 3;
while (retry-- > 0 && !check_surface_exist(surface_id)) {
fprintf(stderr, "Retry: Waiting for IVI Surface %u (retry %d/3)...\n", surface_id, 3 - retry);
usleep(SURFACE_WAIT_US / 3);
}
if (!check_surface_exist(surface_id)) {
fprintf(stderr, "Error: IVI Surface %u does not exist\n", surface_id);
return FALSE;
}
char cmd[512];
printf("Waiting %dms for IVI Surface stabilization...\n", SURFACE_WAIT_US / 1000);
usleep(SURFACE_WAIT_US);
snprintf(cmd, sizeof(cmd), "LayerManagerControl create layer %d %d %d",
LAYER_ID, SCREEN_WIDTH, SCREEN_HEIGHT);
if (!exec_layermanager_cmd(cmd, "Failed to create layer")) return FALSE;
snprintf(cmd, sizeof(cmd), "LayerManagerControl add surface %u to layer %d",
surface_id, LAYER_ID);
if (!exec_layermanager_cmd(cmd, "Failed to bind IVI Surface to layer")) {
snprintf(cmd, sizeof(cmd), "LayerManagerControl destroy layer %d", LAYER_ID);
exec_layermanager_cmd(cmd, "Cleanup: Failed to destroy layer");
return FALSE;
}
snprintf(cmd, sizeof(cmd), "LayerManagerControl set layer %d destination region 0 0 %d %d",
LAYER_ID, SCREEN_WIDTH, SCREEN_HEIGHT);
if (!exec_layermanager_cmd(cmd, "Failed to set layer destination")) goto cleanup;
snprintf(cmd, sizeof(cmd), "LayerManagerControl set surface %u destination region 0 0 %d %d",
surface_id, SCREEN_WIDTH, SCREEN_HEIGHT);
if (!exec_layermanager_cmd(cmd, "Failed to set IVI Surface destination")) goto cleanup;
snprintf(cmd, sizeof(cmd), "LayerManagerControl set layer %d visibility 1", LAYER_ID);
if (!exec_layermanager_cmd(cmd, "Failed to enable layer visibility")) goto cleanup;
snprintf(cmd, sizeof(cmd), "LayerManagerControl set surface %u visibility 1", surface_id);
if (!exec_layermanager_cmd(cmd, "Failed to enable IVI Surface visibility")) goto cleanup;
printf("Waiting %dms for rendering pipeline init...\n", RENDER_DELAY_US / 1000);
usleep(RENDER_DELAY_US);
snprintf(cmd, sizeof(cmd), "LayerManagerControl set screen %d render order %d",
SCREEN_ID, LAYER_ID);
if (!exec_layermanager_cmd(cmd, "Failed to set render order")) goto cleanup;
layer_initialized = TRUE;
printf("Layer setup completed (IVI Surface ID: %u, Layer ID: %d)\n", surface_id, LAYER_ID);
return TRUE;
cleanup:
snprintf(cmd, sizeof(cmd), "LayerManagerControl destroy layer %d", LAYER_ID);
exec_layermanager_cmd(cmd, "Cleanup: Failed to destroy layer (ignored)");
return FALSE;
}
// -------------------------- 清理图层和 Surface --------------------------
void cleanup_layer_and_surface(void) {
if (!layer_initialized || videosink_surface_id == 0) return;
char cmd[512];
snprintf(cmd, sizeof(cmd), "LayerManagerControl set surface %u visibility 0", videosink_surface_id);
exec_layermanager_cmd(cmd, "Cleanup: Failed to disable IVI Surface visibility (ignored)");
snprintf(cmd, sizeof(cmd), "LayerManagerControl destroy layer %d", LAYER_ID);
exec_layermanager_cmd(cmd, "Cleanup: Failed to destroy layer (ignored)");
if (dummy_buffer) {
wl_buffer_destroy(dummy_buffer);
dummy_buffer = NULL;
printf("Debug: Destroyed dummy buffer\n");
}
if (ivi_surface) {
ivi_surface_destroy(ivi_surface);
ivi_surface = NULL;
}
if (wayland_surface) {
wl_surface_destroy(wayland_surface);
wayland_surface = NULL;
}
layer_initialized = FALSE;
videosink_surface_id = 0;
}
// -------------------------- 检查 LayerManagerControl --------------------------
gboolean check_layermanager(void) {
if (system("LayerManagerControl --help > /dev/null 2>&1") != 0) {
fprintf(stderr, "Error: 'LayerManagerControl' not found\n");
return FALSE;
}
printf("LayerManagerControl tool validated\n");
return TRUE;
}
// -------------------------- 创建 IVI Surface --------------------------
gboolean create_ivi_valid_surface(void) {
if (!wl_compositor || !ivi_app || !wl_shm) {
fprintf(stderr, "Error: Missing required Wayland interfaces\n");
return FALSE;
}
wayland_surface = wl_compositor_create_surface(wl_compositor);
if (!wayland_surface) {
fprintf(stderr, "Error: Failed to create base wl_surface\n");
return FALSE;
}
printf("Debug: Created base wl_surface (ptr: %p)\n", wayland_surface);
dummy_buffer = create_valid_dummy_buffer();
if (!dummy_buffer) {
fprintf(stderr, "Error: Failed to create valid dummy buffer\n");
wl_surface_destroy(wayland_surface);
wayland_surface = NULL;
return FALSE;
}
wl_surface_attach(wayland_surface, dummy_buffer, 0, 0);
printf("Debug: Attached valid wl_buffer to wl_surface (ptr: %p)\n", dummy_buffer);
wl_surface_damage_buffer(wayland_surface, 0, 0, 1, 1);
wl_surface_commit(wayland_surface);
int roundtrip_result = wl_display_roundtrip(wl_display);
if (roundtrip_result < 0) {
fprintf(stderr, "Warning: wl_display_roundtrip failed after 1st commit\n");
} else {
printf("Debug: 1st commit with valid buffer (compositor accepted)\n");
}
ivi_surface = ivi_application_surface_create(ivi_app, IVI_SURFACE_ID, wayland_surface);
if (!ivi_surface) {
fprintf(stderr, "Error: Failed to create ivi_surface\n");
wl_surface_destroy(wayland_surface);
wayland_surface = NULL;
wl_buffer_destroy(dummy_buffer);
dummy_buffer = NULL;
return FALSE;
}
printf("Debug: Created ivi_surface with ID %u (ptr: %p)\n", IVI_SURFACE_ID, ivi_surface);
videosink_surface_id = IVI_SURFACE_ID;
printf("Debug: Fixed IVI Surface ID to %u\n", videosink_surface_id);
wl_surface_commit(wayland_surface);
wl_display_roundtrip(wl_display);
printf("Debug: 2nd commit to confirm IVI association\n");
if (check_surface_exist(videosink_surface_id)) {
printf("Debug: create_ivi_valid_surface completed successfully\n");
return TRUE;
} else {
return FALSE;
}
}
// -------------------------- 向元素传递 Wayland 上下文(核心修复) --------------------------
static gboolean set_wayland_context_to_element(GstElement *element, struct wl_display *display) {
if (!element || !display) {
fprintf(stderr, "Error: Invalid element or display for GstContext\n");
return FALSE;
}
// 创建上下文
GstContext *context = gst_context_new(GST_CONTEXT_TYPE_WAYLAND_DISPLAY, TRUE);
if (!context) {
fprintf(stderr, "Error: Failed to create GstContext\n");
return FALSE;
}
// 设置上下文结构(display 字段)
GstStructure *s = gst_context_writable_structure(context);
gst_structure_set(s, "display", WL_TYPE_DISPLAY, display, NULL);
// 直接向元素设置上下文(而非仅向 pipeline 设置)
gst_element_set_context(element, context);
printf("Debug: Set GstContext to element %s\n", GST_ELEMENT_NAME(element));
// 验证元素是否接收到上下文
GstContext *query_ctx = gst_element_get_context(element, GST_CONTEXT_TYPE_WAYLAND_DISPLAY);
if (query_ctx) {
const GstStructure *recv_struct = gst_context_get_structure(query_ctx);
if (recv_struct) {
const GValue *val = gst_structure_get_value(recv_struct, "display");
if (val && G_VALUE_HOLDS_BOXED(val)) {
const struct wl_display *recv_display = g_value_get_boxed(val);
if (recv_display == display) {
printf("Debug: Element %s received display handle (verified)\n", GST_ELEMENT_NAME(element));
gst_context_unref(query_ctx);
gst_context_unref(context);
return TRUE;
}
}
}
gst_context_unref(query_ctx);
}
fprintf(stderr, "Warning: Element %s did not receive valid display handle\n", GST_ELEMENT_NAME(element));
gst_context_unref(context);
return FALSE;
}
// -------------------------- 主函数 --------------------------
int main(int argc, char *argv[]) {
GstElement *pipeline = NULL;
GstElement *videosink = NULL;
GstBus *bus = NULL;
GstMessage *msg = NULL;
GstStateChangeReturn ret;
const char *video_path = NULL;
char *abs_path = NULL;
gchar *video_uri = NULL;
int exit_code = EXIT_SUCCESS;
setlocale(LC_ALL, "C");
gst_init(&argc, &argv);
printf("GStreamer initialized: %s (SA8538 IVI-Shell compatible)\n", gst_version_string());
if (argc < 2) {
fprintf(stderr, "Usage: %s <video_file_path>\n", argv[0]);
return EXIT_FAILURE;
}
video_path = argv[1];
printf("Video to play: %s\n", video_path);
if (!check_layermanager()) {
return EXIT_FAILURE;
}
abs_path = realpath(video_path, NULL);
if (!abs_path) {
fprintf(stderr, "Error: Invalid video path\n");
return EXIT_FAILURE;
}
printf("Resolved absolute path: %s\n", abs_path);
const char *xdg_runtime = getenv("XDG_RUNTIME_DIR");
const char *wayland_display = getenv("WAYLAND_DISPLAY");
if (!xdg_runtime || !wayland_display) {
fprintf(stderr, "Error: Wayland env missing\n");
free(abs_path);
return EXIT_FAILURE;
}
char *wayland_socket = safe_path_join(xdg_runtime, wayland_display);
if (!wayland_socket || access(wayland_socket, F_OK) != 0) {
fprintf(stderr, "Error: Wayland socket not found\n");
free(wayland_socket);
free(abs_path);
return EXIT_FAILURE;
}
printf("Wayland env validated: socket=%s\n", wayland_socket);
free(wayland_socket);
wl_display = wl_display_connect(NULL);
if (!wl_display) {
fprintf(stderr, "Error: Failed to connect Wayland Display\n");
free(abs_path);
return EXIT_FAILURE;
}
wl_registry = wl_display_get_registry(wl_display);
wl_registry_add_listener(wl_registry, ®istry_listener, NULL);
wl_display_roundtrip(wl_display);
if (!wl_compositor || !ivi_app || !wl_shm) {
fprintf(stderr, "Error: Missing required Wayland interfaces\n");
free(abs_path);
goto cleanup_wayland;
}
if (!create_ivi_valid_surface()) {
fprintf(stderr, "Error: Create valid IVI Surface failed\n");
free(abs_path);
goto cleanup_wayland;
}
printf("Debug: Entering pipeline creation step...\n");
// 创建 pipeline
pipeline = gst_element_factory_make("playbin", "video_pipeline");
if (!pipeline) {
fprintf(stderr, "FATAL: Failed to create playbin pipeline\n");
free(abs_path);
goto cleanup_wayland;
}
printf("Debug: Pipeline created (ptr: %p)\n", pipeline);
// 创建 waylandsink 并直接向其传递上下文(核心修复)
videosink = gst_element_factory_make("waylandsink", "video_sink");
if (!videosink) {
fprintf(stderr, "FATAL: Failed to create waylandsink\n");
free(abs_path);
goto cleanup_gst;
}
printf("Debug: Waylandsink created (ptr: %p)\n", videosink);
// 关键:向 waylandsink 直接传递 display 上下文
if (!set_wayland_context_to_element(videosink, wl_display)) {
fprintf(stderr, "ERROR: Failed to set display context to waylandsink - will not use external surface\n");
goto cleanup_gst;
}
// 绑定外部 wl_surface 到 waylandsink
if (!wayland_surface) {
fprintf(stderr, "FATAL: wayland_surface is NULL\n");
goto cleanup_gst;
}
gst_video_overlay_set_window_handle(GST_VIDEO_OVERLAY(videosink), (guintptr)wayland_surface);
// 额外设置属性,减少自动创建 surface 的概率
g_object_set(G_OBJECT(videosink),
"sync", TRUE,
"force-aspect-ratio", FALSE, // 禁用强制比例,避免 surface 重建
NULL);
g_object_set(G_OBJECT(pipeline), "video-sink", videosink, NULL);
printf("Debug: Bound waylandsink to wl_surface (ptr: %p)\n", wayland_surface);
// 强制刷新
gst_video_overlay_expose(GST_VIDEO_OVERLAY(videosink));
printf("Debug: Sent expose event to waylandsink\n");
// 设置视频 URI
video_uri = g_strconcat("file://", abs_path, NULL);
free(abs_path);
if (!video_uri) {
fprintf(stderr, "Error: Invalid URI\n");
goto cleanup_gst;
}
g_object_set(G_OBJECT(pipeline), "uri", video_uri, NULL);
printf("Video URI set: %s\n", video_uri);
g_free(video_uri);
// 启动播放
printf("Starting playback...\n");
ret = gst_element_set_state(pipeline, GST_STATE_READY);
if (ret == GST_STATE_CHANGE_FAILURE) {
fprintf(stderr, "Error: Failed to set pipeline to READY\n");
goto cleanup_gst;
}
usleep(500000);
ret = gst_element_set_state(pipeline, GST_STATE_PLAYING);
if (ret == GST_STATE_CHANGE_FAILURE) {
fprintf(stderr, "Error: Failed to start playback\n");
goto cleanup_gst;
}
// 绑定图层
if (!setup_layer_and_surface(videosink_surface_id)) {
exit_code = EXIT_FAILURE;
goto cleanup_gst;
}
// 主消息循环
bus = gst_element_get_bus(pipeline);
if (!bus) {
fprintf(stderr, "Error: Get GStreamer bus failed\n");
exit_code = EXIT_FAILURE;
goto cleanup_gst;
}
printf("Enter main loop (Ctrl+C to stop)...\n");
gboolean exit_loop = FALSE;
while (!exit_loop) {
msg = gst_bus_timed_pop(bus, 10 * GST_MSECOND);
if (msg) {
switch (GST_MESSAGE_TYPE(msg)) {
case GST_MESSAGE_EOS:
printf("Playback completed (EOS)\n");
exit_loop = TRUE;
break;
case GST_MESSAGE_ERROR: {
GError *err;
gchar *debug;
gst_message_parse_error(msg, &err, &debug);
fprintf(stderr, "GStreamer Error: %s (debug: %s)\n", err->message, debug);
g_error_free(err);
g_free(debug);
exit_loop = TRUE;
exit_code = EXIT_FAILURE;
break;
}
default:
break;
}
gst_message_unref(msg);
}
wl_display_dispatch_pending(wl_display);
usleep(10000);
}
gst_object_unref(bus);
cleanup_gst:
cleanup_layer_and_surface();
if (videosink) gst_object_unref(videosink);
if (pipeline) {
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
}
cleanup_wayland:
if (ivi_surface) ivi_surface_destroy(ivi_surface);
if (wayland_surface) wl_surface_destroy(wayland_surface);
if (dummy_buffer) wl_buffer_destroy(dummy_buffer);
if (ivi_app) ivi_application_destroy(ivi_app);
if (wl_shm) wl_shm_destroy(wl_shm);
if (wl_registry) wl_registry_destroy(wl_registry);
if (wl_compositor) wl_compositor_destroy(wl_compositor);
if (wl_display) wl_display_disconnect(wl_display);
printf("Exiting with code: %d\n", exit_code);
return exit_code;
}
最新发布