I am trying to use appsrc element of Gstreamer on a trivial example. I am creating a buffer, filling it with dummy data and trying to send it to a fakesink. The code is a watered down version of the tutorial given in link below. It only has two elements, appsrc and fakesink. My code is also given below.
When I run this code I get "Error: Internal data stream error." I have searched for a solution and as far as I can tell, the issue is attributed to mismatch of caps between elements. I don't think this is the issue here since I only have two elements.
I have tried setting "caps" property of appsrc to NULL here, but I have also tried passing the proper "caps" property which was "audio/G729." Both have failed. Also it seems like the appsrc is fine for the first 4 chunks but then it generates an error. It is always after 4 Chunks. Not sure if that is a clue.
Also, I am running the code on an embedded system (ARM Cortex-A15) but I don't think that is related. I can succesfully stream a G729 encoded Audio file on this system via following command:
gst-launch-1.0 -v filesrc location=encodedData.g729 ! 'audio/G729' ! rtpg729pay ! udpsink host=192.168.XX.XX auto-multicast=true port=5004
What could be the reason behind this error? How can I fix this?
Thanks for all the responses.
Link: Link to Tutorial
Code:
#include <gst/gst.h>
#include <glib.h>
#include "glibconfig.h"
#include <stdio.h>
#include <string.h>
#define CHUNK_SIZE 10
typedef struct gstreamstruct {
GstElement *pipeline, *app_source, *fakesink;
guint sourceid; /* To control the GSource */
GMainLoop *main_loop; /* GLib's Main Loop */
guint sample;
} gstreamstruct;
static gboolean busCall (GstBus *bus, GstMessage *msg, gpointer data)
{
GMainLoop *loop = (GMainLoop *) data;
switch (GST_MESSAGE_TYPE (msg)) {
case GST_MESSAGE_EOS:
g_print ("End of stream\n");
g_main_loop_quit (loop);
break;
case GST_MESSAGE_ERROR: {
gchar *debug;
GError *error;
gst_message_parse_error (msg, &error, &debug);
g_free (debug);
g_printerr ("Error: %s\n", error->message);
g_error_free (error);
g_main_loop_quit (loop);
break;
}
default:
break;
}
return TRUE;
}
static gboolean pushData (gstreamstruct *streamer)
{
printf("--->PushData!\n");
GstMapInfo map;
GstBuffer *buffer;
GstFlowReturn ret;
guint8 *raw;
int i;
/* Create a new empty buffer */
buffer = gst_buffer_new_and_alloc (CHUNK_SIZE);
/* Set its timestamp and duration */
GST_BUFFER_TIMESTAMP (buffer) = gst_util_uint64_scale (streamer->sample, GST_SECOND, 1000);
GST_BUFFER_DURATION (buffer) = gst_util_uint64_scale (CHUNK_SIZE, GST_SECOND, 1000);
//Put some dummy into buffer
gst_buffer_map (buffer, &map, GST_MAP_WRITE);
raw = (guint8 *)map.data;
for(i = 0; i<CHUNK_SIZE; i++)
{
raw[0] = 0;
}
//update sample value
streamer->sample += CHUNK_SIZE;
printf("currentDuration: %d ms\n", streamer->sample);
gst_buffer_unmap (buffer, &map);
/* Push the buffer into the appsrc */
g_signal_emit_by_name (streamer->app_source, "push-buffer", buffer, &ret);
/* Free the buffer now that we are done with it */
gst_buffer_unref (buffer);
if (ret != GST_FLOW_OK)
{
/* We got some error, stop sending data */
printf("Data sending Failed!\n");
return FALSE;
}
return TRUE;
}
/* This signal callback triggers when appsrc needs data.
* Here, we add an idle handler to the mainloop to start pushing data into the appsrc
*
* Whenever Gstreamer goes idle, it will call this function. Maybe we can utilize this for
* G729 etc!
*
* */
static void startFeed (GstElement *source, guint size, gstreamstruct *streamer)
{
if (streamer->sourceid == 0)
{
g_print ("Start feeding\n");
streamer->sourceid = g_idle_add ((GSourceFunc) pushData, streamer);
}
}
/* This callback triggers when appsrc has enough data and we can stop sending.
* We remove the idle handler from the mainloop */
static void stopFeed (GstElement *source, gstreamstruct *streamer)
{
if (streamer->sourceid != 0)
{
g_print ("Stop feeding\n");
g_source_remove (streamer->sourceid);
streamer->sourceid = 0;
}
}
void appSrcTest (void* args)
{
printf("---> appSrcTest\n");
gstreamstruct my_streamer;
GstCaps *caps;
GstBus *bus;
//GstPad *pad;
guint bus_watch_id;
memset (&my_streamer, 0, sizeof (gstreamstruct));
gst_init (NULL, NULL);
my_streamer.main_loop = g_main_loop_new (NULL, FALSE);
printf("Gst Initialized!\n");
my_streamer.sample = 0;
my_streamer.app_source = gst_element_factory_make("appsrc", "appSrc");
my_streamer.fakesink = gst_element_factory_make("fakesink", "fakeSink");
my_streamer.pipeline = gst_pipeline_new ("g729-pipeline");
if(!my_streamer.app_source || !my_streamer.fakesink || !my_streamer.pipeline)
{
g_printerr ("Not all elements could be created.\n");
return;
}
printf("Elements Created!\n");
caps=NULL;
/*
caps = gst_caps_new_simple ("audio/G729",
"channels", G_TYPE_INT, 1,
"rate", G_TYPE_INT, 8000,
NULL);
*/
//g_object_set (G_OBJECT(my_streamer.app_source), "caps", caps, "format", GST_FORMAT_TIME, NULL);
g_signal_connect (my_streamer.app_source, "need-data", G_CALLBACK (startFeed), &my_streamer);
g_signal_connect (my_streamer.app_source, "enough-data", G_CALLBACK (stopFeed), &my_streamer);
printf("Properties Set!\n");
/* we add a message handler */
bus = gst_pipeline_get_bus (GST_PIPELINE (my_streamer.pipeline));
bus_watch_id = gst_bus_add_watch (bus, busCall, my_streamer.main_loop);
gst_object_unref (bus);
gst_bin_add_many (GST_BIN (my_streamer.pipeline), my_streamer.app_source, my_streamer.fakesink, NULL);
printf("Elements Added!\n");
printf("Pipeline Starting!\n");
gst_element_set_state (my_streamer.pipeline, GST_STATE_PLAYING);
g_main_loop_run (my_streamer.main_loop);
gst_element_set_state (my_streamer.pipeline, GST_STATE_NULL);
gst_object_unref (my_streamer.pipeline);
g_source_remove (bus_watch_id);
g_main_loop_unref (my_streamer.main_loop);
}
The output from this code is generated as:
Gst Initialized!
Elements Created!
Properties Set!
Elements Added!
Pipeline Starting!
Start feeding
--->PushData!
currentDuration: 10 ms
--->PushData!
currentDuration: 20 ms
--->PushData!
currentDuration: 30 ms
--->PushData!
currentDuration: 40 ms
Error: Internal data stream error.
Edit: After more trials I have realized that the error is not generated after 4 Chunks consistently. When I reboot the system and call the function the error is generated after 156 Chunks for instance. After a couple more tries, the error starts occur much sooner (like 4 Chunks.) Also I have tried running the code with GST_DEBUG=2 but could not really find anything useful. Below you can find the DEBUG output. DEBUG:
---> appSrcTest
Gst Initialized!
Elements Created!
Properties Set!
Elements Added!
Pipeline Starting!
Start feeding
--->PushData!
currentDuration: 10 ms
--->PushData!
currentDuration: 20 ms
--->PushData!
currentDuration: 30 ms
--->PushData!
currentDuration: 40 ms
--->PushData!
0:00:00.084280528 1344 0x18fa00 WARN basesrc gstbasesrc.c:3055:gst_base_src_loop:<appSrc> error: Internal data stream error.
currentDuration: 50 ms
--->PushData!
0:00:00.084342504 1344 0x18fa00 WARN basesrc gstbasesrc.c:3055:gst_base_src_loop:<appSrc> error: streaming stopped, reason not-linked (-1)
currentDuration: 60 ms
--->PushData!
currentDuration: 70 ms
Error: Internal data stream error.
Edit 2: After further debugging I have realized that the fakesink element was not linked to appsrc. So I manually linked them via the following line
gst_element_link_pads (my_streamer.app_source, "src", my_streamer.fakesink, "sink");
I think it works fine now, I will come back again after I verify it completely.
Edit 3: Yeah, I can confirm that was the issue. I forgot to link the elements.
I forgot to link the elements. The following line solves the issue.
gst_element_link_pads (my_streamer.app_source, "src", my_streamer.fakesink, "sink");