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