From 8abeb02397eec9c7a167069d9f3a319ae9ee31c7 Mon Sep 17 00:00:00 2001 From: Ichthyostega Date: Mon, 12 May 2025 03:43:09 +0200 Subject: [PATCH] XV-Display: prototype for flexible control of ouput pixel format As an example, the `PixbufDisplayer` needs packed RGB888 data, while the `XvDisplayer` expects YUV (MPEG-style) pixels. The research setup is not well equipped to handle any kind of content or format negotiation; yet for the experimentation, the connections can be wired as !SigC-Signals. After the preceding refactorings, `DummyImageGenerator` can be configured to perform the conversion to YUV only when necessary, and to use the working buffer flexibly. When supplied with packed RGB pixel data, the display in the Gtk::Image is now correct, and also handles layout and scaling appropriately. --- src/include/display-handles.h | 19 +- src/stage/ctrl/demo-controller.cpp | 15 +- src/stage/ctrl/demo-controller.hpp | 10 +- src/stage/output/displayer.cpp | 2 +- src/stage/output/displayer.hpp | 11 +- src/stage/output/pixbuf-displayer.cpp | 42 +-- src/stage/output/pixbuf-displayer.hpp | 7 +- src/stage/output/xv-displayer.hpp | 11 +- src/stage/panel/viewer-panel.cpp | 9 +- src/stage/widget/video-display-widget.cpp | 5 +- src/stage/widget/video-display-widget.hpp | 4 + src/stage/workspace/panel-manager.cpp | 2 +- .../engine/worker/dummy-image-generator.cpp | 17 +- .../engine/worker/dummy-image-generator.hpp | 5 + wiki/thinkPad.ichthyo.mm | 267 ++++++++++++------ 15 files changed, 279 insertions(+), 147 deletions(-) diff --git a/src/include/display-handles.h b/src/include/display-handles.h index 5e104f885..d2ce897ee 100644 --- a/src/include/display-handles.h +++ b/src/include/display-handles.h @@ -14,6 +14,9 @@ /** @file display-handles.h ** Opaque handles and similar typedefs used to communicate via the ** lumiera::Display and lumiera::DummyPlayer facade interfaces. + ** + ** @deprecated this is part of prototyping code; as of 5/2025 it is clear + ** that we will not use any of these interface schemes (yet something similar) ** ** @see stage::DisplayService ** @@ -43,4 +46,18 @@ typedef struct lumiera_playprocess_struct lumiera_playprocess; typedef lumiera_playprocess* LumieraPlayProcess; -#endif +#ifdef __cplusplus +namespace lumiera { + + /** Supported Displayer formats */ + enum DisplayerInput { + DISPLAY_NONE, + DISPLAY_YUV, + DISPLAY_RGB, + DISPLAY_BGR, + DISPLAY_BGR0, + DISPLAY_RGB16 + }; +} // namespace lumiera +#endif /*__cplusplus*/ +#endif /*LUMIERA_DISPLAY_HANDLES_H*/ diff --git a/src/stage/ctrl/demo-controller.cpp b/src/stage/ctrl/demo-controller.cpp index 841f45020..987ce877a 100644 --- a/src/stage/ctrl/demo-controller.cpp +++ b/src/stage/ctrl/demo-controller.cpp @@ -37,17 +37,28 @@ namespace ctrl { using std::make_unique; using steam::node::TickService; using steam::node::DummyImageGenerator; + using lumiera::DisplayerInput; - DemoController::DemoController(FrameSink outputSink) + DemoController::DemoController() : imageGen_{make_unique(FPS)} , tick_{} - , output_{std::move (outputSink)} + , output_{} , playing_{false} { } DemoController::~DemoController() { stop(); } + /** Signal slot to be called after the output window was created + * and the actually usable video display technology has been determined. + * @param displayFormat format for the frames expected in the passed image buffer. + */ + void + DemoController::activate (lumiera::DisplayerInput displayFormat) + { + REQUIRE (imageGen_); + imageGen_->configure (displayFormat); + } void DemoController::processFrame() diff --git a/src/stage/ctrl/demo-controller.hpp b/src/stage/ctrl/demo-controller.hpp index 1b8a993ab..8d049432a 100644 --- a/src/stage/ctrl/demo-controller.hpp +++ b/src/stage/ctrl/demo-controller.hpp @@ -32,6 +32,7 @@ #define DEMO_CONTROLLER_H #include "stage/gtk-base.hpp" +#include "include/display-handles.h" #include "lib/nocopy.hpp" #include @@ -47,21 +48,19 @@ namespace stage { namespace ctrl { using std::unique_ptr; - using FrameSink = std::function; /** @deprecated we need a durable design for the playback process */ class DemoController : util::NonCopyable + , public sigc::trackable { unique_ptr imageGen_; unique_ptr tick_; - FrameSink output_; - bool playing_; public: ~DemoController(); - DemoController(FrameSink); + DemoController(); bool isPlaying() const { return playing_; } @@ -69,7 +68,10 @@ namespace ctrl { void pause(); void stop(); + void activate (lumiera::DisplayerInput); + sigc::signal output_; private: + bool playing_; void processFrame(); }; diff --git a/src/stage/output/displayer.cpp b/src/stage/output/displayer.cpp index 7ef3107ec..a80fbcc2a 100644 --- a/src/stage/output/displayer.cpp +++ b/src/stage/output/displayer.cpp @@ -38,7 +38,7 @@ namespace output { DisplayerInput Displayer::format() { - return DISPLAY_NONE; + return lumiera::DISPLAY_NONE; } void diff --git a/src/stage/output/displayer.hpp b/src/stage/output/displayer.hpp index 52436073c..3392aa8e5 100644 --- a/src/stage/output/displayer.hpp +++ b/src/stage/output/displayer.hpp @@ -28,19 +28,12 @@ #include "lib/nocopy.hpp" +#include "include/display-handles.h" namespace stage { namespace output { - /** Supported Displayer formats */ - enum DisplayerInput { - DISPLAY_NONE, - DISPLAY_YUV, - DISPLAY_RGB, - DISPLAY_BGR, - DISPLAY_BGR0, - DISPLAY_RGB16 - }; + using lumiera::DisplayerInput; /** diff --git a/src/stage/output/pixbuf-displayer.cpp b/src/stage/output/pixbuf-displayer.cpp index af0692966..fb366d1d5 100644 --- a/src/stage/output/pixbuf-displayer.cpp +++ b/src/stage/output/pixbuf-displayer.cpp @@ -23,13 +23,6 @@ #include "stage/gtk-base.hpp" #include "stage/output/pixbuf-displayer.hpp" -#include "lib/format-cout.hpp" - -#if false ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display -#include -#endif ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display -#include - namespace stage { @@ -44,7 +37,7 @@ namespace output { REQUIRE (height > 0); auto iconSet = Gtk::IconSet::lookup_default (Gtk::StockID("panel_play")); drawingArea_.set(iconSet, Gtk::IconSize(Gtk::ICON_SIZE_DIALOG)); - cout << "USING PixbufDisplayer" <gobj(); - REQUIRE (window != NULL); - - #if false ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display - GdkGC *gc = gdk_gc_new( window ); - REQUIRE(gc != NULL); - - GdkPixbuf *pixbuf = gdk_pixbuf_new_from_data( (const guchar*)image, GDK_COLORSPACE_RGB, FALSE, 8, - preferredWidth(), preferredHeight(), preferredWidth() * 3, NULL, NULL ); - REQUIRE(pixbuf != NULL); - - GdkPixbuf *scaled_image = gdk_pixbuf_scale_simple( pixbuf, destWidth, destHeight, GDK_INTERP_NEAREST ); - REQUIRE(scaled_image != NULL); - - gdk_draw_pixbuf( window, gc, scaled_image, 0, 0, orgX, orgY, -1, -1, GDK_RGB_DITHER_NORMAL, 0, 0 ); - - g_object_unref( scaled_image ); - g_object_unref( pixbuf ); - g_object_unref( gc ); - #endif ///////////////////////////////////////////////////////////////////////////////////////////////////TICKET #950 : new solution for video display auto* imageData = static_cast (image); - auto rawBuf = Gdk::Pixbuf::create_from_data (imageData + auto imgBuf = Gdk::Pixbuf::create_from_data (imageData ,Gdk::COLORSPACE_RGB ,false // has_alpha ,8 // bits_per_sample ,videoWidth ,videoHeight - ,0 // rowstride (can be used to round up to even powers of two per row) + ,3 * videoWidth // rowstride (offset between consecutive rows) ); - ASSERT (rawBuf); - auto scaledBuf = rawBuf->scale_simple (destWidth, destHeight, Gdk::INTERP_NEAREST); - drawingArea_.set (scaledBuf); + ASSERT (imgBuf); + if (uint(destWidth) != videoWidth + or uint(destHeight) != videoHeight) + imgBuf = imgBuf->scale_simple (destWidth, destHeight, Gdk::INTERP_NEAREST); + drawingArea_.set (imgBuf); drawingArea_.queue_draw(); - cout << "bong.."<format()); } @@ -70,11 +71,11 @@ namespace widget { { REQUIRE (videoWidth > 0); REQUIRE (videoHeight > 0); - + /* ///////////////////////////////TICKET #1403 : temporarily disabled XV for experimentation with Pixbuf (but XV works and is usable) displayer_ = make_unique (*this, videoWidth, videoHeight); if (displayer_->usable()) return; - + */ displayer_ = make_unique (*this, videoWidth, videoHeight); if (displayer_->usable()) return; diff --git a/src/stage/widget/video-display-widget.hpp b/src/stage/widget/video-display-widget.hpp index daf8c8155..4a625af0e 100644 --- a/src/stage/widget/video-display-widget.hpp +++ b/src/stage/widget/video-display-widget.hpp @@ -32,6 +32,7 @@ namespace stage { namespace widget { using stage::output::Displayer; + using lumiera::DisplayerInput; /** @@ -48,8 +49,11 @@ namespace widget { public: VideoDisplayWidget(); + /** signal slot to display the next frame */ void pushFrame (void* const); + /** signal to configure the image generation format */ + sigc::signal signal_activate; private: virtual void on_realize() override; diff --git a/src/stage/workspace/panel-manager.cpp b/src/stage/workspace/panel-manager.cpp index a60a80c8a..605dc9de8 100644 --- a/src/stage/workspace/panel-manager.cpp +++ b/src/stage/workspace/panel-manager.cpp @@ -74,7 +74,7 @@ namespace workspace { ///////////////////////////////////////////////////////TICKET #172 : observed as a reason for crashes when closing the GUI. It was invoked after end of main, when the GUI as already gone. #if false ///////////////////////////////////////////////////TICKET #937 : disabled for GTK-3 transition. TODO investigate why this logic existed... - ///////////////////////////////////////////////////////TICKET #1027 + ///////////////////////////////////////////////////////TICKET #1027: but now the destructors of components attached to docking panel are not invoked any more !! for(int i = 0; i < 4; i++) if(dockPlaceholders_[i]) g_object_unref(dockPlaceholders_[i]); diff --git a/src/steam/engine/worker/dummy-image-generator.cpp b/src/steam/engine/worker/dummy-image-generator.cpp index 237f50eca..1085194ec 100644 --- a/src/steam/engine/worker/dummy-image-generator.cpp +++ b/src/steam/engine/worker/dummy-image-generator.cpp @@ -80,10 +80,21 @@ namespace node { DummyImageGenerator::DummyImageGenerator(uint fps) : fps_{fps} + , useRGB_{false} , beat_{false} , frame_{0} { } + void + DummyImageGenerator::configure (lumiera::DisplayerInput displayFormat) + { + INFO (steam, "ImageGen: use format %d", displayFormat); + REQUIRE ( displayFormat == lumiera::DISPLAY_NONE + or displayFormat == lumiera::DISPLAY_YUV + or displayFormat == lumiera::DISPLAY_RGB); + + useRGB_ = (displayFormat == lumiera::DISPLAY_RGB); + } void DummyImageGenerator::generateFrame (DummyFrame buffer) @@ -153,9 +164,11 @@ namespace node { DummyFrame outBuff = current(); // next output buffer to return - generateFrame (workBuf_.data()); + DummyFrame workspace = useRGB_? outBuff : workBuf_.data(); + generateFrame (workspace); + if (not useRGB_) + rgb_buffer_to_yuy2(workBuf_.data(), outBuff, W*H); - rgb_buffer_to_yuy2(workBuf_.data(), outBuff, W*H); return outBuff; } diff --git a/src/steam/engine/worker/dummy-image-generator.hpp b/src/steam/engine/worker/dummy-image-generator.hpp index e54d000cf..ee542d521 100644 --- a/src/steam/engine/worker/dummy-image-generator.hpp +++ b/src/steam/engine/worker/dummy-image-generator.hpp @@ -33,6 +33,8 @@ #include "lib/error.hpp" #include "include/display-facade.h" +#include "include/display-handles.h" + #include @@ -44,12 +46,15 @@ namespace node { class DummyImageGenerator { uint fps_; + bool useRGB_; public: static const uint W = 320; static const uint H = 240; DummyImageGenerator(uint fps); + void configure (lumiera::DisplayerInput); + /** generate the next frame and occupy the alternate buffer. * @return the buffer containing the new frame */ diff --git a/wiki/thinkPad.ichthyo.mm b/wiki/thinkPad.ichthyo.mm index 845136d66..fc2af33a2 100644 --- a/wiki/thinkPad.ichthyo.mm +++ b/wiki/thinkPad.ichthyo.mm @@ -129648,14 +129648,14 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - - - + + + - + @@ -129682,7 +129682,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -129703,7 +129703,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -129716,7 +129716,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -129758,25 +129758,29 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - - + + + + + - - + + - - + + + + + @@ -129808,7 +129812,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -129923,7 +129927,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -129988,7 +129992,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -130017,11 +130021,9 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - - - +

