deinterleave / intreleave pipeline

Lucas M mccauley.lucas at gmail.com
Mon Sep 14 09:18:03 PDT 2015


Hello All,

I've been playing around with gstreamer for a new project and have hit a
stumbling block. I'm new to the framework and could use some help.

I want to read a WAV file that can be from 2 to 4 channels, deinterleave it,
send the individual channels to a custom plugin, then interleave back to a
two-channel audio stream for playback. As of now, I'm just trying to do
filesrc -> wavparse -> deinterleave -> interleave -> sink, with no success.
If I don't go through the deinter/inter step (i.e. filesrc -> wavparse ->
sink), then all works ok for stereo files. If I dynamically connect the
deinterleaver to the sink when the deinterleaver's pads are connected I get
the first channel but no others (playback is mono, from the first pad
created on deinterleave).

Looking at the debug messages printed when running, it seems that the
problem is with gst_element_set_state(pipeline, GST_STATE_PLAYING) not
reaching the sink, so playback never really starts. I'm deducing this from
the "stream-start event without group-id. Consider implementing group-id
handling in the upstream elements" FIXME message I'm getting. I don't get
this with any of the other configurations that lead to sound actually coming
out... When I move up to the (very) verbose GST_DEBUG=4, it seems that
everything went ok in setting up the pipe - all links are successful and the
caps all seem correct.



Any insight to where I should be looking, or what I'm doing wrong would be
much appreciated. 


My sandbox code is below.  

/*
 * main.c
 *
 *  Created on: Sep 8, 2015
 *      Author: lucas
 */


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#include <gst/gst.h>


typedef struct g_ctx {

	GMainLoop 		*loop;

	guint			bus_id;

	GstElement 		*pipeline;
	GstElement 		*source;
	GstElement 		*wavdec;
	GstElement 		*deinter;
	GstElement		*inter;
	GstElement 		*sink;

	gboolean 		timer_run;

} g_ctx_t;

#define CTX_CAST(X) 	((g_ctx_t *)(X))

static g_ctx_t gctx; /* Application context */


static void			app_init( int argc, char *argv[], g_ctx_t *ctx);
static int			create_pipeline(g_ctx_t *ctx);
static int			create_bus(g_ctx_t *ctx);
static int			play_file(g_ctx_t *ctx, const gchar *filename);
static gboolean		print_position_cb(GstElement *pipeline);
static gboolean		bus_cb(GstBus *bus, GstMessage *msg, gpointer ctx);
static void			pad_add_cb(GstElement *elem, GstPad *pad, gpointer ctx);




int main(int argc, char **argv) {

	g_ctx_t *ctx = &gctx;

	app_init(argc, argv, ctx);

//	play_file(ctx, "/Users/lucas/Desktop/wav2ch.wav");
	play_file(ctx, "/Users/lucas/Desktop/wav4ch.wav");

	g_main_loop_run(ctx->loop);

	return 0;
}


static void app_init(int argc, char *argv[], g_ctx_t *ctx) {

	gst_init(&argc, &argv);

	ctx->loop = g_main_loop_new(NULL, FALSE);

	create_pipeline(ctx);
	create_bus(ctx);

	return;
}



static int create_bus(g_ctx_t * ctx) {

	GstBus *bus;

	bus = gst_pipeline_get_bus(GST_PIPELINE(ctx->pipeline));
	ctx->bus_id = gst_bus_add_watch(bus, bus_cb, (gpointer) ctx);
	gst_object_unref(bus);

	return 0;
}



static gboolean bus_cb(GstBus *bus, GstMessage *msg, gpointer ctx) {

	switch ( GST_MESSAGE_TYPE(msg) ) {

		case GST_MESSAGE_ERROR:
			g_print("ERROR\n");
			break;

		case GST_MESSAGE_EOS:
			CTX_CAST(ctx)->timer_run = FALSE;
			g_main_loop_quit(CTX_CAST(ctx)->loop);
			break;

		default:
			break;
	}

	return TRUE;
}

