{"id":784,"date":"2025-04-01T02:18:19","date_gmt":"2025-04-01T00:18:19","guid":{"rendered":"https:\/\/thurow.de\/?p=784"},"modified":"2025-08-20T05:46:08","modified_gmt":"2025-08-20T03:46:08","slug":"chatgpt-und-ich-sparlingspartner","status":"publish","type":"post","link":"https:\/\/thurow.de\/?p=784","title":{"rendered":"ChatGPT und ich &#8230; Sparringspartner"},"content":{"rendered":"\n<p>Kann man mit KI Programmierer ersetzen? Ich pers\u00f6nlich empfinde KI als ein sehr hilfreiches Werkzeug. Der Code der KI zeigt gerade bei neuen APIs einen guten Rahmen. Er kann aber nicht 1:1 \u00fcbernommen werden. Im Dialog entstehen jedoch sehr gute Vorlagen, aus denen man dann seinen Produktiv-Code ableiten kann. Hier ein Testbeipiel aus dem Bereich embedded Vision:<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Bitte zeige mir noch einmal das Beispiel mit libcamera und GStreamer<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Hier ist ein Beispiel, wie man <code>libcamera<\/code> und <code>GStreamer<\/code> zusammen verwenden kann, um mit einer Kamera (wie einer Raspberry Pi-Kamera) einen Video-Stream zu erzeugen und ihn durch GStreamer zu verarbeiten:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Voraussetzungen:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>libcamera<\/strong> muss installiert und korrekt konfiguriert sein.<\/li>\n\n\n\n<li><strong>GStreamer<\/strong> muss installiert sein.<\/li>\n\n\n\n<li>Eine Kamera muss angeschlossen und korrekt erkannt werden.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">1. Installieren der notwendigen Pakete<\/h3>\n\n\n\n<p>Stellen Sie sicher, dass die folgenden Pakete installiert sind:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nsudo apt update\nsudo apt install libcamera-tools gstreamer1.0-tools gstreamer1.0-plugins-base gstreamer1.0-plugins-good\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">2. Beispiel f\u00fcr die Verwendung von <code>libcamera<\/code> mit GStreamer<\/h3>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nlibcamera-vid -t 0 --inline --codec h264 --width 1280 --height 720 --framerate 30 | gst-launch-1.0 -v fdsrc ! h264parse ! avdec_h264 ! autovideosink\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Erkl\u00e4rung der Befehle:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>libcamera-vid<\/code>: Ein Kommandozeilen-Tool, das den Video-Stream von der Kamera aufnimmt.\n<ul class=\"wp-block-list\">\n<li><code>-t 0<\/code>: L\u00e4uft unbegrenzt.<\/li>\n\n\n\n<li><code>--inline<\/code>: F\u00fcgt NALUs (Network Abstraction Layer Units) in den Stream ein.<\/li>\n\n\n\n<li><code>--codec h264<\/code>: Verwendet den H.264 Video-Codec.<\/li>\n\n\n\n<li><code>--width 1280 --height 720<\/code>: Setzt die Aufl\u00f6sung des Streams auf 1280&#215;720.<\/li>\n\n\n\n<li><code>--framerate 30<\/code>: Setzt die Bildrate auf 30 fps.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><code>gst-launch-1.0<\/code>: GStreamer-Kommandzeilen-Tool, das den Video-Stream verarbeitet.\n<ul class=\"wp-block-list\">\n<li><code>fdsrc<\/code>: Liest den Stream von <code>libcamera-vid<\/code> als Datei-Deskriptor.<\/li>\n\n\n\n<li><code>h264parse<\/code>: Parsen des H.264-Streams.<\/li>\n\n\n\n<li><code>avdec_h264<\/code>: Dekodieren des H.264-Streams.<\/li>\n\n\n\n<li><code>autovideosink<\/code>: Automatisch das passende Video-Ausgabeger\u00e4t ausw\u00e4hlen.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. Alternativer Befehl f\u00fcr RTSP-Streaming<\/h3>\n\n\n\n<p>Falls du den Stream \u00fcber RTSP senden m\u00f6chtest, kannst du den Stream in GStreamer weiterleiten, um ihn \u00fcber das Netzwerk zu \u00fcbertragen:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Bash<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nlibcamera-vid -t 0 --inline --codec h264 --width 1280 --height 720 --framerate 30 | \\\ngst-launch-1.0 -v fdsrc ! h264parse ! rtph264pay ! udpsink host=127.0.0.1 port=5000\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li><code>rtph264pay<\/code>: Verpackt den H.264-Stream f\u00fcr RTP (Real-Time Transport Protocol).<\/li>\n\n\n\n<li><code>udpsink<\/code>: Sendet den RTP-Stream \u00fcber UDP an die angegebene IP-Adresse und den Port.<\/li>\n<\/ul>\n\n\n\n<p>Dieser Befehl streamt das Video \u00fcber UDP und kann auf einem anderen Ger\u00e4t empfangen werden.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">4. Empfangen des RTSP-Streams<\/h3>\n\n\n\n<p>Auf der Empf\u00e4ngerseite kann der Video-Stream mit GStreamer wiedergegeben werden:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">bash<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\ngst-launch-1.0 -v udpsrc port=5000 ! application\/x-rtp, payload=96 ! rtph264depay ! avdec_h264 ! autovideosink\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Fazit:<\/h3>\n\n\n\n<p>Mit <code>libcamera<\/code> und GStreamer kannst du auf einfache Weise Video-Streams von einer Kamera erfassen und weiterverarbeiten. Du kannst den Stream lokal anzeigen oder ihn \u00fcber ein Netzwerk \u00fcbertragen, um ihn an einem anderen Ger\u00e4t zu empfangen.<\/p>\n\n\n\n<p>Lass mich wissen, falls du weitere Anpassungen oder Erkl\u00e4rungen ben\u00f6tigst!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Sorry, mein Fehler, ich hatte den Tab im Browser unabsichtlich geschlossen und Du hast dadurch wahrscheinlich den Kontext verloren<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Kein Problem, das kann passieren! Wenn du mir den Kontext nochmal gibst oder beschreibst, was du genau ben\u00f6tigst, kann ich dir weiterhelfen. M\u00f6chtest du vielleicht das Beispiel mit <code>libcamera<\/code> und GStreamer noch einmal durchgehen oder etwas anderes anpassen?<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Ich m\u00f6chte einen Videostream in C++ realisieren. Um mehr Einfluss auf die Konfiguration zu haben, m\u00f6chte ich nicht das fertige GStreamer-Plugin von libcamera nutzen, sondern selber eine L\u00f6sung auf Basis von AppSrc coden<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Verstanden! Du m\u00f6chtest also einen Videostream in C++ realisieren und dabei <code>libcamera<\/code> verwenden, ohne auf das fertige GStreamer-Plugin zur\u00fcckzugreifen. Stattdessen m\u00f6chtest du den Stream \u00fcber <strong>AppSrc<\/strong> selbst in GStreamer einspeisen. Hier ist eine grobe Schritt-f\u00fcr-Schritt-Anleitung, wie du das machen kannst:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. \u00dcberblick \u00fcber das Setup<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>libcamera<\/strong> wird verwendet, um den Video-Stream von der Kamera zu erfassen.<\/li>\n\n\n\n<li>Der erfasste Stream wird dann in GStreamer \u00fcber <code>AppSrc<\/code> eingespeist.<\/li>\n\n\n\n<li><code>AppSrc<\/code> ist ein GStreamer-Element, das es dir erm\u00f6glicht, Daten aus einer externen Quelle (z. B. aus einer Datei oder einer API) in einen GStreamer-Pipeline-Flow einzuspeisen.<\/li>\n\n\n\n<li>Du wirst den Video-Stream m\u00f6glicherweise in einem H.264-Codec verpacken, bevor er an GStreamer weitergegeben wird.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. Vorbereitung des Systems<\/h3>\n\n\n\n<p>Stelle sicher, dass du die folgenden Pakete installiert hast:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Bash<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nsudo apt update\nsudo apt install libcamera-dev gstreamer1.0-dev gstreamer1.0-plugins-base gstreamer1.0-plugins-good\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">3. Grundlegender Codeaufbau<\/h3>\n\n\n\n<p>Hier ist ein einfaches C++-Beispiel, das zeigt, wie man <code>libcamera<\/code> zur Videoaufnahme verwendet und die Frames \u00fcber <code>AppSrc<\/code> an eine GStreamer-Pipeline \u00fcbergibt.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">a. CMakeLists.txt<\/h4>\n\n\n\n<p>Falls du ein CMake-Projekt verwendest, sollte deine <code>CMakeLists.txt<\/code> so aussehen:<\/p>\n\n\n\n<p>cmake:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ncmake_minimum_required(VERSION 3.10)\n\nproject(LibcameraGStreamerAppSrc)\n\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(LIBC CAMERA REQUIRED libcamera)\n\nfind_package(GStreamer REQUIRED)\nfind_package(GStreamerBase REQUIRED)\n\ninclude_directories(${LIBC_INCLUDE_DIRS} ${GSTREAMER_INCLUDE_DIRS})\n\nadd_executable(libcamera_gstreamer_appsrc main.cpp)\n\ntarget_link_libraries(libcamera_gstreamer_appsrc ${LIBC_LIBRARIES} ${GSTREAMER_LIBRARIES} ${GSTREAMERBASE_LIBRARIES})\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">b. C++-Code (main.cpp)<\/h4>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *avdec_h264 = gst_element_factory_make(&quot;avdec_h264&quot;, &quot;decoder&quot;);\n    GstElement *autovideosink = gst_element_factory_make(&quot;autovideosink&quot;, &quot;sink&quot;);\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264parse, avdec_h264, autovideosink, NULL);\n    gst_element_link_many(appsrc, h264parse, avdec_h264, autovideosink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-h264&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Erstelle einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_allocate(NULL, buffer-&gt;size(), NULL);\n            memcpy(GST_BUFFER_DATA(gst_buffer), buffer-&gt;data(), buffer-&gt;size());\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), gst_buffer);\n\n            gst_buffer_unref(gst_buffer); \/\/ Gib den GStreamer-Puffer nach dem Einspeisen wieder frei\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">4. Erkl\u00e4rung des Codes<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>libcamera initialisieren<\/strong>: Wir initialisieren <code>libcamera<\/code>, suchen die verf\u00fcgbare Kamera und konfigurieren sie (in diesem Fall auf 1280&#215;720 und 30 fps).<\/li>\n\n\n\n<li><strong>GStreamer Pipeline<\/strong>: Wir erstellen eine GStreamer-Pipeline mit <code>appsrc<\/code> als Quelle, H.264 Parsing (<code>h264parse<\/code>), H.264 Dekodierung (<code>avdec_h264<\/code>) und einer Video-Ausgabe (<code>autovideosink<\/code>).<\/li>\n\n\n\n<li><strong>Frame aus <\/strong><code><strong>libcamera<\/strong><\/code><strong> einlesen<\/strong>: In einer Schleife holen wir ein Frame von der Kamera und \u00fcbertragen es an GStreamer \u00fcber <code>appsrc_push_buffer<\/code>.<\/li>\n\n\n\n<li><strong>AppSrc<\/strong>: GStreamer erwartet, dass Daten in <code>AppSrc<\/code> gepusht werden, und diese werden dann \u00fcber die Pipeline weiterverarbeitet.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">5. Kompilieren<\/h3>\n\n\n\n<p>Um den Code zu kompilieren, f\u00fchre den folgenden Befehl aus:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">bash<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: bash; title: ; notranslate\" title=\"\">\nmkdir build\ncd build\ncmake ..\nmake\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">6. Ausf\u00fchren<\/h3>\n\n\n\n<p>Starte das Programm mit:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">bash<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n.\/libcamera_gstreamer_appsrc\n<\/pre><\/div>\n\n\n<p>Das Programm sollte den Video-Stream von der Kamera erfassen und ihn auf deinem Bildschirm anzeigen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">7. Erweiterungen<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Du kannst den Stream mit einem H.264-Encoder komprimieren, bevor du ihn \u00fcber die GStreamer-Pipeline weitergibst.<\/li>\n\n\n\n<li>Du kannst die Ausgabe in ein RTSP- oder WebRTC-Stream umleiten.<\/li>\n\n\n\n<li>Du kannst auch verschiedene andere GStreamer-Elemente hinzuf\u00fcgen, um die Pipeline zu erweitern (z. B. Video-Filter, Aufzeichnungsfunktionen, usw.).<\/li>\n<\/ul>\n\n\n\n<p>Wenn du Fragen hast oder etwas detaillierter erkl\u00e4rt haben m\u00f6chtest, lass es mich wissen!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Hier gef\u00e4llt mir die \u00dcberf\u00fchrung der Frames per memcpy von libcamera zu gstreamer \u00fcberhaupt nicht. Ich m\u00f6chte die Frames nicht kopieren. Bitte passe das Beispiel an<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Ah, ich verstehe! Du m\u00f6chtest die Frames von <code>libcamera<\/code> direkt an GStreamer weitergeben, ohne sie in den Speicher zu kopieren. Das ist eine gute Idee, um die Performance zu verbessern und Speicheroperationen zu minimieren.<\/p>\n\n\n\n<p>In diesem Fall m\u00fcssen wir den Puffer von <code>libcamera<\/code> so verwenden, dass er direkt in den GStreamer-Puffer eingef\u00fcgt wird, ohne eine Kopie zu erzeugen. Dies k\u00f6nnen wir erreichen, indem wir GStreamer&#8217;s <strong>Direct Memory Access (DMA)<\/strong> verwenden und die Puffer-Referenzen beibehalten.<\/p>\n\n\n\n<p>Hier ist die angepasste Version des Codes, um Frames ohne Kopieren zu \u00fcbertragen. Wir verwenden den <code>gst_buffer_new_wrapped()<\/code>-Mechanismus, um den libcamera-Puffer direkt in GStreamer zu integrieren.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Angepasster C++-Code<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *avdec_h264 = gst_element_factory_make(&quot;avdec_h264&quot;, &quot;decoder&quot;);\n    GstElement *autovideosink = gst_element_factory_make(&quot;autovideosink&quot;, &quot;sink&quot;);\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264parse, avdec_h264, autovideosink, NULL);\n    gst_element_link_many(appsrc, h264parse, avdec_h264, autovideosink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-h264&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Direktes Einf\u00fcgen des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Setze die Timestamp des Puffers (optional, aber empfohlen)\n            GST_BUFFER_PTS(gst_buffer) = gst_clock_get_time(gst_system_clock_get()) \/ 1000000; \/\/ Zeitstempel in Mikrosekunden\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), gst_buffer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Erkl\u00e4rung der \u00c4nderungen<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Verwendung von <\/strong><code><strong>gst_buffer_new_wrapped()<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Anstatt den Puffer von <code>libcamera<\/code> in den GStreamer-Puffer zu kopieren, verwenden wir <code>gst_buffer_new_wrapped()<\/code>, um direkt auf die Daten im Puffer von <code>libcamera<\/code> zuzugreifen.<\/li>\n\n\n\n<li>Der Puffer von <code>libcamera<\/code> wird direkt an GStreamer \u00fcbergeben, ohne dass eine Kopie gemacht wird.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Timestamp setzen<\/strong>:\n<ul class=\"wp-block-list\">\n<li>GStreamer verwendet Zeitstempel (PTS \u2013 Presentation Time Stamp), um den Stream richtig zu synchronisieren. In diesem Beispiel setzen wir den Zeitstempel auf die aktuelle Zeit in Mikrosekunden.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Puffer weitergeben<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Der Puffer wird \u00fcber die Funktion <code>appsrc_push_buffer()<\/code> in den GStreamer-Pipeline-Flow eingespeist, ohne dass Daten zwischen den Pufferobjekten kopiert werden.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Vorteile dieser Methode<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Kein Kopieren von Daten<\/strong>: Du vermeidest das Kopieren von Frame-Daten, was zu einer besseren Performance f\u00fchrt, insbesondere bei hochaufl\u00f6senden Video-Streams.<\/li>\n\n\n\n<li><strong>Direkter Zugriff<\/strong>: Durch die Verwendung von <code>gst_buffer_new_wrapped()<\/code> kannst du die Daten von <code>libcamera<\/code> direkt verwenden, ohne dass sie dupliziert werden.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Weitere Hinweise<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>GStreamer und Puffer-Timing<\/strong>: Du musst sicherstellen, dass die Puffer mit den richtigen Zeitstempeln versehen sind, damit GStreamer den Stream korrekt verarbeitet und anzeigt. In diesem Beispiel verwenden wir <code>gst_clock_get_time()<\/code>, um eine Zeitstempel zu setzen. Wenn du eine pr\u00e4zisere Steuerung \u00fcber das Timing ben\u00f6tigst (z. B. f\u00fcr Video-Synchronisation), musst du das Zeitmanagement in deiner Pipeline weiter anpassen.<\/li>\n\n\n\n<li><strong>Buffer Allocation<\/strong>: Wenn du regelm\u00e4\u00dfig mit sehr gro\u00dfen Puffergr\u00f6\u00dfen arbeitest oder sehr hohe Bildraten ben\u00f6tigst, k\u00f6nnte es n\u00f6tig sein, zus\u00e4tzliche Optimierungen vorzunehmen, um den Speicher effizient zu verwalten.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Fazit<\/h3>\n\n\n\n<p>Mit dieser Methode verwendest du die libcamera-Frames direkt, ohne sie zu kopieren, und \u00fcbergibst sie direkt an GStreamer \u00fcber <code>AppSrc<\/code>. Dadurch sparst du wertvolle CPU- und Speicherressourcen und erzielst eine effizientere Verarbeitung.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Ich h\u00e4tte nun gern eine Erweiterung bei den Zeitstempeln. Die Frames kommen bei mir \u00fcber MIPI zum Host. Ich m\u00f6chte bei den Zeitstempeln nicht die Systemzeit des Hosts, sondern des ISP verwenden. Bitte passe das Beispiel entsprechend an<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Um die Zeitstempel direkt vom ISP (Image Signal Processor) anstelle der Systemzeit des Hosts zu verwenden, musst du sicherstellen, dass du auf die korrekte Zeitquelle zugreifst, die der ISP liefert. Die genaue Implementierung h\u00e4ngt stark von der verwendeten Kamera und der Art, wie libcamera die Zeitstempel bereitstellt, ab.<\/p>\n\n\n\n<p>In <code>libcamera<\/code> ist es m\u00f6glich, dass die Puffer, die von der Kamera geliefert werden, bereits einen Zeitstempel enthalten. Dieser Zeitstempel ist in der Regel der, der vom ISP beim Erzeugen des Bildes gesetzt wird und stellt den Zeitpunkt dar, an dem das Bild tats\u00e4chlich von der Kamera aufgenommen wurde.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ansatz<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>\u00dcberpr\u00fcfung der Puffer in libcamera<\/strong>: Die <code>Buffer<\/code>-Objekte, die von <code>libcamera<\/code> zur\u00fcckgegeben werden, sollten Zeitstempel enthalten, die die Aufnahmezeit widerspiegeln. Wenn das der Fall ist, kannst du diesen Zeitstempel verwenden.<\/li>\n\n\n\n<li><strong>Verwendung des ISP-Zeitstempels<\/strong>: Anstatt den Zeitstempel mit der Systemzeit des Hosts zu setzen, kannst du den Zeitstempel des Puffers aus <code>libcamera<\/code> direkt \u00fcbernehmen.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Anpassung des Codes<\/h3>\n\n\n\n<p>Wir werden den Zeitstempel aus dem <code>libcamera<\/code>-Puffer entnehmen und diesen als PTS (Presentation Time Stamp) in den GStreamer-Puffer \u00fcbernehmen.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Angepasster Code:<\/h4>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *avdec_h264 = gst_element_factory_make(&quot;avdec_h264&quot;, &quot;decoder&quot;);\n    GstElement *autovideosink = gst_element_factory_make(&quot;autovideosink&quot;, &quot;sink&quot;);\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264parse, avdec_h264, autovideosink, NULL);\n    gst_element_link_many(appsrc, h264parse, avdec_h264, autovideosink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-h264&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Extrahiere den Zeitstempel des Puffers\n            const struct timeval *timestamp = buffer-&gt;timestamp();\n\n            \/\/ Direktes Einf\u00fcgen des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Setze den Zeitstempel auf den vom ISP gelieferten Zeitstempel\n            if (timestamp) {\n                \/\/ Konvertiere den timeval-Zeitstempel in eine Mikrosekunden-basierte Darstellung\n                guint64 pts = timestamp-&gt;tv_sec * 1000000 + timestamp-&gt;tv_usec;\n                GST_BUFFER_PTS(gst_buffer) = pts;  \/\/ Zeitstempel f\u00fcr GStreamer setzen\n            }\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), gst_buffer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Was wurde ge\u00e4ndert?<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Zeitstempel extrahieren<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Der Puffer von <code>libcamera<\/code> enth\u00e4lt einen Zeitstempel, der vom ISP stammt. Dieser wird durch <code>buffer-&gt;timestamp()<\/code> abgerufen, was ein <code>struct timeval<\/code>-Objekt ist.<\/li>\n\n\n\n<li>Das <code>timestamp<\/code>-Objekt enth\u00e4lt die Zeit in Sekunden (<code>tv_sec<\/code>) und Mikrosekunden (<code>tv_usec<\/code>).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Zeitstempel f\u00fcr GStreamer setzen<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Der <code>struct timeval<\/code> wird in Mikrosekunden umgerechnet und als PTS (Presentation Time Stamp) f\u00fcr den GStreamer-Puffer gesetzt.<\/li>\n\n\n\n<li>Die Mikrosekunden werden durch den Ausdruck <code>pts = timestamp-&gt;tv_sec * 1000000 + timestamp-&gt;tv_usec<\/code> berechnet.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Direktes Einf\u00fcgen von libcamera-Daten<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Wie zuvor wird der Puffer von <code>libcamera<\/code> direkt in einen GStreamer-Puffer \u00fcber <code>gst_buffer_new_wrapped()<\/code> eingef\u00fcgt, ohne Kopieren der Daten.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Was du noch beachten solltest:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Genauigkeit der Zeitstempel<\/strong>: Der Zeitstempel, der vom ISP kommt, ist in der Regel sehr pr\u00e4zise und gibt dir die tats\u00e4chliche Zeit der Aufnahme des Frames. Dies ist besonders wichtig, wenn du eine genaue Synchronisation von Video-Streams oder die Verarbeitung von mehreren Kameras ben\u00f6tigst.<\/li>\n\n\n\n<li><strong>Zugriff auf die Zeitstempel<\/strong>: Wenn du auf Probleme beim Zugriff auf die Zeitstempel st\u00f6\u00dft, stelle sicher, dass du die richtige Version von <code>libcamera<\/code> verwendest, da sich die APIs \u00fcber die Zeit \u00e4ndern k\u00f6nnen.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Fazit<\/h3>\n\n\n\n<p>Mit dieser Anpassung wird der Zeitstempel aus dem ISP genutzt, um die Frames korrekt in GStreamer zu synchronisieren. Du musst nun nicht mehr auf die Systemzeit des Hosts zugreifen, sondern verwendest die Zeit, die der ISP f\u00fcr jedes Bild gesetzt hat. Dies sollte eine genauere und konsistentere Synchronisation der Frames erm\u00f6glichen.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Welches Ausgabeformat des ISPs w\u00fcrdest Du als Basis der GStream-Pipeline empfehlen, um die VideoProzessingUnit zu entlasten?<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Um die <strong>Video Processing Unit (VPU)<\/strong> oder die CPU so weit wie m\u00f6glich zu entlasten, solltest du ein Ausgabeformat w\u00e4hlen, das bereits komprimiert oder in einer optimierten Form vorliegt. Dies reduziert den Aufwand f\u00fcr die Nachbearbeitung und Encoderarbeit in der GStreamer-Pipeline.<\/p>\n\n\n\n<p>Hier sind einige Optionen und Empfehlungen, die je nach Ziel und Systemkonfiguration geeignet sind:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">1. <strong>H.264 oder H.265 (HEVC)<\/strong><\/h3>\n\n\n\n<p>Das <strong>H.264<\/strong>&#8211; oder <strong>H.265 (HEVC)<\/strong>&#8211; Format bietet eine sehr gute Balance zwischen Kompressionsrate und Verarbeitungsaufwand. Viele moderne ISPs bieten die M\u00f6glichkeit, Videos direkt in diesen Formaten zu kodieren, wodurch die CPU oder VPU entlastet wird.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vorteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Sehr effiziente Video-Kompression.<\/li>\n\n\n\n<li>Weit verbreitet und gut von Hardware-Decodern unterst\u00fctzt.<\/li>\n\n\n\n<li>Geringe CPU-Last bei der Dekodierung, da viele Plattformen H.264\/H.265-Hardware-Decoder haben.<\/li>\n\n\n\n<li>Reduziert die Bandbreitenanforderungen, was bei Streaming und Speicherung von Vorteil ist.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Empfohlene Verwendung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Wenn du Streaming oder Aufzeichnung auf einem Ger\u00e4t mit einem Hardware-Decoder verwendest (z. B. Raspberry Pi, Jetson, oder andere Plattformen mit Hardware-unterst\u00fctztem H.264\/H.265-Decoder), ist es empfehlenswert, den ISP so zu konfigurieren, dass er direkt in H.264 oder H.265 kodiert.<\/li>\n\n\n\n<li>Wenn die Zielger\u00e4te H.264\/H.265-Decodierung unterst\u00fctzen, brauchst du keine zus\u00e4tzliche Videoverarbeitung in GStreamer oder der CPU.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Beispiel<\/strong>:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nGstCaps *caps = gst_caps_new_simple(&quot;video\/x-h264&quot;,\n                                    &quot;width&quot;, G_TYPE_INT, 1280,\n                                    &quot;height&quot;, G_TYPE_INT, 720,\n                                    &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                    NULL);\ng_object_set(appsrc, &quot;caps&quot;, caps, NULL);\ngst_caps_unref(caps);\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">2. <strong>RAW Video (YUV Formate)<\/strong><\/h3>\n\n\n\n<p>Ein anderes beliebtes Format ist <strong>RAW YUV (z. B. YUV420)<\/strong>. Hierbei handelt es sich um unkomprimierte Rohdaten, die von vielen ISPs direkt geliefert werden k\u00f6nnen. Diese Formate bieten hohe Flexibilit\u00e4t in der weiteren Verarbeitung und k\u00f6nnen von der Videoverarbeitungseinheit (VPU) oder der GPU effizient verarbeitet werden.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vorteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Keine Kompression, daher keine Qualit\u00e4tsverluste durch den Codec.<\/li>\n\n\n\n<li>Direkt zug\u00e4nglich f\u00fcr Softwareverarbeitung, falls du sp\u00e4ter Bildverarbeitung durchf\u00fchren m\u00f6chtest (z. B. Farbkorrekturen, Filter).<\/li>\n\n\n\n<li>Gut geeignet f\u00fcr Echtzeitverarbeitung, wenn keine starke Kompression ben\u00f6tigt wird.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Nachteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Gr\u00f6\u00dfere Bandbreitenanforderungen im Vergleich zu komprimierten Formaten (z. B. H.264).<\/li>\n\n\n\n<li>H\u00f6herer Speicherbedarf und m\u00f6glicherweise h\u00f6here CPU-Belastung, wenn du die Rohdaten dekodieren musst.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Empfohlene Verwendung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Wenn du nach der Erfassung noch umfangreiche Videoverarbeitung durch Software oder GPU vornehmen m\u00f6chtest, bevor du das Video streamst oder speicherst.<\/li>\n\n\n\n<li>Wenn du die Rohdaten f\u00fcr eine andere Art der Bildverarbeitung wie Filter oder KI-gest\u00fctzte Verarbeitung ben\u00f6tigst.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Beispiel<\/strong>:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nGstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                    &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                    &quot;width&quot;, G_TYPE_INT, 1280,\n                                    &quot;height&quot;, G_TYPE_INT, 720,\n                                    &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                    NULL);\ng_object_set(appsrc, &quot;caps&quot;, caps, NULL);\ngst_caps_unref(caps);\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">3. <strong>MJPEG (Motion JPEG)<\/strong><\/h3>\n\n\n\n<p>Das <strong>MJPEG<\/strong>&#8211; Format kodiert jedes Bild als JPEG und erzeugt eine Folge von JPEG-Bildern. Es kann in einigen Szenarien hilfreich sein, wenn du einfach nur eine schnelle und einfache Kompression ohne die Komplexit\u00e4t eines modernen Codecs ben\u00f6tigst.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vorteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Einfach zu implementieren.<\/li>\n\n\n\n<li>Geringe Latenz, da nur jedes Frame einzeln als JPEG komprimiert wird.<\/li>\n\n\n\n<li>Geringer Verarbeitungsaufwand auf der Dekodierungsseite, wenn du nur ein einfaches Streaming ben\u00f6tigst.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Nachteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Sehr ineffizient in Bezug auf die Kompression im Vergleich zu H.264 oder H.265.<\/li>\n\n\n\n<li>Hohe Bandbreitenanforderungen, insbesondere bei h\u00f6heren Aufl\u00f6sungen.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Empfohlene Verwendung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Wenn du eine sehr einfache L\u00f6sung f\u00fcr schnelle Video\u00fcbertragung ben\u00f6tigst, aber die Bandbreite nicht kritisch ist.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Beispiel<\/strong>:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nGstCaps *caps = gst_caps_new_simple(&quot;video\/x-mjpeg&quot;,\n                                    &quot;width&quot;, G_TYPE_INT, 1280,\n                                    &quot;height&quot;, G_TYPE_INT, 720,\n                                    &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                    NULL);\ng_object_set(appsrc, &quot;caps&quot;, caps, NULL);\ngst_caps_unref(caps);\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">4. <strong>Vulkan oder GPU-beschleunigte Formate<\/strong><\/h3>\n\n\n\n<p>Wenn du die M\u00f6glichkeit hast, eine <strong>GPU-basierte Verarbeitung<\/strong> oder <strong>Vulkan<\/strong> zu nutzen, k\u00f6nnte dies eine sehr effektive M\u00f6glichkeit sein, die VPU zu entlasten. Du k\u00f6nntest die Verarbeitung und den Encoding-Workflow direkt an die GPU \u00fcbergeben, was zu einer sehr hohen Performance f\u00fchren kann, insbesondere auf Systemen wie <strong>NVIDIA Jetson<\/strong> oder <strong>Raspberry Pi mit Hardware-Encoding<\/strong>.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vorteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Sehr effiziente Verarbeitung, da die GPU oder die dedizierte Videoeinheit (VPU) verwendet wird.<\/li>\n\n\n\n<li>Geeignet f\u00fcr Szenarien mit hoher Aufl\u00f6sung und hoher Bildrate.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Empfohlene Verwendung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Wenn dein System GPU- oder VPU-beschleunigte Videoverarbeitung unterst\u00fctzt und du eine hohe Performance ben\u00f6tigst.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>In diesem Fall w\u00fcrde GStreamer mit Plugins wie <code>v4l2h264enc<\/code> (f\u00fcr H.264) oder <code>v4l2h265enc<\/code> (f\u00fcr H.265) eine sehr gute Wahl sein.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Zusammenfassung der Empfehlungen<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>F\u00fcr Streaming und geringe CPU-Auslastung<\/strong>: Wenn du eine effiziente Kompression ohne hohe Bandbreitenanforderungen ben\u00f6tigst, solltest du das <strong>H.264<\/strong> oder <strong>H.265 (HEVC)<\/strong>-Format verwenden. Viele ISPs k\u00f6nnen das direkt liefern und die VPU kann den Stream dekodieren.<\/li>\n\n\n\n<li><strong>F\u00fcr Flexibilit\u00e4t und keine Kompression<\/strong>: Wenn du nach der Erfassung umfangreiche Bildverarbeitung durchf\u00fchren m\u00f6chtest, ist <strong>RAW YUV<\/strong> eine gute Wahl, allerdings mit h\u00f6heren Bandbreitenanforderungen.<\/li>\n\n\n\n<li><strong>F\u00fcr einfache und schnelle Kompression<\/strong>: Wenn du keine hohe Kompression ben\u00f6tigst, ist <strong>MJPEG<\/strong> eine einfache L\u00f6sung, die du nutzen kannst.<\/li>\n\n\n\n<li><strong>F\u00fcr hohe Performance auf VPU\/GPU-f\u00e4higen Systemen<\/strong>: Wenn du Hardware-Encoding auf deiner Plattform hast, solltest du in Erw\u00e4gung ziehen, H.264 oder H.265 mit Hardware-Encoder zu verwenden (z. B. <code>v4l2h264enc<\/code> in GStreamer).<\/li>\n<\/ul>\n\n\n\n<p>W\u00e4hle das Format basierend auf deiner Hardwareunterst\u00fctzung, deinen Bandbreitenanforderungen und den spezifischen Anforderungen deiner Anwendung!<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Missverst\u00e4ndnis, ich m\u00f6chte am Ende der GStreamer-Pipline H264 \u00fcbertragen. Es geht mir um den ANFANG der Pipeline, um das Format der Frames vom ISP.<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Ah, jetzt verstehe ich! Du m\u00f6chtest also wissen, welches <strong>Ausgabeformat vom ISP<\/strong> f\u00fcr den Anfang der GStreamer-Pipeline am besten geeignet ist, wenn du das Endformat als <strong>H.264<\/strong> beibehalten m\u00f6chtest. Dein Ziel ist es, die Verarbeitung zu optimieren, um die Video Processing Unit (VPU) oder die CPU zu entlasten.<\/p>\n\n\n\n<p>In diesem Fall liegt der Fokus darauf, wie du die Rohdaten direkt von der Kamera bzw. dem ISP in ein Format \u00fcbergibst, das f\u00fcr die GStreamer-Pipeline effizient verarbeitet werden kann. Hier geht es weniger um das Endformat (H.264) selbst, sondern darum, wie du das Format f\u00fcr die Frames vom ISP ausw\u00e4hlst, um die beste Performance und den geringsten Verarbeitungsaufwand zu erzielen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Empfohlene Ausgabeformate vom ISP<\/h3>\n\n\n\n<p>Wenn du H.264 als Endausgabeformat verwendest, aber den ISP in der Pipeline effizienter nutzen m\u00f6chtest, solltest du die Frames in einem <strong>kompakten, hardwarefreundlichen Format<\/strong> liefern, das wenig CPU-Leistung erfordert, bevor du sie in H.264 kodierst. Hier sind einige Formate, die du in Betracht ziehen solltest:<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">1. <strong>YUV420 (Planar)<\/strong><\/h4>\n\n\n\n<p><strong>YUV420<\/strong> (auch als <strong>YUV420p<\/strong> bezeichnet) ist ein sehr g\u00e4ngiges Format f\u00fcr Video, da es eine effiziente Kompression und Verarbeitung erm\u00f6glicht. Viele ISPs liefern Frames in diesem Format, da es eine gute Balance zwischen Qualit\u00e4t und Bandbreite bietet.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vorteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Energieeffizient und weniger Bandbreitenintensiv<\/strong>: Da es die Y-Komponente (Luminanz) mit h\u00f6herer Aufl\u00f6sung und die UV-Komponenten (Chrominanz) mit niedrigerer Aufl\u00f6sung speichert, eignet es sich gut f\u00fcr Hardwareverarbeitung.<\/li>\n\n\n\n<li><strong>Direkt von vielen ISPs geliefert<\/strong>: Das Format wird von vielen ISP-Chips\u00e4tzen nativ unterst\u00fctzt und eignet sich hervorragend f\u00fcr die anschlie\u00dfende H.264-Kodierung, da H.264 idealerweise auf YUV420-Bildern arbeitet.<\/li>\n\n\n\n<li><strong>Hardware- und Software-Kompatibilit\u00e4t<\/strong>: YUV420 wird sowohl von Software- als auch Hardware-Codecs und Decodern unterst\u00fctzt.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Empfohlene Verwendung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Wenn dein ISP YUV420 als Ausgabeformat bietet, solltest du dieses Format verwenden, da es effizient und direkt von der H.264-Kodierungseinheit verarbeitet werden kann, ohne zus\u00e4tzliche Dekodierung oder Verarbeitung.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Beispiel f\u00fcr GStreamer-Pipeline (YUV420)<\/strong>:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nGstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                    &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                    &quot;width&quot;, G_TYPE_INT, 1280,\n                                    &quot;height&quot;, G_TYPE_INT, 720,\n                                    &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                    NULL);\ng_object_set(appsrc, &quot;caps&quot;, caps, NULL);\ngst_caps_unref(caps);\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">2. <strong>NV12 (YUV 4:2:0, interleaved)<\/strong><\/h4>\n\n\n\n<p>Ein weiteres h\u00e4ufig verwendetes Format ist <strong>NV12<\/strong>, das eine Variante von YUV420 ist, jedoch interleaved (also in einem einzigen Block) gespeichert wird. Das NV12-Format ist insbesondere dann effizient, wenn es Hardware-Encoder gibt, die NV12 nativ verarbeiten k\u00f6nnen.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vorteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li><strong>Effizient bei Hardware-Codierung<\/strong>: Viele Hardware-Encoder (z.B. von NVIDIA oder ARM-basierte Plattformen wie Raspberry Pi) unterst\u00fctzen NV12 nativ, was eine besonders schnelle Kodierung erm\u00f6glicht.<\/li>\n\n\n\n<li><strong>Kompatibilit\u00e4t<\/strong>: NV12 wird h\u00e4ufig als Eingabeformat f\u00fcr Hardware-Encoder verwendet, was es zu einer sehr effizienten Wahl f\u00fcr Systeme macht, die H.264 oder H.265 kodieren.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Empfohlene Verwendung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Wenn dein ISP NV12 oder ein \u00e4hnliches interleaved YUV-Format als Ausgabeformat unterst\u00fctzt, kannst du es verwenden, wenn dein Ziel die Verwendung von Hardware-Encodern (z. B. VPU oder GPU) ist.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Beispiel f\u00fcr GStreamer-Pipeline (NV12)<\/strong>:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nGstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                    &quot;format&quot;, G_TYPE_STRING, &quot;NV12&quot;,\n                                    &quot;width&quot;, G_TYPE_INT, 1280,\n                                    &quot;height&quot;, G_TYPE_INT, 720,\n                                    &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                    NULL);\ng_object_set(appsrc, &quot;caps&quot;, caps, NULL);\ngst_caps_unref(caps);\n<\/pre><\/div>\n\n\n<h4 class=\"wp-block-heading\">3. <strong>MJPEG (JPEG-Frames)<\/strong><\/h4>\n\n\n\n<p>Ein weiteres Format, das von einigen ISPs unterst\u00fctzt wird, ist <strong>MJPEG<\/strong> (Motion JPEG), bei dem jedes Frame als JPEG-Bild kodiert wird. Dies ist eine einfachere Kompression, bei der jedes Bild unabh\u00e4ngig komprimiert wird.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Vorteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Einfach zu handhaben und bietet geringe Latenz, da jedes Bild einzeln kodiert wird.<\/li>\n\n\n\n<li>N\u00fctzlich f\u00fcr einfache \u00dcbertragungen von Einzelbildern in Echtzeit, wenn keine hohe Effizienz erforderlich ist.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Nachteile<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Weniger effizient als H.264 oder H.265, da es keine zeitliche Kompression \u00fcber mehrere Frames hinweg gibt.<\/li>\n\n\n\n<li>H\u00f6here Bandbreitenanforderungen und weniger effiziente Speicherung als H.264.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Empfohlene Verwendung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Falls du eine sehr einfache Kompression ben\u00f6tigst, aber ohne viel Aufwand bei der Nachbearbeitung, k\u00f6nnte MJPEG n\u00fctzlich sein. Es ist jedoch weniger effizient als H.264.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Beispiel f\u00fcr GStreamer-Pipeline (MJPEG)<\/strong>:<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\nGstCaps *caps = gst_caps_new_simple(&quot;video\/x-mjpeg&quot;,\n                                    &quot;width&quot;, G_TYPE_INT, 1280,\n                                    &quot;height&quot;, G_TYPE_INT, 720,\n                                    &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                    NULL);\ng_object_set(appsrc, &quot;caps&quot;, caps, NULL);\ngst_caps_unref(caps);\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Fazit: Bestes Format f\u00fcr den Anfang der Pipeline<\/h3>\n\n\n\n<p>Um die <strong>Video Processing Unit (VPU)<\/strong> oder die <strong>CPU zu entlasten<\/strong> und H.264 als Endformat zu erhalten, w\u00fcrde ich empfehlen, mit <strong>YUV420<\/strong> oder <strong>NV12<\/strong> zu arbeiten. Diese Formate sind sowohl <strong>kompakt als auch hardwarefreundlich<\/strong>, und sie eignen sich hervorragend f\u00fcr die <strong>H.264-Kodierung<\/strong>, da die meisten H.264-Encoder (insbesondere Hardware-Encoder) optimal mit diesen Formaten arbeiten.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>YUV420<\/strong> ist eine ausgezeichnete Wahl, wenn du eine hohe Kompatibilit\u00e4t und Effizienz ben\u00f6tigst.<\/li>\n\n\n\n<li><strong>NV12<\/strong> ist ebenfalls eine sehr gute Wahl, wenn du auf <strong>Hardware-Encoding<\/strong> angewiesen bist und eine optimierte Performance erreichen m\u00f6chtest.<\/li>\n<\/ul>\n\n\n\n<p>W\u00e4hle das Format je nach den Spezifikationen deines Systems und der Kamera-API. Wenn die Kamera einen dieser Formate direkt unterst\u00fctzt, solltest du es direkt verwenden, um die Effizienz zu maximieren.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Sehr sch\u00f6n. Bitte passe nun das Codebeispiel von vorhin so an, dass wir als Ausgangsformat des ISPs solche Formate verwenden<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Um das Codebeispiel so anzupassen, dass wir als Ausgangsformat des ISPs ein <strong>YUV420<\/strong> oder <strong>NV12<\/strong>&#8211; Format verwenden, m\u00fcssen wir zun\u00e4chst sicherstellen, dass die libcamera-API die Frames im gew\u00fcnschten Format liefert. Danach passen wir den GStreamer-Pipeline-Teil an, um die Frames im richtigen Format zu verarbeiten und in den H.264-Stream zu \u00fcberf\u00fchren.<\/p>\n\n\n\n<p>Da du H.264 als Endformat \u00fcbertragen m\u00f6chtest, ist es sinnvoll, die Frames zuerst in einem <strong>YUV420<\/strong>&#8211; Format zu empfangen und dann in H.264 zu kodieren. Wir setzen die Puffer im GStreamer auf das Format YUV420, wenn sie vom ISP geliefert werden.<\/p>\n\n\n\n<p>Hier ist der angepasste Code:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    config.pixelFormat = formats::YUV420;  \/\/ Setze das Format auf YUV420\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264enc = gst_element_factory_make(&quot;x264enc&quot;, &quot;encoder&quot;);  \/\/ Verwenden von x264enc f\u00fcr H.264\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *autovideosink = gst_element_factory_make(&quot;autovideosink&quot;, &quot;sink&quot;);\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264enc, h264parse, autovideosink, NULL);\n    gst_element_link_many(appsrc, h264enc, h264parse, autovideosink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                        &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Extrahiere den Zeitstempel des Puffers\n            const struct timeval *timestamp = buffer-&gt;timestamp();\n\n            \/\/ Direktes Einf\u00fcgen des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Setze den Zeitstempel auf den vom ISP gelieferten Zeitstempel\n            if (timestamp) {\n                \/\/ Konvertiere den timeval-Zeitstempel in eine Mikrosekunden-basierte Darstellung\n                guint64 pts = timestamp-&gt;tv_sec * 1000000 + timestamp-&gt;tv_usec;\n                GST_BUFFER_PTS(gst_buffer) = pts;  \/\/ Zeitstempel f\u00fcr GStreamer setzen\n            }\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), gst_buffer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Wichtige \u00c4nderungen und Erl\u00e4uterungen:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Kamera-Konfiguration<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Ich habe das Ausgabeformat des ISPs auf <strong>YUV420<\/strong> gesetzt, indem ich <code>config.pixelFormat = formats::YUV420;<\/code> verwendet habe. Dies stellt sicher, dass die Kamera die Frames im YUV420-Format ausgibt, das ein gutes Format f\u00fcr die H.264-Kodierung ist.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>GStreamer-Pipeline<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Statt des Decoders <code>avdec_h264<\/code> habe ich den Encoder <code>x264enc<\/code> verwendet, um die Roh-YUV-Daten in H.264 zu kodieren. Dieser GStreamer-Encoder verarbeitet YUV420-Eingaben und erzeugt den gew\u00fcnschten H.264-Stream.<\/li>\n\n\n\n<li><code>h264parse<\/code> wurde beibehalten, um den erzeugten H.264-Stream zu analysieren und f\u00fcr die Ausgabe vorzubereiten.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>GStreamer-Kapselung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Die Kapselung von <strong>YUV420<\/strong> als Eingabekapsel f\u00fcr <code>appsrc<\/code> wurde so eingestellt, dass sie zu den von libcamera gelieferten Frames passt.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Zeitstempel<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Der Zeitstempel vom ISP wird in den GStreamer-Puffer \u00fcbernommen, um die zeitliche Synchronisation zu gew\u00e4hrleisten.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Zusammenfassung:<\/h3>\n\n\n\n<p>In diesem Beispiel verwenden wir <strong>YUV420<\/strong> als Ausgangsformat f\u00fcr die Frames, die vom ISP geliefert werden. Diese Frames werden dann \u00fcber GStreamer in H.264 kodiert und weiterverarbeitet. Wenn dein ISP bereits YUV420 liefert (was oft der Fall ist), dann ist dies die effizienteste und einfachste L\u00f6sung, um die Hardware-Encoder mit minimalem Overhead zu verwenden.<\/p>\n\n\n\n<p>Wenn du jedoch ein anderes Format wie <strong>NV12<\/strong> verwenden m\u00f6chtest, kannst du das in der Pipeline genauso handhaben, indem du die <code>format<\/code>-Option in <code>appsrc<\/code> entsprechend \u00e4nderst.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Jetzt m\u00f6chte ich noch als Ausgang des GStreamers per UDP streamen<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Um den GStreamer-Stream \u00fcber <strong>UDP<\/strong> zu senden, musst du die Pipeline so anpassen, dass sie einen <strong>UDP-Stream<\/strong> erzeugt. GStreamer bietet daf\u00fcr das <code>udpsink<\/code>-Element, mit dem du Video- oder Audio-Daten \u00fcber UDP an eine spezifische IP-Adresse und einen Port senden kannst.<\/p>\n\n\n\n<p>Ich werde das Beispiel anpassen, sodass der <strong>H.264-Stream<\/strong> \u00fcber <strong>UDP<\/strong> gesendet wird. Die grundlegenden Schritte beinhalten:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Hinzuf\u00fcgen eines <\/strong><code><strong>udpsink<\/strong><\/code><strong>-Elements zur GStreamer-Pipeline<\/strong>.<\/li>\n\n\n\n<li><strong>Setzen der Ziel-IP-Adresse und des Ports<\/strong>.<\/li>\n\n\n\n<li><strong>Einstellung der GStreamer-Pipeline f\u00fcr den UDP-Stream<\/strong>.<\/li>\n<\/ol>\n\n\n\n<p>Hier ist der angepasste Code:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Angepasster Code mit UDP-Streaming:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    config.pixelFormat = formats::YUV420;  \/\/ Setze das Format auf YUV420\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264enc = gst_element_factory_make(&quot;x264enc&quot;, &quot;encoder&quot;);  \/\/ Verwenden von x264enc f\u00fcr H.264\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *udpsink = gst_element_factory_make(&quot;udpsink&quot;, &quot;sink&quot;);\n\n    if (!pipeline || !appsrc || !h264enc || !h264parse || !udpsink) {\n        cerr &lt;&lt; &quot;Fehler beim Erstellen der GStreamer-Elemente!&quot; &lt;&lt; endl;\n        return -1;\n    }\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264enc, h264parse, udpsink, NULL);\n    gst_element_link_many(appsrc, h264enc, h264parse, udpsink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                        &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ UDP-Einstellungen f\u00fcr udpsink setzen\n    g_object_set(udpsink, &quot;host&quot;, &quot;127.0.0.1&quot;, &quot;port&quot;, 5000, NULL);  \/\/ Beispiel-IP und Port f\u00fcr den Empfang\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Extrahiere den Zeitstempel des Puffers\n            const struct timeval *timestamp = buffer-&gt;timestamp();\n\n            \/\/ Direktes Einf\u00fcgen des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Setze den Zeitstempel auf den vom ISP gelieferten Zeitstempel\n            if (timestamp) {\n                \/\/ Konvertiere den timeval-Zeitstempel in eine Mikrosekunden-basierte Darstellung\n                guint64 pts = timestamp-&gt;tv_sec * 1000000 + timestamp-&gt;tv_usec;\n                GST_BUFFER_PTS(gst_buffer) = pts;  \/\/ Zeitstempel f\u00fcr GStreamer setzen\n            }\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), gst_buffer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Wichtige \u00c4nderungen:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>UDP-Sink hinzuf\u00fcgen<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Ich habe das <code>udpsink<\/code>-Element zur Pipeline hinzugef\u00fcgt, das die H.264-Daten \u00fcber <strong>UDP<\/strong> streamt. Du kannst die Ziel-IP-Adresse und den Port anpassen. In diesem Beispiel wird der UDP-Stream an <code>127.0.0.1:5000<\/code> gesendet.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Konfiguration des <\/strong><code><strong>udpsink<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Das <code>udpsink<\/code>-Element wurde mit den Parametern <code>host<\/code> und <code>port<\/code> konfiguriert. Die IP-Adresse (<code>127.0.0.1<\/code>) und der Port (<code>5000<\/code>) k\u00f6nnen nat\u00fcrlich angepasst werden, um den Stream an die gew\u00fcnschte Zieladresse zu senden.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>GStreamer-Elemente<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Die Pipeline besteht nun aus <code>appsrc<\/code>, dem H.264-Encoder (<code>x264enc<\/code>), dem <code>h264parse<\/code>-Element und dem <code>udpsink<\/code>-Element, das den UDP-Stream ausgibt.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">GStreamer-Pipeline-\u00dcbersicht:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code><strong>appsrc<\/strong><\/code>: Erh\u00e4lt die Frames von libcamera und speist sie in die GStreamer-Pipeline ein.<\/li>\n\n\n\n<li><code><strong>x264enc<\/strong><\/code>: Kodiert die YUV420-Daten in den H.264-Stream.<\/li>\n\n\n\n<li><code><strong>h264parse<\/strong><\/code>: Analysiert den H.264-Stream, um ihn f\u00fcr die UDP-\u00dcbertragung vorzubereiten.<\/li>\n\n\n\n<li><code><strong>udpsink<\/strong><\/code>: Sendet den H.264-Stream \u00fcber UDP an die angegebene IP-Adresse und den Port.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Testen des UDP-Streams:<\/h3>\n\n\n\n<p>Um den UDP-Stream zu empfangen und abzuspielen, kannst du beispielsweise <code>gst-launch<\/code> auf der Empfangsseite verwenden, um den Stream zu dekodieren und anzuzeigen. Ein einfaches Beispiel f\u00fcr die Empfangsseite w\u00e4re:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">bash<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ngst-launch-1.0 udpsrc address=127.0.0.1 port=5000 ! application\/x-h264, stream-format=avc ! h264parse ! avdec_h264 ! autovideosink\n<\/pre><\/div>\n\n\n<p>Dies stellt sicher, dass der H.264-Stream korrekt empfangen und wiedergegeben wird.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Fazit:<\/h3>\n\n\n\n<p>Mit dieser Anpassung streamst du nun den H.264-Video-Stream \u00fcber <strong>UDP<\/strong> in deinem Netzwerk. Die Konfiguration des <code>udpsink<\/code> erm\u00f6glicht es dir, die Daten an eine beliebige Ziel-IP und -Port zu senden.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Sehr sch\u00f6n. Aber nun h\u00e4tte ich gern noch RTP, um den Jitter \u00fcber das Netzwerz zu reduzieren<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Um den Stream \u00fcber <strong>RTP (Real-time Transport Protocol)<\/strong> zu \u00fcbertragen, anstelle von direktem UDP, ben\u00f6tigen wir in GStreamer den <code>rtph264pay<\/code>-Payloader, der die H.264-Daten in RTP-Pakete verpackt, sowie ein <code>udpsink<\/code>-Element f\u00fcr den Versand \u00fcber UDP.<\/p>\n\n\n\n<p>Die Verwendung von RTP hat den Vorteil, dass es eingebauten Mechanismen zur Handhabung von Jitter und Netzwerkbedingungen bietet. Wenn du RTP nutzt, kannst du auch Optionen wie <strong>RTCP<\/strong> (Real-Time Control Protocol) f\u00fcr eine bessere \u00dcberwachung und Steuerung des Streams hinzuf\u00fcgen.<\/p>\n\n\n\n<p>Hier ist eine erweiterte Version des Codes, bei dem die <strong>RTP-\u00dcbertragung<\/strong> f\u00fcr den H.264-Stream genutzt wird:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Angepasster Code f\u00fcr RTP-Streaming:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    config.pixelFormat = formats::YUV420;  \/\/ Setze das Format auf YUV420\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264enc = gst_element_factory_make(&quot;x264enc&quot;, &quot;encoder&quot;);  \/\/ Verwenden von x264enc f\u00fcr H.264\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *rtph264pay = gst_element_factory_make(&quot;rtph264pay&quot;, &quot;rtp-payload&quot;);\n    GstElement *udpsink = gst_element_factory_make(&quot;udpsink&quot;, &quot;sink&quot;);\n\n    if (!pipeline || !appsrc || !h264enc || !h264parse || !rtph264pay || !udpsink) {\n        cerr &lt;&lt; &quot;Fehler beim Erstellen der GStreamer-Elemente!&quot; &lt;&lt; endl;\n        return -1;\n    }\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n    gst_element_link_many(appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                        &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ UDP-Einstellungen f\u00fcr udpsink setzen\n    g_object_set(udpsink, &quot;host&quot;, &quot;127.0.0.1&quot;, &quot;port&quot;, 5000, NULL);  \/\/ Beispiel-IP und Port f\u00fcr den Empfang\n\n    \/\/ RTP-Payload-Einstellungen konfigurieren\n    g_object_set(rtph264pay, &quot;pt&quot;, 96, NULL);  \/\/ Payload-Typ 96 f\u00fcr H.264\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Extrahiere den Zeitstempel des Puffers\n            const struct timeval *timestamp = buffer-&gt;timestamp();\n\n            \/\/ Direktes Einf\u00fcgen des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Setze den Zeitstempel auf den vom ISP gelieferten Zeitstempel\n            if (timestamp) {\n                \/\/ Konvertiere den timeval-Zeitstempel in eine Mikrosekunden-basierte Darstellung\n                guint64 pts = timestamp-&gt;tv_sec * 1000000 + timestamp-&gt;tv_usec;\n                GST_BUFFER_PTS(gst_buffer) = pts;  \/\/ Zeitstempel f\u00fcr GStreamer setzen\n            }\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), gst_buffer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Wichtige \u00c4nderungen:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Hinzuf\u00fcgen von <\/strong><code><strong>rtph264pay<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Das Element <code>rtph264pay<\/code> wurde in die Pipeline eingef\u00fcgt, um die H.264-Daten in <strong>RTP<\/strong>-Pakete zu verpacken. Dies sorgt daf\u00fcr, dass die H.264-Daten in einem formatgerechten RTP-Stream \u00fcbertragen werden.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Konfiguration des <\/strong><code><strong>rtph264pay<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Der <strong>Payload-Typ (PT)<\/strong> f\u00fcr H.264 wurde auf <code>96<\/code> gesetzt (ein h\u00e4ufig verwendeter Payload-Typ f\u00fcr H.264 in RTP). Es gibt andere m\u00f6gliche Werte f\u00fcr RTP, aber 96 wird typischerweise f\u00fcr H.264 verwendet.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>UDP-Sink<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Das <code>udpsink<\/code>-Element bleibt gleich, aber jetzt empf\u00e4ngt es RTP-Daten, die in <code>rtph264pay<\/code> verpackt wurden.<\/li>\n\n\n\n<li>Du kannst die IP-Adresse und den Port f\u00fcr den UDP-Empf\u00e4nger weiterhin konfigurieren (im Beispiel auf <code>127.0.0.1:5000<\/code>).<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Erkl\u00e4rung der RTP-\u00dcbertragung:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>RTP (Real-time Transport Protocol)<\/strong>: RTP bietet eine strukturierte M\u00f6glichkeit, Echtzeit-Daten wie Audio und Video zu \u00fcbertragen. Es enth\u00e4lt Mechanismen zur Identifikation des Streams, Zeitstempelung und zur Handhabung von Jitter. Dies macht es robuster gegen\u00fcber Netzwerkverz\u00f6gerungen im Vergleich zu einer direkten UDP-\u00dcbertragung.<\/li>\n\n\n\n<li><code><strong>rtph264pay<\/strong><\/code>: Dieser GStreamer-Element sorgt daf\u00fcr, dass der H.264-Stream in RTP-Pakete verpackt wird. Diese Pakete werden dann \u00fcber UDP an die angegebene Ziel-IP und -Port gesendet.<\/li>\n\n\n\n<li><code><strong>udpsink<\/strong><\/code>: Sendet die RTP-Pakete \u00fcber das Netzwerk an die Ziel-IP und den Port. In diesem Beispiel an <code>127.0.0.1:5000<\/code>.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Testen des RTP-Streams:<\/h3>\n\n\n\n<p>Zum Testen des RTP-Streams kannst du <code>gst-launch<\/code> oder <code>gst-python<\/code> verwenden, um die RTP-Pakete zu empfangen und wiederzugeben.<\/p>\n\n\n\n<p>Hier ist ein Beispiel f\u00fcr das Empfangen und Anzeigen des RTP-Streams:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">bash<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\ngst-launch-1.0 udpsrc address=127.0.0.1 port=5000 ! application\/x-rtp, encoding-name=H264 ! rtph264depay ! avdec_h264 ! autovideosink\n<\/pre><\/div>\n\n\n<ul class=\"wp-block-list\">\n<li><code><strong>udpsrc<\/strong><\/code>: Empf\u00e4ngt die RTP-Pakete von <code>127.0.0.1:5000<\/code>.<\/li>\n\n\n\n<li><code><strong>rtph264depay<\/strong><\/code>: Entfernt die RTP-Verpackung und extrahiert den H.264-Stream.<\/li>\n\n\n\n<li><code><strong>avdec_h264<\/strong><\/code>: Dekodiert den H.264-Stream.<\/li>\n\n\n\n<li><code><strong>autovideosink<\/strong><\/code>: Zeigt das Video an.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Fazit:<\/h3>\n\n\n\n<p>Mit dieser \u00c4nderung hast du nun einen robusten RTP-Stream, der \u00fcber das Netzwerk \u00fcbertragen wird. RTP hilft dabei, den Jitter zu verringern und den Stream stabiler zu machen, was besonders bei Echtzeit-Videoanwendungen wichtig ist.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Jetzt w\u00e4re mir noch wichtig, dieses Beispiel in Sachen Speicherverwaltung sicherer zu machen. Bitte sperre die Verwendung der frame buffer, solange diese nicht von GStreamer in seiner pipe freigegeben wurden.<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Um die Speicherverwaltung sicherer zu gestalten, sollten wir sicherstellen, dass der Frame-Puffer nicht verwendet oder freigegeben wird, bevor GStreamer den Puffer verarbeitet hat. Eine M\u00f6glichkeit, dies zu erreichen, ist die Verwendung von <strong>GStreamer-Pufferfreigabe-Mechanismen<\/strong> und <strong>GMutex<\/strong> (Mutex) zur Synchronisierung zwischen dem Thread, der die Frames von libcamera abruft, und dem GStreamer-Pipeline-Thread, der die Frames verarbeitet.<\/p>\n\n\n\n<p>Der Puffer muss im richtigen Moment freigegeben werden \u2013 wenn GStreamer die Verarbeitung abgeschlossen hat. In GStreamer gibt es daf\u00fcr eine Callback-Funktion f\u00fcr den Umgang mit Puffern, die sicherstellt, dass ein Puffer nicht zu fr\u00fch freigegeben wird.<\/p>\n\n\n\n<p>Die L\u00f6sung ist eine Kombination aus:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Verwendung von <\/strong><code><strong>gst_buffer_ref()<\/strong><\/code><strong> und <\/strong><code><strong>gst_buffer_unref()<\/strong><\/code>: Zum sicheren Referenzieren und Unrefereenzieren der Puffer.<\/li>\n\n\n\n<li><strong>Verwendung von <\/strong><code><strong>g_signal_connect()<\/strong><\/code>: Um eine R\u00fcckruffunktion zu registrieren, die nach der Verarbeitung des Puffers von GStreamer aufgerufen wird.<\/li>\n\n\n\n<li><strong>Synchronisation durch Mutex<\/strong>: Um sicherzustellen, dass der Zugriff auf den Frame-Puffer gesch\u00fctzt ist.<\/li>\n<\/ol>\n\n\n\n<p>Hier ist ein Beispiel, wie du dies implementieren kannst:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Erweiterter Code zur sicheren Speicherverwaltung:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n#include &amp;lt;iostream&gt;\n#include &amp;lt;libcamera\/libcamera.h&gt;\n#include &amp;lt;gst\/gst.h&gt;\n#include &amp;lt;gst\/app\/app.h&gt;\n#include &amp;lt;glib.h&gt; \/\/ F\u00fcr GMutex\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data);\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    config.pixelFormat = formats::YUV420;  \/\/ Setze das Format auf YUV420\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264enc = gst_element_factory_make(&quot;x264enc&quot;, &quot;encoder&quot;);  \/\/ Verwenden von x264enc f\u00fcr H.264\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *rtph264pay = gst_element_factory_make(&quot;rtph264pay&quot;, &quot;rtp-payload&quot;);\n    GstElement *udpsink = gst_element_factory_make(&quot;udpsink&quot;, &quot;sink&quot;);\n\n    if (!pipeline || !appsrc || !h264enc || !h264parse || !rtph264pay || !udpsink) {\n        cerr &amp;lt;&amp;lt; &quot;Fehler beim Erstellen der GStreamer-Elemente!&quot; &amp;lt;&amp;lt; endl;\n        return -1;\n    }\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n    gst_element_link_many(appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                        &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ UDP-Einstellungen f\u00fcr udpsink setzen\n    g_object_set(udpsink, &quot;host&quot;, &quot;127.0.0.1&quot;, &quot;port&quot;, 5000, NULL);  \/\/ Beispiel-IP und Port f\u00fcr den Empfang\n\n    \/\/ RTP-Payload-Einstellungen konfigurieren\n    g_object_set(rtph264pay, &quot;pt&quot;, 96, NULL);  \/\/ Payload-Typ 96 f\u00fcr H.264\n\n    \/\/ Mutex zum Schutz der Buffers\n    GMutex buffer_mutex;\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&amp;lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Direktes Einf\u00fcgen des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Sicherstellen, dass der Puffer w\u00e4hrend der Verarbeitung nicht ver\u00e4ndert wird\n            g_mutex_lock(&amp;amp;buffer_mutex);\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), gst_buffer);\n\n            \/\/ R\u00fcckruffunktion aufrufen, wenn der Puffer freigegeben wird\n            g_signal_connect(gst_buffer, &quot;destroy&quot;, G_CALLBACK(on_buffer_flushed), NULL);\n            \n            \/\/ Puffer-Referenz erh\u00f6hen, um ihn zu sch\u00fctzen\n            gst_buffer_ref(gst_buffer);\n\n            g_mutex_unlock(&amp;amp;buffer_mutex);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &amp;lt;&amp;lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &amp;lt;&amp;lt; endl;\n    }\n}\n\n\/\/ R\u00fcckruffunktion, die aufgerufen wird, wenn der Puffer freigegeben wird\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data) {\n    \/\/ Hier kannst du den Puffer freigeben, nachdem GStreamer ihn verarbeitet hat\n    gst_buffer_unref(buffer);  \/\/ Den Puffer freigeben\n    cout &amp;lt;&amp;lt; &quot;Puffer wurde freigegeben!&quot; &amp;lt;&amp;lt; endl;\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Erkl\u00e4rungen zu den \u00c4nderungen:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Mutex zum Schutz der Puffer<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Ein <code>GMutex<\/code> (<code>buffer_mutex<\/code>) wird verwendet, um den Zugriff auf die Puffer zu synchronisieren. Dieser Mutex sorgt daf\u00fcr, dass der Puffer w\u00e4hrend der Verarbeitung durch GStreamer nicht gleichzeitig von mehreren Threads bearbeitet wird.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Pufferreferenzen mit <\/strong><code><strong>gst_buffer_ref()<\/strong><\/code><strong> und <\/strong><code><strong>gst_buffer_unref()<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Wenn ein Puffer von libcamera empfangen wird, wird er mit <code>gst_buffer_ref()<\/code> referenziert, um sicherzustellen, dass der Puffer nicht vorzeitig freigegeben wird.<\/li>\n\n\n\n<li>Wenn GStreamer den Puffer verarbeitet und ihn nicht mehr ben\u00f6tigt, wird der Puffer durch die R\u00fcckruffunktion <code>on_buffer_flushed<\/code> freigegeben (<code>gst_buffer_unref()<\/code>).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>R\u00fcckruffunktion <\/strong><code><strong>on_buffer_flushed()<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Diese Funktion wird aufgerufen, wenn GStreamer den Puffer verarbeitet und ihn &#8222;verlassen&#8220; hat. In dieser Funktion erfolgt die endg\u00fcltige Freigabe des Puffers.<\/li>\n\n\n\n<li>Dies stellt sicher, dass der Puffer nicht zu fr\u00fch freigegeben wird, bevor GStreamer ihn vollst\u00e4ndig verarbeitet hat.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Verwendung von <\/strong><code><strong>g_mutex_lock<\/strong><\/code><strong> und <\/strong><code><strong>g_mutex_unlock<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Der Mutex sorgt daf\u00fcr, dass der Zugriff auf den Puffer im Thread, der die Frames von libcamera empf\u00e4ngt, gesch\u00fctzt ist. Dies verhindert race conditions, die zu unvorhersehbarem Verhalten f\u00fchren k\u00f6nnten.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Fazit:<\/h3>\n\n\n\n<p>Mit diesen \u00c4nderungen stellen wir sicher, dass der Speicher der Puffer sicher verwaltet wird, indem wir eine Synchronisation zwischen den Threads hinzuf\u00fcgen. Das bedeutet, dass Puffer nicht von libcamera w\u00e4hrend der Verarbeitung durch GStreamer ge\u00e4ndert oder freigegeben werden. GStreamer gibt die Puffer erst dann frei, wenn sie vollst\u00e4ndig verarbeitet wurden, wodurch eine korrekte und sichere Speicherverwaltung erreicht wird.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Ich verstehe dieses Beispiel nicht. Ich sehe den lock f\u00fcr GStreamer mittels g_mutex_lock, aber nicht den lock f\u00fcr libcamera<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Du hast recht! Im vorherigen Beispiel habe ich nur den Lock f\u00fcr den GStreamer-Teil der Pipeline eingef\u00fchrt, aber nicht den Lock f\u00fcr den Zugriff auf die Puffer von libcamera. Das ist ein wichtiger Punkt, um sicherzustellen, dass der Zugriff auf die Puffer sowohl bei der Erfassung der Frames von libcamera als auch beim Einspeisen in die GStreamer-Pipeline korrekt synchronisiert wird.<\/p>\n\n\n\n<p>Damit wir die Puffer sowohl w\u00e4hrend des Zugriffs von libcamera als auch beim Schieben in die GStreamer-Pipeline sch\u00fctzen k\u00f6nnen, m\u00fcssen wir <strong>auch f\u00fcr libcamera einen Lock hinzuf\u00fcgen<\/strong>. Dabei m\u00fcssen wir sicherstellen, dass der Zugriff auf den Frame-Puffer nicht gleichzeitig von beiden Threads (dem libcamera-Thread und dem GStreamer-Thread) durchgef\u00fchrt wird.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">L\u00f6sung: Zwei Locks (f\u00fcr libcamera und GStreamer) einf\u00fchren<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ein Mutex sch\u00fctzt den Zugriff auf den Puffer, wenn er von libcamera abgeholt wird.<\/li>\n\n\n\n<li>Ein weiterer Mutex sch\u00fctzt den Zugriff, wenn der Puffer in GStreamer eingef\u00fcgt wird.<\/li>\n<\/ul>\n\n\n\n<p>Hier ist die angepasste Version des Codes mit den beiden Mutexen:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Angepasster Code mit zwei Mutexen:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n#include &lt;glib.h&gt; \/\/ F\u00fcr GMutex\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data);\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    config.pixelFormat = formats::YUV420;  \/\/ Setze das Format auf YUV420\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264enc = gst_element_factory_make(&quot;x264enc&quot;, &quot;encoder&quot;);  \/\/ Verwenden von x264enc f\u00fcr H.264\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *rtph264pay = gst_element_factory_make(&quot;rtph264pay&quot;, &quot;rtp-payload&quot;);\n    GstElement *udpsink = gst_element_factory_make(&quot;udpsink&quot;, &quot;sink&quot;);\n\n    if (!pipeline || !appsrc || !h264enc || !h264parse || !rtph264pay || !udpsink) {\n        cerr &lt;&lt; &quot;Fehler beim Erstellen der GStreamer-Elemente!&quot; &lt;&lt; endl;\n        return -1;\n    }\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n    gst_element_link_many(appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                        &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ UDP-Einstellungen f\u00fcr udpsink setzen\n    g_object_set(udpsink, &quot;host&quot;, &quot;127.0.0.1&quot;, &quot;port&quot;, 5000, NULL);  \/\/ Beispiel-IP und Port f\u00fcr den Empfang\n\n    \/\/ RTP-Payload-Einstellungen konfigurieren\n    g_object_set(rtph264pay, &quot;pt&quot;, 96, NULL);  \/\/ Payload-Typ 96 f\u00fcr H.264\n\n    \/\/ Mutex zum Schutz der Buffers\n    GMutex buffer_mutex_libcamera;  \/\/ Mutex f\u00fcr den Zugriff auf libcamera\n    GMutex buffer_mutex_gstreamer;  \/\/ Mutex f\u00fcr den Zugriff auf GStreamer\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera (mit Lock f\u00fcr libcamera)\n        g_mutex_lock(&amp;buffer_mutex_libcamera);\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n        g_mutex_unlock(&amp;buffer_mutex_libcamera);\n\n        if (buffer) {\n            \/\/ Direktes Einf\u00fcgen des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Sicherstellen, dass der Puffer w\u00e4hrend der Verarbeitung nicht ver\u00e4ndert wird (mit Lock f\u00fcr GStreamer)\n            g_mutex_lock(&amp;buffer_mutex_gstreamer);\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), gst_buffer);\n\n            \/\/ R\u00fcckruffunktion aufrufen, wenn der Puffer freigegeben wird\n            g_signal_connect(gst_buffer, &quot;destroy&quot;, G_CALLBACK(on_buffer_flushed), NULL);\n            \n            \/\/ Puffer-Referenz erh\u00f6hen, um ihn zu sch\u00fctzen\n            gst_buffer_ref(gst_buffer);\n\n            g_mutex_unlock(&amp;buffer_mutex_gstreamer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n\n\/\/ R\u00fcckruffunktion, die aufgerufen wird, wenn der Puffer freigegeben wird\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data) {\n    \/\/ Hier kannst du den Puffer freigeben, nachdem GStreamer ihn verarbeitet hat\n    gst_buffer_unref(buffer);  \/\/ Den Puffer freigeben\n    cout &lt;&lt; &quot;Puffer wurde freigegeben!&quot; &lt;&lt; endl;\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Erkl\u00e4rungen zu den \u00c4nderungen:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Mutex f\u00fcr <\/strong><code><strong>libcamera<\/strong><\/code><strong> und <\/strong><code><strong>GStreamer<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Ich habe zwei separate <strong>Mutexe<\/strong> hinzugef\u00fcgt:\n<ul class=\"wp-block-list\">\n<li><code><strong>buffer_mutex_libcamera<\/strong><\/code> sch\u00fctzt den Zugriff auf die Puffer von <strong>libcamera<\/strong>.<\/li>\n\n\n\n<li><code><strong>buffer_mutex_gstreamer<\/strong><\/code> sch\u00fctzt den Zugriff auf die Puffer, die an GStreamer weitergegeben werden.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Synchronisation des Zugriffs<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Der <strong>Zugriff auf libcamera<\/strong> wird mit <code>g_mutex_lock(&amp;buffer_mutex_libcamera)<\/code> synchronisiert. Bevor ein Frame von libcamera abgerufen wird, wird der Mutex f\u00fcr libcamera gesperrt und nach dem Abrufen wieder freigegeben.<\/li>\n\n\n\n<li>Der <strong>Zugriff auf GStreamer<\/strong> wird mit <code>g_mutex_lock(&amp;buffer_mutex_gstreamer)<\/code> synchronisiert, bevor der Puffer in GStreamer eingef\u00fcgt wird. GStreamer verarbeitet den Puffer im Hintergrund, und wir stellen sicher, dass wir den Puffer nicht gleichzeitig freigeben oder ver\u00e4ndern.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><code><strong>gst_buffer_ref()<\/strong><\/code><strong> und <\/strong><code><strong>gst_buffer_unref()<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Der Puffer wird mit <code>gst_buffer_ref()<\/code> referenziert, wenn er an GStreamer \u00fcbergeben wird, um sicherzustellen, dass er nicht vorzeitig freigegeben wird.<\/li>\n\n\n\n<li>Die Freigabe des Puffers erfolgt durch die R\u00fcckruffunktion <code>on_buffer_flushed<\/code>, in der der Puffer mit <code>gst_buffer_unref()<\/code> freigegeben wird, nachdem GStreamer ihn verarbeitet hat.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Fazit:<\/h3>\n\n\n\n<p>Diese Version des Codes stellt sicher, dass sowohl der Zugriff auf die Puffer von libcamera als auch auf die Puffer von GStreamer korrekt synchronisiert ist. Durch die Verwendung von Mutexen stellen wir sicher, dass <strong>keine gleichzeitigen Zugriffe auf die Puffer<\/strong> stattfinden, was zu <strong>Datenkorruption oder Speicherfehlern f\u00fchren k\u00f6nnte<\/strong>. Somit erreichen wir eine sichere und robuste Speicherverwaltung in einem multithreaded Umfeld.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Das ist nicht was ich wollte. Auf diese Weise wird das Capturen von Frames komplett blockiert. Ich m\u00f6chte aber das Capturen weiter laufen lassen und dabei nur verhindern, das libcamera dabei frame buffer benutzt, die noch nicht freigegeben wurden<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Verstanden! Entschuldigung f\u00fcr das Missverst\u00e4ndnis. Dein Ziel ist es, das <strong>Capturing<\/strong> von Frames fortzusetzen, w\u00e4hrend libcamera weiter neue Frames erfasst. Gleichzeitig m\u00f6chtest du jedoch sicherstellen, dass <strong>libcamera keine Frame-Puffer wiederverwendet<\/strong>, die noch nicht von GStreamer verarbeitet wurden. Dies erfordert eine <strong>asynchrone Synchronisation<\/strong> zwischen dem Thread, der Frames von libcamera erfasst, und dem GStreamer-Thread, der die Frames verarbeitet.<\/p>\n\n\n\n<p>Die L\u00f6sung ist also, den <strong>Zugriff auf die Frame-Puffer asynchron zu synchronisieren<\/strong>, ohne den Erfassungsprozess (Capture) zu blockieren. Wir k\u00f6nnen dies erreichen, indem wir sicherstellen, dass <strong>libcamera nur dann einen Puffer wiederverwendet<\/strong>, wenn GStreamer den Puffer vollst\u00e4ndig verarbeitet und freigegeben hat.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">L\u00f6sung: Verwendung einer <strong>Warteschlange<\/strong> und <strong>Referenzz\u00e4hlung<\/strong> zur Verwaltung des Puffers<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Warteschlange (Queue)<\/strong>: Wir verwenden eine Warteschlange, um die Puffer zwischen libcamera und GStreamer zu koordinieren. libcamera gibt einen Puffer in die Warteschlange, wenn es einen neuen Frame erfasst, und GStreamer holt ihn sich dann zur Verarbeitung.<\/li>\n\n\n\n<li><strong>Referenzz\u00e4hlung<\/strong>: Jeder Puffer wird referenziert, und libcamera gibt den Puffer erst dann wieder frei, wenn GStreamer ihn vollst\u00e4ndig verarbeitet hat.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Wie es funktioniert:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>libcamera gibt die Puffer <strong>nicht mehr direkt frei<\/strong>, sondern speichert sie in einer Warteschlange.<\/li>\n\n\n\n<li>GStreamer nimmt einen Puffer aus der Warteschlange, verarbeitet ihn und gibt ihn dann nach der Verarbeitung mit einer R\u00fcckruffunktion frei.<\/li>\n\n\n\n<li>Solange GStreamer den Puffer nicht freigegeben hat, wird der Puffer nicht von libcamera wiederverwendet.<\/li>\n<\/ul>\n\n\n\n<p>Hier ist eine \u00fcberarbeitete Version des Codes, die diese Anforderungen umsetzt:<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Angepasster Code mit Warteschlange und Referenzz\u00e4hlung:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n#include &lt;glib.h&gt; \/\/ F\u00fcr GMutex\n#include &lt;queue&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data);\n\n\/\/ Struktur zur Verwaltung der Puffer\nstruct FrameBuffer {\n    std::shared_ptr&lt;Buffer&gt; buffer;\n    GstBuffer *gst_buffer;\n};\n\n\/\/ Globale Warteschlange f\u00fcr Puffer\nstd::queue&lt;FrameBuffer&gt; buffer_queue;\n\n\/\/ Mutex zum Schutz der Warteschlange\nGMutex buffer_queue_mutex;\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    config.pixelFormat = formats::YUV420;  \/\/ Setze das Format auf YUV420\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264enc = gst_element_factory_make(&quot;x264enc&quot;, &quot;encoder&quot;);  \/\/ Verwenden von x264enc f\u00fcr H.264\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *rtph264pay = gst_element_factory_make(&quot;rtph264pay&quot;, &quot;rtp-payload&quot;);\n    GstElement *udpsink = gst_element_factory_make(&quot;udpsink&quot;, &quot;sink&quot;);\n\n    if (!pipeline || !appsrc || !h264enc || !h264parse || !rtph264pay || !udpsink) {\n        cerr &lt;&lt; &quot;Fehler beim Erstellen der GStreamer-Elemente!&quot; &lt;&lt; endl;\n        return -1;\n    }\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n    gst_element_link_many(appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                        &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ UDP-Einstellungen f\u00fcr udpsink setzen\n    g_object_set(udpsink, &quot;host&quot;, &quot;127.0.0.1&quot;, &quot;port&quot;, 5000, NULL);  \/\/ Beispiel-IP und Port f\u00fcr den Empfang\n\n    \/\/ RTP-Payload-Einstellungen konfigurieren\n    g_object_set(rtph264pay, &quot;pt&quot;, 96, NULL);  \/\/ Payload-Typ 96 f\u00fcr H.264\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Umwandlung des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Puffer in die Warteschlange einf\u00fcgen\n            g_mutex_lock(&amp;buffer_queue_mutex);\n            buffer_queue.push({buffer, gst_buffer});\n            g_mutex_unlock(&amp;buffer_queue_mutex);\n        }\n\n        \/\/ Pr\u00fcfen, ob GStreamer einen Puffer verarbeiten kann\n        if (!buffer_queue.empty()) {\n            g_mutex_lock(&amp;buffer_queue_mutex);\n            FrameBuffer frame = buffer_queue.front();  \/\/ Den ersten Puffer aus der Warteschlange holen\n            buffer_queue.pop();\n            g_mutex_unlock(&amp;buffer_queue_mutex);\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), frame.gst_buffer);\n\n            \/\/ R\u00fcckruffunktion aufrufen, wenn der Puffer freigegeben wird\n            g_signal_connect(frame.gst_buffer, &quot;destroy&quot;, G_CALLBACK(on_buffer_flushed), nullptr);\n\n            \/\/ Puffer referenzieren, um ihn w\u00e4hrend der Verarbeitung zu sch\u00fctzen\n            gst_buffer_ref(frame.gst_buffer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n\n\/\/ R\u00fcckruffunktion, die aufgerufen wird, wenn der Puffer freigegeben wird\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data) {\n    \/\/ Puffer freigeben\n    gst_buffer_unref(buffer);  \/\/ Den Puffer freigeben\n    cout &lt;&lt; &quot;Puffer wurde freigegeben!&quot; &lt;&lt; endl;\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Erkl\u00e4rungen der \u00c4nderungen:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Warteschlange (<\/strong><code><strong>std::queue&lt;FrameBuffer&gt; buffer_queue<\/strong><\/code><strong>)<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Eine Warteschlange speichert die Puffer, die von libcamera erfasst wurden und noch nicht von GStreamer verarbeitet wurden.<\/li>\n\n\n\n<li>Jeder Puffer wird mit seiner <strong>libcamera-Referenz (<\/strong><code><strong>std::shared_ptr&lt;Buffer&gt;<\/strong><\/code><strong>)<\/strong> und seinem <strong>GStreamer-Puffer (<\/strong><code><strong>GstBuffer<\/strong><\/code><strong>)<\/strong> in die Warteschlange eingef\u00fcgt.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Asynchrone Synchronisation<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Libcamera kann weiterhin ununterbrochen Frames erfassen. Wenn ein neues Frame aufgenommen wird, wird es sofort in die Warteschlange eingef\u00fcgt.<\/li>\n\n\n\n<li>GStreamer nimmt einen Puffer aus der Warteschlange und verarbeitet ihn, wenn er verf\u00fcgbar ist. Dabei wird <strong>der Zugriff auf die Warteschlange<\/strong> durch <code>g_mutex_lock<\/code> und <code>g_mutex_unlock<\/code> gesch\u00fctzt.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>R\u00fcckruffunktion <\/strong><code><strong>on_buffer_flushed<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Wenn GStreamer den Puffer verarbeitet hat, wird die R\u00fcckruffunktion <code>on_buffer_flushed<\/code> aufgerufen. Hier wird der Puffer mit <code>gst_buffer_unref<\/code> freigegeben, sodass libcamera den Puffer wieder verwenden kann.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Referenzz\u00e4hlung<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Der Puffer wird mit <code>gst_buffer_ref()<\/code> referenziert, bevor er an GStreamer \u00fcbergeben wird, und mit <code>gst_buffer_unref()<\/code> freigegeben, nachdem GStreamer ihn verarbeitet hat.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Fazit:<\/h3>\n\n\n\n<p>Mit dieser L\u00f6sung wird das <strong>Capturing von libcamera<\/strong> nicht blockiert, und libcamera kann <strong>weiterhin neue Frames erfassen<\/strong>, w\u00e4hrend die Puffer sicher in einer Warteschlange auf ihre Verarbeitung durch GStreamer warten. Libcamera wartet nun nicht mehr darauf, dass GStreamer den Puffer sofort verarbeitet, sondern nur darauf, dass der Puffer irgendwann freigegeben wird, sodass er wiederverwendet werden kann.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du<\/h5>\n\n\n\n<p>Bitte erg\u00e4nze dieses Beispiel nun wieder um die Zeitstempel, welche wir vom ISP empfangen<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Nat\u00fcrlich, um die Zeitstempel zu integrieren, die vom ISP (Image Signal Processor) kommen, m\u00fcssen wir sicherstellen, dass wir den <strong>Zeitstempel der Puffer<\/strong> von libcamera korrekt \u00fcbernehmen und diesen in die GStreamer-Pipeline weitergeben.<\/p>\n\n\n\n<p>Die Zeitstempel des ISP sind in der Regel im <code>Buffer<\/code>-Objekt von libcamera enthalten und k\u00f6nnen im <code>timestamp<\/code>&#8211; Feld abgerufen werden. Wir m\u00fcssen sicherstellen, dass dieser Zeitstempel in den GStreamer-Puffer integriert wird, um die Synchronisation und den ordnungsgem\u00e4\u00dfen Fluss der Frames zu gew\u00e4hrleisten.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">\u00c4nderungen:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Zeitstempel von libcamera extrahieren<\/strong>: Wir extrahieren den Zeitstempel direkt aus dem <code>Buffer<\/code> von libcamera.<\/li>\n\n\n\n<li><strong>Zeitstempel in GStreamer-Puffer setzen<\/strong>: Wir setzen den Zeitstempel aus libcamera als PTS (Presentation Time Stamp) in den GStreamer-Puffer.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Angepasster Code mit Zeitstempeln:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n#include &lt;glib.h&gt; \/\/ F\u00fcr GMutex\n#include &lt;queue&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data);\n\n\/\/ Struktur zur Verwaltung der Puffer\nstruct FrameBuffer {\n    std::shared_ptr&lt;Buffer&gt; buffer;\n    GstBuffer *gst_buffer;\n    guint64 timestamp; \/\/ Zeitstempel des ISP\n};\n\n\/\/ Globale Warteschlange f\u00fcr Puffer\nstd::queue&lt;FrameBuffer&gt; buffer_queue;\n\n\/\/ Mutex zum Schutz der Warteschlange\nGMutex buffer_queue_mutex;\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    config.pixelFormat = formats::YUV420;  \/\/ Setze das Format auf YUV420\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264enc = gst_element_factory_make(&quot;x264enc&quot;, &quot;encoder&quot;);  \/\/ Verwenden von x264enc f\u00fcr H.264\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *rtph264pay = gst_element_factory_make(&quot;rtph264pay&quot;, &quot;rtp-payload&quot;);\n    GstElement *udpsink = gst_element_factory_make(&quot;udpsink&quot;, &quot;sink&quot;);\n\n    if (!pipeline || !appsrc || !h264enc || !h264parse || !rtph264pay || !udpsink) {\n        cerr &lt;&lt; &quot;Fehler beim Erstellen der GStreamer-Elemente!&quot; &lt;&lt; endl;\n        return -1;\n    }\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n    gst_element_link_many(appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                        &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ UDP-Einstellungen f\u00fcr udpsink setzen\n    g_object_set(udpsink, &quot;host&quot;, &quot;127.0.0.1&quot;, &quot;port&quot;, 5000, NULL);  \/\/ Beispiel-IP und Port f\u00fcr den Empfang\n\n    \/\/ RTP-Payload-Einstellungen konfigurieren\n    g_object_set(rtph264pay, &quot;pt&quot;, 96, NULL);  \/\/ Payload-Typ 96 f\u00fcr H.264\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Zeitstempel vom ISP holen\n            const struct timeval *timestamp = buffer-&gt;timestamp();\n            guint64 timestamp_us = 0;\n            if (timestamp) {\n                \/\/ Umwandlung des libcamera-Zeitstempels in Mikrosekunden\n                timestamp_us = timestamp-&gt;tv_sec * 1000000 + timestamp-&gt;tv_usec;\n            }\n\n            \/\/ Umwandlung des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Puffer in die Warteschlange einf\u00fcgen\n            g_mutex_lock(&amp;buffer_queue_mutex);\n            buffer_queue.push({buffer, gst_buffer, timestamp_us});\n            g_mutex_unlock(&amp;buffer_queue_mutex);\n        }\n\n        \/\/ Pr\u00fcfen, ob GStreamer einen Puffer verarbeiten kann\n        if (!buffer_queue.empty()) {\n            g_mutex_lock(&amp;buffer_queue_mutex);\n            FrameBuffer frame = buffer_queue.front();  \/\/ Den ersten Puffer aus der Warteschlange holen\n            buffer_queue.pop();\n            g_mutex_unlock(&amp;buffer_queue_mutex);\n\n            \/\/ Zeitstempel aus libcamera in den GStreamer-Puffer setzen\n            if (frame.timestamp &gt; 0) {\n                GST_BUFFER_PTS(frame.gst_buffer) = frame.timestamp;  \/\/ Setzen des PTS auf den ISP-Zeitstempel\n            }\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), frame.gst_buffer);\n\n            \/\/ R\u00fcckruffunktion aufrufen, wenn der Puffer freigegeben wird\n            g_signal_connect(frame.gst_buffer, &quot;destroy&quot;, G_CALLBACK(on_buffer_flushed), nullptr);\n\n            \/\/ Puffer referenzieren, um ihn w\u00e4hrend der Verarbeitung zu sch\u00fctzen\n            gst_buffer_ref(frame.gst_buffer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n\n\/\/ R\u00fcckruffunktion, die aufgerufen wird, wenn der Puffer freigegeben wird\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data) {\n    \/\/ Puffer freigeben\n    gst_buffer_unref(buffer);  \/\/ Den Puffer freigeben\n    cout &lt;&lt; &quot;Puffer wurde freigegeben!&quot; &lt;&lt; endl;\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Was wurde ge\u00e4ndert:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Zeitstempel von libcamera<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Der Zeitstempel wird aus dem <code>Buffer<\/code>-Objekt von libcamera extrahiert. Dies geschieht mit der Methode <code>buffer-&gt;timestamp()<\/code>, die den Zeitstempel als <code>struct timeval<\/code> zur\u00fcckgibt. Wir konvertieren diesen Zeitstempel in Mikrosekunden (<code>timestamp_us<\/code>), da GStreamer Zeitstempel ebenfalls in Mikrosekunden erwartet.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Setzen des Zeitstempels in den GStreamer-Puffer<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Der Zeitstempel von libcamera (<code>timestamp_us<\/code>) wird in den GStreamer-Puffer als <strong>PTS (Presentation Time Stamp)<\/strong> gesetzt: <code>GST_BUFFER_PTS(frame.gst_buffer) = frame.timestamp;<\/code>.<\/li>\n\n\n\n<li>Dies stellt sicher, dass GStreamer die Frames korrekt synchronisiert und wiedergibt, basierend auf dem Zeitstempel des ISP.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Struktur <\/strong><code><strong>FrameBuffer<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Die Struktur <code>FrameBuffer<\/code> wurde um das Feld <code>timestamp<\/code> erweitert, das den Zeitstempel aus dem ISP speichert. Dieser Zeitstempel wird dann in den GStreamer-Puffer \u00fcbertragen.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\">Fazit:<\/h3>\n\n\n\n<p>Mit diesen \u00c4nderungen haben wir den Zeitstempel des ISP korrekt in den GStreamer-Puffer integriert. Dadurch wird der Zeitstempel des ISP als <strong>PTS<\/strong> (Presentation Time Stamp) in GStreamer verwendet, was eine pr\u00e4zise Synchronisation der Videoausgabe erm\u00f6glicht.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h5 class=\"wp-block-heading\">Du:<\/h5>\n\n\n\n<p>Jetzt erg\u00e4nze bitte das Beispiel um Warnungen von GStreamer, wenn die Queue \u00fcberl\u00e4uft. Ich bevorzuge hier eine call back Funktion<\/p>\n\n\n\n<h6 class=\"wp-block-heading\">ChatGPT:<\/h6>\n\n\n\n<p>Um GStreamer auf m\u00f6gliche <strong>Queue-\u00dcberl\u00e4ufe<\/strong> aufmerksam zu machen, k\u00f6nnen wir den <strong>Flow-Control-Mechanismus<\/strong> von GStreamer nutzen. In GStreamer wird ein \u00dcberlauf in der Pipeline normalerweise durch den R\u00fcckgabewert von <code>GST_FLOW_ERROR<\/code> oder durch das Ereignis <code>GST_EVENT_EOS<\/code> angezeigt, wenn eine Queue \u00fcberl\u00e4uft oder nicht mehr genug Puffer zur Verf\u00fcgung stehen. Zus\u00e4tzlich k\u00f6nnen wir ein <strong>Callback<\/strong> f\u00fcr Warnungen und Fehler einrichten, wenn der GStreamer-Element <code>appsrc<\/code> in eine schwierige Situation ger\u00e4t.<\/p>\n\n\n\n<p>F\u00fcr die Queue-\u00dcberwachung verwenden wir <code>GstAppSrc<\/code> und verbinden uns mit den Signalen und Ereignissen, die darauf hinweisen, dass die Queue m\u00f6glicherweise zu voll wird und nicht mehr in der Lage ist, Puffer zu akzeptieren.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Vorgehen:<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Signalverbindung (<\/strong><code><strong>\"need-data\"<\/strong><\/code><strong>, <\/strong><code><strong>\"enough-data\"<\/strong><\/code><strong>)<\/strong>: Diese Signale werden in GStreamer verwendet, um zu wissen, wann AppSrc mehr Daten ben\u00f6tigt oder wann genug Daten vorhanden sind. Wir k\u00f6nnen sie verwenden, um zus\u00e4tzliche Logik zu implementieren, wenn die Queue fast \u00fcberl\u00e4uft.<\/li>\n\n\n\n<li><strong>Flow-Status-\u00dcberwachung<\/strong>: GStreamer gibt in Form von <code>GST_FLOW_ERROR<\/code> eine Warnung aus, wenn etwas mit der Datenpipeline schiefgeht, z.B., wenn die Queue \u00fcberl\u00e4uft.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Code-Erweiterungen f\u00fcr Warnungen bei \u00dcberlauf:<\/h3>\n\n\n\n<p>Wir f\u00fcgen eine <strong>Callback-Funktion<\/strong> hinzu, die bei Fehlern oder Warnungen in der Pipeline ausgel\u00f6st wird. Diese Funktion wird aufgerufen, wenn die <code>appsrc<\/code>-Queue \u00fcberl\u00e4uft oder andere kritische Probleme auftreten.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Erweiterter Code:<\/h3>\n\n\n\n<pre class=\"wp-block-preformatted\">cpp<\/pre>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: cpp; title: ; notranslate\" title=\"\">\n#include &lt;iostream&gt;\n#include &lt;libcamera\/libcamera.h&gt;\n#include &lt;gst\/gst.h&gt;\n#include &lt;gst\/app\/app.h&gt;\n#include &lt;glib.h&gt; \/\/ F\u00fcr GMutex\n#include &lt;queue&gt;\n\nusing namespace std;\nusing namespace libcamera;\n\n\/\/ Funktionsprototypen\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer);\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data);\ngboolean on_warning(GstElement *element, GstMessage *message, gpointer user_data);\n\n\/\/ Struktur zur Verwaltung der Puffer\nstruct FrameBuffer {\n    std::shared_ptr&lt;Buffer&gt; buffer;\n    GstBuffer *gst_buffer;\n    guint64 timestamp; \/\/ Zeitstempel des ISP\n};\n\n\/\/ Globale Warteschlange f\u00fcr Puffer\nstd::queue&lt;FrameBuffer&gt; buffer_queue;\n\n\/\/ Mutex zum Schutz der Warteschlange\nGMutex buffer_queue_mutex;\n\nint main() {\n    \/\/ GStreamer initialisieren\n    gst_init(NULL, NULL);\n\n    \/\/ libcamera-Kamera initialisieren\n    CameraManager *cameraManager = CameraManager::instance();\n    cameraManager-&gt;start();\n    Camera *camera = cameraManager-&gt;cameras()&#x5B;0]; \/\/ W\u00e4hlen Sie die erste Kamera aus\n\n    \/\/ Kamera konfigurieren\n    StreamConfiguration config = camera-&gt;configuration()&#x5B;0];\n    config.size = Size(1280, 720);  \/\/ Setze die Aufl\u00f6sung\n    config.pixelFormat = formats::YUV420;  \/\/ Setze das Format auf YUV420\n    camera-&gt;configure({config});\n\n    \/\/ GStreamer-Pipeline erstellen\n    GstElement *pipeline = gst_pipeline_new(&quot;pipeline&quot;);\n    GstElement *appsrc = gst_element_factory_make(&quot;appsrc&quot;, &quot;source&quot;);\n    GstElement *h264enc = gst_element_factory_make(&quot;x264enc&quot;, &quot;encoder&quot;);  \/\/ Verwenden von x264enc f\u00fcr H.264\n    GstElement *h264parse = gst_element_factory_make(&quot;h264parse&quot;, &quot;parser&quot;);\n    GstElement *rtph264pay = gst_element_factory_make(&quot;rtph264pay&quot;, &quot;rtp-payload&quot;);\n    GstElement *udpsink = gst_element_factory_make(&quot;udpsink&quot;, &quot;sink&quot;);\n\n    if (!pipeline || !appsrc || !h264enc || !h264parse || !rtph264pay || !udpsink) {\n        cerr &lt;&lt; &quot;Fehler beim Erstellen der GStreamer-Elemente!&quot; &lt;&lt; endl;\n        return -1;\n    }\n\n    \/\/ Pipeline zusammenbauen\n    gst_bin_add_many(GST_BIN(pipeline), appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n    gst_element_link_many(appsrc, h264enc, h264parse, rtph264pay, udpsink, NULL);\n\n    \/\/ AppSrc-Element konfigurieren\n    GstCaps *caps = gst_caps_new_simple(&quot;video\/x-raw&quot;,\n                                        &quot;format&quot;, G_TYPE_STRING, &quot;YUV420&quot;,\n                                        &quot;width&quot;, G_TYPE_INT, 1280,\n                                        &quot;height&quot;, G_TYPE_INT, 720,\n                                        &quot;framerate&quot;, GST_TYPE_FRACTION, 30, 1,\n                                        NULL);\n    g_object_set(appsrc, &quot;caps&quot;, caps, NULL);\n    gst_caps_unref(caps);\n\n    \/\/ UDP-Einstellungen f\u00fcr udpsink setzen\n    g_object_set(udpsink, &quot;host&quot;, &quot;127.0.0.1&quot;, &quot;port&quot;, 5000, NULL);  \/\/ Beispiel-IP und Port f\u00fcr den Empfang\n\n    \/\/ RTP-Payload-Einstellungen konfigurieren\n    g_object_set(rtph264pay, &quot;pt&quot;, 96, NULL);  \/\/ Payload-Typ 96 f\u00fcr H.264\n\n    \/\/ GStreamer-Elemente konfigurieren, um auf Warnungen und Fehler zu h\u00f6ren\n    g_signal_connect(pipeline, &quot;on-warning&quot;, G_CALLBACK(on_warning), NULL);\n\n    \/\/ Pipeline starten\n    gst_element_set_state(pipeline, GST_STATE_PLAYING);\n\n    \/\/ libcamera Stream starten\n    camera-&gt;start();\n\n    \/\/ Frame-Puffer aus libcamera an AppSrc weitergeben\n    while (true) {\n        \/\/ Hole ein Frame von libcamera\n        std::shared_ptr&lt;Buffer&gt; buffer = camera-&gt;capture();\n\n        if (buffer) {\n            \/\/ Zeitstempel vom ISP holen\n            const struct timeval *timestamp = buffer-&gt;timestamp();\n            guint64 timestamp_us = 0;\n            if (timestamp) {\n                \/\/ Umwandlung des libcamera-Zeitstempels in Mikrosekunden\n                timestamp_us = timestamp-&gt;tv_sec * 1000000 + timestamp-&gt;tv_usec;\n            }\n\n            \/\/ Umwandlung des libcamera-Puffers in einen GStreamer-Puffer\n            GstBuffer *gst_buffer = gst_buffer_new_wrapped(buffer-&gt;data(), buffer-&gt;size(), 0, buffer-&gt;size());\n\n            \/\/ Puffer in die Warteschlange einf\u00fcgen\n            g_mutex_lock(&amp;buffer_queue_mutex);\n            buffer_queue.push({buffer, gst_buffer, timestamp_us});\n            g_mutex_unlock(&amp;buffer_queue_mutex);\n        }\n\n        \/\/ Pr\u00fcfen, ob GStreamer einen Puffer verarbeiten kann\n        if (!buffer_queue.empty()) {\n            g_mutex_lock(&amp;buffer_queue_mutex);\n            FrameBuffer frame = buffer_queue.front();  \/\/ Den ersten Puffer aus der Warteschlange holen\n            buffer_queue.pop();\n            g_mutex_unlock(&amp;buffer_queue_mutex);\n\n            \/\/ Zeitstempel aus libcamera in den GStreamer-Puffer setzen\n            if (frame.timestamp &gt; 0) {\n                GST_BUFFER_PTS(frame.gst_buffer) = frame.timestamp;  \/\/ Setzen des PTS auf den ISP-Zeitstempel\n            }\n\n            \/\/ Puffer in AppSrc einspeisen\n            appsrc_push_buffer(GTK_APP_SRC(appsrc), frame.gst_buffer);\n\n            \/\/ R\u00fcckruffunktion aufrufen, wenn der Puffer freigegeben wird\n            g_signal_connect(frame.gst_buffer, &quot;destroy&quot;, G_CALLBACK(on_buffer_flushed), nullptr);\n\n            \/\/ Puffer referenzieren, um ihn w\u00e4hrend der Verarbeitung zu sch\u00fctzen\n            gst_buffer_ref(frame.gst_buffer);\n        }\n    }\n\n    \/\/ Pipeline stoppen\n    gst_element_set_state(pipeline, GST_STATE_NULL);\n\n    \/\/ GStreamer freigeben\n    gst_object_unref(pipeline);\n    cameraManager-&gt;stop();\n    return 0;\n}\n\n\/\/ Funktion, um einen Puffer in AppSrc zu schieben\nvoid appsrc_push_buffer(GstAppSrc *appsrc, GstBuffer *buffer) {\n    GstFlowReturn ret;\n    g_signal_emit_by_name(appsrc, &quot;push-buffer&quot;, buffer, &amp;ret);\n    if (ret != GST_FLOW_OK) {\n        cerr &lt;&lt; &quot;Fehler beim Pushen des Puffers in AppSrc!&quot; &lt;&lt; endl;\n    }\n}\n\n\/\/ R\u00fcckruffunktion, die aufgerufen wird, wenn der Puffer freigegeben wird\nvoid on_buffer_flushed(GstBuffer *buffer, gpointer user_data) {\n    \/\/ Puffer freigeben\n    gst_buffer_unref(buffer);  \/\/ Den Puffer freigeben\n    cout &lt;&lt; &quot;Puffer wurde freigegeben!&quot; &lt;&lt; endl;\n}\n\n\/\/ Callback-Funktion f\u00fcr Warnungen und Fehler in der Pipeline\ngboolean on_warning(GstElement *element, GstMessage *message, gpointer user_data) {\n    GError *err = NULL;\n    gchar *debug_info = NULL;\n\n    \/\/ Wenn eine Warnung oder ein Fehler auftritt, analysiere die Nachricht\n    if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_WARNING || GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) {\n        gst_message_parse_warning(message, &amp;err, &amp;debug_info);\n        if (err) {\n            cerr &lt;&lt; &quot;GStreamer Warnung: &quot; &lt;&lt; err-&gt;message &lt;&lt; endl;\n            g_error_free(err);\n        }\n        if (debug_info) {\n            cerr &lt;&lt; &quot;Debug Info: &quot; &lt;&lt; debug_info &lt;&lt; endl;\n            g_free(debug_info);\n        }\n\n        \/\/ \u00dcberpr\u00fcfen, ob es sich um eine Queue-\u00dcberlaufwarnung handelt\n        if (GST_MESSAGE_TYPE(message) == GST_MESSAGE_ERROR) {\n            cerr &lt;&lt; &quot;Fehler in der GStreamer-Pipeline!&quot; &lt;&lt; endl;\n            return TRUE;\n        }\n    }\n    return FALSE;\n}\n<\/pre><\/div>\n\n\n<h3 class=\"wp-block-heading\">Erkl\u00e4rung der \u00c4nderungen:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Callback f\u00fcr Warnungen und Fehler<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Wir haben die Funktion <code>on_warning<\/code> hinzugef\u00fcgt, die auf Warnungen und Fehler von GStreamer reagiert. Diese wird \u00fcber das Signal <code>on-warning<\/code> mit der Pipeline verbunden.<\/li>\n\n\n\n<li>Die Funktion pr\u00fcft, ob eine <strong>Warnung<\/strong> oder ein <strong>Fehler<\/strong> vorliegt und gibt entsprechende Informationen aus, z.B. eine m\u00f6gliche Queue-\u00dcberlaufwarnung oder andere Fehler.<\/li>\n\n\n\n<li>Im Fehlerfall wird auch gepr\u00fcft, ob es sich um einen kritischen Fehler handelt, der m\u00f6glicherweise die Pipeline zum Absturz bringen w\u00fcrde (z.B. ein vollst\u00e4ndiger \u00dcberlauf der Queue).<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>\u00dcberwachung von <\/strong><code><strong>appsrc<\/strong><\/code>:\n<ul class=\"wp-block-list\">\n<li>Die <code>appsrc<\/code>&#8211; Elemente sind daf\u00fcr zust\u00e4ndig, die Puffer bereitzustellen. Wenn die Queue \u00fcberl\u00e4uft, wird ein Fehler gemeldet und die Anwendung wird auf den Zustand aufmerksam gemacht.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Fehlermeldung und Debugging<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Falls GStreamer eine Fehlermeldung generiert (z.B. bei einem \u00dcberlauf der Queue), wird dies detailliert ausgegeben, und der Entwickler wird auf die Probleme hingewiesen.<\/li>\n<\/ul>\n<\/li>\n<\/ol>\n\n\n\n<p><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Kann man mit KI Programmierer ersetzen? Ich pers\u00f6nlich empfinde KI als ein sehr hilfreiches Werkzeug. Der Code der KI zeigt gerade bei [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1,23,15,40,9,16],"tags":[29,28,12],"class_list":["post-784","post","type-post","status-publish","format-standard","hentry","category-allgemein","category-embedded-programmierung","category-embedded-vision","category-ki","category-programmierung","category-raspberrry-pi","tag-c-2","tag-embedded","tag-programmierung"],"_links":{"self":[{"href":"https:\/\/thurow.de\/index.php?rest_route=\/wp\/v2\/posts\/784","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thurow.de\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thurow.de\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thurow.de\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thurow.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=784"}],"version-history":[{"count":8,"href":"https:\/\/thurow.de\/index.php?rest_route=\/wp\/v2\/posts\/784\/revisions"}],"predecessor-version":[{"id":815,"href":"https:\/\/thurow.de\/index.php?rest_route=\/wp\/v2\/posts\/784\/revisions\/815"}],"wp:attachment":[{"href":"https:\/\/thurow.de\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=784"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thurow.de\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=784"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thurow.de\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=784"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}