GStreamer plugin src -> opusdec sink gives "error: decoder not initialized" - c

I have the following two pipelines to transmit opus encoded audio from server to client:
The server:
gst-launch-1.0 -v alsasrc ! audioconvert ! audioresample ! audio/x-raw, rate=16000, channels=1, format=S16LE ! opusenc ! rtpopuspay ! udpsink host=0.0.0.0 port=4000
The client:
gst-launch-1.0 udpsrc port=4000 ! application/x-rtp,payload=96,encoding-name=OPUS ! rtpopusdepay ! opusdec ! autoaudiosink
I try to create a custom GstElement based plugin to replace rtpopusdepay in the client side with a hand-crafted one (to be backward compatible with an existing server implementation that doesn't use rtpopuspay but uses a hand-crafted byte-format to wrap the opus encoded data).
To test the concept I would like to use the pipelines above, but replace the client side with:
GST_PLUGIN_PATH=. gst-launch-1.0 udpsrc port=4000 ! simpacketdepay ! opusdec ! autoaudiosink
Where simpacketdepay is the plugin I created. The plugin is quite simple, it has fixed caps (ANY for its sink and "audio/x-opus" for its src). In its chain function I simply remove the payload rtpopuspay adds to the encoded opus stream (first 96 bits) and push the data forward.
The full code:
#include "gstsimpacketdepay.h"
#include <stdio.h>
#include <string.h>
#include <gst/gst.h>
#include <gst/gstcaps.h>
GST_DEBUG_CATEGORY_STATIC (gst_simpacketdepay_debug);
#define GST_CAT_DEFAULT gst_simpacketdepay_debug
/* Enum to identify properties */
enum
{
PROP_0
};
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE(
"sink",
GST_PAD_SINK,
GST_PAD_ALWAYS,
GST_STATIC_CAPS("ANY")
);
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE (
"src",
GST_PAD_SRC,
GST_PAD_ALWAYS,
GST_STATIC_CAPS("audio/x-opus, rate=16000, channels=1, channel-mapping-family=0, stream-count=1, coupled-count=0")
);
/* Define our element type. Standard GObject/GStreamer boilerplate stuff */
#define gst_simpacketdepay_parent_class parent_class
G_DEFINE_TYPE(GstSimpacketdepay, gst_simpacketdepay, GST_TYPE_ELEMENT);
static GstFlowReturn gst_simpacketdepay_chain (GstPad *pad, GstObject *parent, GstBuffer *buf);
static void gst_simpacketdepay_class_init (GstSimpacketdepayClass * klass)
{
GObjectClass *gobject_class;
GstElementClass *gstelement_class;
gstelement_class = (GstElementClass *) klass;
/* Set sink and src pad capabilities */
gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get(&src_factory));
gst_element_class_add_pad_template (gstelement_class, gst_static_pad_template_get(&sink_factory));
/* Set metadata describing the element */
gst_element_class_set_details_simple (
gstelement_class,
"simpacketdepay plugin",
"simpacketdepay plugin",
"Sim Packet depay",
"Test"
);
}
static void gst_simpacketdepay_init (GstSimpacketdepay * simpacketdepay)
{
simpacketdepay->sinkpad = gst_pad_new_from_static_template (&sink_factory, "sink");
simpacketdepay->srcpad = gst_pad_new_from_static_template (&src_factory, "src");
gst_pad_use_fixed_caps(simpacketdepay->sinkpad);
gst_pad_use_fixed_caps(simpacketdepay->srcpad);
gst_element_add_pad (GST_ELEMENT (simpacketdepay), simpacketdepay->sinkpad);
gst_element_add_pad (GST_ELEMENT (simpacketdepay), simpacketdepay->srcpad);
gst_pad_set_chain_function (simpacketdepay->sinkpad, gst_simpacketdepay_chain);
}
static GstFlowReturn gst_simpacketdepay_chain (GstPad *pad, GstObject *parent, GstBuffer *inBuf)
{
GstSimpacketdepay *filter = GST_SIMPACKETDEPAY(parent);
GstMapInfo info;
gst_buffer_map(inBuf, &info, GST_MAP_READ);
const size_t inSize = info.size;
printf("Incoming size %lu\n", info.size);
gst_buffer_unmap(inBuf, &info);
GstBuffer* outBuf = gst_buffer_copy(inBuf);
GstMemory* const inMemory = gst_buffer_get_memory(inBuf, 0);
GstMemory* const outMemory = gst_memory_share(inMemory, 12, inSize - 12);
gst_buffer_remove_all_memory(outBuf);
gst_buffer_prepend_memory(outBuf, outMemory);
gst_buffer_map(outBuf, &info, GST_MAP_READ);
printf("Outgoing size: %lu\n", info.size);
fflush(stdout);
gst_buffer_unmap(outBuf, &info);
gst_buffer_unref (inBuf);
GstFlowReturn result = gst_pad_push (filter->srcpad, outBuf);
return result;
}
static gboolean simpacketdepay_plugin_init (GstPlugin * plugin)
{
GST_DEBUG_CATEGORY_INIT (gst_simpacketdepay_debug, "simpacketdepay", 0, "simpacketdepay");
return gst_element_register (plugin, "simpacketdepay", GST_RANK_NONE, GST_TYPE_SIMPACKETDEPAY);
}
#ifndef VERSION
#define VERSION "1.0.0"
#endif
#ifndef PACKAGE
#define PACKAGE "FIXME_package"
#endif
#ifndef PACKAGE_NAME
#define PACKAGE_NAME "FIXME_package_name"
#endif
#ifndef GST_PACKAGE_ORIGIN
#define GST_PACKAGE_ORIGIN "http://FIXME.org/"
#endif
GST_PLUGIN_DEFINE (
GST_VERSION_MAJOR,
GST_VERSION_MINOR,
simpacketdepay,
"FIXME plugin description",
simpacketdepay_plugin_init,
VERSION,
"LGPL",
PACKAGE_NAME,
GST_PACKAGE_ORIGIN
)
The negotiations and everything goes well, until I push the first buffer to the source pad from gst_simpacketdepay_chain with GstFlowReturn result = gst_pad_push (filter->srcpad, outBuf);
Then I get the following error (pasted the detailed debug log here)
0:00:00.510871708 42302 0x55fbd0c44000 LOG audiodecoder gstaudiodecoder.c:2034:gst_audio_decoder_chain:<opusdec0> received buffer of size 160 with ts 0:00:00.006492658, duration 99:99:99.999999999
0:00:00.510877845 42302 0x55fbd0c44000 WARN audiodecoder gstaudiodecoder.c:2084:gst_audio_decoder_chain:<opusdec0> error: decoder not initialized
0:00:00.510882963 42302 0x55fbd0c44000 DEBUG GST_MESSAGE gstelement.c:2110:gst_element_message_full_with_details:<opusdec0> start
0:00:00.510896592 42302 0x55fbd0c44000 INFO GST_ERROR_SYSTEM gstelement.c:2140:gst_element_message_full_with_details:<opusdec0> posting message: GStreamer error: negotiation problem.
0:00:00.510910301 42302 0x55fbd0c44000 LOG GST_MESSAGE gstmessage.c:303:gst_message_new_custom: source opusdec0: creating new message 0x7f519c002910 error
0:00:00.510919198 42302 0x55fbd0c44000 WARN structure gststructure.c:1861:priv_gst_structure_append_to_gstring: No value transform to serialize field 'gerror' of type 'GError'
0:00:00.510929043 42302 0x55fbd0c44000 DEBUG GST_BUS gstbus.c:315:gst_bus_post:<bus1> [msg 0x7f519c002910] posting on bus error message: 0x7f519c002910, time 99:99:99.999999999, seq-num 43, element 'opusdec0', GstMessageError, gerror=(GError)NULL, debug=(string)"gstaudiodecoder.c\(2084\):\ gst_audio_decoder_chain\ \(\):\ /GstPipeline:pipeline0/GstOpusDec:opusdec0:\012decoder\ not\ initialized";
0:00:00.510937098 42302 0x55fbd0c44000 DEBUG bin gstbin.c:3718:gst_bin_handle_message_func:<pipeline0> [msg 0x7f519c002910] handling child opusdec0 message of type error
0:00:00.510942210 42302 0x55fbd0c44000 DEBUG bin gstbin.c:3727:gst_bin_handle_message_func:<pipeline0> got ERROR message, unlocking state change
0:00:00.510947151 42302 0x55fbd0c44000 DEBUG bin gstbin.c:4065:gst_bin_handle_message_func:<pipeline0> posting message upward
0:00:00.510955219 42302 0x55fbd0c44000 WARN structure gststructure.c:1861:priv_gst_structure_append_to_gstring: No value transform to serialize field 'gerror' of type 'GError'
0:00:00.510962328 42302 0x55fbd0c44000 DEBUG GST_BUS gstbus.c:315:gst_bus_post:<bus2> [msg 0x7f519c002910] posting on bus error message: 0x7f519c002910, time 99:99:99.999999999, seq-num 43, element 'opusdec0', GstMessageError, gerror=(GError)NULL, debug=(string)"gstaudiodecoder.c\(2084\):\ gst_audio_decoder_chain\ \(\):\ /GstPipeline:pipeline0/GstOpusDec:opusdec0:\012decoder\ not\ initialized";
<opusdec0> error: decoder not initialized? Do I need to do something special to initialize the opus decoder? What step do I miss?