Erfolg-1 : es wird irgendwas angezeigt @@ -130038,56 +130040,59 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + - + + - - + + - + - - - - - - + + + + + + - - - + + + - + - - - - - + + + + + - + - - - - - + + + + + - - - +

beziehe mich hier per Gedächtnis auf Sachverhalte, die ich irgenwann irgendwo mal gelesen habe; demnach kann XV mit irgend einer Art von »Compositor« zusammenarbeiten, notfalls aber seine sichtbare (clipping)-Region auch per Colour-Key herausfinden; dabei geht es um die Frage, welcher Teil des Videobildes tatsächlich auf dem Desktop zu sehen ist, denn das Fenster könnte partiell verdeckt sein @@ -130096,45 +130101,71 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + - + - - - - + + + + - + - + - - - + + + - + + - - - + + + + + + +

+ ...und gehört in einen dedizierten OutputManager +

+ + +
+
+ + + + +

+ er rechnet ja ohnehin zunächst in RGB, und macht dann eine YUV-Konvertierung +

+ + +
+ +
+
+ + + + - - - +

Tip: suche nach "image data" @@ -130160,7 +130191,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -130179,7 +130210,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - + @@ -130199,9 +130230,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

sieht zwar nach nitpicking aus, aber da wir dann explizit per structured-Binding auf die Komponenten zugreifen, könnte der Code etwas klarer werden @@ -130220,9 +130249,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

...sonst ist es nicht möglich, das als virtuellen Zugriff für Input und Output zu verwenden @@ -130239,6 +130266,82 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +

+ ...was jetzt einfach an dem Test-Setup liegt, in dem die Komponenten nochmal gewrapped sind +

+ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -130253,9 +130356,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

...denn sonst endet man doch wieder mit z.B. einem Debian-Paket, das build-depends on the world of media processing @@ -130276,9 +130377,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - - - +

sie könnte zwar im System installiert und vorhanden sein, aber nicht richtig konfiguriert, vielleicht überhaupt nie nutzbar sein, oder aber derzeit grade nicht nutzbar (weil eine externe Verbindung oder Ressource fehlt) @@ -142480,7 +142579,7 @@ StM_bind(Builder<R1> b1, Extension<R1,R2> extension) - +