diff options
| author | Mikhail Burakov <mburakov@mailbox.org> | 2024-08-10 19:52:35 +0200 | 
|---|---|---|
| committer | Mikhail Burakov <mburakov@mailbox.org> | 2024-08-11 13:13:43 +0200 | 
| commit | 26f8747e9c69ee83084234d509056632741e74aa (patch) | |
| tree | 0abadb57e2f210b3e3896cd18d86c675b3e24aac | |
| parent | 27e3197d32aa1e23ec817142ac1c70409f954992 (diff) | |
Initial implementation of video context (WIP)
| -rw-r--r-- | main.c | 19 | ||||
| -rw-r--r-- | video_context.c | 193 | ||||
| -rw-r--r-- | video_context.h | 28 | 
3 files changed, 234 insertions, 6 deletions
| @@ -28,6 +28,7 @@  #include "io_context.h"  #include "proto.h"  #include "util.h" +#include "video_context.h"  static volatile sig_atomic_t g_signal;  static void OnSignal(int status) { g_signal = status; } @@ -48,10 +49,15 @@ static bool SetupSignalHandler(int sig, void (*func)(int)) {  }  static void HandleClientSession(struct IoContext* io_context) { -  struct Proto* proto = NULL; +  struct VideoContext* video_context = VideoContextCreate(io_context); +  if (!video_context) { +    LOG("Failed to create video context"); +    return; +  } +    struct AudioContext* audio_context = NULL;    while (!g_signal) { -    proto = IoContextRead(io_context); +    struct Proto* proto = IoContextRead(io_context);      if (!proto) {        LOG("Failed to read proto");        goto leave; @@ -61,7 +67,8 @@ static void HandleClientSession(struct IoContext* io_context) {        case kProtoTypeHello:          if (audio_context) {            LOG("Audio reconfiguration prohibited"); -          goto rollback_proto; +          proto->Destroy(proto); +          goto leave;          }          audio_context = AudioContextCreate(io_context, proto);          if (!audio_context) { @@ -74,14 +81,14 @@ static void HandleClientSession(struct IoContext* io_context) {          break;        default:          LOG("Unexpected proto received"); -        goto rollback_proto; +        proto->Destroy(proto); +        goto leave;      }    } -rollback_proto: -  proto->Destroy(proto);  leave:    if (audio_context) AudioContextDestroy(audio_context); +  VideoContextDestroy(video_context);  }  int main(int argc, char* argv[]) { diff --git a/video_context.c b/video_context.c new file mode 100644 index 0000000..968fb5c --- /dev/null +++ b/video_context.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2024 Mikhail Burakov. This file is part of streamer. + * + * streamer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * streamer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with streamer.  If not, see <https://www.gnu.org/licenses/>. + */ + +#include "video_context.h" + +#include <assert.h> +#include <errno.h> +#include <pipewire/pipewire.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <threads.h> +#include <wayland-client.h> + +#include "util.h" +#include "wlr-export-dmabuf-unstable-v1.h" + +struct VideoContext { +  struct IoContext* io_context; + +  // Wayland globals +  struct wl_display* display; +  struct wl_registry* registry; +  struct wl_output* output; +  struct zwlr_export_dmabuf_manager_v1* export_dmabuf_manager; + +  // Threading +  struct pw_thread_loop* thread_loop; +  struct spa_source* source; +}; + +static void OnRegistryGlobal(void* data, struct wl_registry* wl_registry, +                             uint32_t name, const char* interface, +                             uint32_t version) { +#define MAYBE_BIND(a, b)                                            \ +  if (!strcmp(interface, a.name)) do {                              \ +      if (!b) b = wl_registry_bind(wl_registry, name, &a, version); \ +      if (!b) LOG("Failed to bind " #a " (%s)", strerror(errno));   \ +      return;                                                       \ +  } while (false) +  struct VideoContext* video_context = data; +  MAYBE_BIND(wl_output_interface, video_context->output); +  MAYBE_BIND(zwlr_export_dmabuf_manager_v1_interface, +             video_context->export_dmabuf_manager); +#undef MAYBE_BIND +} + +static void OnRegistryGlobalRemove(void* data, struct wl_registry* registry, +                                   uint32_t name) { +  (void)data; +  (void)registry; +  (void)name; +} + +static bool InitWaylandGlobals(struct VideoContext* video_context) { +  video_context->display = wl_display_connect(NULL); +  if (!video_context->display) { +    LOG("Failed to open display"); +    return false; +  } + +  video_context->registry = wl_display_get_registry(video_context->display); +  if (!video_context->registry) { +    LOG("Failed to get registry"); +    goto rollback_display; +  } + +  static const struct wl_registry_listener kRegistryListener = { +      .global = OnRegistryGlobal, +      .global_remove = OnRegistryGlobalRemove, +  }; +  if (wl_registry_add_listener(video_context->registry, &kRegistryListener, +                               video_context)) { +    LOG("Failed to add registry listener"); +    goto rollback_registry; +  } +  if (wl_display_roundtrip(video_context->display) == -1) { +    LOG("Failed to roundtrip display"); +    goto rollback_registry; +  } + +  if (!video_context->output || !video_context->export_dmabuf_manager) { +    LOG("Some required globals are missing"); +    goto rollback_globals; +  } + +  return true; + +rollback_globals: +  if (video_context->export_dmabuf_manager) +    zwlr_export_dmabuf_manager_v1_destroy(video_context->export_dmabuf_manager); +  if (video_context->output) wl_output_destroy(video_context->output); +rollback_registry: +  wl_registry_destroy(video_context->registry); +rollback_display: +  wl_display_disconnect(video_context->display); +  return false; +} + +static void OnDisplayData(void* arg, int fd, uint32_t mask) { +  (void)fd; +  (void)mask; +  struct VideoContext* video_context = arg; +  if (wl_display_dispatch(video_context->display) == -1) { +    LOG("Failed to dispatch display"); +    // TODO(mburakov): Now what?.. +  } +} + +struct VideoContext* VideoContextCreate(struct IoContext* io_context) { +  struct VideoContext* video_context = malloc(sizeof(struct VideoContext)); +  if (!video_context) { +    LOG("Failed to allocate video context (%s)", strerror(errno)); +    return NULL; +  } + +  *video_context = (struct VideoContext){ +      .io_context = io_context, +  }; +  if (!InitWaylandGlobals(video_context)) { +    LOG("Failed to init wayland globals"); +    goto rollback_video_context; +  } + +  video_context->thread_loop = pw_thread_loop_new("video-capture", NULL); +  if (!video_context->thread_loop) { +    LOG("Failed to create thread loop"); +    goto rollback_wayland_globals; +  } + +  pw_thread_loop_lock(video_context->thread_loop); +  if (pw_thread_loop_start(video_context->thread_loop)) { +    LOG("Failed to start thread loop"); +    goto rollback_thread_loop; +  } + +  int events_fd = wl_display_get_fd(video_context->display); +  if (events_fd == -1) { +    LOG("Failed to get display fd"); +    goto rollback_thread_loop; +  } + +  video_context->source = +      pw_loop_add_io(pw_thread_loop_get_loop(video_context->thread_loop), +                     events_fd, SPA_IO_IN, false, OnDisplayData, video_context); +  if (!video_context->source) { +    LOG("Failed to add thread loop io"); +    goto rollback_thread_loop; +  } + +  pw_thread_loop_unlock(video_context->thread_loop); +  return video_context; + +rollback_thread_loop: +  pw_thread_loop_unlock(video_context->thread_loop); +  pw_thread_loop_destroy(video_context->thread_loop); +rollback_wayland_globals: +  zwlr_export_dmabuf_manager_v1_destroy(video_context->export_dmabuf_manager); +  wl_output_destroy(video_context->output); +  wl_registry_destroy(video_context->registry); +  wl_display_disconnect(video_context->display); +rollback_video_context: +  free(video_context); +  return NULL; +} + +void VideoContextDestroy(struct VideoContext* video_context) { +  pw_thread_loop_lock(video_context->thread_loop); +  assert(pw_loop_destroy_source( +      pw_thread_loop_get_loop(video_context->thread_loop), +      video_context->source)); +  pw_thread_loop_unlock(video_context->thread_loop); +  pw_thread_loop_destroy(video_context->thread_loop); +  zwlr_export_dmabuf_manager_v1_destroy(video_context->export_dmabuf_manager); +  wl_output_release(video_context->output); +  wl_registry_destroy(video_context->registry); +  wl_display_disconnect(video_context->display); +  free(video_context); +} diff --git a/video_context.h b/video_context.h new file mode 100644 index 0000000..472a1a1 --- /dev/null +++ b/video_context.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 Mikhail Burakov. This file is part of streamer. + * + * streamer is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * streamer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with streamer.  If not, see <https://www.gnu.org/licenses/>. + */ + +#ifndef STREAMER_VIDEO_CONTEXT_H_ +#define STREAMER_VIDEO_CONTEXT_H_ + +struct VideoContext; +struct IoContext; + +struct VideoContext* VideoContextCreate(struct IoContext* io_context); +void VideoContextDestroy(struct VideoContext* video_context); + +#endif  // STREAMER_VIDEO_CONTEXT_H_ + | 