I was able to solve the issue. When the plugin element enters playing state we should push a gst_event_new_caps event to the source pad. Even with fixed caps... I haven't found anything in the documentation that can explain this requirement.
So I added the following state change handler and the pipeline started to work:
static GstStateChangeReturn gst_simpacketdepay_change_state (GstElement *element, GstStateChange transition)
{
const GstStateChangeReturn result = GST_ELEMENT_CLASS(parent_class)->change_state (element, transition);
if (result == GST_STATE_CHANGE_FAILURE) {
return result;
}
switch (transition) {
case GST_STATE_CHANGE_PAUSED_TO_PLAYING: {
GstSimpacketdepay *filter = GST_SIMPACKETDEPAY(element);
gst_pad_push_event(filter->srcpad, gst_event_new_caps(gst_pad_template_get_caps(gst_static_pad_template_get(&src_factory))));
} break;
default:
break;
}
return result;
}
I'm sad to see how underdocumented this part of GStreamer is.

Related

Cannot move camera using libuvc

I'm trying to control my camera using libuvc.
I tried this code I modified from the example:
#include <libuvc/libuvc.h>
#include <stdio.h>
#include <unistd.h>
int main() {
uvc_context_t *ctx;
uvc_device_t *dev;
uvc_device_handle_t *devh;
uvc_stream_ctrl_t ctrl;
uvc_error_t res;
/* Initialize a UVC service context. Libuvc will set up its own libusb
* context. Replace NULL with a libusb_context pointer to run libuvc
* from an existing libusb context. */
res = uvc_init(&ctx, NULL);
if (res < 0) {
uvc_perror(res, "uvc_init");
return res;
}
puts("UVC initialized");
/* Locates the first attached UVC device, stores in dev */
res = uvc_find_device(
ctx, &dev,
0, 0, NULL); /* filter devices: vendor_id, product_id, "serial_num" */
if (res < 0) {
uvc_perror(res, "uvc_find_device"); /* no devices found */
} else {
puts("Device found");
/* Try to open the device: requires exclusive access */
res = uvc_open(dev, &devh);
if (res < 0) {
uvc_perror(res, "uvc_open"); /* unable to open device */
} else {
puts("Device opened");
uvc_print_diag(devh, stderr);
//uvc_set_pantilt_abs(devh, 100, 100);
int result = uvc_set_pantilt_abs(devh, 5, 50);
printf("%d\n", result);
//sleep(5);
/* Release our handle on the device */
uvc_close(devh);
puts("Device closed");
}
/* Release the device descriptor */
uvc_unref_device(dev);
}
/* Close the UVC context. This closes and cleans up any existing device handles,
* and it closes the libusb context if one was not provided. */
uvc_exit(ctx);
puts("UVC exited");
return 0;
}
I tried both uvc_set_pantilt_abs and uvc_set_pantilt_rel and both are returning 0 so it means the action is successful. Except the camera does not move.
I'm sure the camera uses UVC because uvc_print_diag indicates
VideoControl:
bcdUVC: 0x0110
Am I doing something wrong? If not how can I troubleshoot it?
I found the answer a while ago but forgot to put it here.
I stumbled upon this project which controls a camera using a commandline tool with libuvc.
After playing a bit with it and compared it with my code I got what I did wrong. He was getting the pantilt data from the camera and then using it to send requests. It seems cameras need to receive a number which must be a multiple of the "step" provided by the camera as the movement unit.
Here's the part where he requests the pantilt information:
int32_t pan;
int32_t panStep;
int32_t panMin;
int32_t panMax;
int32_t tilt;
int32_t tiltStep;
int32_t tiltMin;
int32_t tiltMax;
// get current value
errorCode = uvc_get_pantilt_abs(devh, &pan, &tilt, UVC_GET_CUR);
handleError(errorCode, "Failed to read pan/tilt settings - possibly unsupported by this camera?\n");
// get steps
errorCode = uvc_get_pantilt_abs(devh, &panStep, &tiltStep, UVC_GET_RES);
handleError(errorCode, "Failed to read pan/tilt settings - possibly unsupported by this camera?\n");
// get min
errorCode = uvc_get_pantilt_abs(devh, &panMin, &tiltMin, UVC_GET_MIN);
handleError(errorCode, "Failed to read pan/tilt settings - possibly unsupported by this camera?\n");
// get max
errorCode = uvc_get_pantilt_abs(devh, &panMax, &tiltMax, UVC_GET_MAX);
handleError(errorCode, "Failed to read pan/tilt settings - possibly unsupported by this camera?\n");
Here's the full code

