diff --git a/flake.nix b/flake.nix index 37a6260..3b795d7 100644 --- a/flake.nix +++ b/flake.nix @@ -24,6 +24,7 @@ ps.cl-cffi-gtk ps.sdl2 ps.cl-opengl + ps.cl-gobject-introspection ]); in { @@ -32,6 +33,9 @@ WAYLAND_DISPLAY="wayland-1"; EGL_PLATFORM="wayland"; buildInputs = [ + pkgs.gst_all_1.gstreamer + pkgs.gst_all_1.gst-plugins-base + pkgs.gst_all_1.gst-plugins-good sbcl' ]; }; diff --git a/gstreamer/tutorial-hello-world.lisp b/gstreamer/tutorial-hello-world.lisp new file mode 100644 index 0000000..62ad4d6 --- /dev/null +++ b/gstreamer/tutorial-hello-world.lisp @@ -0,0 +1,59 @@ +(load (sb-ext:posix-getenv "ASDF")) +(asdf:load-system 'cl-cffi-gtk) +(asdf:load-system 'cl-gobject-introspection) + +(defpackage :tutorial-hello-world + (:use :cl)) + +(in-package :tutorial-hello-world) + +(gir:require-namespace "GLib") +(defvar *gtk* (gir:require-namespace "Gtk" "3.0")) +(gir:nget *gtk* "WindowType" :toplevel) ; get enum value + +;; name wildly from +;; https://stackoverflow.com/questions/36296165/python-bindings-for-gstreamer-how-to-import-typelib +(defvar *gstreamer* (gir:require-namespace "Gst")) + +;; overall capacities of this Glib bindings +;; https://github.com/andy128k/cl-gobject-introspection + +;; back to hello-world +;; how'd i figure out what are the names to call, sad + +(gir:nget *gstreamer* 'init ) +(gir:nget *gstreamer* 'parse-launch) +(gir:nget *gstreamer* "State" :playing) +(gir:nget *gstreamer* "Element" 'set-state) +(gir:nget *gstreamer* "Element") + +(progn + (gir:invoke (*gstreamer* 'init) '()) + (let ((pipeline (gir:invoke (*gstreamer* 'parse-launch) + "playbin uri=https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm" + ))) + (gir:invoke (pipeline 'set-state) + (gir:nget *gstreamer* "State" :playing)))) + +(defun tutorial-main () + (gir:invoke (*gstreamer* 'init) '()) + (let ((pipeline (gir:invoke (*gstreamer* 'parse-launch) + "videotestsrc ! videoconvert ! autovideosink" + ;"playbin uri=https://gstreamer.freedesktop.org/data/media/sintel_trailer-480p.webm" + ))) + (gir:invoke (pipeline 'set-state) + (gir:nget *gstreamer* "State" :playing)) + ;; (let* ((bus (gir:invoke (pipeline 'get-bus))) + ;; (msg (gir:invoke (bus 'timed-pop-filtered) + ;; (gir:nget *gstreamer* :clock-time-none) + ;; (list + ;; (gir:nget *gstreamer* "MessageType" :error) + ;; (gir:nget *gstreamer* "MessageType" :eos)))))) + (gir:invoke (pipeline 'set-state) + (gir:nget *gstreamer* "State" :paused)))) +;; call + +;; so, it's only uri stuff didn't work +;; videotestsc worked +;; and error i got was from trying to set :null ? but it should be correct enum +;; oh well, ok diff --git a/gstreamer/tutorial-hello-world.lisp~ b/gstreamer/tutorial-hello-world.lisp~ new file mode 100644 index 0000000..e69de29 diff --git a/gtk-maybe-video.lisp b/gtk-maybe-video.lisp new file mode 100644 index 0000000..39527e6 --- /dev/null +++ b/gtk-maybe-video.lisp @@ -0,0 +1,174 @@ +(load (sb-ext:posix-getenv "ASDF")) +(asdf:load-system 'cl-cffi-gtk) + +(defpackage :video-processor + (:use :cl)) + +(in-package :video-processor) + +(defun create-main-window () + (let ((window (gtk:gtk-window-new :toplevel))) + (setf (gtk:gtk-window-title window) "Video Processor") + (setf (gtk:gtk-window-default-size window) '(800 600)) + (g:g-signal-connect window "destroy" + (lambda (widget) + (declare (ignore widget)) + (gtk:leave-gtk-main))) + window)) + +(defun create-drawing-area () + (let ((area (gtk:gtk-drawing-area-new))) + (g:g-signal-connect area "draw" + #'draw-callback) + area)) + + +(defun draw-callback (widget cr) + (declare (ignore widget)) + ;; Clear the surface with a light gray color + (let ((cr (gobject:pointer cr))) + (cairo:cairo-set-source-rgb cr 0.9 0.9 0.9) + (cairo:cairo-paint cr) + + ;; Draw a blue rectangle + (cairo:cairo-set-source-rgb cr 0.0 0.0 1.0) + (cairo:cairo-rectangle cr 50 50 200 100) + (cairo:cairo-fill cr) + + ;; Draw some text + (cairo:cairo-set-source-rgb cr 0.0 0.0 0.0) + + + (cairo:cairo-select-font-face cr "Sans" :normal :normal) + (cairo:cairo-set-font-size cr 40.0) + (cairo:cairo-move-to cr 100 300) + (cairo:cairo-show-text cr "Waiting for video..."))) + +(defun main () + (gtk:within-main-loop + (let* ((window (create-main-window)) + (area (create-drawing-area))) + (gtk:gtk-container-add window area) + (gtk:gtk-widget-show-all window)))) + +(defun start-video-processing () + ;; Initialize Pipewire connection here + ;; Start a thread to continuously read frames and trigger redraws + ) + +;(main) +;(start-video-processing) + +;; ok, i can create a window + +;; copying stuff with hopes that it will help to get frames from pipewire +(cffi:define-foreign-library libpipewire + (:unix (:or "/lib/x86_64-linux-gnu/libpipewire-0.3.so.0" + "libpipewire-0.3.so.0" + "libpipewire-0.3.so")) + (t (:default "libpipewire-0.3"))) + +(cffi:use-foreign-library libpipewire) + +;; Define constants +(defconstant +pw-stream-flag-autoconnect+ 2) + +(defun setup-pipewire () + (pw-init (cffi:null-pointer) (cffi:null-pointer)) + (let* ((loop (pw-loop-new)) + (context (pw-context-new loop (cffi:null-pointer) 0)) + (core (pw-context-connect context (cffi:null-pointer) 0)) + (stream (pw-stream-new core "my-stream" (cffi:null-pointer)))) + + ;; Set up stream parameters + (let ((params (pw-stream-get-properties stream))) + (pw-properties-set params "media.type" "Audio") + (pw-properties-set params "media.category" "Capture") + (pw-properties-set params "media.role" "Music")) + + ;; Connect the stream + (let ((stream-flags +pw-stream-flag-autoconnect+) + (target 0)) ; Changed to 0 instead of null-pointer for target-id + (pw-stream-connect stream "output" target stream-flags (cffi:null-pointer) 0)) + + ;; Return the created objects + (values loop context core stream))) + +(defun setup-pipewire () + (pw-init (cffi:null-pointer) (cffi:null-pointer)) + (let* ((loop (pw-loop-new))) + (unless loop + (error "Failed to create Pipewire loop")) + + (let ((context (pw-context-new loop (cffi:null-pointer) 0))) + (unless context + (error "Failed to create Pipewire context")) + + (let ((core (pw-context-connect context (cffi:null-pointer) 0))) + (unless core + (error "Failed to connect Pipewire context")) + + (let ((stream (pw-stream-new core "my-stream" (cffi:null-pointer)))) + (unless stream + (error "Failed to create Pipewire stream")) + + ;; Set up stream parameters + (let ((params (pw-stream-get-properties stream))) + (unless params + (error "Failed to get stream properties")) + + (unless (= 0 (pw-properties-set params "media.type" "Audio")) + (error "Failed to set media.type")) + (unless (= 0 (pw-properties-set params "media.category" "Capture")) + (error "Failed to set media.category")) + (unless (= 0 (pw-properties-set params "media.role" "Music")) + (error "Failed to set media.role")) + + ;; Connect the stream + (let ((stream-flags +pw-stream-flag-autoconnect+) + (target 0)) + (unless (>= (pw-stream-connect stream "output" target stream-flags (cffi:null-pointer) 0) 0) + (error "Failed to connect stream")) + + ;; Return the created objects + (values loop context core stream)))))))) + +;; Define necessary CFFI bindings +(cffi:defcfun ("pw_init" pw-init) :void + (argc :pointer) + (argv :pointer)) + +(cffi:defcfun ("pw_loop_new" pw-loop-new) :pointer) + +(cffi:defcfun ("pw_context_new" pw-context-new) :pointer + (loop :pointer) + (properties :pointer) + (user-data-size :int)) + +(cffi:defcfun ("pw_context_connect" pw-context-connect) :pointer + (context :pointer) + (properties :pointer) + (user-data-size :int)) + +(cffi:defcfun ("pw_stream_new" pw-stream-new) :pointer + (core :pointer) + (name :string) + (properties :pointer)) + +(cffi:defcfun ("pw_stream_get_properties" pw-stream-get-properties) :pointer + (stream :pointer)) + +(cffi:defcfun ("pw_properties_set" pw-properties-set) :int + (properties :pointer) + (key :string) + (value :string)) + +(cffi:defcfun ("pw_stream_connect" pw-stream-connect) :int + (stream :pointer) + (direction :string) + (target-id :int32) + (flags :int) + (params :pointer) + (n-params :uint32)) + +(setup-pipewire) diff --git a/notes.org b/notes.org index dc92ab5..3c811ff 100644 --- a/notes.org +++ b/notes.org @@ -33,6 +33,8 @@ $ nix shell nixpkgs#kdePackages.qttools * the receiving of the signals * all the different tabs i had +** overall freedesktop portal dbus api docs +https://docs.flatpak.org/en/latest/portal-api-reference.html#gdbus-org.freedesktop.portal.ScreenCast ** searching github for 'get-managed-objects usage https://github.com/search?q=get-managed-objects&type=code @@ -125,3 +127,58 @@ https://gitlab.gnome.org/-/snippets/19 ** doc about cl bindings to gtk3 https://www.crategus.com/books/cl-gtk/gtk-tutorial.html#example-window-simple-c + +** well, i guess i could go and try to read guide on pipewire with C examples +https://docs.pipewire.org/page_tutorial.html +this is tutorial for the system + +and here is example program that maybe does similar thing +https://docs.pipewire.org/video-play_8c-example.html + +* [2024-07-28 Sun] + +** ok, now i'm trying to get frames from the pipewire 'out' node +and one option i'm exploring is gstreamer + +which is a framework for creating applications that stream video / audio +with some kind of ability to define \ declare pipeline and then start +and then use api somehow + +there's c api, and gobject accessibility layer +so either c to common lisp, or gobject common lisp thingy maybe exist + +** reading docs +https://gstreamer.freedesktop.org/documentation/frequently-asked-questions/using.html?gi-language=c + +added via +gst_all_1.gstreamer + +** more readin +gst-launch-1.0 videotestsrc ! videoconvert ! autovideosink +this is test for how it's installed + +needed to add plugins + +https://nixos.wiki/wiki/GStreamer + +** i guess i can try to go with the tutorials? +https://gstreamer.freedesktop.org/documentation/tutorials/index.html?gi-language=c + +but with common lisp over gobject? +i think i'm already using this in the gtk bindings playground? maybe not +https://cl-gtk2.common-lisp.dev/doc/gobject/Creating-GObjects-classes-and-implementing-GInterfaces.html +#+begin_quote +GObject binding at the moment provides only limited scenarios of creating GObject classes. +#+end_quote + +** and there's something +https://github.com/BohongHuang/cl-gobject-introspection-wrapper + +which is based on older + +https://github.com/andy128k/cl-gobject-introspection + +https://search.nixos.org/packages?channel=unstable&from=0&size=50&sort=relevance&type=packages&query=gobject + + +they are packaged in nix, this is just unbelievable how much is done by humanity, omg