I'm trying to record two channels from my Clarret 8pre sound card as stereo to a wav file using the gstreamer api without using gst_launch on MacOS using cpp, but i use the c api of gstreamer.
i was able to perform that from the terminal using:
gst-launch-1.0 osxaudiosrc device=97 ! audioconvert ! deinterleave name=d interleave name=i ! audioconvert ! wavenc ! filesink location=stereo.wav d.src_4 ! queue ! i.sink_0 d.src_5 ! queue ! i.sink_1
but as i learned the gstreamer api is a bit different.
so in general i first connect and link osxaudiosource device=97 ! audioconvert ! audioresample ! capsfilter ! deinterleave and separately i connect interleave ! encoder ! sink
then in the pad-added callback to the deinterleave element, i pass the interleave element as a data, i wait for src_4 and src_5 from deinterleave and connect them to new sinks sink_0 and sink_1 created by gst_element_request_pad_simple(sink, "sink_%u");
the capsfilter is probably not needed, i was trying to see how to overcome the warning the i get which causes the pipeline to not record anything.
WARN interleave interleave.c:319:gst_interleave_set_channel_positions:<interleave> Invalid channel positions, using NONE
i'm really new to gstreamer, i was able to create a function to record a single channel, now i'm trying to record stereo and encounter this issue, any information regarding it would be greatly appreciated.
this is the full code:
#include <gst/gst.h>
static void on_pad_added(GstElement *element, GstPad *pad, gpointer data);
int main(int argc, char *argv[])
{
setenv("GST_DEBUG", "3", true);
// return my_sound_dev::gst_main(reinterpret_cast<GstMainFunc>(my_main), argc, argv);
GMainLoop *loop;
GstElement *pipeline, *src, *convert, *deinterleave, *interleave, *resample, *encoder, *sink;
GstPad *pad, *sinkpad;
GstCaps *caps;
gst_init(&argc, &argv);
src = gst_element_factory_make("osxaudiosrc", "source");
convert = gst_element_factory_make("audioconvert", "convert");
resample = gst_element_factory_make("audioresample", "resample");
deinterleave = gst_element_factory_make("deinterleave", "deinterleave");
GstElement *capsfilter = gst_element_factory_make("capsfilter", "filter");
caps = gst_caps_from_string("audio/x-raw,channels=20,layout=(string)interleaved");
//caps = gst_caps_from_string("audio/x-raw,channels=2,channel-mask=(bitmask)0x3,layout=(string)interleaved");
g_object_set (G_OBJECT (capsfilter), "caps", caps, NULL);
interleave = gst_element_factory_make("interleave", "interleave");
encoder = gst_element_factory_make("wavenc", "encoder");
sink = gst_element_factory_make("filesink", "sink");
g_object_set(src, "device", 97, NULL);
pipeline = gst_pipeline_new("audio-pipeline");
gst_bin_add_many(GST_BIN(pipeline), src, deinterleave, interleave, convert, resample, encoder, sink, capsfilter, NULL);
g_object_set(sink, "location", "/Users/ufk/stereo.wav", NULL);
if (!gst_element_link_many(src, convert, resample, capsfilter, deinterleave, NULL))
{
g_error("Failed to link elements");
return -1;
}
if(!gst_element_link(interleave, encoder)){
g_error("Failed to link interleave and encoder");
return -1;
}
if(!gst_element_link(encoder, sink)){
g_error("Failed to link encoder and sink");
return -1;
}
loop = g_main_loop_new(NULL, FALSE);
g_signal_connect(deinterleave, "pad-added", G_CALLBACK(on_pad_added), interleave);
if(gst_element_set_state(pipeline, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE){
g_error("Could not set pipeline to playing state");
return -1;
}
g_main_loop_run(loop);
gst_element_set_state(pipeline, GST_STATE_NULL);
gst_object_unref(GST_OBJECT(pipeline));
g_main_loop_unref(loop);
return 0;
}
void print_pads(GstElement* element) {
GValue item = G_VALUE_INIT;
gboolean done = FALSE;
GstIterator* it = gst_element_iterate_pads(element);
while (!done) {
switch (gst_iterator_next(it, &item)) {
case GST_ITERATOR_OK: {
GstPad *pad = GST_PAD(g_value_get_object(&item));
g_print ("pad name: %s\n", GST_PAD_NAME(pad));
g_value_reset(&item);
break;
}
case GST_ITERATOR_RESYNC:
gst_iterator_resync(it);
break;
case GST_ITERATOR_ERROR:
case GST_ITERATOR_DONE:
done = TRUE;
break;
}
}
g_value_unset(&item);
gst_iterator_free(it);
}
void on_pad_added(GstElement *element, GstPad *pad, gpointer data)
{
char *name;
GstPad *sinkpad;
GstElement *queue = gst_element_factory_make("queue", "queue");
GstElement *sink = (GstElement *)data;
name = gst_pad_get_name(pad);
if(g_strcmp0(name, "src_4") == 0){
sinkpad = gst_element_request_pad_simple(sink, "sink_%u");
if (!sinkpad)
{
g_error("could not create sink for src_4");
}
if (gst_pad_is_linked(sinkpad))
{
MoLog::fatal() << "sinkpad already linked";
}
if (gst_pad_is_linked(pad))
{
MoLog::fatal() << "new_pad already linked";
}
if (!gst_pad_can_link(pad, sinkpad))
{
MoLog::error() << "can't link new_pad to sinkpad";
GstCaps *new_pad_caps = gst_pad_get_current_caps(pad);
GstCaps *sinkpad_caps = gst_pad_get_pad_template_caps(sinkpad);
if (!gst_caps_can_intersect(new_pad_caps, sinkpad_caps)) {
MoLog::fatal() << "Caps are not compatible";
} else
{
MoLog::info() << "caps can intersect";
}
gst_caps_unref(new_pad_caps);
gst_caps_unref(sinkpad_caps);
}
GstPadLinkReturn ret = gst_pad_link(pad, sinkpad);
if (ret != GST_PAD_LINK_OK){
print_pads(sink);
g_error("Error linking pad src_4: %d",ret);
} else
{
g_print("src_4 linked successfully!");
}
gst_object_unref(sinkpad);
}
else if(g_strcmp0(name, "src_5") == 0){
sinkpad = gst_element_request_pad_simple(sink, "sink_%u");
if (!sinkpad)
{
g_error("could not create sink for src_5");
}
if ((gst_pad_link(pad, sinkpad)) != GST_PAD_LINK_OK){
g_error("Error linking pad src_5");
} else
{
g_print("successfully linked src_5\n");
}
gst_object_unref(sinkpad);
}
g_free(name);
}
i also have a print_pads(GstElement* element) for debugging to understand when it creates the pads, but i'm quite lost from here. i used queue in the gst-launch but not here.. i'm not sure if i should and if so what's the flow
thanks
so.. it was actually simpler than i thought, it appears that the command like of
gst_parse_launchis not that different from the flow of the C implementation, i just needed to better understand what's going on.first i created a capsfilter for 44100 and
S16LElater i want to record it to a wav file using libsndfile instead of filesink so i prepared that before hand.
and my inital flow is this:
it's
osxaudiosrc ! audioconvert !audioresample! capsfilter! deinterleavecreated to
queueelements and twoaudioresampleelements to be used after the queue:i request the sink pad for each queue:
and connect each queue to it's resample and to the interleave and the interleave to the sink
connnected to the
pad-addedsignal of thedeinterleaveelement:i created a common function to connect each channel to a queue
and in my case i wait for all the pads to be added by counting how much pads where added and compare it the channels in the sound card:
and i use this function to like the src pads from interleave to each queue:
and voila! works like charm.