How to get h264 frames via gstreamer

I'm familiar with ffmpeg, but not with GStreamer. I know how to get a H264 frame through ffmpeg, for example, I can get a H264 frame through AVPacket. But I don't know how to use GStreamer to get a frame of h264. I don't intend to save the H264 data directly as a local file because I need to do other processing. Can anyone give me some sample code? I'll be very grateful. Here's what I learned from other people's code.
#include <stdio.h>
#include <string.h>
#include <fstream>
#include <unistd.h>
#include <gst/gst.h>
#include <gst/app/gstappsrc.h>
typedef struct {
GstPipeline *pipeline;
GstAppSrc *src;
GstElement *filter1;
GstElement *encoder;
GstElement *filter2;
GstElement *parser;
GstElement *qtmux;
GstElement *sink;
GstClockTime timestamp;
guint sourceid;
} gst_app_t;
static gst_app_t gst_app;
int main()
{
gst_app_t *app = &gst_app;
GstStateChangeReturn state_ret;
gst_init(NULL, NULL); //Initialize Gstreamer
app->timestamp = 0; //Set timestamp to 0
//Create pipeline, and pipeline elements
app->pipeline = (GstPipeline*)gst_pipeline_new("mypipeline");
app->src = (GstAppSrc*)gst_element_factory_make("appsrc", "mysrc");
app->filter1 = gst_element_factory_make ("capsfilter", "myfilter1");
app->encoder = gst_element_factory_make ("omxh264enc", "myomx");
app->filter2 = gst_element_factory_make ("capsfilter", "myfilter2");
app->parser = gst_element_factory_make("h264parse" , "myparser");
app->qtmux = gst_element_factory_make("qtmux" , "mymux");
app->sink = gst_element_factory_make ("filesink" , NULL);
if( !app->pipeline ||
!app->src || !app->filter1 ||
!app->encoder || !app->filter2 ||
!app->parser || !app->qtmux ||
!app->sink ) {
printf("Error creating pipeline elements!\n");
exit(2);
}
//Attach elements to pipeline
gst_bin_add_many(
GST_BIN(app->pipeline),
(GstElement*)app->src,
app->filter1,
app->encoder,
app->filter2,
app->parser,
app->qtmux,
app->sink,
NULL);
//Set pipeline element attributes
g_object_set (app->src, "format", GST_FORMAT_TIME, NULL);
GstCaps *filtercaps1 = gst_caps_new_simple ("video/x-raw",
"format", G_TYPE_STRING, "I420",
"width", G_TYPE_INT, 1280,
"height", G_TYPE_INT, 720,
"framerate", GST_TYPE_FRACTION, 1, 1,
NULL);
g_object_set (G_OBJECT (app->filter1), "caps", filtercaps1, NULL);
GstCaps *filtercaps2 = gst_caps_new_simple ("video/x-h264",
"stream-format", G_TYPE_STRING, "byte-stream",
NULL);
g_object_set (G_OBJECT (app->filter2), "caps", filtercaps2, NULL);
g_object_set (G_OBJECT (app->sink), "location", "output.h264", NULL);
//Link elements together
g_assert( gst_element_link_many(
(GstElement*)app->src,
app->filter1,
app->encoder,
app->filter2,
app->parser,
app->qtmux,
app->sink,
NULL ) );
//Play the pipeline
state_ret = gst_element_set_state((GstElement*)app->pipeline, GST_STATE_PLAYING);
g_assert(state_ret == GST_STATE_CHANGE_ASYNC);
//Get a pointer to the test input
FILE *testfile = fopen("test.yuv", "rb");
g_assert(testfile != NULL);
//Push the data from buffer to gstpipeline 100 times
for(int i = 0; i < 100; i++) {
char* filebuffer = (char*)malloc (1382400); //Allocate memory for framebuffer
if (filebuffer == NULL) {printf("Memory error\n"); exit (2);} //Errorcheck
size_t bytesread = fread(filebuffer, 1 , (1382400), testfile); //Read to filebuffer
//printf("File Read: %zu bytes\n", bytesread);
GstBuffer *pushbuffer; //Actual databuffer
GstFlowReturn ret; //Return value
pushbuffer = gst_buffer_new_wrapped (filebuffer, 1382400); //Wrap the data
//Set frame timestamp
GST_BUFFER_PTS (pushbuffer) = app->timestamp;
GST_BUFFER_DTS (pushbuffer) = app->timestamp;
GST_BUFFER_DURATION (pushbuffer) = gst_util_uint64_scale_int (1, GST_SECOND, 1);
app->timestamp += GST_BUFFER_DURATION (pushbuffer);
//printf("Frame is at %lu\n", app->timestamp);
ret = gst_app_src_push_buffer( app->src, pushbuffer); //Push data into pipeline
g_assert(ret == GST_FLOW_OK);
}
usleep(100000);
//Declare end of stream
gst_app_src_end_of_stream (GST_APP_SRC (app->src));
printf("End Program.\n");
return 0;
}
Here is a link to the source of the code
link
Your example serves for the purpose of feeding the data from application to the GStreamer with a hope to encode with x264 and the result goes to file.
What you need (I am guessing here) is to read data from file - lets say movie.mp4 and get the decoded data into your application (?)
I believe you have two options:
1, Use appsink instead of filesink and feed the data from file using filesrc. So if you need also other processing beside grabbing the h264 frames (like playing or sending via network), you would have to use tee to split the pipeline into two output branches like example gst-launch below. One branch of output pipeline would go to for example windowed output - autovideosink and the other part would go to your application.
To demonstrate this split and still show you what is really happening I will use debugging element identity which is able to dump data which goes through it.
This way you will learn to use this handy tool for experiments and verification that you know what you are doing. This is not the solution you need.
gst-launch-1.0 -q filesrc location= movie.mp4 ! qtdemux name=qt ! video/x-h264 ! h264parse ! tee name=t t. ! queue ! avdec_h264 ! videoconvert ! autovideosink t. ! queue ! identity dump=1 ! fakesink sync=true
This pipeline plays the video into window (autovideosink) and the other branch of tee goes to the debugging element called identity which dumps the frame in hexdump manner (with addresses, character representation and everything).
So what you see in the stdout of the gst-launch are actual h264 frames (but you do not see boundaries or anything.. its just really raw dump).
To understand the gst-launch syntax (mainly the aliases with name=) check this part of the documentation.
In real code you would not use identity and fakesink but instead you would link there just appsink and connect the appsink signals to callbacks in your C source code.
There are nice examples for this, I will not attempt to give you complete solution. This example demonstrate how to get samples out of appsink.
The important bits are:
/* The appsink has received a buffer */
static GstFlowReturn new_sample (GstElement *sink, CustomData *data) {
GstSample *sample;
/* Retrieve the buffer */
g_signal_emit_by_name (sink, "pull-sample", &sample);
if (sample) {
/* The only thing we do in this example is print a * to indicate a received buffer */
g_print ("*");
gst_sample_unref (sample);
return GST_FLOW_OK;
}
return GST_FLOW_ERROR;
}
// somewhere in main()
// construction and linkage of elements
g_signal_connect (data.app_sink, "new-sample", G_CALLBACK (new_sample), &data);
2, Second solution is to use pad probe registered for buffers only. Pad probe is a way to register a callback on any pad of any element in pipeline and tell GStreamer in what information are you interested in on that probe. You can ask it to call the callback upon every event, or any downstream event, or on any buffer going through that probe. In the callback which pad probe calls you will extract the buffer and the actuall data in that buffer.
Again there are many examples on how to use pad probes.
One very nice example containing logic of almost exactly what you need can be found here
The important bits:
static GstPadProbeReturn
cb_have_data (GstPad *pad,
GstPadProbeInfo *info,
gpointer user_data)
{
// ... the code for writing the buffer data somewhere ..
}
// ... later in main()
pad = gst_element_get_static_pad (src, "src");
gst_pad_add_probe (pad, GST_PAD_PROBE_TYPE_BUFFER,
(GstPadProbeCallback) cb_have_data, NULL, NULL);

