<div dir="ltr">Hi,<div><br></div><div> I have a question about how to use the threaded main loop data callback feature. I've been looking at the documentation and trying it out.</div><div> To try the data callback, I've taken the AsyncDeviceList example , changed the mainloop to a threaded mainloop and tried to make the callback here use the pa_threaded_mainloop_signal(ml,1) and pa_threaded_mainloop_accept(). I'm not sure if I'm putting the signal(ml, 1) in the wrong place but the programs hangs for me. What I've noticed is after the call to signal(1) from the callback, when the wait() wakes up, the return value of get_state() is still OPERATION_RUNNING.</div>
<div style> From my understanding this makes sense because the accept() needs to be called in order for the signal to progress. However, the documentation's example of data callbacks is coded this way so I'm a bit lost as to how properly go about this.</div>
<div style> Some help and clarification would be helpful. Also, I will add this example to the wiki page when resolved.</div><div><div><br></div><div style> Thanks for your time,</div><div style>Mitchell</div><div style>
<br></div><div style>p.s. I have copied the code below and also attached it.</div><div style><pre style="padding:0.5em;white-space:pre-wrap;border:1px solid rgb(203,203,203);background-color:rgb(255,248,237);color:rgb(0,0,0)">
gcc -Wall -o pulsedevicelist pulsedevicelist.c -lpulse</pre></div><div style>Resources:</div><div style><a href="http://freedesktop.org/software/pulseaudio/doxygen/threaded_mainloop.html">http://freedesktop.org/software/pulseaudio/doxygen/threaded_mainloop.html</a><br>
</div><div style><a href="http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/Samples/AsyncDeviceList">http://www.freedesktop.org/wiki/Software/PulseAudio/Documentation/Developer/Clients/Samples/AsyncDeviceList</a><br>
</div><div><div><br></div><div>#include <stdio.h></div><div>#include <string.h></div><div>#include <pulse/pulseaudio.h></div><div>#include <pulse/stream.h></div><div><br></div><div>// Field list is here: <a href="http://0pointer.de/lennart/projects/pulseaudio/doxygen/structpa__sink__info.html">http://0pointer.de/lennart/projects/pulseaudio/doxygen/structpa__sink__info.html</a></div>
<div>typedef struct pa_devicelist {</div><div> uint8_t initialized;</div><div> char name[512];</div><div> uint32_t index;</div><div> char description[256];</div><div>} pa_devicelist_t;</div><div>
<br></div><div>void pa_state_cb(pa_context *c, void *userdata);</div><div>void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata);</div><div>void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata);</div>
<div>int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output);</div><div><br></div><div>pa_threaded_mainloop *pa_ml;</div><div><br></div><div>int main(int argc, char *argv[]) {</div><div> int ctr;</div><div>
<br></div><div> // This is where we'll store the input device list</div><div> pa_devicelist_t pa_input_devicelist[16];</div><div><br></div><div> // This is where we'll store the output device list</div><div>
pa_devicelist_t pa_output_devicelist[16];</div><div><br></div><div> if (pa_get_devicelist(pa_input_devicelist, pa_output_devicelist) < 0) {</div><div> fprintf(stderr, "failed to get device list\n");</div>
<div> return 1;</div><div> }</div><div><br></div><div> for (ctr = 0; ctr < 16; ctr++) {</div><div> if (! pa_output_devicelist[ctr].initialized) {</div><div> break;</div><div> }</div>
<div> printf("=======[ Output Device #%d ]=======\n", ctr+1);</div><div> printf("Description: %s\n", pa_output_devicelist[ctr].description);</div><div> printf("Name: %s\n", pa_output_devicelist[ctr].name);</div>
<div> printf("Index: %d\n", pa_output_devicelist[ctr].index);</div><div> printf("\n");</div><div> }</div><div><br></div><div> for (ctr = 0; ctr < 16; ctr++) {</div><div> if (! pa_input_devicelist[ctr].initialized) {</div>
<div> break;</div><div> }</div><div> printf("=======[ Input Device #%d ]=======\n", ctr+1);</div><div> printf("Description: %s\n", pa_input_devicelist[ctr].description);</div>
<div> printf("Name: %s\n", pa_input_devicelist[ctr].name);</div><div> printf("Index: %d\n", pa_input_devicelist[ctr].index);</div><div> printf("\n");</div><div> }</div>
<div><br></div><div> return 0;</div><div>}</div><div><br></div><div>int pa_get_devicelist(pa_devicelist_t *input, pa_devicelist_t *output) {</div><div> // Define our pulse audio loop and connection variables</div><div>
<span class="" style="white-space:pre"> </span>//pa_threaded_mainloop *pa_ml;</div><div> pa_mainloop_api *pa_mlapi;</div><div> pa_operation *pa_op;</div><div> pa_context *pa_ctx;</div><div><br></div><div> pa_operation_state_t temp;</div>
<div><br></div><div> // We'll need these state variables to keep track of our requests</div><div> int state = 0;</div><div> int pa_ready = 0;</div><div><br></div><div> // Initialize our device lists</div><div>
memset(input, 0, sizeof(pa_devicelist_t) * 16);</div><div> memset(output, 0, sizeof(pa_devicelist_t) * 16);</div><div><br></div><div> // Create a mainloop API and connection to the default server</div><div> pa_ml = pa_threaded_mainloop_new();</div>
<div> pa_mlapi = pa_threaded_mainloop_get_api(pa_ml);</div><div> pa_ctx = pa_context_new(pa_mlapi, "test");</div><div><br></div><div> //pa_stream *s = pa_stream_new(pa_ctx, "stream", &ss, &map);</div>
<div><br></div><div> // This function connects to the pulse server</div><div> pa_context_connect(pa_ctx, NULL, PA_CONTEXT_NOFLAGS, NULL);</div><div><br></div><div> // This function defines a callback so the server will tell us it's state.</div>
<div> // Our callback will wait for the state to be ready. The callback will</div><div> // modify the variable to 1 so we know when we have a connection and it's</div><div> // ready.</div><div> // If there's an error, the callback will set pa_ready to 2</div>
<div> pa_context_set_state_callback(pa_ctx, pa_state_cb, &pa_ready);</div><div> pa_threaded_mainloop_start(pa_ml);</div><div> // Now we'll enter into an infinite loop until we get the data we receive</div>
<div> // or if there's an error</div><div> for (;;) {</div><div> // We can't do anything until PA is ready, so just iterate the mainloop</div><div> // and continue</div><div> if (pa_ready == 0) {</div>
<div> //pa_mainloop_iterate(pa_ml, 1, NULL);</div><div> continue;</div><div> }</div><div> // We couldn't get a connection to the server, so exit out</div><div> if (pa_ready == 2) {</div>
<div> pa_context_disconnect(pa_ctx);</div><div> pa_context_unref(pa_ctx);</div><div> pa_threaded_mainloop_free(pa_ml);</div><div> return -1;</div><div> }</div><div> // At this point, we're connected to the server and ready to make</div>
<div> // requests</div><div> switch (state) {</div><div> // State 0: we haven't done anything yet</div><div> case 0:</div><div> // This sends an operation to the server. pa_sinklist_info is</div>
<div> // our callback function and a pointer to our devicelist will</div><div> // be passed to the callback The operation ID is stored in the</div><div> // pa_op variable</div>
<div><br></div><div> <span class="" style="white-space:pre"> </span>pa_threaded_mainloop_lock(pa_ml);</div><div><br></div><div> pa_op = pa_context_get_sink_info_list(pa_ctx,</div><div> pa_sinklist_cb,</div>
<div> output</div><div> );</div><div><br></div><div> while (pa_operation_get_state(pa_op) == PA_OPERATION_RUNNING) { // MF</div><div> temp = pa_operation_get_state(pa_op); // MF</div>
<div> <span class="" style="white-space:pre"> </span> pa_threaded_mainloop_wait(pa_ml); // MF</div><div> } // MF</div><div> // Update state for next iteration through the loop</div>
<div> state++;</div><div> break;</div><div> case 1:</div><div> // Now we wait for our operation to complete. When it's</div><div> // complete our pa_output_devicelist is filled out, and we move</div>
<div> // along to the next state</div><div> if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {</div><div> pa_operation_unref(pa_op);</div><div><br></div><div><br></div>
<div> pa_threaded_mainloop_accept(pa_ml); // MF</div><div> pa_threaded_mainloop_unlock(pa_ml); // MF</div><div><br></div><div> // Now we perform another operation to get the source</div>
<div> // (input device) list just like before. This time we pass</div><div> // a pointer to our input structure</div><div> pa_threaded_mainloop_lock(pa_ml);</div><div>
<br></div><div> pa_op = pa_context_get_source_info_list(pa_ctx,</div><div> pa_sourcelist_cb,</div><div> input</div><div> );</div>
<div><br></div><div> // Update the state so we know what to do next</div><div> state++;</div><div> }</div><div> break;</div><div> case 2:</div>
<div> if (pa_operation_get_state(pa_op) == PA_OPERATION_DONE) {</div><div> // Now we're done, clean up and disconnect and return</div><div> pa_operation_unref(pa_op);</div>
<div> pa_context_disconnect(pa_ctx);</div><div> pa_context_unref(pa_ctx);</div><div> pa_threaded_mainloop_free(pa_ml);</div><div> return 0;</div>
<div> }</div><div> break;</div><div> default:</div><div> // We should never see this state</div><div> fprintf(stderr, "in state %d\n", state);</div>
<div> return -1;</div><div> }</div><div> // Iterate the main loop and go again. The second argument is whether</div><div> // or not the iteration should block until something is ready to be</div>
<div> // done. Set it to zero for non-blocking.</div><div> //pa_mainloop_iterate(pa_ml, 1, NULL);</div><div> }</div><div>}</div><div><br></div><div>// This callback gets called when our context changes state. We really only</div>
<div>// care about when it's ready or if it has failed</div><div>void pa_state_cb(pa_context *c, void *userdata) {</div><div> pa_context_state_t state;</div><div> int *pa_ready = (int *)userdata;</div><div>
<br></div><div> state = pa_context_get_state(c);</div><div> switch (state) {</div><div> // There are just here for reference</div><div> case PA_CONTEXT_UNCONNECTED:</div><div> case PA_CONTEXT_CONNECTING:</div>
<div> case PA_CONTEXT_AUTHORIZING:</div><div> case PA_CONTEXT_SETTING_NAME:</div><div> default:</div><div> break;</div><div> case PA_CONTEXT_FAILED:</div>
<div> case PA_CONTEXT_TERMINATED:</div><div> *pa_ready = 2;</div><div> break;</div><div> case PA_CONTEXT_READY:</div><div> *pa_ready = 1;</div>
<div> break;</div><div> }</div><div>}</div><div><br></div><div>// pa_mainloop will call this function when it's ready to tell us about a sink.</div><div>// Since we're not threading, there's no need for mutexes on the devicelist</div>
<div>// structure</div><div>void pa_sinklist_cb(pa_context *c, const pa_sink_info *l, int eol, void *userdata) {</div><div> pa_devicelist_t *pa_devicelist = (pa_devicelist_t *)userdata;</div><div> int ctr = 0;</div>
<div><br></div><div> // If eol is set to a positive number, you're at the end of the list</div><div> if (eol > 0) {</div><div> <span class="" style="white-space:pre"> </span>pa_threaded_mainloop_signal(pa_ml, 1); // MF</div>
<div> return;</div><div> }</div><div><br></div><div> // We know we've allocated 16 slots to hold devices. Loop through our</div><div> // structure and find the first one that's "uninitialized." Copy the</div>
<div> // contents into it and we're done. If we receive more than 16 devices,</div><div> // they're going to get dropped. You could make this dynamically allocate</div><div> // space for the device list, but this is a simple example.</div>
<div> for (ctr = 0; ctr < 16; ctr++) {</div><div> if (! pa_devicelist[ctr].initialized) {</div><div> strncpy(pa_devicelist[ctr].name, l->name, 511);</div><div> strncpy(pa_devicelist[ctr].description, l->description, 255);</div>
<div> pa_devicelist[ctr].index = l->index;</div><div> pa_devicelist[ctr].initialized = 1;</div><div> break;</div><div> }</div><div> }</div><div><br></div><div>}</div><div><br>
</div><div>// See above. This callback is pretty much identical to the previous</div><div>void pa_sourcelist_cb(pa_context *c, const pa_source_info *l, int eol, void *userdata) {</div><div> pa_devicelist_t *pa_devicelist = (pa_devicelist_t *)userdata;</div>
<div> int ctr = 0;</div><div><br></div><div> if (eol > 0) {</div><div> return;</div><div> }</div><div><br></div><div> for (ctr = 0; ctr < 16; ctr++) {</div><div> if (! pa_devicelist[ctr].initialized) {</div>
<div> strncpy(pa_devicelist[ctr].name, l->name, 511);</div><div> strncpy(pa_devicelist[ctr].description, l->description, 255);</div><div> pa_devicelist[ctr].index = l->index;</div>
<div> pa_devicelist[ctr].initialized = 1;</div><div> break;</div><div> }</div><div> }</div><div><br></div><div>}</div></div><div><br></div></div></div>