I've been trying to make a program for running some cameras through gstreamer. The program starts by setting up the pipeline and then switches between PLAYING or PAUSED states depending on the user input. I notices after running this program for a while, that the memory use for the program would slowly increase over time. After trying a lot of different things I was able to reproduce the problem with a very small example program:
#include <gst/gst.h>
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <iostream>
int main(int argc, char* argv[])
{
gst_init(&argc, &argv);
GError* err = NULL;
std::string pipeline_str = "fakesrc ! fakesink sync = false ";
GstElement* pipeline = gst_parse_launch(pipeline_str.c_str(), &err);
gst_element_set_state(pipeline, GST_STATE_READY);
gst_element_get_state(pipeline, NULL, NULL, 4000000000ll);
for (;;)
{
gst_element_set_state(pipeline, GST_STATE_PAUSED);
GstStateChangeReturn result = gst_element_get_state(pipeline, NULL, NULL, 4000000000ll);
if (result != GST_STATE_CHANGE_SUCCESS){
std::cout << "error" << std::endl;
break;
}
usleep(100);
gst_element_set_state(pipeline, GST_STATE_PLAYING);
result = gst_element_get_state(pipeline, NULL, NULL, 4000000000ll);
if (result != GST_STATE_CHANGE_SUCCESS){
std::cout << "error" << std::endl;
break;
}
usleep(100);
}
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(pipeline);
return 0;
}
Running this I can see the memory usage increase quite fast using htop. So it seems to me the problem happens when switching between states? Has anyone had similar issues?
Update
The accepted answer below works. But it seems the issue all along was not emptying the bus. If I get the bus with GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
and then flush the bus every loop with gst_bus_set_flushing(bus, TRUE);
, then I don't see a rise in memory use anymore.
State changes are async. You are generating more state change requests than the pipeline can handle, so most likely your message queue gets build up by those requests.
Wait for the state changes to complete before trying to change it again. Check the bus for the state change emitted from the pipeline.
EDIT, Example:
#include <gst/gst.h>
#include <stdio.h>
#include <unistd.h>
#include <string>
#include <iostream>
int main(int argc, char* argv[])
{
gst_init(&argc, &argv);
GError* err = NULL;
std::string pipeline_str = "videotestsrc ! fakesink sync=false";
GstElement* pipeline = gst_parse_launch(pipeline_str.c_str(), &err);
GstBus* bus = gst_pipeline_get_bus(GST_PIPELINE(pipeline));
gst_element_set_state(pipeline, GST_STATE_READY);
for (;;)
{
GstMessage* msg = gst_bus_timed_pop_filtered(bus, -1, GST_MESSAGE_STATE_CHANGED);
if (msg->src == GST_OBJECT(pipeline))
{
gst_message_unref(msg);
break;
}
gst_message_unref(msg);
}
for (;;)
{
gst_element_set_state(pipeline, GST_STATE_PAUSED);
for (;;)
{
GstMessage* msg = gst_bus_timed_pop_filtered(bus, -1, GST_MESSAGE_STATE_CHANGED);
if (msg->src == GST_OBJECT(pipeline))
{
gst_message_unref(msg);
break;
}
gst_message_unref(msg);
}
gst_element_set_state(pipeline, GST_STATE_PLAYING);
for (;;)
{
GstMessage* msg = gst_bus_timed_pop_filtered(bus, -1, GST_MESSAGE_STATE_CHANGED);
if (msg->src == GST_OBJECT(pipeline))
{
gst_message_unref(msg);
break;
}
gst_message_unref(msg);
}
}
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(bus);
gst_object_unref(pipeline);
return 0;
}