Getting undefined references for C functions while making ESP8266 project

I followed this tutorial for installing a Kaa application into an ESP8266, and it worked after a few modifications: https://kaaproject.github.io/kaa/docs/v0.10.0/Programming-guide/Using-Kaa-endpoint-SDKs/C/SDK-ESP8266/
One of the modifications I had to make was to move a line of code in eagle.app.v6.ld because of byte overflow (arrow points to change I made):
...
.irom0.text : ALIGN(4)
{
_irom0_text_start = ABSOLUTE(.);
*(.literal.* .text.*) --> moved from ".text : ALIGN(4){...}"
...
}
...
After I did this I still had some byte overflow, so I modified the original cmake command from the documentation to disable cmake extensions that were taking up space:
cmake \
-DCMAKE_TOOLCHAIN_FILE=../kaa/toolchains/esp8266.cmake \
-DKAA_PLATFORM=esp8266 \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DWITH_EXTENSION_CONFIGURATION=OFF \
-DWITH_EXTENSION_EVENT=OFF \
-DWITH_EXTENSION_LOGGING=OFF \
-DWITH_EXTENSION_NOTIFICATION=OFF \
-DWITH_EXTENSION_USER=OFF \
-DWITH_ENCRYPTION=OFF \
-DKAA_MAX_LOG_LEVEL=3 ..
Finally, when I ran the make command. it worked. I then created and flashed the binaries into my ESP. Then I reset my ESP with GPIO0 high (so it can boot from flash) and the ESP sent "Hello, Kaa!" into the serial port I was connected to.
Now, however. I am trying to make it so that my ESP8266 connects to my Kaa server and generates an Endpoint Profile, so I need the code to create a transport channel to communicate to both the Bootstrap server and Operations server.
To do this, I tried using the code from Your First Kaa Application that generates fake temp readings and receives the configuration schema and pushes data to my Cassandra server via log appender:
https://kaaproject.github.io/kaa/docs/v0.10.0/Programming-guide/Your-first-Kaa-application/
So in my attempt to do this, I left my directory the same:
CMakeLists.txt
driver/
uart.h
uart.c
ld/
eagle.app.v6.ld
eagle.rom.addr.v6.ld
kaa/
<put Kaa SDK here> --> replaced with new SDK with log and configuration schema
user/
user_main.c
src/
kaa_demo.c --> replaced with new code from "Your First Kaa Application"
I then replaced this code into my kaa_demo.c file:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <kaa/kaa.h>
#include <kaa/platform/kaa_client.h>
#include <kaa/kaa_error.h>
#include <extensions/configuration/kaa_configuration_manager.h>
#include <extensions/logging/kaa_logging.h>
#include <kaa/gen/kaa_logging_gen.h>
#include <kaa/platform/kaa_client.h>
#include <kaa/utilities/kaa_log.h>
#include <kaa/platform-impl/common/ext_log_upload_strategies.h>
static int32_t sample_period;
static time_t last_sample_time;
extern kaa_error_t ext_unlimited_log_storage_create(void **log_storage_context_p, kaa_logger_t *logger);
/* Retrieves current temperature. */
static int32_t get_temperature_sample(void)
{
/* For the sake of example, random data is used */
return rand() % 10 + 25;
}
/* Periodically called by Kaa SDK. */
static void example_callback(void *context)
{
time_t current_time = time(NULL);
/* Respect sample period */
if (difftime(current_time, last_sample_time) >= sample_period) {
int32_t temperature = get_temperature_sample();
printf("Sampled temperature: %i\n", temperature);
last_sample_time = current_time;
kaa_user_log_record_t *log_record = kaa_logging_data_collection_create();
log_record->temperature = temperature;
kaa_logging_add_record(kaa_client_get_context(context)->log_collector, log_record, NULL);
}
}
/* Receives new configuration data. */
static kaa_error_t on_configuration_updated(void *context, const kaa_root_configuration_t *conf)
{
(void) context;
printf("Received configuration data. New sample period: %i seconds\n", conf->sample_period);
sample_period = conf->sample_period;
return KAA_ERR_NONE;
}
int main(void)
{
/* Init random generator used to generate temperature */
srand(time(NULL));
/* Prepare Kaa client. */
kaa_client_t *kaa_client = NULL;
kaa_error_t error = kaa_client_create(&kaa_client, NULL);
if (error) {
return EXIT_FAILURE;
}
/* Configure notification manager. */
kaa_configuration_root_receiver_t receiver = {
.context = NULL,
.on_configuration_updated = on_configuration_updated
};
error = kaa_configuration_manager_set_root_receiver(
kaa_client_get_context(kaa_client)->configuration_manager,
&receiver);
if (error) {
return EXIT_FAILURE;
}
/* Obtain default configuration shipped within SDK. */
const kaa_root_configuration_t *dflt = kaa_configuration_manager_get_configuration(
kaa_client_get_context(kaa_client)->configuration_manager);
printf("Default sample period: %i seconds\n", dflt->sample_period);
sample_period = dflt->sample_period;
/* Configure data collection. */
void *log_storage_context = NULL;
void *log_upload_strategy_context = NULL;
/* The internal memory log storage distributed with Kaa SDK. */
error = ext_unlimited_log_storage_create(&log_storage_context,
kaa_client_get_context(kaa_client)->logger);
if (error) {
return EXIT_FAILURE;
}
/* Create a strategy based on timeout. */
error = ext_log_upload_strategy_create(
kaa_client_get_context(kaa_client), &log_upload_strategy_context,
KAA_LOG_UPLOAD_BY_TIMEOUT_STRATEGY);
if (error) {
return EXIT_FAILURE;
}
/* Strategy will upload logs every 5 seconds. */
error = ext_log_upload_strategy_set_upload_timeout(log_upload_strategy_context, 5);
if (error) {
return EXIT_FAILURE;
}
/* Specify log bucket size constraints. */
kaa_log_bucket_constraints_t bucket_sizes = {
.max_bucket_size = 32, /* Bucket size in bytes. */
.max_bucket_log_count = 2, /* Maximum log count in one bucket. */
};
/* Initialize the log storage and strategy (by default, they are not set). */
error = kaa_logging_init(kaa_client_get_context(kaa_client)->log_collector,
log_storage_context, log_upload_strategy_context, &bucket_sizes);
if (error) {
return EXIT_FAILURE;
}
/* Start Kaa SDK's main loop. example_callback is called once per second. */
error = kaa_client_start(kaa_client, example_callback, kaa_client, 1);
/* Should get here only after Kaa stops. */
kaa_client_destroy(kaa_client);
if (error) {
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
I left the CMakeLists.txt file the same:
cmake_minimum_required(VERSION 3.0.2)
project(kaa_demo C)
# Add Kaa SDK directory
add_subdirectory(kaa)
# Add source files
add_library(kaa_demo_s STATIC user/user_main.c driver/uart.c src/kaa_demo.c)
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=c99")
if(NOT DEFINED ESP_RTOS_SDK)
set(ESP_RTOS_SDK /opt/Espressif/esp-rtos-sdk)
endif()
# specify include directories
target_include_directories(kaa_demo_s PUBLIC driver)
target_include_directories(kaa_demo_s PUBLIC .)
target_include_directories(kaa_demo_s PUBLIC
${ESP_RTOS_SDK}/extra_include
${ESP_RTOS_SDK}/include
${ESP_RTOS_SDK}/include/lwip
${ESP_RTOS_SDK}/include/lwip/ipv4
${ESP_RTOS_SDK}/include/lwip/ipv6
${ESP_RTOS_SDK}/include/espressif/
)
exec_program(xtensa-lx106-elf-gcc .
ARGS -print-libgcc-file-name
OUTPUT_VARIABLE ESP8266_LIBGCC
)
link_directories(${CMAKE_CURRENT_SOURCE_DIR}/ld)
target_link_libraries(kaa_demo_s PUBLIC
kaac
${ESP_RTOS_SDK}/lib/libfreertos.a
${ESP_RTOS_SDK}/lib/libhal.a
${ESP_RTOS_SDK}/lib/libpp.a
${ESP_RTOS_SDK}/lib/libphy.a
${ESP_RTOS_SDK}/lib/libnet80211.a
${ESP_RTOS_SDK}/lib/libwpa.a
${ESP_RTOS_SDK}/lib/liblwip.a
${ESP_RTOS_SDK}/lib/libmain.a
${ESP_RTOS_SDK}/lib/libssl.a
${ESP_RTOS_SDK}/lib/libhal.a
${ESP8266_LIBGCC}
-Teagle.app.v6.ld
)
file(WRITE ${CMAKE_BINARY_DIR}/blank.c "")
add_executable(kaa_demo ${CMAKE_BINARY_DIR}/blank.c)
target_link_libraries(kaa_demo kaa_demo_s)
I left the user_main.c file the same:
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include "uart.h"
extern int main(void);
static void main_task(void *pvParameters)
{
(void)pvParameters;
main();
for (;;);
}
void user_init(void)
{
uart_init_new();
UART_SetBaudrate(UART0, 115200);
UART_SetPrintPort(UART0);
portBASE_TYPE error = xTaskCreate(main_task, "main_task", 512, NULL, 2, NULL );
if (error < 0) {
printf("Error creating main_task! Error code: %ld\r\n", error);
}
}
I left eagle.app.v6.ld (except for the modification described in the beginning) and eagle.rom.addr.ld, uart.h, and uart.c the same (obtained from github)
So when I change to the build directory and run this cmake command:
cmake \
-DCMAKE_TOOLCHAIN_FILE=../kaa/toolchains/esp8266.cmake \
-DKAA_PLATFORM=esp8266 \
-DCMAKE_BUILD_TYPE=MinSizeRel \
-DWITH_EXTENSION_CONFIGURATION=OFF \
-DWITH_EXTENSION_EVENT=OFF \
-DWITH_EXTENSION_LOGGING=OFF \
-DWITH_EXTENSION_NOTIFICATION=OFF \
-DWITH_EXTENSION_USER=OFF \
-DWITH_ENCRYPTION=OFF \
-DKAA_MAX_LOG_LEVEL=3 ..
I get this: (esp8266 is my username, kaa-app is the main directory that contains: CMakeLists.txt, build, drivers, etc.)
-- Default SDK location will be used: /opt/Espressif/esp-rtos-sdk
-- Toolchain path: /opt/Espressif/crosstool-NG/builds/xtensa-lx106-elf
-- ESP8266 SDK path: /opt/Espressif/esp-rtos-sdk
==================================
BUILD_TYPE = MinSizeRel
KAA_PLATFORM = esp8266
KAA_MAX_LOG_LEVEL = 3
==================================
BOOTSTRAP ENABLED
PROFILE ENABLED
KAA WILL BE INSTALLED TO /usr/local
-- Could NOT find Doxygen (missing: DOXYGEN_EXECUTABLE)
-- Configuring done
-- Generating done
-- Build files have been written to: /home/esp8266/Documents/kaa-app/build
So then I run make, and I get this error:
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.example_callback+0x8): undefined reference to `time'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.example_callback+0xc): undefined reference to `difftime'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.example_callback+0x20): undefined reference to `kaa_logging_add_record'
libkaa_demo_s.a(kaa_demo2.c.obj): In function `example_callback':
kaa_demo2.c:(.text.example_callback+0x35): undefined reference to `time'
kaa_demo2.c:(.text.example_callback+0x42): undefined reference to `difftime'
kaa_demo2.c:(.text.example_callback+0x9e): undefined reference to `kaa_logging_add_record'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x10): undefined reference to `srand'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x14): undefined reference to `kaa_configuration_manager_set_root_receiver'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x18): undefined reference to `kaa_configuration_manager_get_configuration'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x1c): undefined reference to `ext_unlimited_log_storage_create'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x20): undefined reference to `ext_log_upload_strategy_create'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x24): undefined reference to `ext_log_upload_strategy_set_upload_timeout'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x28): undefined reference to `kaa_logging_init'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x3a): undefined reference to `time'
libkaa_demo_s.a(kaa_demo2.c.obj):(.text.startup.main+0x40): undefined reference to `srand'
libkaa_demo_s.a(kaa_demo2.c.obj): In function `main':
kaa_demo2.c:(.text.startup.main+0x69): undefined reference to `kaa_configuration_manager_set_root_receiver'
kaa_demo2.c:(.text.startup.main+0x7b): undefined reference to `kaa_configuration_manager_get_configuration'
kaa_demo2.c:(.text.startup.main+0xa5): undefined reference to `ext_unlimited_log_storage_create'
kaa_demo2.c:(.text.startup.main+0xb8): undefined reference to `ext_log_upload_strategy_create'
kaa_demo2.c:(.text.startup.main+0xc5): undefined reference to `ext_log_upload_strategy_set_upload_timeout'
kaa_demo2.c:(.text.startup.main+0xe6): undefined reference to `kaa_logging_init'
collect2: error: ld returned 1 exit status
CMakeFiles/kaa_demo.dir/build.make:120: recipe for target 'kaa_demo' failed
make[2]: *** [kaa_demo] Error 1
CMakeFiles/Makefile2:107: recipe for target 'CMakeFiles/kaa_demo.dir/all' failed
make[1]: *** [CMakeFiles/kaa_demo.dir/all] Error 2
Makefile:127: recipe for target 'all' failed
make: *** [all] Error 2
So it seems that when it tries linking the files it cannot find the new headers I included:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <time.h>
#include <kaa/kaa.h>
#include <kaa/platform/kaa_client.h>
#include <kaa/kaa_error.h>
#include <extensions/configuration/kaa_configuration_manager.h>
#include <extensions/logging/kaa_logging.h>
#include <kaa/gen/kaa_logging_gen.h>
#include <kaa/platform/kaa_client.h>
#include <kaa/utilities/kaa_log.h>
#include <kaa/platform-impl/common/ext_log_upload_strategies.h>
However, the target directory that is included in CMakeLists.txt contains the header files that I need (for the C headers):
${ESP_RTOS_SDK}/extra_include
So I really do not know what I need to add or modify, I am completely stuck. Any help would be much appreciated! Thanks!
For data_collection you have to set -DWITH_EXTENSION_LOGGING=ON .
For esp8266 you have a different "time.h" library /kaa/src/kaa/platform-impl/esp8266/platform that doesn't support the function time(NULL), but support
kaa_time_t kaa_esp8266_get_time(void);

Failed to create element 'rtpbin'

I am trying to run the example code (test-video.c) provided in Gstreamer's gst-rtsp-server-1.5.1. Following is the code for your reference:
/* GStreamer
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include <gst/gst.h>
#include <gst/rtsp-server/rtsp-server.h>
/* define this if you want the resource to only be available when using
* user/password as the password */
#undef WITH_AUTH
/* define this if you want the server to use TLS (it will also need WITH_AUTH
* to be defined) */
#undef WITH_TLS
/* this timeout is periodically run to clean up the expired sessions from the
* pool. This needs to be run explicitly currently but might be done
* automatically as part of the mainloop. */
static gboolean
timeout (GstRTSPServer * server)
{
GstRTSPSessionPool *pool;
pool = gst_rtsp_server_get_session_pool (server);
gst_rtsp_session_pool_cleanup (pool);
g_object_unref (pool);
return TRUE;
}
int
main (int argc, char *argv[])
{
GMainLoop *loop;
GstRTSPServer *server;
GstRTSPMountPoints *mounts;
GstRTSPMediaFactory *factory;
#ifdef WITH_AUTH
GstRTSPAuth *auth;
GstRTSPToken *token;
gchar *basic;
GstRTSPPermissions *permissions;
#endif
#ifdef WITH_TLS
GTlsCertificate *cert;
GError *error = NULL;
#endif
gst_init (&argc, &argv);
loop = g_main_loop_new (NULL, FALSE);
/* create a server instance */
server = gst_rtsp_server_new ();
#ifdef WITH_AUTH
/* make a new authentication manager. it can be added to control access to all
* the factories on the server or on individual factories. */
auth = gst_rtsp_auth_new ();
#ifdef WITH_TLS
cert = g_tls_certificate_new_from_pem ("-----BEGIN CERTIFICATE-----"
"MIICJjCCAY+gAwIBAgIBBzANBgkqhkiG9w0BAQUFADCBhjETMBEGCgmSJomT8ixk"
"ARkWA0NPTTEXMBUGCgmSJomT8ixkARkWB0VYQU1QTEUxHjAcBgNVBAsTFUNlcnRp"
"ZmljYXRlIEF1dGhvcml0eTEXMBUGA1UEAxMOY2EuZXhhbXBsZS5jb20xHTAbBgkq"
"hkiG9w0BCQEWDmNhQGV4YW1wbGUuY29tMB4XDTExMDExNzE5NDcxN1oXDTIxMDEx"
"NDE5NDcxN1owSzETMBEGCgmSJomT8ixkARkWA0NPTTEXMBUGCgmSJomT8ixkARkW"
"B0VYQU1QTEUxGzAZBgNVBAMTEnNlcnZlci5leGFtcGxlLmNvbTBcMA0GCSqGSIb3"
"DQEBAQUAA0sAMEgCQQDYScTxk55XBmbDM9zzwO+grVySE4rudWuzH2PpObIonqbf"
"hRoAalKVluG9jvbHI81eXxCdSObv1KBP1sbN5RzpAgMBAAGjIjAgMAkGA1UdEwQC"
"MAAwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDQYJKoZIhvcNAQEFBQADgYEAYx6fMqT1"
"Gvo0jq88E8mc+bmp4LfXD4wJ7KxYeadQxt75HFRpj4FhFO3DOpVRFgzHlOEo3Fwk"
"PZOKjvkT0cbcoEq5whLH25dHoQxGoVQgFyAP5s+7Vp5AlHh8Y/vAoXeEVyy/RCIH"
"QkhUlAflfDMcrrYjsmwoOPSjhx6Mm/AopX4="
"-----END CERTIFICATE-----"
"-----BEGIN PRIVATE KEY-----"
"MIIBVAIBADANBgkqhkiG9w0BAQEFAASCAT4wggE6AgEAAkEA2EnE8ZOeVwZmwzPc"
"88DvoK1ckhOK7nVrsx9j6TmyKJ6m34UaAGpSlZbhvY72xyPNXl8QnUjm79SgT9bG"
"zeUc6QIDAQABAkBRFJZ32VbqWMP9OVwDJLiwC01AlYLnka0mIQZbT/2xq9dUc9GW"
"U3kiVw4lL8v/+sPjtTPCYYdzHHOyDen6znVhAiEA9qJT7BtQvRxCvGrAhr9MS022"
"tTdPbW829BoUtIeH64cCIQDggG5i48v7HPacPBIH1RaSVhXl8qHCpQD3qrIw3FMw"
"DwIga8PqH5Sf5sHedy2+CiK0V4MRfoU4c3zQ6kArI+bEgSkCIQCLA1vXBiE31B5s"
"bdHoYa1BXebfZVd+1Hd95IfEM5mbRwIgSkDuQwV55BBlvWph3U8wVIMIb4GStaH8"
"W535W8UBbEg=" "-----END PRIVATE KEY-----", -1, &error);
if (cert == NULL) {
g_printerr ("failed to parse PEM: %s\n", error->message);
return -1;
}
gst_rtsp_auth_set_tls_certificate (auth, cert);
g_object_unref (cert);
#endif
/* make user token */
token =
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
"user", NULL);
basic = gst_rtsp_auth_make_basic ("user", "password");
gst_rtsp_auth_add_basic (auth, basic, token);
g_free (basic);
gst_rtsp_token_unref (token);
/* configure in the server */
gst_rtsp_server_set_auth (server, auth);
#endif
/* get the mount points for this server, every server has a default object
* that be used to map uri mount points to media factories */
mounts = gst_rtsp_server_get_mount_points (server);
/* make a media factory for a test stream. The default media factory can use
* gst-launch syntax to create pipelines.
* any launch line works as long as it contains elements named pay%d. Each
* element with pay%d names will be a stream */
factory = gst_rtsp_media_factory_new ();
gst_rtsp_media_factory_set_launch (factory, "( "
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
"x264enc ! rtph264pay name=pay0 pt=96 "
"audiotestsrc ! audio/x-raw,rate=8000 ! "
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
#ifdef WITH_AUTH
/* add permissions for the user media role */
permissions = gst_rtsp_permissions_new ();
gst_rtsp_permissions_add_role (permissions, "user",
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
gst_rtsp_media_factory_set_permissions (factory, permissions);
gst_rtsp_permissions_unref (permissions);
#ifdef WITH_TLS
gst_rtsp_media_factory_set_profiles (factory, GST_RTSP_PROFILE_SAVP);
#endif
#endif
/* attach the test factory to the /test url */
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
/* don't need the ref to the mapper anymore */
g_object_unref (mounts);
/* attach the server to the default maincontext */
if (gst_rtsp_server_attach (server, NULL) == 0)
goto failed;
/* add a timeout for the session cleanup */
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
/* start serving, this never stops */
#ifdef WITH_TLS
g_print ("stream ready at rtsps://127.0.0.1:8554/test\n");
#else
g_print ("stream ready at rtsp://127.0.0.1:8554/test\n");
#endif
g_main_loop_run (loop);
return 0;
/* ERRORS */
failed:
{
g_print ("failed to attach the server\n");
return -1;
}
}
The program compiles successfully and shows following output:
stream ready at rtsp://127.0.0.1:8554/test
When I open VLC and provide above link to stream, the program shows following error:
** (GStreamer_FirstApp.exe:1836): WARNING **: failed to create element 'rtpbin',
check your installation
I have searched web for solution to this issue but no success yet. Any help in solving this issue will be appreciated.
Looks like you haven't installed rtpmanager plugin (it's also a part of gst-plugins-good), which contains rtpbin element.
Seems there is similar issue here: http://gstreamer-devel.966125.n4.nabble.com/rtpbin-issue-using-gst-rtsp-server-1-0-on-Android-td4669600.html

ALSA equivalent to /dev/audio dump?

This will be my poorest question ever...
On an old netbook, I installed an even older version of Debian, and toyed around a bit. One of the rather pleasing results was a very basic MP3 player (using libmpg123), integrated for adding background music to a little application doing something completely different. I grew rather fond of this little solution.
In that program, I dumped the decoded audio (from mpg123_decode()) to /dev/audio via a simple fwrite().
This worked fine - on the netbook.
Now, I came to understand that /dev/audio was something done by OSS, and is no longer supported on newer (ALSA) machines. Sure enough, my laptop (running a current Linux Mint) does not have this device.
So apparently I have to use ALSA instead. Searching the web, I've found a couple of tutorials, and they pretty much blow my mind. Modes, parameters, capabilities, access type, sample format, sample rate, number of channels, number of periods, period size... I understand that ALSA is a powerful API for the ambitious, but that's not what I am looking for (or have the time to grok). All I am looking for is how to play the output of mpg123_decode (the format of which I don't even know, not being an audio geek by a long shot).
Can anybody give me some hints on what needs to be done?
tl;dr
How do I get ALSA to play raw audio data?
There's an OSS compatibility layer for ALSA in the alsa-oss package. Install it and run your program inside the "aoss" program. Or, modprobe the modules listed here:
http://wiki.debian.org/SoundFAQ/#line-105
Then, you'll need to change your program to use "/dev/dsp" or "/dev/dsp0" instead of "/dev/audio". It should work how you remembered... but you might want to cross your fingers just in case.
You could install sox and open a pipe to the play command with the correct samplerate and sample size arguments.
Using ALSA directly is overly complicated, so I hope a Gstreamer solution is fine to you too. Gstreamer gives a nice abstraction to ALSA/OSS/Pulseaudio/you name it -- and is ubiquitous in the Linux world.
I wrote a little library that will open a FILE object where you can fwrite PCM data into:
Gstreamer file. The actual code is less than 100 lines.
Use use it like that:
FILE *output = fopen_gst(rate, channels, bit_depth); // open audio output file
while (have_more_data) fwrite(data, amount, 1, output); // output audio data
fclose(output); // close the output file
I added an mpg123 example, too.
Here is the whole file (in case Github get's out of business ;-) ):
/**
* gstreamer_file.c
* Copyright 2012 René Kijewski <rene.SURNAME#fu-berlin.de>
* License: LGPL 3.0 (http://www.gnu.org/licenses/lgpl-3.0)
*/
#include "gstreamer_file.h"
#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <glib.h>
#include <gst/gst.h>
#ifndef _GNU_SOURCE
# error "You need to add -D_GNU_SOURCE to the GCC parameters!"
#endif
/**
* Cookie passed to the callbacks.
*/
typedef struct {
/** { file descriptor to read from, fd to write to } */
int pipefd[2];
/** Gstreamer pipeline */
GstElement *pipeline;
} cookie_t;
static ssize_t write_gst(void *cookie_, const char *buf, size_t size) {
cookie_t *cookie = cookie_;
return write(cookie->pipefd[1], buf, size);
}
static int close_gst(void *cookie_) {
cookie_t *cookie = cookie_;
gst_element_set_state(cookie->pipeline, GST_STATE_NULL); /* we are finished */
gst_object_unref(GST_OBJECT(cookie->pipeline)); /* we won't access the pipeline anymore */
close(cookie->pipefd[0]); /* we won't write anymore */
close(cookie->pipefd[1]); /* we won't read anymore */
free(cookie); /* dispose the cookie */
return 0;
}
FILE *fopen_gst(long rate, int channels, int depth) {
/* initialize Gstreamer */
if (!gst_is_initialized()) {
GError *error;
if (!gst_init_check(NULL, NULL, &error)) {
g_error_free(error);
return NULL;
}
}
/* get a cookie */
cookie_t *cookie = malloc(sizeof(*cookie));
if (!cookie) {
return NULL;
}
/* open a pipe to be used between the caller and the Gstreamer pipeline */
if (pipe(cookie->pipefd) != 0) {
close(cookie->pipefd[0]);
close(cookie->pipefd[1]);
free(cookie);
return NULL;
}
/* set up the pipeline */
char description[256];
snprintf(description, sizeof(description),
"fdsrc fd=%d ! " /* read from a file descriptor */
"audio/x-raw-int, rate=%ld, channels=%d, " /* get PCM data */
"endianness=1234, width=%d, depth=%d, signed=true ! "
"audioconvert ! audioresample ! " /* convert/resample if needed */
"autoaudiosink", /* output to speakers (using ALSA, OSS, Pulseaudio ...) */
cookie->pipefd[0], rate, channels, depth, depth);
cookie->pipeline = gst_parse_launch_full(description, NULL,
GST_PARSE_FLAG_FATAL_ERRORS, NULL);
if (!cookie->pipeline) {
close(cookie->pipefd[0]);
close(cookie->pipefd[1]);
free(cookie);
return NULL;
}
/* open a FILE with specialized write and close functions */
cookie_io_functions_t io_funcs = { NULL, write_gst, NULL, close_gst };
FILE *result = fopencookie(cookie, "w", io_funcs);
if (!result) {
close_gst(cookie);
return NULL;
}
/* start the pipeline (of cause it will wait for some data first) */
gst_element_set_state(cookie->pipeline, GST_STATE_PLAYING);
return result;
}
And ten years later, the "actual" answer is found: That's the wrong way to do it in the first place.
libmpg123 comes with a companion library, libout123, which abstracts the underlying audio system for you. Based on libmpg123 example code:
#include <stdlib.h>
#include "mpg123.h"
#include "out123.h"
int main()
{
mpg123_handle * _mpg_handle;
out123_handle * _out_handle;
double rate, channels, encoding;
size_t position, buffer_size;
unsigned char * buffer;
char filename[] = "Example.mp3";
mpg123_open( _mpg_handle, filename );
mpg123_getformat( _mpg_handle, &rate, &channels, &encoding );
out123_open( _out_handle, NULL, NULL );
mpg123_format_none( _mpg_handle );
mpg123_format( _mpg_handle, rate, channels, encoding );
out123_start( _out_handle, rate, channels, encoding );
buffer_size = mpg123_outblock( _mpg_handle );
buffer = malloc( buffer_size );
do
{
mpg123_read( _mpg_handle, buffer.get(), buffer_size, &position );
out123_play( _out_handle, buffer.get(), position );
} while ( position );
out123_close( _out_handle );
mpg123_close( _mpg_handle );
free( buffer );
}

Resources