static int create_pipeline(g_ctx_t *ctx)	{

	/*
	 *  file -> wavparse -> deinterleave -> interleave -> sink
	 *
	 *	The wave file can have from two to four audio channels.
	 *	We extract the first two with deinterleave, then pass them
	 *	on to the output through interleave.
	 *
	 */

	ctx->pipeline = gst_pipeline_new("my-pipeline");

	ctx->source   = gst_element_factory_make("filesrc", NULL);
	ctx->wavdec   = gst_element_factory_make("wavparse", NULL);
	ctx->deinter  = gst_element_factory_make("deinterleave", NULL);
	ctx->inter    = gst_element_factory_make("interleave", NULL);
	ctx->sink     = gst_element_factory_make("autoaudiosink", NULL);

	/* Seems to be needed */
	g_object_set(G_OBJECT(ctx->deinter), "keep-positions", TRUE, NULL);

	gst_bin_add_many(GST_BIN(ctx->pipeline), ctx->source, ctx->wavdec,
											 ctx->deinter, ctx->inter,
											 ctx->sink, NULL);

	if ( !gst_element_link_many(ctx->source, ctx->wavdec, ctx->deinter, NULL) )
{
		g_warning("%s failed, line %d\n", __FUNCTION__, __LINE__);
		return 1;
	}

	/* Deinter and inter are linked in pad-added callback */

	if ( !gst_element_link(ctx->inter, ctx->sink) ) {
		g_warning("%s failed, line %d\n", __FUNCTION__, __LINE__);
		return 1;
	}

	g_signal_connect(ctx->deinter, "pad-added", G_CALLBACK(pad_add_cb),
(gpointer) ctx);

	return 0;
}



static void pad_add_cb(GstElement * elem, GstPad * pad, gpointer data) {

	gchar *srcPadName, *sinkPadName;
	GstPad *sink_pad;
	g_ctx_t *ctx = CTX_CAST(data);
	int n;

	srcPadName = gst_pad_get_name(pad);
	g_print("Pad %s created\n", srcPadName);

	/* Connect only first two outputs */
	n = (int) strlen((const char *) srcPadName);
	if ( atoi((const char *) &srcPadName[n - 1]) < 2 ) {
		sink_pad = gst_element_get_compatible_pad(ctx->inter, pad, NULL);
		gst_pad_link(pad, sink_pad);
		sinkPadName = gst_pad_get_name(sink_pad);
		gst_object_unref(sink_pad);
		g_print("Linked %s to %s\n", srcPadName, sinkPadName);
		g_free(sinkPadName);
	}

	g_free(srcPadName);

	return;
}



static int play_file(g_ctx_t * ctx, const gchar * filename) {

	if ( 0 == gst_element_set_state(GST_ELEMENT(ctx->pipeline), GST_STATE_NULL)
) {
		g_warning("%s failed, line %d\n", __FUNCTION__, __LINE__);
	}

	g_object_set(G_OBJECT(ctx->source), "location", filename, NULL);

	if ( 0 == gst_element_set_state(GST_ELEMENT(ctx->pipeline),
GST_STATE_PLAYING) ) {
		g_warning("%s failed, line %d\n", __FUNCTION__, __LINE__);
	}

	ctx->timer_run = TRUE;
	g_timeout_add(200, (GSourceFunc) print_position_cb, ctx->pipeline);

	return 0;
}




#define MY_GST_TIME_ARGS(t) \
        GST_CLOCK_TIME_IS_VALID (t) ? \
        (guint) (((GstClockTime)(t)) / (GST_SECOND * 60 * 60)) : 99, \
        GST_CLOCK_TIME_IS_VALID (t) ? \
        (guint) ((((GstClockTime)(t)) / (GST_SECOND * 60)) % 60) : 99, \
        GST_CLOCK_TIME_IS_VALID (t) ? \
        (guint) ((((GstClockTime)(t)) / GST_SECOND) % 60) : 99

static gboolean print_position_cb(GstElement * pipeline) {

	gint64 pos, len;
	static int first = 1;

	if ( 1 == first ) {
		first = 0;
		if ( gst_element_query_position(pipeline, GST_FORMAT_TIME, &pos) &&
			 gst_element_query_duration(pipeline, GST_FORMAT_TIME, &len) ) {
			g_print("Time: %u:%02u:%02u / %u:%02u:%02u\r",
					MY_GST_TIME_ARGS(pos), MY_GST_TIME_ARGS(len));
		}
	} else {
		if ( gst_element_query_position(pipeline, GST_FORMAT_TIME, &pos) ) {
			g_print("Time: %u:%02u:%02u\r",	MY_GST_TIME_ARGS(pos));
		}
	}

	if ( gctx.timer_run ) {
		return TRUE;
	} else {
		first = 0;
		return FALSE;
	}
}










--
View this message in context: http://gstreamer-devel.966125.n4.nabble.com/deinterleave-intreleave-pipeline-tp4673615.html
Sent from the GStreamer-devel mailing list archive at Nabble.com.


More information about the gstreamer-devel mailing list