gst_rtsp_media_seek_trickmode can't find source pad
I'm trying to get gst_rtsp_seek_trickmode working. The server works and creates an RTSP Stream from a file on it's own. I then use the "play-request" signal to run function "play_request_callback", which tells the media to seek to a specific point and start playing the file backward.
One thing I've noticed for sure is that when I call "gst_rtsp_media_seek_trickmode" as done in the following source code, then follow it up by "gst_rtsp_media_get_rates", it appears that the rates were not correctly changed. Whatever the case, when the RTSP stream actually plays.. it plays as if it totally missed this.
Theory: I suspect I see whats going on? I've gotten into the "gst_rtsp_media_seek_trickmode" function and traced through the actual up to date gstreamer code, and keep finding that it gets to the "gst_element_send_event" function. In the "gst_element_send_event" function, I believe the event looks good but then it gets to into gst_bin_iterate_sinks, within gst_bin_send_event.
It seems to me like it should see SEEK_EVENT as a DOWNSTREAM_EVENT from "GST_EVENT_IS_DOWNSTREAM" and send SEEK_EVENT to the source, instead it sees that SEEK_EVENT is not marked as a DOWNSTREAM_EVENT, assumes it's an upstream event and sends to sink?
Here is my source code:
#include <gst/gst.h>
#include <glib.h>
#include <direct.h>
#include <thread>
#include "C:\gstreamer\gst-build\subprojects\gst-rtsp-server\gst\rtsp-server\rtsp-server.h"
#include "Stream_File_to_RTSP.h"
#define DEFAULT_RTSP_PORT "8554"
static char* port = (char*)DEFAULT_RTSP_PORT;
static GOptionEntry entries[] = {
{"port", 'p', 0, G_OPTION_ARG_STRING, &port,"Port to listen on (default: " DEFAULT_RTSP_PORT ")", "PORT"},
{NULL}
};
int main(int argc, char* argv[])
{
GMainLoop* loop;
GstRTSPServer* server;
GstRTSPMountPoints* mounts;
GstRTSPMediaFactory* factory;
GOptionContext* optctx;
GError* error = NULL;
GstClock* global_clock;
char workingDirBuff[FILENAME_MAX];
_getcwd(workingDirBuff, FILENAME_MAX);
printf("Current Working Directory: %s\n", workingDirBuff);
//char* pipeline = (char*)"( videotestsrc ! x264enc ! rtph264pay name=pay0 pt=96 )";
//char* pipeline = (char*)"rtspsrc location = \"rtsp://10.100.0.252/axis-media/media.amp?resolution=320x180&compression=60\" ! rtph264depay ! rtph264pay name=pay0 pt=96";
char* pipeline = (char*)"filesrc location=RTSPvideoLong.flv ! flvdemux ! h264parse ! rtph264pay name=pay0 pt=96";// !filesink location = rtpOutput";
optctx = g_option_context_new("<launch line> - Test RTSP Server, Launch\n\n"
"Example: \"( videotestsrc ! x264enc ! rtph264pay name=pay0 pt=96 )\"");
g_option_context_add_main_entries(optctx, entries, NULL);
g_option_context_add_group(optctx, gst_init_get_option_group());
if (!g_option_context_parse(optctx, &argc, &argv, &error)) {
g_printerr("Error parsing options: %s\n", error->message);
g_option_context_free(optctx);
g_clear_error(&error);
return -1;
}
g_option_context_free(optctx);
global_clock = gst_system_clock_obtain();
gst_net_time_provider_new(global_clock, "0.0.0.0", 8554);
loop = g_main_loop_new(NULL, FALSE);
/* create a server instance */
server = gst_rtsp_server_new();
g_object_set(server, "service", port, NULL);
/* get the mount points for this server, every server has a default object
* that be used to map uri mount points to media factories */
mounts = gst_rtsp_server_get_mount_points(server);
/* make a media factory for a test stream. The default media factory can use
* gst-launch syntax to create pipelines.
* any launch line works as long as it contains elements named pay%d. Each
* element with pay%d names will be a stream */
factory = gst_rtsp_media_factory_new();
gst_rtsp_media_factory_set_launch(factory, pipeline); //argv[1]);
gst_rtsp_media_factory_set_shared(factory, TRUE);
gst_rtsp_media_factory_set_clock(factory, global_clock);
//questionable
g_signal_connect(factory, "media-constructed", G_CALLBACK(media_constructed_callback), NULL);
//g_signal_connect(factory, "media-constructed", G_CALLBACK(media_constructed_callback), NULL, NULL, G_SIGNAL_RUN_LAST);
g_print("gst_rtsp_mount_points_add_factory(mounts, \" / test\", factory);\n");
/* attach the test factory to the /test url */
// gst_rtsp_mount_points_add_factory(mounts, "/test", factory);
gst_rtsp_mount_points_add_factory(mounts, "/test", factory);
g_print("g_object_unref\n");
/* don't need the ref to the mapper anymore */
g_object_unref(mounts);
/* attach the server to the default maincontext */
gst_rtsp_server_attach(server, NULL);
g_signal_connect(server, "client-connected", G_CALLBACK(client_connected_callback), NULL);
/* start serving */
g_print("stream ready at rtsp://127.0.0.1:%s/test\n", port);
g_main_loop_run(loop);
return 0;
}
static void client_connected_callback(GstRTSPServer* server, GstRTSPClient* client, gpointer user_data) {
//now that I can access the client.. attach to play signal
g_signal_connect(client, "play-request", G_CALLBACK(play_request_callback), NULL);
}
static void play_request_callback(GstRTSPClient* client, GstRTSPContext* ctx, gpointer user_data) {
seek_backward(ctx->media);
}
static void seek_backward(GstRTSPMedia * RTSP_media) {
GstRTSPTimeRange range;
//gst_rtsp_range_parse("npt=10-5", &range);
gdouble rate = 0;
gdouble applied_rate = 0;
//range 1978 -> 1995
range.unit = GST_RTSP_RANGE_NPT;
range.min.type = GST_RTSP_TIME_SECONDS;
range.min.seconds = 978;
range.max.type = GST_RTSP_TIME_SECONDS;
range.max.seconds = 1995;
GstSeekFlags flags = GstSeekFlags::GST_SEEK_FLAG_TRICKMODE_KEY_UNITS;
//GstSeekFlags flags = GstSeekFlags::GST_SEEK_FLAG_KEY_UNIT;
//wait for Media to be prepared
//while (gst_rtsp_media_get_status(RTSP_media) != GST_RTSP_MEDIA_STATUS_PREPARED) {
// g_print("gst_rtsp_media_get_status status = %s", gst_rtsp_media_get_status(RTSP_media));
//}
gst_rtsp_media_set_rate_control(RTSP_media, TRUE);
GstRTSPMediaPrivate* priv = RTSP_media->priv;
gboolean success = gst_rtsp_media_seek_trickmode(RTSP_media, &range, flags, -2, 0);
gst_rtsp_media_get_rates(RTSP_media, &rate, &applied_rate);
g_print("gst_rtsp_media_seek_trickmode success = %d and rate = %f \n", success);
}
static void media_constructed_callback(GstRTSPMediaFactory* self, GstRTSPMedia* RTSP_media, gpointer user_data) {
g_signal_connect(RTSP_media, "prepared", G_CALLBACK(prepared_callback), NULL);
}
static void prepared_callback(GstRTSPMedia* RTSP_media, gpointer user_data) {
}