Hey!
I tried creating RTSP stream with clock sync using audio stream, but audio stream stops after short time. Works on VLC without clock so I know its not stream issue, must be clock or my client.
My client:
#include <gst/gst.h>
#include <gst/net/gstnet.h>
#include <iostream>
#define PLAYBACK_DELAY_MS 100
// Callback to configure the source for synchronization
static void source_created(GstElement *pipe, GstElement *source) {
g_object_set(source,
"latency", PLAYBACK_DELAY_MS,
"ntp-time-source", 3, // Use NTP timestamps from RTP
"buffer-mode", 4, // Sync buffer with the clock
"ntp-sync", TRUE, // Enable NTP-based synchronization
NULL);
std::cout << "Source configured for synchronization." << std::endl;
}
// Message handler for bus messages
static gboolean handle_message(GstBus *bus, GstMessage *message, gpointer user_data) {
GMainLoop *loop = static_cast<GMainLoop *>(user_data);
switch (GST_MESSAGE_TYPE(message)) {
case GST_MESSAGE_ERROR: {
GError *err = nullptr;
gchar *debug_info = nullptr;
gst_message_parse_error(message, &err, &debug_info);
std::cerr << "Error received from element "
<< GST_OBJECT_NAME(message->src)
<< ": " << err->message << std::endl;
if (debug_info) {
std::cerr << "Debugging info: " << debug_info << std::endl;
}
g_clear_error(&err);
g_free(debug_info);
g_main_loop_quit(loop);
break;
}
case GST_MESSAGE_WARNING: {
GError *err = nullptr;
gchar *debug_info = nullptr;
gst_message_parse_warning(message, &err, &debug_info);
std::cerr << "Warning received from element "
<< GST_OBJECT_NAME(message->src)
<< ": " << err->message << std::endl;
if (debug_info) {
std::cerr << "Debugging info: " << debug_info << std::endl;
}
g_clear_error(&err);
g_free(debug_info);
break;
}
case GST_MESSAGE_EOS:
std::cerr << "Unexpected EOS received for a live stream. Ignoring it." << std::endl;
break;
default:
break;
}
return TRUE;
}
int main(int argc, char *argv[]) {
gst_init(&argc, &argv);
if (argc < 4) {
std::cerr << "Usage: " << argv[0]
<< " <RTSP URI> <Clock IP> <Clock Port>" << std::endl;
std::cerr << "Example: " << argv[0]
<< " rtsp://192.168.0.90:8554/audio 192.168.0.90 8555" << std::endl;
return -1;
}
const gchar *rtsp_uri = argv[1];
const gchar *clock_ip = argv[2];
gint clock_port = atoi(argv[3]);
// Create the network clock
GstClock *net_clock = gst_net_client_clock_new("net_clock", clock_ip, clock_port, 0);
if (!net_clock) {
std::cerr << "Failed to create net clock client for "
<< clock_ip << ":" << clock_port << std::endl;
return -1;
}
// Wait for the clock to stabilize
if (!gst_clock_wait_for_sync(net_clock, 10 * GST_SECOND)) {
std::cerr << "Failed to synchronize with the network clock" << std::endl;
gst_object_unref(net_clock);
return -1;
}
std::cout << "Network clock synchronized." << std::endl;
// Create the playback pipeline
GstElement *pipe = gst_element_factory_make("playbin", NULL);
if (!pipe) {
std::cerr << "Failed to create playbin element." << std::endl;
gst_object_unref(net_clock);
return -1;
}
g_object_set(pipe, "uri", rtsp_uri, NULL);
// Use the network clock
gst_pipeline_use_clock(GST_PIPELINE(pipe), net_clock);
// Connect the source-created signal
g_signal_connect(pipe, "source-setup", G_CALLBACK(source_created), NULL);
// Create a GLib main loop
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
// Add a bus watch to handle messages
GstBus *bus = gst_element_get_bus(pipe);
gst_bus_add_watch(bus, handle_message, loop);
// Start playback
if (gst_element_set_state(pipe, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) {
std::cerr << "Failed to set pipeline to PLAYING state." << std::endl;
gst_object_unref(bus);
gst_object_unref(pipe);
gst_object_unref(net_clock);
g_main_loop_unref(loop);
return -1;
}
std::cout << "Playing stream from: " << rtsp_uri << std::endl;
// Run the main loop
g_main_loop_run(loop);
// Clean up
gst_element_set_state(pipe, GST_STATE_NULL);
gst_object_unref(bus);
gst_object_unref(pipe);
gst_object_unref(net_clock);
g_main_loop_unref(loop);
return 0;
}
My server:
#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
#include <gst/net/gstnettimeprovider.h>
int main(int argc, char *argv[]) {
gst_init(&argc, &argv);
// Obtain the global clock
GstClock *global_clock = gst_system_clock_obtain();
// Create a network time provider for the global clock
gst_net_time_provider_new(global_clock, "0.0.0.0", 8555);
// Create an RTSP server instance
GstRTSPServer *server = gst_rtsp_server_new();
// Bind the server to the specific IP address
gst_rtsp_server_set_address(server, "0.0.0.0");
// Get the mount points for this server
GstRTSPMountPoints *mounts = gst_rtsp_server_get_mount_points(server);
// Create a factory for the audio stream
GstRTSPMediaFactory *factory = gst_rtsp_media_factory_new();
// Define the pipeline for the audio stream
const gchar *pipeline_str =
"audiotestsrc is-live=true wave=sine freq=440 ! audioconvert ! "
"audioresample ! rtpL16pay name=pay0 pt=96";
gst_rtsp_media_factory_set_launch(factory, pipeline_str);
// Set the clock for the media factory
gst_rtsp_media_factory_set_clock(factory, global_clock);
// Add the factory to the mount points
gst_rtsp_mount_points_add_factory(mounts, "/audio", factory);
g_object_unref(mounts);
// Attach the server
if (gst_rtsp_server_attach(server, NULL) == 0) {
g_printerr("Failed to attach the RTSP server.\n");
return -1;
}
g_print("RTSP server ready at rtsp://192.168.0.90:8554/audio\n");
g_print("Clock is available at 192.168.0.90:8555\n");
// Run the GLib main loop
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(loop);
// Clean up
g_main_loop_unref(loop);
g_object_unref(server);
g_object_unref(global_clock);
return 0;
}
Thank you in advance