summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-07-08 21:59:14 +0200
committerwm4 <wm4@nowhere>2020-07-08 21:59:25 +0200
commit9be9e0877cbfe1ec19d359464a9121db56f6b0ed (patch)
treebcad4dbba4c6b6c4015cada86e3940bfb13ef9f0
parent1fe6d5ec636aa1e459f4908537d3dd166c27cff3 (diff)
downloadmpv-examples-9be9e0877cbfe1ec19d359464a9121db56f6b0ed.tar.bz2
mpv-examples-9be9e0877cbfe1ec19d359464a9121db56f6b0ed.tar.xz
sdl: add software rendering example
Even though SDL will probably use OpenGL or so anyway.
-rw-r--r--libmpv/README.md1
-rw-r--r--libmpv/sdl/main_sw.c202
2 files changed, 203 insertions, 0 deletions
diff --git a/libmpv/README.md b/libmpv/README.md
index b252668..fd0b081 100644
--- a/libmpv/README.md
+++ b/libmpv/README.md
@@ -138,6 +138,7 @@ video is not a normal QML element. Uses the opengl-cb API for video.
### sdl
Show how to embed the mpv OpenGL renderer in SDL. Uses the render API for video.
+In addition, main_sw demonstrates the render API software renderer.
### streamcb
diff --git a/libmpv/sdl/main_sw.c b/libmpv/sdl/main_sw.c
new file mode 100644
index 0000000..588223b
--- /dev/null
+++ b/libmpv/sdl/main_sw.c
@@ -0,0 +1,202 @@
+// Build with: gcc -o main_sw main_sw.c `pkg-config --libs --cflags mpv sdl2` -std=c99
+
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <SDL.h>
+
+#include <mpv/client.h>
+#include <mpv/render.h>
+
+static Uint32 wakeup_on_mpv_render_update, wakeup_on_mpv_events;
+
+static void die(const char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+ exit(1);
+}
+
+static void on_mpv_events(void *ctx)
+{
+ SDL_Event event = {.type = wakeup_on_mpv_events};
+ SDL_PushEvent(&event);
+}
+
+static void on_mpv_render_update(void *ctx)
+{
+ SDL_Event event = {.type = wakeup_on_mpv_render_update};
+ SDL_PushEvent(&event);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc != 2)
+ die("pass a single media file as argument");
+
+ mpv_handle *mpv = mpv_create();
+ if (!mpv)
+ die("context init failed");
+
+ // Some minor options can only be set before mpv_initialize().
+ if (mpv_initialize(mpv) < 0)
+ die("mpv init failed");
+
+ mpv_request_log_messages(mpv, "debug");
+
+ // Jesus Christ SDL, you suck!
+ SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "no");
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ die("SDL init failed");
+
+ SDL_Window *window;
+ SDL_Renderer *renderer;
+ if (SDL_CreateWindowAndRenderer(1000, 500, SDL_WINDOW_SHOWN |
+ SDL_WINDOW_RESIZABLE,
+ &window, &renderer))
+ die("failed to create SDL window");
+
+ mpv_render_param params[] = {
+ {MPV_RENDER_PARAM_API_TYPE, MPV_RENDER_API_TYPE_SW},
+ // Tell libmpv that you will call mpv_render_context_update() on render
+ // context update callbacks, and that you will _not_ block on the core
+ // ever (see <libmpv/render.h> "Threading" section for what libmpv
+ // functions you can call at all when this is active).
+ // In particular, this means you must call e.g. mpv_command_async()
+ // instead of mpv_command().
+ // If you want to use synchronous calls, either make them on a separate
+ // thread, or remove the option below (this will disable features like
+ // DR and is not recommended anyway).
+ {MPV_RENDER_PARAM_ADVANCED_CONTROL, &(int){1}},
+ {0}
+ };
+
+ mpv_render_context *mpv_rd;
+ if (mpv_render_context_create(&mpv_rd, mpv, params) < 0)
+ die("failed to initialize mpv render context");
+
+ // We use events for thread-safe notification of the SDL main loop.
+ // Generally, the wakeup callbacks (set further below) should do as least
+ // work as possible, and merely wake up another thread to do actual work.
+ // On SDL, waking up the mainloop is the ideal course of action. SDL's
+ // SDL_PushEvent() is thread-safe, so we use that.
+ wakeup_on_mpv_render_update = SDL_RegisterEvents(1);
+ wakeup_on_mpv_events = SDL_RegisterEvents(1);
+ if (wakeup_on_mpv_render_update == (Uint32)-1 ||
+ wakeup_on_mpv_events == (Uint32)-1)
+ die("could not register events");
+
+ // When normal mpv events are available.
+ mpv_set_wakeup_callback(mpv, on_mpv_events, NULL);
+
+ // When there is a need to call mpv_render_context_update(), which can
+ // request a new frame to be rendered.
+ // (Separate from the normal event handling mechanism for the sake of
+ // users which run OpenGL on a different thread.)
+ mpv_render_context_set_update_callback(mpv_rd, on_mpv_render_update, NULL);
+
+ SDL_Texture *tex = NULL;
+ int tex_w = -1, tex_h = -1;
+
+ // Play this file.
+ const char *cmd[] = {"loadfile", argv[1], NULL};
+ mpv_command_async(mpv, 0, cmd);
+
+ while (1) {
+ SDL_Event event;
+ if (SDL_WaitEvent(&event) != 1)
+ die("event loop error");
+ int redraw = 0;
+ switch (event.type) {
+ case SDL_QUIT:
+ goto done;
+ case SDL_WINDOWEVENT:
+ if (event.window.event == SDL_WINDOWEVENT_EXPOSED)
+ redraw = 1;
+ break;
+ case SDL_KEYDOWN:
+ if (event.key.keysym.sym == SDLK_SPACE) {
+ const char *cmd_pause[] = {"cycle", "pause", NULL};
+ mpv_command_async(mpv, 0, cmd_pause);
+ }
+ if (event.key.keysym.sym == SDLK_s) {
+ // Also requires MPV_RENDER_PARAM_ADVANCED_CONTROL if you want
+ // screenshots to be rendered on GPU (like --vo=gpu would do).
+ const char *cmd_scr[] = {"screenshot-to-file",
+ "screenshot.png",
+ "window",
+ NULL};
+ printf("attempting to save screenshot to %s\n", cmd_scr[1]);
+ mpv_command_async(mpv, 0, cmd_scr);
+ }
+ break;
+ default:
+ // Happens when there is new work for the render thread (such as
+ // rendering a new video frame or redrawing it).
+ if (event.type == wakeup_on_mpv_render_update) {
+ uint64_t flags = mpv_render_context_update(mpv_rd);
+ if (flags & MPV_RENDER_UPDATE_FRAME)
+ redraw = 1;
+ }
+ // Happens when at least 1 new event is in the mpv event queue.
+ if (event.type == wakeup_on_mpv_events) {
+ // Handle all remaining mpv events.
+ while (1) {
+ mpv_event *mp_event = mpv_wait_event(mpv, 0);
+ if (mp_event->event_id == MPV_EVENT_NONE)
+ break;
+ }
+ }
+ }
+ if (redraw) {
+ int w, h;
+ SDL_GetWindowSize(window, &w, &h);
+ if (!tex || tex_w != w || tex_h != h) {
+ SDL_DestroyTexture(tex);
+ tex = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_RGBX8888,
+ SDL_TEXTUREACCESS_STREAMING, w, h);
+ if (!tex) {
+ printf("could not allocate texture\n");
+ exit(1);
+ }
+ tex_w = w;
+ tex_h = h;
+ }
+ void *pixels;
+ int pitch;
+ if (SDL_LockTexture(tex, NULL, &pixels, &pitch)) {
+ printf("could not lock texture\n");
+ exit(1);
+ }
+ mpv_render_param params[] = {
+ {MPV_RENDER_PARAM_SW_SIZE, (int[2]){w, h}},
+ {MPV_RENDER_PARAM_SW_FORMAT, "0bgr"},
+ {MPV_RENDER_PARAM_SW_STRIDE, &(size_t){pitch}},
+ {MPV_RENDER_PARAM_SW_POINTER, pixels},
+ {0}
+ };
+ int r = mpv_render_context_render(mpv_rd, params);
+ if (r < 0) {
+ printf("mpv_render_context_render error: %s\n",
+ mpv_error_string(r));
+ exit(1);
+ }
+ SDL_UnlockTexture(tex);
+ SDL_RenderCopy(renderer, tex, NULL, NULL);
+ SDL_RenderPresent(renderer);
+ }
+ }
+done:
+
+ SDL_DestroyTexture(tex);
+
+ // Destroy the GL renderer and all of the GL objects it allocated. If video
+ // is still running, the video track will be deselected.
+ mpv_render_context_free(mpv_rd);
+
+ mpv_detach_destroy(mpv);
+
+ printf("properly terminated\n");
+ return 0;
+}