GstRTMPMetadata C Example
The GstRTMPMetadata documentation from RidgeRun is presently being developed. |
Inserting metadata using a C application
The following example shows how to inject metadata into an RTMP/FLV stream from a C application using RidgeRun’s RTMP/FLV metadata support for GStreamer’s flvmux
.
It demonstrates three mechanisms:
- Setting meta-string (simple string insertion).
- Setting meta-binary (binary payload via GBytes).
- Injecting a custom GstMeta directly on buffers using a pad probe (one-shot).
These mechanisms are enabled by RidgeRun’s RTMP/FLV metadata support and are not available in stock GStreamer.
/** * Copyright (C) 2025 RidgeRun, LLC (http://www.ridgerun.com) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 2 as * published by the Free Software Foundation. * */ #include <gst/gst.h> #include <glib.h> #include <string.h> #include <gst/rtmp/gstflvmeta.h> // public FLV meta API from RidgeRun patches /* Periodically inject GstFlvMeta into buffers passing through flvmux's 'video' pad */ static GstPadProbeReturn inject_every_n_cb(GstPad *pad, GstPadProbeInfo *info, gpointer u) { if (!(info->type & GST_PAD_PROBE_TYPE_BUFFER)) return GST_PAD_PROBE_OK; static guint counter = 0; counter++; /* Inject every 30 buffers (adjust as needed) */ if (counter % 30 != 0) return GST_PAD_PROBE_OK; GstBuffer *buf = GST_PAD_PROBE_INFO_BUFFER(info); if (!buf) return GST_PAD_PROBE_OK; /* Make buffer writable and attach a GstFlvMeta payload */ buf = gst_buffer_make_writable(buf); GST_PAD_PROBE_INFO_DATA(info) = buf; /* Use the string directly (no need to copy) */ const gchar *txt = "periodic-meta-test"; gsize len = strlen(txt); /* Note: gst_buffer_add_flv_meta() copies the payload internally in RidgeRun's API. * If your version requires ownership handling, adjust accordingly. */ gst_buffer_add_flv_meta(buf, (guint8 *)txt, len); g_print("[meta] Injected periodic GstFlvMeta on flvmux:video (buf #%u)\n", counter); return GST_PAD_PROBE_OK; } int main (int argc, char *argv[]) { gst_init (&argc, &argv); /* Elements */ GstElement *pipeline = gst_pipeline_new ("p"); GstElement *src = gst_element_factory_make ("videotestsrc", "src"); GstElement *enc = gst_element_factory_make ("x264enc", "enc"); GstElement *mux = gst_element_factory_make ("flvmux", "mux"); GstElement *sink = gst_element_factory_make ("rtmpsink", "sink"); if (!pipeline || !src || !enc || !mux || !sink) { g_printerr ("Could not create one of the elements.\n"); return -1; } /* Basic configuration */ g_object_set (src, "is-live", TRUE, NULL); g_object_set (enc, "tune", 0x00000004 /* zerolatency */, NULL); /* RTMP target (adjust as needed) */ g_object_set (sink, "location", "rtmp://127.0.0.1/live/test", NULL); /* flvmux metadata via properties */ g_object_set (mux, "meta-string", "external-function-test", NULL); /* Optional: initial meta-binary BEFORE PLAYING */ const guint8 raw_data[] = { 'h','e','l','l','o',' ','R','T','M','P',' ','m','e','t','a' }; GBytes *bytes0 = g_bytes_new(raw_data, sizeof(raw_data)); g_object_set(mux, "meta-binary", bytes0, NULL); g_bytes_unref(bytes0); g_print("[meta] Initial meta-binary configured (before PLAYING)\n"); /* Build and link */ gst_bin_add_many (GST_BIN (pipeline), src, enc, mux, sink, NULL); gst_element_link (src, enc); /* Request flvmux 'video' pad and add a pad-probe for periodic GstFlvMeta injection */ GstPad *mux_vsink = gst_element_request_pad_simple (mux, "video"); gst_pad_add_probe (mux_vsink, GST_PAD_PROBE_TYPE_BUFFER, inject_every_n_cb, NULL, NULL); /* Link enc:src -> flvmux:video */ GstPad *enc_src = gst_element_get_static_pad (enc, "src"); if (gst_pad_link (enc_src, mux_vsink) != GST_PAD_LINK_OK) { g_printerr ("Failed to link encoder to flvmux:video pad\n"); gst_object_unref (enc_src); return -1; } gst_object_unref (enc_src); /* Link flvmux -> rtmpsink */ if (!gst_element_link (mux, sink)) { g_printerr ("Failed to link flvmux to rtmpsink\n"); return -1; } /* Run */ gst_element_set_state (pipeline, GST_STATE_PLAYING); g_print ("Streaming... (meta-string, meta-binary and periodic GstFlvMeta every 30 buffers)\n"); /* Wait for EOS/ERROR */ GstBus *bus = gst_element_get_bus (pipeline); GstMessage *msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS); if (msg) gst_message_unref (msg); gst_object_unref (bus); /* Teardown */ gst_element_set_state (pipeline, GST_STATE_NULL); gst_element_release_request_pad (mux, mux_vsink); gst_object_unref (mux_vsink); gst_object_unref (pipeline); return 0; }
Application Walk-through
This code builds a pipeline equivalent to:
videotestsrc is-live=true ! x264enc ! \ flvmux name=mux meta-string="external-function-test" ! \ rtmpsink location=rtmp://127.0.0.1/live/test
- Before setting the pipeline to PLAYING, we call:
- g_object_set(mux, "meta-string", "...") — injects a UTF-8 string immediately.
- g_object_set(mux, "meta-binary", GBytes*) — injects an opaque binary payload.
- Additionally, a pad probe injects a GstFlvMeta every 30 buffers into
flvmux:video
, demonstrating the GstMeta path (periodic insertion).
Building the application
Save the code as main.c and build using the required GStreamer modules (core, base, and rtmp):
gcc -Wall -g main.c -o main \ $(pkg-config --cflags --libs gstreamer-1.0 gstreamer-base-1.0 gstreamer-rtmp-1.0)
Notes:
- The RTMP metadata support exposes symbols through the
gstreamer-rtmp-1.0
library, and its public header depends on base types fromgstreamer-base-1.0
. - If your system installs the public FLV metadata header (
gstflvmeta.h
) in a non-standard path, add an include directory with-I
accordingly. For example:
gcc -Wall -g main.c -o main \ -I /opt/gstreamer/include \ $(pkg-config --cflags --libs gstreamer-1.0 gstreamer-base-1.0 gstreamer-rtmp-1.0)
If you see errors related to gstflvmeta.h
, verify that your environment (devenv or install prefix) points to the build that includes RidgeRun’s RTMP metadata patches.
Running the application
Start an RTMP server (e.g., SRS):
docker run --rm -p 1935:1935 ossrs/srs
After compilation, you should have a binary called main (or your chosen output name).
Run the app:
./main
On the receiver side, use a pipeline that enables both GstMeta attachment and the metadata signal:
GST_DEBUG=flv*:6,flvdemux:6,gstflv*:6 \ gst-launch-1.0 -m \ rtmpsrc location="rtmp://127.0.0.1/live/test" ! \ flvdemux attach-flvmeta=true flv-meta-signal=true name=d \ d.video ! identity silent=false ! fakesink sync=false \ d.audio ! fakesink sync=false
You should observe flvdemux parsing Script Data metadata. With attach-flvmeta=true, the first audio/video buffers carry a custom GstFlvMeta
(a type of GstMeta
); with flv-meta-signal=true, the element emits a flv-meta signal containing the payload as GBytes.
Expected output (excerpt)
When metadata is received you should see the flv-meta
signal and debug lines like:
0:00:00.012916250 71081 0x5f5cc835b0c0 DEBUG flvdemux gstflvdemux.c:474:gst_flv_demux_parse_metadata_item:<d> meta-string => (string) external-function-test Got message from element /GstPipeline:pipeline0/GstFlvDemux:flvdemux0: flv-meta signal with 19 bytes [meta] Injected periodic GstFlvMeta on flvmux:video (buf #30)
Notes
- You can set meta-string / meta-binary multiple times' during streaming to inject new metadata events.
- For structured payloads, prefer meta-binary and define your own schema on top of the bytes.
- The gstflvmeta.h API is part of the RidgeRun patch set that exposes a public FLV metadata helper for buffers (creation, retrieval).