MythTV  0.26-pre
audiooutputpulse.cpp
Go to the documentation of this file.
00001 /*
00002  * Copyright (C) 2008 Alan Calvert, 2010 foobum@gmail.com
00003  * 
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version.
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00017  * 02110-1301, USA.
00018  */
00019 
00020 #include <QString>
00021 
00022 #include "audiooutputpulse.h"
00023 
00024 #define LOC     QString("PulseAudio: ")
00025 
00026 #define PULSE_MAX_CHANNELS 8
00027 
00028 AudioOutputPulseAudio::AudioOutputPulseAudio(const AudioSettings &settings) :
00029     AudioOutputBase(settings),
00030     pcontext(NULL), pstream(NULL), mainloop(NULL),
00031     m_aosettings(NULL)
00032 {
00033     volume_control.channels = 0;
00034     for (unsigned int i = 0; i < PA_CHANNELS_MAX; ++i)
00035         volume_control.values[i] = PA_VOLUME_MUTED;
00036 
00037     ChooseHost();
00038 
00039     InitSettings(settings);
00040     if (settings.init)
00041         Reconfigure(settings);
00042 }
00043 
00044 AudioOutputPulseAudio::~AudioOutputPulseAudio()
00045 {
00046     KillAudio();
00047     if (pcontext)
00048     {
00049         pa_context_unref(pcontext);
00050         pcontext = NULL;
00051     }
00052 }
00053 
00054 AudioOutputSettings* AudioOutputPulseAudio::GetOutputSettings(bool /*digital*/)
00055 {
00056     AudioFormat fmt;
00057     m_aosettings = new AudioOutputSettings();
00058     QString fn_log_tag = "OpenDevice, ";
00059 
00060     /* Start the mainloop and connect a context so we can retrieve the
00061        parameters of the default sink */
00062     mainloop = pa_threaded_mainloop_new();
00063     if (!mainloop)
00064     {
00065         VBERROR(fn_log_tag + "Failed to get new threaded mainloop");
00066         delete m_aosettings;
00067         return NULL;
00068     }
00069 
00070     pa_threaded_mainloop_start(mainloop);
00071     pa_threaded_mainloop_lock(mainloop);
00072 
00073     if (!ContextConnect())
00074     {
00075         pa_threaded_mainloop_unlock(mainloop);
00076         pa_threaded_mainloop_stop(mainloop);
00077         delete m_aosettings;
00078         return NULL;
00079     }
00080 
00081     /* Get the samplerate and channel count of the default sink, supported rate
00082        and channels are added in SinkInfoCallback */
00083     /* We should in theory be able to feed pulse any samplerate but allowing it
00084        to resample results in weird behaviour (odd channel maps, static) post
00085        pause / reset */
00086     pa_operation *op = pa_context_get_sink_info_by_index(pcontext, 0,
00087                                                          SinkInfoCallback,
00088                                                          this);
00089     if (op)
00090     {
00091         pa_operation_unref(op);
00092         pa_threaded_mainloop_wait(mainloop);
00093     }
00094     else
00095         VBERROR("Failed to determine default sink samplerate");
00096 
00097     pa_threaded_mainloop_unlock(mainloop);
00098 
00099     // All formats except S24 (pulse wants S24LSB)
00100     while ((fmt = m_aosettings->GetNextFormat()))
00101     {
00102         if (fmt == FORMAT_S24
00103 // define from PA 0.9.15 only
00104 #ifndef PA_MAJOR
00105             || fmt == FORMAT_S24LSB
00106 #endif
00107             )
00108             continue;
00109         m_aosettings->AddSupportedFormat(fmt);
00110     }
00111 
00112     pa_context_disconnect(pcontext);
00113     pa_context_unref(pcontext);
00114     pcontext = NULL;
00115     pa_threaded_mainloop_stop(mainloop);
00116     mainloop = NULL;
00117 
00118     return m_aosettings;
00119 }
00120 
00121 bool AudioOutputPulseAudio::OpenDevice()
00122 {
00123     QString fn_log_tag = "OpenDevice, ";
00124     if (channels > PULSE_MAX_CHANNELS )
00125     {
00126         VBERROR(fn_log_tag + QString("audio channel limit %1, but %2 requested")
00127                              .arg(PULSE_MAX_CHANNELS).arg(channels));
00128         return false;
00129     }
00130 
00131     sample_spec.rate = samplerate;
00132     sample_spec.channels = volume_control.channels = channels;
00133     switch (output_format)
00134     {
00135         case FORMAT_U8:     sample_spec.format = PA_SAMPLE_U8;         break;
00136         case FORMAT_S16:    sample_spec.format = PA_SAMPLE_S16NE;      break;
00137 // define from PA 0.9.15 only
00138 #ifdef PA_MAJOR
00139         case FORMAT_S24LSB: sample_spec.format = PA_SAMPLE_S24_32NE;   break;
00140 #endif
00141         case FORMAT_S32:    sample_spec.format = PA_SAMPLE_S32NE;      break;
00142         case FORMAT_FLT:    sample_spec.format = PA_SAMPLE_FLOAT32NE;  break;
00143         default:
00144             VBERROR(fn_log_tag + QString("unsupported sample format %1")
00145                                  .arg(output_format));
00146             return false;
00147     }
00148 
00149     if (!pa_sample_spec_valid(&sample_spec))
00150     {
00151         VBERROR(fn_log_tag + "invalid sample spec");
00152         return false;
00153     }
00154     else
00155     {
00156         char spec[PA_SAMPLE_SPEC_SNPRINT_MAX];
00157         pa_sample_spec_snprint(spec, sizeof(spec), &sample_spec);
00158         VBAUDIO(fn_log_tag + QString("using sample spec %1").arg(spec));
00159     }
00160 
00161     pa_channel_map *pmap = NULL;
00162 
00163     if(!(pmap = pa_channel_map_init_auto(&channel_map, channels,
00164                                          PA_CHANNEL_MAP_WAVEEX)) < 0)
00165     {
00166         VBERROR(fn_log_tag + "failed to init channel map");
00167         return false;
00168     }
00169 
00170     channel_map = *pmap;
00171 
00172     mainloop = pa_threaded_mainloop_new();
00173     if (!mainloop)
00174     {
00175         VBERROR(fn_log_tag + "failed to get new threaded mainloop");
00176         return false;
00177     }
00178 
00179     pa_threaded_mainloop_start(mainloop);
00180     pa_threaded_mainloop_lock(mainloop);
00181 
00182     if (!ContextConnect())
00183     {
00184         pa_threaded_mainloop_unlock(mainloop);
00185         pa_threaded_mainloop_stop(mainloop);
00186         return false;
00187     }
00188 
00189     if (!ConnectPlaybackStream())
00190     {
00191         pa_threaded_mainloop_unlock(mainloop);
00192         pa_threaded_mainloop_stop(mainloop);
00193         return false;
00194     }
00195 
00196     pa_threaded_mainloop_unlock(mainloop);
00197     return true;
00198 }
00199 
00200 void AudioOutputPulseAudio::CloseDevice()
00201 {
00202     if (mainloop)
00203         pa_threaded_mainloop_lock(mainloop);
00204 
00205     if (pstream)
00206     {
00207         FlushStream("CloseDevice");
00208         pa_stream_disconnect(pstream);
00209         pa_stream_unref(pstream);
00210         pstream = NULL;
00211     }
00212 
00213     if (pcontext)
00214     {
00215         pa_context_drain(pcontext, NULL, NULL);
00216         pa_context_disconnect(pcontext);
00217         pa_context_unref(pcontext);
00218         pcontext = NULL;
00219     }
00220 
00221     if (mainloop)
00222     {
00223         pa_threaded_mainloop_unlock(mainloop);
00224         pa_threaded_mainloop_stop(mainloop);
00225         mainloop = NULL;
00226     }
00227 }
00228 
00229 void AudioOutputPulseAudio::WriteAudio(uchar *aubuf, int size)
00230 {
00231     QString fn_log_tag = "WriteAudio, ";
00232     pa_stream_state_t sstate = pa_stream_get_state(pstream);
00233 
00234     VBAUDIOTS(fn_log_tag + QString("writing %1 bytes").arg(size));
00235 
00236     /* NB This "if" check can be replaced with PA_STREAM_IS_GOOD() in
00237        PulseAudio API from 0.9.11. As 0.9.10 is still widely used
00238        we use the more verbose version for now */
00239 
00240     if (sstate == PA_STREAM_CREATING || sstate == PA_STREAM_READY)
00241     {
00242         int write_status = PA_ERR_INVALID;
00243         size_t to_write = size;
00244         unsigned char *buf_ptr = aubuf;
00245 
00246         pa_threaded_mainloop_lock(mainloop);
00247         while (to_write > 0)
00248         {
00249             write_status = 0;
00250             size_t writable = pa_stream_writable_size(pstream);
00251             if (writable > 0)
00252             {
00253                 size_t write = min(to_write, writable);
00254                 write_status = pa_stream_write(pstream, buf_ptr, write,
00255                                                NULL, 0, PA_SEEK_RELATIVE);
00256 
00257                 if (0 != write_status)
00258                     break;
00259 
00260                 buf_ptr += write;
00261                 to_write -= write;
00262             }
00263             else
00264             {
00265                 pa_threaded_mainloop_wait(mainloop);
00266             }
00267         }
00268         pa_threaded_mainloop_unlock(mainloop);
00269 
00270         if (to_write > 0)
00271         {
00272             if (write_status != 0)
00273                 VBERROR(fn_log_tag + QString("stream write failed: %1")
00274                                      .arg(write_status == PA_ERR_BADSTATE
00275                                                 ? "PA_ERR_BADSTATE"
00276                                                 : "PA_ERR_INVALID"));
00277 
00278             VBERROR(fn_log_tag + QString("short write, %1 of %2")
00279                                  .arg(size - to_write).arg(size));
00280         }
00281     }
00282     else
00283         VBERROR(fn_log_tag + QString("stream state not good: %1")
00284                              .arg(sstate,0,16));
00285 }
00286 
00287 int AudioOutputPulseAudio::GetBufferedOnSoundcard(void) const
00288 {
00289     pa_usec_t latency = 0;
00290     size_t buffered = 0;
00291 
00292     if (!pcontext || pa_context_get_state(pcontext) != PA_CONTEXT_READY)
00293         return 0;
00294 
00295     if (!pstream || pa_stream_get_state(pstream) != PA_STREAM_READY)
00296         return 0;
00297 
00298     const pa_buffer_attr *buf_attr =  pa_stream_get_buffer_attr(pstream);
00299     size_t bfree = pa_stream_writable_size(pstream);
00300     buffered = buf_attr->tlength - bfree;
00301 
00302     pa_threaded_mainloop_lock(mainloop);
00303 
00304     while (pa_stream_get_latency(pstream, &latency, NULL) < 0)
00305     {
00306         if (pa_context_errno(pcontext) != PA_ERR_NODATA)
00307         {
00308             latency = 0;
00309             break;
00310         }
00311         pa_threaded_mainloop_wait(mainloop);
00312     }
00313 
00314     pa_threaded_mainloop_unlock(mainloop);
00315 
00316     return ((uint64_t)latency * samplerate *
00317             output_bytes_per_frame / 1000000) + buffered;
00318 }
00319 
00320 int AudioOutputPulseAudio::GetVolumeChannel(int channel) const
00321 {
00322     return (float)volume_control.values[channel] /
00323            (float)PA_VOLUME_NORM * 100.0f;
00324 }
00325 
00326 void AudioOutputPulseAudio::SetVolumeChannel(int channel, int volume)
00327 {
00328     QString fn_log_tag = "SetVolumeChannel, ";
00329 
00330     if (channel < 0 || channel > PULSE_MAX_CHANNELS || volume < 0)
00331     {
00332         VBERROR(fn_log_tag + QString("bad volume params, channel %1, volume %2")
00333                              .arg(channel).arg(volume));
00334         return;
00335     }
00336 
00337     volume_control.values[channel] =
00338         (float)volume / 100.0f * (float)PA_VOLUME_NORM;
00339 
00340     volume = min(100, volume);
00341     volume = max(0, volume);
00342 
00343     if (gCoreContext->GetSetting("MixerControl", "PCM").toLower() == "pcm")
00344     {
00345         uint32_t stream_index = pa_stream_get_index(pstream);
00346         pa_threaded_mainloop_lock(mainloop);
00347         pa_operation *op =
00348             pa_context_set_sink_input_volume(pcontext, stream_index,
00349                                              &volume_control,
00350                                              OpCompletionCallback, this);
00351         pa_threaded_mainloop_unlock(mainloop);
00352         if (op)
00353             pa_operation_unref(op);
00354         else
00355             VBERROR(fn_log_tag +
00356                     QString("set stream volume operation failed, stream %1, "
00357                             "error %2 ")
00358                     .arg(stream_index)
00359                     .arg(pa_strerror(pa_context_errno(pcontext))));
00360     }
00361     else
00362     {
00363         uint32_t sink_index = pa_stream_get_device_index(pstream);
00364         pa_threaded_mainloop_lock(mainloop);
00365         pa_operation *op =
00366             pa_context_set_sink_volume_by_index(pcontext, sink_index,
00367                                                 &volume_control,
00368                                                 OpCompletionCallback, this);
00369         pa_threaded_mainloop_unlock(mainloop);
00370         if (op)
00371             pa_operation_unref(op);
00372         else
00373             VBERROR(fn_log_tag +
00374                     QString("set sink volume operation failed, sink %1, "
00375                             "error %2 ")
00376                     .arg(sink_index)
00377                     .arg(pa_strerror(pa_context_errno(pcontext))));
00378     }
00379 }
00380 
00381 void AudioOutputPulseAudio::Drain(void)
00382 {
00383     AudioOutputBase::Drain();
00384     pa_threaded_mainloop_lock(mainloop);
00385     pa_operation *op = pa_stream_drain(pstream, NULL, this);
00386     pa_threaded_mainloop_unlock(mainloop);
00387 
00388     if (op)
00389         pa_operation_unref(op);
00390     else
00391         VBERROR("Drain, stream drain failed");
00392 }
00393 
00394 bool AudioOutputPulseAudio::ContextConnect(void)
00395 {
00396     QString fn_log_tag = "ContextConnect, ";
00397     if (pcontext)
00398     {
00399         VBERROR(fn_log_tag + "context appears to exist, but shouldn't (yet)");
00400         pa_context_unref(pcontext);
00401         pcontext = NULL;
00402         return false;
00403     }
00404     pa_proplist *proplist = pa_proplist_new();
00405     if (!proplist)
00406     {
00407         VBERROR(fn_log_tag + QString("failed to create new proplist"));
00408         return false;
00409     }
00410     pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "MythTV");
00411     pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "mythtv");
00412     pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video");
00413     pcontext =
00414         pa_context_new_with_proplist(pa_threaded_mainloop_get_api(mainloop),
00415                                      "MythTV", proplist);
00416     if (!pcontext)
00417     {
00418         VBERROR(fn_log_tag + "failed to acquire new context");
00419         return false;
00420     }
00421     pa_context_set_state_callback(pcontext, ContextStateCallback, this);
00422 
00423     char *pulse_host = ChooseHost();
00424     int chk = pa_context_connect(
00425         pcontext, pulse_host, (pa_context_flags_t)0, NULL);
00426 
00427     delete(pulse_host);
00428 
00429     if (chk < 0)
00430     {
00431         VBERROR(fn_log_tag + QString("context connect failed: %1")
00432                              .arg(pa_strerror(pa_context_errno(pcontext))));
00433         return false;
00434     }
00435     bool connected = false;
00436     pa_context_state_t state = pa_context_get_state(pcontext);
00437     for (; !connected; state = pa_context_get_state(pcontext))
00438     {
00439         switch(state)
00440         {
00441             case PA_CONTEXT_READY:
00442                 VBAUDIO(fn_log_tag +"context connection ready");
00443                 connected = true;
00444                 continue;
00445 
00446             case PA_CONTEXT_FAILED:
00447             case PA_CONTEXT_TERMINATED:
00448                 VBERROR(fn_log_tag +
00449                         QString("context connection failed or terminated: %1")
00450                         .arg(pa_strerror(pa_context_errno(pcontext))));
00451                 return false;
00452 
00453             default:
00454                 VBAUDIO(fn_log_tag + "waiting for context connection ready");
00455                 pa_threaded_mainloop_wait(mainloop);
00456                 break;
00457         }
00458     }
00459 
00460     pa_operation *op =
00461         pa_context_get_server_info(pcontext, ServerInfoCallback, this);
00462 
00463     if (op)
00464         pa_operation_unref(op);
00465     else
00466         VBERROR(fn_log_tag + "failed to get PulseAudio server info");
00467 
00468     return true;
00469 }
00470 
00471 char *AudioOutputPulseAudio::ChooseHost(void)
00472 {
00473     QString fn_log_tag = "ChooseHost, ";
00474     char *pulse_host = NULL;
00475     char *device = strdup(main_device.toAscii().constData());
00476     const char *host;
00477 
00478     for (host=device; host && *host != ':' && *host != 0; host++);
00479 
00480     if (host && *host != 0)
00481         host++;
00482 
00483     if ( !(!host || *host == 0 || strcmp(host,"default") == 0))
00484     {
00485         if ((pulse_host = new char[strlen(host)]))
00486             strcpy(pulse_host, host);
00487         else
00488             VBERROR(fn_log_tag +
00489                     QString("allocation of pulse host '%1' char[%2] failed")
00490                     .arg(host).arg(strlen(host) + 1));
00491     }
00492 
00493     if (!pulse_host && strcmp(host,"default") != 0)
00494     {
00495         char *env_pulse_host = getenv("PULSE_SERVER");
00496         if (env_pulse_host && (*env_pulse_host != '\0'))
00497         {
00498             int host_len = strlen(env_pulse_host) + 1;
00499 
00500             if ((pulse_host = new char[host_len]))
00501                 strcpy(pulse_host, env_pulse_host);
00502             else
00503             {
00504                 VBERROR(fn_log_tag +
00505                         QString("allocation of pulse host '%1' char[%2] failed")
00506                         .arg(env_pulse_host).arg(host_len));
00507             }
00508         }
00509     }
00510 
00511     VBAUDIO(fn_log_tag + QString("chosen PulseAudio server: %1")
00512                          .arg((pulse_host != NULL) ? pulse_host : "default"));
00513 
00514     free(device);
00515 
00516     return pulse_host;
00517 }
00518 
00519 bool AudioOutputPulseAudio::ConnectPlaybackStream(void)
00520 {
00521     QString fn_log_tag = "ConnectPlaybackStream, ";
00522     pa_proplist *proplist = pa_proplist_new();
00523     if (!proplist)
00524     {
00525         VBERROR(fn_log_tag + QString("failed to create new proplist"));
00526         return false;
00527     }
00528     pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video");
00529     pstream =
00530         pa_stream_new_with_proplist(pcontext, "MythTV playback", &sample_spec,
00531                                     &channel_map, proplist);
00532     if (!pstream)
00533     {
00534         VBERROR("failed to create new playback stream");
00535         return false;
00536     }
00537     pa_stream_set_state_callback(pstream, StreamStateCallback, this);
00538     pa_stream_set_write_callback(pstream, WriteCallback, this);
00539     pa_stream_set_overflow_callback(pstream, BufferFlowCallback, (char*)"over");
00540     pa_stream_set_underflow_callback(pstream, BufferFlowCallback,
00541                                      (char*)"under");
00542     if (set_initial_vol)
00543     {
00544         int volume = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
00545         pa_cvolume_set(&volume_control, channels,
00546                        (float)volume * (float)PA_VOLUME_NORM / 100.0f);
00547     }
00548     else
00549         pa_cvolume_reset(&volume_control, channels);
00550 
00551     fragment_size = (samplerate * 25 * output_bytes_per_frame) / 1000;
00552 
00553     buffer_settings.maxlength = (uint32_t)-1;
00554     buffer_settings.tlength = fragment_size * 4;
00555     buffer_settings.prebuf = (uint32_t)-1;
00556     buffer_settings.minreq = (uint32_t)-1;
00557 
00558     int flags = PA_STREAM_INTERPOLATE_TIMING
00559         | PA_STREAM_ADJUST_LATENCY
00560         | PA_STREAM_AUTO_TIMING_UPDATE
00561         | PA_STREAM_NO_REMIX_CHANNELS;
00562 
00563     pa_stream_connect_playback(pstream, NULL, &buffer_settings,
00564                                (pa_stream_flags_t)flags, NULL, NULL);
00565 
00566     pa_context_state_t cstate;
00567     pa_stream_state_t sstate;
00568     bool connected = false, failed = false;
00569 
00570     while (!(connected || failed))
00571     {
00572         switch (cstate = pa_context_get_state(pcontext))
00573         {
00574             case PA_CONTEXT_FAILED:
00575             case PA_CONTEXT_TERMINATED:
00576                 VBERROR(QString("context is stuffed, %1")
00577                             .arg(pa_strerror(pa_context_errno(pcontext))));
00578                 failed = true;
00579                 break;
00580             default:
00581                 switch (sstate = pa_stream_get_state(pstream))
00582                 {
00583                     case PA_STREAM_READY:
00584                         connected = true;
00585                         break;
00586                     case PA_STREAM_FAILED:
00587                     case PA_STREAM_TERMINATED:
00588                         VBERROR(QString("stream failed or was terminated, "
00589                                         "context state %1, stream state %2")
00590                                     .arg(cstate).arg(sstate));
00591                         failed = true;
00592                         break;
00593                     default:
00594                         pa_threaded_mainloop_wait(mainloop);
00595                         break;
00596                 }
00597         }
00598     }
00599 
00600     const pa_buffer_attr *buf_attr = pa_stream_get_buffer_attr(pstream);
00601     fragment_size = buf_attr->tlength >> 2;
00602     soundcard_buffer_size = buf_attr->maxlength;
00603 
00604     VBAUDIO(QString("fragment size %1, soundcard buffer size %2")
00605                 .arg(fragment_size).arg(soundcard_buffer_size));
00606 
00607     return (connected && !failed);
00608 }
00609 
00610 void AudioOutputPulseAudio::FlushStream(const char *caller)
00611 {
00612     QString fn_log_tag = QString("FlushStream (%1), ").arg(caller);
00613     pa_threaded_mainloop_lock(mainloop);
00614     pa_operation *op = pa_stream_flush(pstream, NULL, this);
00615     pa_threaded_mainloop_unlock(mainloop);
00616     if (op)
00617         pa_operation_unref(op);
00618     else
00619         VBERROR(fn_log_tag + "stream flush operation failed ");
00620 }
00621 
00622 void AudioOutputPulseAudio::ContextStateCallback(pa_context *c, void *arg)
00623 {
00624     QString fn_log_tag = "_ContextStateCallback, ";
00625     AudioOutputPulseAudio *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
00626     switch (pa_context_get_state(c))
00627     {
00628         case PA_CONTEXT_READY:
00629             pa_threaded_mainloop_signal(audoutP->mainloop, 0);
00630             break;
00631         case PA_CONTEXT_TERMINATED:
00632         case PA_CONTEXT_FAILED:
00633             pa_threaded_mainloop_signal(audoutP->mainloop, 0);
00634             break;
00635         case PA_CONTEXT_CONNECTING:
00636         case PA_CONTEXT_UNCONNECTED:
00637         case PA_CONTEXT_AUTHORIZING:
00638         case PA_CONTEXT_SETTING_NAME:
00639             break;
00640     }
00641 }
00642 
00643 void AudioOutputPulseAudio::StreamStateCallback(pa_stream *s, void *arg)
00644 {
00645     QString fn_log_tag = "StreamStateCallback, ";
00646     AudioOutputPulseAudio *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
00647     switch (pa_stream_get_state(s))
00648     {
00649         case PA_STREAM_READY:
00650         case PA_STREAM_TERMINATED:
00651         case PA_STREAM_FAILED:
00652             pa_threaded_mainloop_signal(audoutP->mainloop, 0);
00653             break;
00654         case PA_STREAM_UNCONNECTED:
00655         case PA_STREAM_CREATING:
00656             break;
00657     }
00658 }
00659 
00660 void AudioOutputPulseAudio::WriteCallback(pa_stream *s, size_t size, void *arg)
00661 {
00662     AudioOutputPulseAudio *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
00663     pa_threaded_mainloop_signal(audoutP->mainloop, 0);
00664 }
00665 
00666 void AudioOutputPulseAudio::BufferFlowCallback(pa_stream *s, void *tag)
00667 {
00668     VBERROR(QString("stream buffer %1 flow").arg((char*)tag));
00669 }
00670 
00671 void AudioOutputPulseAudio::OpCompletionCallback(
00672     pa_context *c, int ok, void *arg)
00673 {
00674     QString fn_log_tag = "OpCompletionCallback, ";
00675     AudioOutputPulseAudio *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
00676     if (!ok)
00677     {
00678         VBERROR(fn_log_tag + QString("bummer, an operation failed: %1")
00679                              .arg(pa_strerror(pa_context_errno(c))));
00680     }
00681     pa_threaded_mainloop_signal(audoutP->mainloop, 0);
00682 }
00683 
00684 void AudioOutputPulseAudio::ServerInfoCallback(
00685     pa_context *context, const pa_server_info *inf, void *arg)
00686 {
00687     QString fn_log_tag = "ServerInfoCallback, ";
00688 
00689     VBAUDIO(fn_log_tag +
00690             QString("PulseAudio server info - host name: %1, server version: "
00691                     "%2, server name: %3, default sink: %4")
00692             .arg(inf->host_name).arg(inf->server_version)
00693             .arg(inf->server_name).arg(inf->default_sink_name));
00694 }
00695 
00696 void AudioOutputPulseAudio::SinkInfoCallback(
00697     pa_context *c, const pa_sink_info *info, int eol, void *arg)
00698 {
00699     AudioOutputPulseAudio *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
00700 
00701     if (!info)
00702     {
00703         pa_threaded_mainloop_signal(audoutP->mainloop, 0);
00704         return;
00705     }
00706 
00707     audoutP->m_aosettings->AddSupportedRate(info->sample_spec.rate);
00708 
00709     for (uint i = 2; i <= info->sample_spec.channels; i++)
00710         audoutP->m_aosettings->AddSupportedChannels(i);
00711 
00712     pa_threaded_mainloop_signal(audoutP->mainloop, 0);
00713 }
00714 
00715 /* vim: set expandtab tabstop=4 shiftwidth=4: */
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends