GstRTMPMetadata C Example

From RidgeRun Developer Wiki

Follow Us On Twitter LinkedIn Email Share this page






NVIDIA partner logo NXP partner logo




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 from gstreamer-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).