Prototype: need to introduce an interface for the frame data feed
Since I want to segregate the code according to the domain, several methods must be provided to the video display to conduct a properly synchronised handover of video pixel data; by using `Glib::Dispatcher` as base class, all aspects of feed synchronisation can be packaged into a single connection interface, without having to share implementation details between the video display and player.
This commit is contained in:
parent
4699ae8f03
commit
2a61def8a4
4 changed files with 174 additions and 47 deletions
|
|
@ -1057,7 +1057,7 @@
|
|||
<node BACKGROUND_COLOR="#e0ceaa" COLOR="#520f69" CREATED="1783904751803" ID="ID_648202546" MODIFIED="1783904801052" TEXT="lasse mir von Claude zwei Code-Templates generieren">
|
||||
<icon BUILTIN="edit"/>
|
||||
<node CREATED="1783904765875" ID="ID_1730135150" MODIFIED="1783904774302" TEXT="die C-Lösung mit g_idle_add()"/>
|
||||
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783904774961" ID="ID_795180608" MODIFIED="1783984701307" STYLE="fork" TEXT="die Gtkmm-Lösung mit dem Glib::Dispatcher">
|
||||
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783904774961" ID="ID_795180608" MODIFIED="1784136111537" STYLE="fork" TEXT="die Gtkmm-Lösung mit dem Glib::Dispatcher">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -1083,6 +1083,7 @@
|
|||
</html></richcontent>
|
||||
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||
<icon BUILTIN="wizard"/>
|
||||
<icon BUILTIN="edit"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
@ -1249,7 +1250,7 @@
|
|||
<icon BUILTIN="yes"/>
|
||||
</node>
|
||||
<node CREATED="1783956516487" ID="ID_1971249448" MODIFIED="1783956541623" TEXT="Claude schreibt mir drei Code-Snippets">
|
||||
<node CREATED="1783956542891" ID="ID_922011151" MODIFIED="1783956927430" TEXT="Setup von Uring und Timer + initiales Befüllen">
|
||||
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783956542891" ID="ID_922011151" MODIFIED="1784136111537" STYLE="fork" TEXT="Setup von Uring und Timer + initiales Befüllen">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -1312,8 +1313,11 @@
|
|||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||
<icon BUILTIN="wizard"/>
|
||||
<icon BUILTIN="edit"/>
|
||||
</node>
|
||||
<node CREATED="1783956564734" ID="ID_519769608" MODIFIED="1783956917195" TEXT="Skelett der Proactor-Loop">
|
||||
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783956564734" ID="ID_519769608" MODIFIED="1784136111536" STYLE="fork" TEXT="Skelett der Proactor-Loop">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -1355,15 +1359,41 @@
|
|||
</p>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||
<icon BUILTIN="wizard"/>
|
||||
<icon BUILTIN="edit"/>
|
||||
</node>
|
||||
<node CREATED="1783956594083" ID="ID_1956714359" MODIFIED="1783956898511" TEXT="onTick + follow-up">
|
||||
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783956594083" ID="ID_1956714359" MODIFIED="1784136509799" STYLE="fork" TEXT="onTick + follow-up">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
void onTick() { if (slots[nextExpected % N].state == Ready) { frameReady.emit(); // GUI thread will draw slots[nextExpected % N] ++nextExpected; // GUI marks the slot Empty once it's done drawing } // else: do nothing — this frame slips one tick later. Not dropped, just delayed. while (slots[nextToSubmit % N].state == Empty) { submitRead(nextToSubmit); // io_uring_prep_read + set_data64(nextToSubmit) ++nextToSubmit; } io_uring_submit(&ring); }
|
||||
<pre>void onTick()
|
||||
  {
|
||||
    if (slots[nextExpected % N].state == Ready)
|
||||
      {
|
||||
         frameReady.emit(); // GUI thread will draw slots[nextExpected % N]
|
||||
        ++nextExpected; // GUI marks the slot Empty once it's done drawing
|
||||
      }
|
||||
 // else: do nothing — this frame slips one tick later. Not dropped, just delayed.
|
||||
|
||||
while (slots[nextToSubmit % N].state == Empty)
|
||||
  {
|
||||
     submitRead(nextToSubmit); // io_uring_prep_read + set_data64(nextToSubmit)
|
||||
++nextToSubmit;
|
||||
   }
|
||||
io_uring_submit(&ring);
|
||||
}
|
||||
|
||||
</pre>
|
||||
</body>
|
||||
</html></richcontent>
|
||||
<node CREATED="1783956613435" ID="ID_121998302" MODIFIED="1783956875196" TEXT="Claude erläutert dazu....">
|
||||
</html>
|
||||
</richcontent>
|
||||
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||
<icon BUILTIN="wizard"/>
|
||||
<icon BUILTIN="edit"/>
|
||||
<node CREATED="1783956613435" ID="ID_121998302" MODIFIED="1784136083060" TEXT="Claude erläutert dazu....">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -1376,7 +1406,7 @@
|
|||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783957483612" ID="ID_918051178" MODIFIED="1783957589661" STYLE="fork" TEXT="a Zuckerl: Chrono-to-Kernel-Timespec">
|
||||
<node BACKGROUND_COLOR="#accdc3" COLOR="#3f4015" CREATED="1783957483612" ID="ID_918051178" MODIFIED="1784136111534" STYLE="fork" TEXT="a Zuckerl: Chrono-to-Kernel-Timespec">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head/>
|
||||
<body>
|
||||
|
|
@ -1435,6 +1465,7 @@
|
|||
</html></richcontent>
|
||||
<edge COLOR="#808080" STYLE="bezier" WIDTH="thin"/>
|
||||
<icon BUILTIN="wizard"/>
|
||||
<icon BUILTIN="edit"/>
|
||||
</node>
|
||||
<node BACKGROUND_COLOR="#e9c7a3" COLOR="#440099" CREATED="1783964845540" ID="ID_784995591" MODIFIED="1783964898453" TEXT="schließe eine allgemeine Überlegung an btr. Lumiera-Engine">
|
||||
<icon BUILTIN="back"/>
|
||||
|
|
@ -2056,7 +2087,7 @@
|
|||
<node CREATED="1784082547951" ID="ID_1261536746" MODIFIED="1784082582921" TEXT="ist ein std::array aus 8bit-Pixeln"/>
|
||||
<node COLOR="#5b280f" CREATED="1784082595689" ID="ID_1135557355" MODIFIED="1784132191641" TEXT="verwende diese Typedef aber auch als »Buffer«-Abstraktion">
|
||||
<icon BUILTIN="stop-sign"/>
|
||||
<node CREATED="1784132084657" ID="ID_845039951" MODIFIED="1784132129048" TEXT="hier beginnt der Knoten">
|
||||
<node CREATED="1784132084657" HGAP="24" ID="ID_845039951" MODIFIED="1784132637445" TEXT="hier beginnt der Knoten" VSHIFT="7">
|
||||
<arrowlink COLOR="#b92b54" DESTINATION="ID_1404935910" ENDARROW="Default" ENDINCLINATION="101;8;" ID="Arrow_ID_1619759457" STARTARROW="None" STARTINCLINATION="53;82;"/>
|
||||
<icon BUILTIN="broken-line"/>
|
||||
</node>
|
||||
|
|
@ -2100,6 +2131,21 @@
|
|||
<node CREATED="1784131484193" ID="ID_169121010" MODIFIED="1784131533952" TEXT="Zugriff als Funktion darstellen ⟹ λ">
|
||||
<linktarget COLOR="#5a94d9" DESTINATION="ID_169121010" ENDARROW="Default" ENDINCLINATION="135;0;" ID="Arrow_ID_84801803" SOURCE="ID_1267317279" STARTARROW="None" STARTINCLINATION="76;6;"/>
|
||||
</node>
|
||||
<node CREATED="1784136965047" ID="ID_1744030854" MODIFIED="1784137036831" TEXT="zu kurz gedacht: es ist eine Zugriffs-Klammer">
|
||||
<richcontent TYPE="NOTE"><html>
|
||||
<head>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
Eine Operation zum Zugriff und eine Operation um den abgeschlossenen Zugriff zu markieren; zudem gibt es auch noch den Dispatcher.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
||||
</richcontent>
|
||||
<icon BUILTIN="messagebox_warning"/>
|
||||
</node>
|
||||
<node CREATED="1784137038242" ID="ID_1199476075" MODIFIED="1784137051164" TEXT="dann doch ein klassisches OO-Callback-Interface"/>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
|
|
|
|||
|
|
@ -29,5 +29,17 @@ __FAIL (string msg)
|
|||
std::abort();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marker class: any copy and copy construction prohibited
|
||||
*/
|
||||
class NonCopyable
|
||||
{
|
||||
protected:
|
||||
~NonCopyable() = default;
|
||||
NonCopyable() = default;
|
||||
NonCopyable (NonCopyable const&) = delete;
|
||||
NonCopyable& operator= (NonCopyable const&) = delete;
|
||||
};
|
||||
|
||||
|
||||
#endif /*COMMONS_H*/
|
||||
|
|
|
|||
|
|
@ -17,8 +17,10 @@
|
|||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
|
||||
using std::unique_ptr;
|
||||
using std::make_unique;
|
||||
|
||||
//-----------------hard-coded-config--
|
||||
const size_t RING_SIZ = 5;
|
||||
|
|
@ -29,56 +31,105 @@ const uint VIDEO_HEIGHT = 240;
|
|||
|
||||
|
||||
class UringVideoPlayer
|
||||
: NonCopyable
|
||||
{
|
||||
using VideoDisplay = EglDisplay<uint8_t, VIDEO_WIDTH,VIDEO_HEIGHT>;
|
||||
using Buffer = VideoDisplay::Buffer;
|
||||
|
||||
Glib::Dispatcher dispatchFrame_;
|
||||
enum BuffState { FREE, READ, SHOW };
|
||||
using AtomicBuffState = std::atomic<BuffState>;
|
||||
|
||||
using BufferAlloc = unique_ptr<Buffer[]>;
|
||||
using BuffStateAlloc = unique_ptr<AtomicBuffState[]>;
|
||||
|
||||
/**
|
||||
* Connector to the VideoDisplay for dispatching video data.
|
||||
* @note inherits from Glib::Dispatcher -> signal frame readiness
|
||||
* @remark the callback functions allow the VideoDisplay to interact
|
||||
* with the buffer data and state in a threadsafe manner
|
||||
*/
|
||||
class BufferDispatcher
|
||||
: public VideoDisplay::PixelFeed
|
||||
{
|
||||
Buffer* retrieveFrame() override;
|
||||
void completedFrame() override;
|
||||
|
||||
UringVideoPlayer& player_;
|
||||
public:
|
||||
BufferDispatcher (UringVideoPlayer& player)
|
||||
: player_{player}
|
||||
{ }
|
||||
};
|
||||
|
||||
size_t nextRead_{0};
|
||||
size_t nextShow_{0};
|
||||
BufferAlloc buffer_{};
|
||||
BuffStateAlloc buffState_{};
|
||||
std::atomic_bool playing_{false};
|
||||
BufferDispatcher dispatchFrame_;
|
||||
VideoDisplay videoDisplayer_;
|
||||
std::thread proactorThread_;
|
||||
|
||||
public:
|
||||
UringVideoPlayer (Gtk::Window& appWindow)
|
||||
: videoDisplayer_{appWindow, dispatchFrame_, [this]{ return retrieveCurrFrame(); }}
|
||||
: dispatchFrame_{*this}
|
||||
, videoDisplayer_{appWindow, dispatchFrame_}
|
||||
, proactorThread_{[this]{ playVideo(); }}
|
||||
{ }
|
||||
|
||||
void terminate();
|
||||
|
||||
private:
|
||||
void
|
||||
playVideo()
|
||||
{
|
||||
// allocate buffer data structure
|
||||
void playVideo();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Logic of the »Proactor Thread«:
|
||||
* - maintain the working buffers
|
||||
* - open the raw video file to retrieve data
|
||||
* - establish an IO_URING connection for reading
|
||||
* - periodic processing to fill and hand-off buffers
|
||||
*/
|
||||
void
|
||||
UringVideoPlayer::playVideo()
|
||||
{
|
||||
buffer_ = make_unique<Buffer[]> (RING_SIZ);
|
||||
buffState_ = make_unique<AtomicBuffState[]> (RING_SIZ);
|
||||
// initialise URing
|
||||
// ++ proactor-loop ++
|
||||
}
|
||||
}
|
||||
|
||||
Buffer*
|
||||
retrieveCurrFrame()
|
||||
{
|
||||
|
||||
UringVideoPlayer::Buffer*
|
||||
UringVideoPlayer::BufferDispatcher::retrieveFrame()
|
||||
{
|
||||
///////OOO threadsafe hand-over of current frame data
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UringVideoPlayer::BufferDispatcher::completedFrame()
|
||||
{
|
||||
///////OOO mark end of buffer access
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
UringVideoPlayer::terminate()
|
||||
{
|
||||
playing_.store (false, std::memory_order_release);
|
||||
proactorThread_.join();
|
||||
videoDisplayer_.cleanUp();
|
||||
}
|
||||
|
||||
|
||||
|
||||
unique_ptr<UringVideoPlayer>
|
||||
launchPlayer (Gtk::Window& appWindow)
|
||||
{
|
||||
appWindow.resize (VIDEO_WIDTH, VIDEO_HEIGHT);
|
||||
|
||||
auto player = std::make_unique<UringVideoPlayer> (appWindow);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
UringVideoPlayer::terminate()
|
||||
{
|
||||
///// signal stop to proactor
|
||||
videoDisplayer_.cleanUp();
|
||||
return std::make_unique<UringVideoPlayer> (appWindow);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -38,20 +38,35 @@ using std::string;
|
|||
*/
|
||||
template<typename PIX, uint WIDTH, uint HEIGHT>
|
||||
class EglDisplay
|
||||
: NonCopyable
|
||||
{
|
||||
|
||||
public:
|
||||
using Buffer = std::array<PIX, WIDTH*HEIGHT * 3 >;
|
||||
using PixFeed = std::function<Buffer*()>;
|
||||
|
||||
EglDisplay(Gtk::Window& appWindow
|
||||
,Glib::Dispatcher& dispatchFrame
|
||||
,PixFeed frameCallback
|
||||
)
|
||||
: retrieveFrame_{frameCallback}
|
||||
/**
|
||||
* Interface retrieve pixel data for video frames to display.
|
||||
* - this is a Glib::Dispatcher, and the attached slot shall be
|
||||
* triggered whenever data for a new frame is available
|
||||
* - pixel data is accessed (threadsafe) by invoking #retrieveFrame()
|
||||
* - when processing is complete and the data will no longer be needed,
|
||||
* access to the pixel buffer is yielded by invoking #completedFrame()
|
||||
*/
|
||||
class PixelFeed
|
||||
: public Glib::Dispatcher
|
||||
{
|
||||
public:
|
||||
virtual ~PixelFeed() { }; ///< this is an interface
|
||||
virtual Buffer* retrieveFrame() =0;
|
||||
virtual void completedFrame() =0;
|
||||
};
|
||||
|
||||
|
||||
EglDisplay (Gtk::Window& appWindow, PixelFeed& pixelFeed)
|
||||
: pixelFeed_{pixelFeed}
|
||||
{
|
||||
openDisplay (appWindow);
|
||||
dispatchFrame.connect([this]{ displayFrame(); });
|
||||
pixelFeed_.connect([this]{ displayFrame(); });
|
||||
}
|
||||
|
||||
void cleanUp();
|
||||
|
|
@ -71,7 +86,7 @@ class EglDisplay
|
|||
float scaleY_{1};
|
||||
|
||||
/** Callback functor to access current pixel data */
|
||||
PixFeed retrieveFrame_{};
|
||||
PixelFeed& pixelFeed_{};
|
||||
|
||||
void openDisplay (Gtk::Window&);
|
||||
void displayFrame();
|
||||
|
|
@ -321,7 +336,7 @@ void
|
|||
EglDisplay<PIX,WIDTH,HEIGHT>::displayFrame()
|
||||
{
|
||||
|
||||
auto currFrame = retrieveFrame_();
|
||||
auto currFrame = pixelFeed_.retrieveFrame();
|
||||
if (not currFrame) return;
|
||||
|
||||
// pick up current buffer with RGB data
|
||||
|
|
@ -340,6 +355,9 @@ EglDisplay<PIX,WIDTH,HEIGHT>::displayFrame()
|
|||
|
||||
// double-buffer flip, automatically invokes glFlush()
|
||||
eglSwapBuffers (display_, surface_);
|
||||
|
||||
// signal that processing of this frame is complete
|
||||
pixelFeed_.completedFrame();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue