/*
 Reading streams from HTTP urls leaks thread after stream finishes
 Affects both versions, 0.1 and 1.0.

 To reproduce:

 Build with

    gcc test-loop.c -o test-loop-1.0 `pkg-config --cflags --libs gstreamer-1.0`

 (This expects pkg-config to work for gstreamer-1.0)

 == Preparation: provide sound-file via webserver ==
 Run a webserver that serves the test sound file.

 For instance using webfsd as a lightweight solution
 (sudo aptitude install webfs). Given a directory 'sounds/' with sound-samples,
 the call would be like this

 webfsd -r sounds/ -p 9999

 (Choose a directory with short sound snippets, like desktop notifications)

 == Playing URI ==
 Run the binary:
 ./test-loop-1.0 http://localhost:9999/test-sound.ogg

 In another shell, watch how the number of threads increase over time

 $ while : ; do ps -eLf | grep test-loop | grep -v grep | wc -l ; sleep 1 ; done

 The number of threads is around the number of repetitions played, never less.

 == Expected Behavior ==
 Threads used for network interaction are cleaned up once the end-of-stream
 is reached.

 == Observed Behavior ==
 Threads keep accumulating.

 == Same for version 0.1 ==
 Since 0.10 is not supported anymore, this is more a FYI.
 Same is happening there.
    gcc test-loop.c -o test-loop-0.1 `pkg-config --cflags --libs gstreamer-0.10`

 (So if there is a fix for 1.0 that is easily back-ported to 0.10, that would
  be great as well, as many distributions still have 0.10)

 == FYI, playing a file-uri works ==
 Same thing works fine with both versions, if the input is a file

 ./test-loop-0.1 file://`pwd`/sounds/test-sound.ogg
 ./test-loop-1.0 file://`pwd`/sounds/test-sound.ogg

 (works means: plays the same sound indefinetly, and only uses a limited
  amount of threads).

 */

#include <assert.h>
#include <gst/gst.h>
#include <glib.h>
#include <string.h>

struct NextStreamData {
	GstElement *pipeline;
	int count;
	const char *next_uri;
};

// All we do in the bus listener: when we reach the end of the stream, we
// re-start that stream.
static gboolean bus_listener(GstBus     *bus,
			     GstMessage *msg,
			     gpointer    userdata) {
	struct NextStreamData *data = (struct NextStreamData*) userdata;

	switch (GST_MESSAGE_TYPE (msg)) {
	case GST_MESSAGE_EOS:
		g_print ("%d. Stream end; Starting new stream again with %s\n",
			 data->count, data->next_uri);
		++data->count;
		gst_element_set_state(data->pipeline, GST_STATE_READY);
		g_object_set(G_OBJECT(data->pipeline), "uri", data->next_uri,
			     NULL);
		gst_element_set_state(data->pipeline, GST_STATE_PLAYING);
		break;

	default:
		break;
	}

	return TRUE;
}

int main (int argc, char *argv[]) {
	GMainLoop *loop;
	GstElement *pipeline;
	GstBus *bus;
	guint bus_watch_id;

	/* Initialisation */
	gst_init (&argc, &argv);

	loop = g_main_loop_new (NULL, FALSE);

	/* Check input arguments */
	if (argc != 2) {
		g_printerr ("Usage: %s <filename or URL>\n", argv[0]);
		return -1;
	}

	/* Create playbin. Different names in different versions  */
#if (GST_VERSION_MAJOR < 1)
	pipeline = gst_element_factory_make("playbin2", "play");
#else
	pipeline = gst_element_factory_make("playbin", "play");
#endif

	/* Register about-to-finish callback to re-set the URI */
	struct NextStreamData replay_data;
	replay_data.pipeline = pipeline;
	replay_data.count = 0;
	replay_data.next_uri = argv[1];   // let's loop forever the same URI

	/* we add a message handler that start */
	bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline));
	bus_watch_id = gst_bus_add_watch(bus, bus_listener, &replay_data);
	gst_object_unref(bus);

	/* Set initial URI */
	g_object_set (G_OBJECT (pipeline), "uri", argv[1], NULL);

	g_print ("Now playing: %s\n", argv[1]);
	gst_element_set_state (pipeline, GST_STATE_PLAYING);

	/* Iterate */
	g_print ("Running...\n");
	g_main_loop_run (loop);

	/* since we loop endlessly, the follwing is never reached */

	/* Out of the main loop, clean up nicely */
	g_print ("Returned, stopping playback\n");
	gst_element_set_state (pipeline, GST_STATE_NULL);

	g_print ("Deleting pipeline\n");
	gst_object_unref (GST_OBJECT (pipeline));
	g_source_remove (bus_watch_id);
	g_main_loop_unref (loop);

	return 0;
}
