MythTV  master
audiooutputpulse.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2008 Alan Calvert, 2010 foobum@gmail.com
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  */
19 
20 #include "audiooutputpulse.h"
21 
22 // QT headers
23 #include <QString>
24 
25 // C++ headers
26 #include <algorithm>
27 using std::min;
28 
29 #define LOC QString("PulseAudio: ")
30 
31 #define PULSE_MAX_CHANNELS 8
32 
34  AudioOutputBase(settings)
35 {
36  m_volumeControl.channels = 0;
37  for (uint & value : m_volumeControl.values)
38  value = PA_VOLUME_MUTED;
39 
40  InitSettings(settings);
41  if (settings.m_init)
42  Reconfigure(settings);
43 }
44 
46 {
47  KillAudio();
48  if (m_pcontext)
49  {
50  pa_context_unref(m_pcontext);
51  m_pcontext = nullptr;
52  }
53 }
54 
56 {
58  QString fn_log_tag = "OpenDevice, ";
59 
60  /* Start the mainloop and connect a context so we can retrieve the
61  parameters of the default sink */
62  m_mainloop = pa_threaded_mainloop_new();
63  if (!m_mainloop)
64  {
65  VBERROR(fn_log_tag + "Failed to get new threaded mainloop");
66  delete m_aoSettings;
67  return nullptr;
68  }
69 
70  pa_threaded_mainloop_start(m_mainloop);
71  pa_threaded_mainloop_lock(m_mainloop);
72 
73  if (!ContextConnect())
74  {
75  pa_threaded_mainloop_unlock(m_mainloop);
76  pa_threaded_mainloop_stop(m_mainloop);
77  delete m_aoSettings;
78  return nullptr;
79  }
80 
81  /* Get the samplerate and channel count of the default sink, supported rate
82  and channels are added in SinkInfoCallback */
83  /* We should in theory be able to feed pulse any samplerate but allowing it
84  to resample results in weird behaviour (odd channel maps, static) post
85  pause / reset */
86  pa_operation *op = pa_context_get_sink_info_by_index(m_pcontext, 0,
88  this);
89  if (op)
90  {
91  pa_operation_unref(op);
92  pa_threaded_mainloop_wait(m_mainloop);
93  }
94  else
95  VBERROR("Failed to determine default sink samplerate");
96 
97  pa_threaded_mainloop_unlock(m_mainloop);
98 
99  // All formats except S24 (pulse wants S24LSB)
100  AudioFormat fmt = FORMAT_NONE;
101  while ((fmt = m_aoSettings->GetNextFormat()))
102  {
103  if (fmt == FORMAT_S24
104 // define from PA 0.9.15 only
105 #ifndef PA_MAJOR
106  || fmt == FORMAT_S24LSB
107 #endif
108  )
109  continue;
111  }
112 
113  pa_context_disconnect(m_pcontext);
114  pa_context_unref(m_pcontext);
115  m_pcontext = nullptr;
116  pa_threaded_mainloop_stop(m_mainloop);
117  m_mainloop = nullptr;
118 
119  return m_aoSettings;
120 }
121 
123 {
124  QString fn_log_tag = "OpenDevice, ";
126  {
127  VBERROR(fn_log_tag + QString("audio channel limit %1, but %2 requested")
129  return false;
130  }
131 
132  m_sampleSpec.rate = m_sampleRate;
133  m_sampleSpec.channels = m_volumeControl.channels = m_channels;
134  switch (m_outputFormat)
135  {
136  case FORMAT_U8: m_sampleSpec.format = PA_SAMPLE_U8; break;
137  case FORMAT_S16: m_sampleSpec.format = PA_SAMPLE_S16NE; break;
138 // define from PA 0.9.15 only
139 #ifdef PA_MAJOR
140  case FORMAT_S24LSB: m_sampleSpec.format = PA_SAMPLE_S24_32NE; break;
141 #endif
142  case FORMAT_S32: m_sampleSpec.format = PA_SAMPLE_S32NE; break;
143  case FORMAT_FLT: m_sampleSpec.format = PA_SAMPLE_FLOAT32NE; break;
144  default:
145  VBERROR(fn_log_tag + QString("unsupported sample format %1")
146  .arg(m_outputFormat));
147  return false;
148  }
149 
150  if (!pa_sample_spec_valid(&m_sampleSpec))
151  {
152  VBERROR(fn_log_tag + "invalid sample spec");
153  return false;
154  }
155  std::string spec(PA_SAMPLE_SPEC_SNPRINT_MAX,'\0');
156  pa_sample_spec_snprint(spec.data(), spec.size(), &m_sampleSpec);
157  VBAUDIO(fn_log_tag + "using sample spec " + spec.data());
158 
159  if(!pa_channel_map_init_auto(&m_channelMap, m_channels, PA_CHANNEL_MAP_WAVEEX))
160  {
161  VBERROR(fn_log_tag + "failed to init channel map");
162  return false;
163  }
164 
165  m_mainloop = pa_threaded_mainloop_new();
166  if (!m_mainloop)
167  {
168  VBERROR(fn_log_tag + "failed to get new threaded mainloop");
169  return false;
170  }
171 
172  pa_threaded_mainloop_start(m_mainloop);
173  pa_threaded_mainloop_lock(m_mainloop);
174 
175  if (!ContextConnect())
176  {
177  pa_threaded_mainloop_unlock(m_mainloop);
178  pa_threaded_mainloop_stop(m_mainloop);
179  return false;
180  }
181 
182  if (!ConnectPlaybackStream())
183  {
184  pa_threaded_mainloop_unlock(m_mainloop);
185  pa_threaded_mainloop_stop(m_mainloop);
186  return false;
187  }
188 
189  pa_threaded_mainloop_unlock(m_mainloop);
190  return true;
191 }
192 
194 {
195  if (m_mainloop)
196  pa_threaded_mainloop_lock(m_mainloop);
197 
198  if (m_pstream)
199  {
200  FlushStream("CloseDevice");
201  pa_stream_disconnect(m_pstream);
202  pa_stream_unref(m_pstream);
203  m_pstream = nullptr;
204  }
205 
206  if (m_pcontext)
207  {
208  pa_context_drain(m_pcontext, nullptr, nullptr);
209  pa_context_disconnect(m_pcontext);
210  pa_context_unref(m_pcontext);
211  m_pcontext = nullptr;
212  }
213 
214  if (m_mainloop)
215  {
216  pa_threaded_mainloop_unlock(m_mainloop);
217  pa_threaded_mainloop_stop(m_mainloop);
218  m_mainloop = nullptr;
219  }
220 }
221 
222 void AudioOutputPulseAudio::WriteAudio(uchar *aubuf, int size)
223 {
224  QString fn_log_tag = "WriteAudio, ";
225  pa_stream_state_t sstate = pa_stream_get_state(m_pstream);
226 
227  VBAUDIOTS(fn_log_tag + QString("writing %1 bytes").arg(size));
228 
229  /* NB This "if" check can be replaced with PA_STREAM_IS_GOOD() in
230  PulseAudio API from 0.9.11. As 0.9.10 is still widely used
231  we use the more verbose version for now */
232 
233  if (sstate == PA_STREAM_CREATING || sstate == PA_STREAM_READY)
234  {
235  int write_status = PA_ERR_INVALID;
236  size_t to_write = size;
237  unsigned char *buf_ptr = aubuf;
238 
239  pa_threaded_mainloop_lock(m_mainloop);
240  while (to_write > 0)
241  {
242  write_status = 0;
243  size_t writable = pa_stream_writable_size(m_pstream);
244  if (writable > 0)
245  {
246  size_t write = min(to_write, writable);
247  write_status = pa_stream_write(m_pstream, buf_ptr, write,
248  nullptr, 0, PA_SEEK_RELATIVE);
249 
250  if (0 != write_status)
251  break;
252 
253  buf_ptr += write;
254  to_write -= write;
255  }
256  else
257  {
258  pa_threaded_mainloop_wait(m_mainloop);
259  }
260  }
261  pa_threaded_mainloop_unlock(m_mainloop);
262 
263  if (to_write > 0)
264  {
265  if (write_status != 0)
266  {
267  VBERROR(fn_log_tag + QString("stream write failed: %1")
268  .arg(write_status == PA_ERR_BADSTATE
269  ? "PA_ERR_BADSTATE"
270  : "PA_ERR_INVALID"));
271  }
272 
273  VBERROR(fn_log_tag + QString("short write, %1 of %2")
274  .arg(size - to_write).arg(size));
275  }
276  }
277  else
278  VBERROR(fn_log_tag + QString("stream state not good: %1")
279  .arg(sstate,0,16));
280 }
281 
283 {
284  pa_usec_t latency = 0;
285  size_t buffered = 0;
286 
287  if (!m_pcontext || pa_context_get_state(m_pcontext) != PA_CONTEXT_READY)
288  return 0;
289 
290  if (!m_pstream || pa_stream_get_state(m_pstream) != PA_STREAM_READY)
291  return 0;
292 
293  const pa_buffer_attr *buf_attr = pa_stream_get_buffer_attr(m_pstream);
294  size_t bfree = pa_stream_writable_size(m_pstream);
295  buffered = buf_attr->tlength - bfree;
296 
297  pa_threaded_mainloop_lock(m_mainloop);
298 
299  while (pa_stream_get_latency(m_pstream, &latency, nullptr) < 0)
300  {
301  if (pa_context_errno(m_pcontext) != PA_ERR_NODATA)
302  {
303  latency = 0;
304  break;
305  }
306  pa_threaded_mainloop_wait(m_mainloop);
307  }
308 
309  pa_threaded_mainloop_unlock(m_mainloop);
310 
311  return (latency * m_sampleRate *
312  m_outputBytesPerFrame / 1000000) + buffered;
313 }
314 
316 {
317  return (float)m_volumeControl.values[channel] /
318  (float)PA_VOLUME_NORM * 100.0F;
319 }
320 
322 {
323  QString fn_log_tag = "SetVolumeChannel, ";
324 
325  if (channel < 0 || channel > PULSE_MAX_CHANNELS || volume < 0)
326  {
327  VBERROR(fn_log_tag + QString("bad volume params, channel %1, volume %2")
328  .arg(channel).arg(volume));
329  return;
330  }
331 
332  m_volumeControl.values[channel] =
333  (float)volume / 100.0F * (float)PA_VOLUME_NORM;
334 
335 // FIXME: This code did nothing at all so has been commented out for now
336 // until it's decided whether it was ever required
337 // volume = min(100, volume);
338 // volume = max(0, volume);
339 
340  if (gCoreContext->GetSetting("MixerControl", "PCM").toLower() == "pcm")
341  {
342  uint32_t stream_index = pa_stream_get_index(m_pstream);
343  pa_threaded_mainloop_lock(m_mainloop);
344  pa_operation *op =
345  pa_context_set_sink_input_volume(m_pcontext, stream_index,
347  OpCompletionCallback, this);
348  pa_threaded_mainloop_unlock(m_mainloop);
349  if (op)
350  pa_operation_unref(op);
351  else
352  {
353  VBERROR(fn_log_tag +
354  QString("set stream volume operation failed, stream %1, "
355  "error %2 ")
356  .arg(stream_index)
357  .arg(pa_strerror(pa_context_errno(m_pcontext))));
358  }
359  }
360  else
361  {
362  uint32_t sink_index = pa_stream_get_device_index(m_pstream);
363  pa_threaded_mainloop_lock(m_mainloop);
364  pa_operation *op =
365  pa_context_set_sink_volume_by_index(m_pcontext, sink_index,
367  OpCompletionCallback, this);
368  pa_threaded_mainloop_unlock(m_mainloop);
369  if (op)
370  pa_operation_unref(op);
371  else
372  {
373  VBERROR(fn_log_tag +
374  QString("set sink volume operation failed, sink %1, "
375  "error %2 ")
376  .arg(sink_index)
377  .arg(pa_strerror(pa_context_errno(m_pcontext))));
378  }
379  }
380 }
381 
383 {
385  pa_threaded_mainloop_lock(m_mainloop);
386  pa_operation *op = pa_stream_drain(m_pstream, nullptr, this);
387  pa_threaded_mainloop_unlock(m_mainloop);
388 
389  if (op)
390  pa_operation_unref(op);
391  else
392  VBERROR("Drain, stream drain failed");
393 }
394 
396 {
397  QString fn_log_tag = "ContextConnect, ";
398  if (m_pcontext)
399  {
400  VBERROR(fn_log_tag + "context appears to exist, but shouldn't (yet)");
401  pa_context_unref(m_pcontext);
402  m_pcontext = nullptr;
403  return false;
404  }
405  pa_proplist *proplist = pa_proplist_new();
406  if (!proplist)
407  {
408  VBERROR(fn_log_tag + QString("failed to create new proplist"));
409  return false;
410  }
411  pa_proplist_sets(proplist, PA_PROP_APPLICATION_NAME, "MythTV");
412  pa_proplist_sets(proplist, PA_PROP_APPLICATION_ICON_NAME, "mythtv");
413  pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video");
414  m_pcontext =
415  pa_context_new_with_proplist(pa_threaded_mainloop_get_api(m_mainloop),
416  "MythTV", proplist);
417  if (!m_pcontext)
418  {
419  VBERROR(fn_log_tag + "failed to acquire new context");
420  return false;
421  }
422  pa_context_set_state_callback(m_pcontext, ContextStateCallback, this);
423 
424  QString pulse_host = ChooseHost();
425  int chk = pa_context_connect(m_pcontext,
426  !pulse_host.isEmpty() ? qPrintable(pulse_host) : nullptr,
427  (pa_context_flags_t)0, nullptr);
428 
429  if (chk < 0)
430  {
431  VBERROR(fn_log_tag + QString("context connect failed: %1")
432  .arg(pa_strerror(pa_context_errno(m_pcontext))));
433  return false;
434  }
435  bool connected = false;
436  pa_context_state_t state = pa_context_get_state(m_pcontext);
437  for (; !connected; state = pa_context_get_state(m_pcontext))
438  {
439  switch(state)
440  {
441  case PA_CONTEXT_READY:
442  VBAUDIO(fn_log_tag +"context connection ready");
443  connected = true;
444  continue;
445 
446  case PA_CONTEXT_FAILED:
447  case PA_CONTEXT_TERMINATED:
448  VBERROR(fn_log_tag +
449  QString("context connection failed or terminated: %1")
450  .arg(pa_strerror(pa_context_errno(m_pcontext))));
451  return false;
452 
453  default:
454  VBAUDIO(fn_log_tag + "waiting for context connection ready");
455  pa_threaded_mainloop_wait(m_mainloop);
456  break;
457  }
458  }
459 
460  pa_operation *op =
461  pa_context_get_server_info(m_pcontext, ServerInfoCallback, this);
462 
463  if (op)
464  pa_operation_unref(op);
465  else
466  VBERROR(fn_log_tag + "failed to get PulseAudio server info");
467 
468  return true;
469 }
470 
472 {
473  QString fn_log_tag = "ChooseHost, ";
474  QStringList parts = m_mainDevice.split(':');
475  QString host = parts.size() > 1 ? parts[1] : QString();
476  QString pulse_host;
477 
478  if (host != "default")
479  pulse_host = host;
480 
481  if (pulse_host.isEmpty() && host != "default")
482  {
483 #if QT_VERSION < QT_VERSION_CHECK(5,10,0)
484  QString env_pulse_host = qgetenv("PULSE_SERVER");
485 #else
486  QString env_pulse_host = qEnvironmentVariable("PULSE_SERVER");
487 #endif
488  if (!env_pulse_host.isEmpty())
489  pulse_host = env_pulse_host;
490  }
491 
492  VBAUDIO(fn_log_tag + QString("chosen PulseAudio server: %1")
493  .arg((pulse_host != nullptr) ? pulse_host : "default"));
494 
495  return pulse_host;
496 }
497 
499 {
500  QString fn_log_tag = "ConnectPlaybackStream, ";
501  pa_proplist *proplist = pa_proplist_new();
502  if (!proplist)
503  {
504  VBERROR(fn_log_tag + QString("failed to create new proplist"));
505  return false;
506  }
507  pa_proplist_sets(proplist, PA_PROP_MEDIA_ROLE, "video");
508  m_pstream =
509  pa_stream_new_with_proplist(m_pcontext, "MythTV playback", &m_sampleSpec,
510  &m_channelMap, proplist);
511  if (!m_pstream)
512  {
513  VBERROR("failed to create new playback stream");
514  return false;
515  }
516  pa_stream_set_state_callback(m_pstream, StreamStateCallback, this);
517  pa_stream_set_write_callback(m_pstream, WriteCallback, this);
518  pa_stream_set_overflow_callback(m_pstream, BufferFlowCallback, (char*)"over");
519  pa_stream_set_underflow_callback(m_pstream, BufferFlowCallback,
520  (char*)"under");
521  if (m_setInitialVol)
522  {
523  int volume = gCoreContext->GetNumSetting("MasterMixerVolume", 80);
524  pa_cvolume_set(&m_volumeControl, m_channels,
525  (float)volume * (float)PA_VOLUME_NORM / 100.0F);
526  }
527  else
528  pa_cvolume_reset(&m_volumeControl, m_channels);
529 
531 
532  m_bufferSettings.maxlength = UINT32_MAX;
533  m_bufferSettings.tlength = m_fragmentSize * 4;
534  m_bufferSettings.prebuf = UINT32_MAX;
535  m_bufferSettings.minreq = UINT32_MAX;
536  m_bufferSettings.fragsize = UINT32_MAX;
537 
538  int flags = PA_STREAM_INTERPOLATE_TIMING
539  | PA_STREAM_ADJUST_LATENCY
540  | PA_STREAM_AUTO_TIMING_UPDATE
541  | PA_STREAM_NO_REMIX_CHANNELS;
542 
543  pa_stream_connect_playback(m_pstream, nullptr, &m_bufferSettings,
544  (pa_stream_flags_t)flags, nullptr, nullptr);
545 
546  pa_stream_state_t sstate = PA_STREAM_UNCONNECTED;
547  bool connected = false;
548  bool failed = false;
549 
550  while (!(connected || failed))
551  {
552  pa_context_state_t cstate = pa_context_get_state(m_pcontext);
553  switch (cstate)
554  {
555  case PA_CONTEXT_FAILED:
556  case PA_CONTEXT_TERMINATED:
557  VBERROR(QString("context is stuffed, %1")
558  .arg(pa_strerror(pa_context_errno(m_pcontext))));
559  failed = true;
560  break;
561  default:
562  switch (sstate = pa_stream_get_state(m_pstream))
563  {
564  case PA_STREAM_READY:
565  connected = true;
566  break;
567  case PA_STREAM_FAILED:
568  case PA_STREAM_TERMINATED:
569  VBERROR(QString("stream failed or was terminated, "
570  "context state %1, stream state %2")
571  .arg(cstate).arg(sstate));
572  failed = true;
573  break;
574  default:
575  pa_threaded_mainloop_wait(m_mainloop);
576  break;
577  }
578  }
579  }
580 
581  const pa_buffer_attr *buf_attr = pa_stream_get_buffer_attr(m_pstream);
582  m_fragmentSize = buf_attr->tlength >> 2;
583  m_soundcardBufferSize = buf_attr->maxlength;
584 
585  VBAUDIO(QString("fragment size %1, soundcard buffer size %2")
587 
588  return (connected && !failed);
589 }
590 
591 void AudioOutputPulseAudio::FlushStream(const char *caller)
592 {
593  QString fn_log_tag = QString("FlushStream (%1), ").arg(caller);
594  pa_threaded_mainloop_lock(m_mainloop);
595  pa_operation *op = pa_stream_flush(m_pstream, nullptr, this);
596  pa_threaded_mainloop_unlock(m_mainloop);
597  if (op)
598  pa_operation_unref(op);
599  else
600  VBERROR(fn_log_tag + "stream flush operation failed ");
601 }
602 
604 {
605  auto *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
606  switch (pa_context_get_state(c))
607  {
608  case PA_CONTEXT_READY:
609  case PA_CONTEXT_TERMINATED:
610  case PA_CONTEXT_FAILED:
611  pa_threaded_mainloop_signal(audoutP->m_mainloop, 0);
612  break;
613  case PA_CONTEXT_CONNECTING:
614  case PA_CONTEXT_UNCONNECTED:
615  case PA_CONTEXT_AUTHORIZING:
616  case PA_CONTEXT_SETTING_NAME:
617  break;
618  }
619 }
620 
622 {
623  auto *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
624  switch (pa_stream_get_state(s))
625  {
626  case PA_STREAM_READY:
627  case PA_STREAM_TERMINATED:
628  case PA_STREAM_FAILED:
629  pa_threaded_mainloop_signal(audoutP->m_mainloop, 0);
630  break;
631  case PA_STREAM_UNCONNECTED:
632  case PA_STREAM_CREATING:
633  break;
634  }
635 }
636 
637 void AudioOutputPulseAudio::WriteCallback(pa_stream */*s*/, size_t /*size*/, void *arg)
638 {
639  auto *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
640  pa_threaded_mainloop_signal(audoutP->m_mainloop, 0);
641 }
642 
643 void AudioOutputPulseAudio::BufferFlowCallback(pa_stream */*s*/, void *tag)
644 {
645  VBERROR(QString("stream buffer %1 flow").arg((char*)tag));
646 }
647 
649  pa_context *c, int ok, void *arg)
650 {
651  QString fn_log_tag = "OpCompletionCallback, ";
652  auto *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
653  if (!ok)
654  {
655  VBERROR(fn_log_tag + QString("bummer, an operation failed: %1")
656  .arg(pa_strerror(pa_context_errno(c))));
657  }
658  pa_threaded_mainloop_signal(audoutP->m_mainloop, 0);
659 }
660 
662  pa_context */*context*/, const pa_server_info *inf, void */*arg*/)
663 {
664  QString fn_log_tag = "ServerInfoCallback, ";
665 
666  VBAUDIO(fn_log_tag +
667  QString("PulseAudio server info - host name: %1, server version: "
668  "%2, server name: %3, default sink: %4")
669  .arg(inf->host_name).arg(inf->server_version)
670  .arg(inf->server_name).arg(inf->default_sink_name));
671 }
672 
674  pa_context */*c*/, const pa_sink_info *info, int /*eol*/, void *arg)
675 {
676  auto *audoutP = static_cast<AudioOutputPulseAudio*>(arg);
677 
678  if (!info)
679  {
680  pa_threaded_mainloop_signal(audoutP->m_mainloop, 0);
681  return;
682  }
683 
684  audoutP->m_aoSettings->AddSupportedRate(info->sample_spec.rate);
685 
686  for (uint i = 2; i <= info->sample_spec.channels; i++)
687  audoutP->m_aoSettings->AddSupportedChannels(i);
688 
689  pa_threaded_mainloop_signal(audoutP->m_mainloop, 0);
690 }
691 
692 /* vim: set expandtab tabstop=4 shiftwidth=4: */
FORMAT_U8
@ FORMAT_U8
Definition: audiooutputsettings.h:27
AudioOutputPulseAudio::AudioOutputPulseAudio
AudioOutputPulseAudio(const AudioSettings &settings)
Definition: audiooutputpulse.cpp:33
FORMAT_NONE
@ FORMAT_NONE
Definition: audiooutputsettings.h:26
AudioOutputPulseAudio::SinkInfoCallback
static void SinkInfoCallback(pa_context *c, const pa_sink_info *info, int eol, void *arg)
Definition: audiooutputpulse.cpp:673
channel
QDomElement channel
Definition: mythplugins/mytharchive/mytharchivehelper/main.cpp:501
AudioOutputPulseAudio::m_volumeControl
pa_cvolume m_volumeControl
Definition: audiooutputpulse.h:66
VBAUDIO
#define VBAUDIO(str)
Definition: audiooutputbase.h:19
FORMAT_S16
@ FORMAT_S16
Definition: audiooutputsettings.h:28
AudioOutputSettings::AddSupportedFormat
void AddSupportedFormat(AudioFormat format)
Definition: audiooutputsettings.cpp:126
AudioOutputPulseAudio::m_channelMap
pa_channel_map m_channelMap
Definition: audiooutputpulse.h:65
AudioOutputPulseAudio::BufferFlowCallback
static void BufferFlowCallback(pa_stream *s, void *tag)
Definition: audiooutputpulse.cpp:643
AudioOutputPulseAudio::WriteCallback
static void WriteCallback(pa_stream *s, size_t size, void *arg)
Definition: audiooutputpulse.cpp:637
AudioOutputPulseAudio::m_pstream
pa_stream * m_pstream
Definition: audiooutputpulse.h:62
AudioOutputBase::KillAudio
void KillAudio(void)
Kill the output thread and cleanup.
Definition: audiooutputbase.cpp:857
mythburn.write
def write(text, progress=True)
Definition: mythburn.py:308
arg
arg(title).arg(filename).arg(doDelete))
VBERROR
#define VBERROR(str)
Definition: audiooutputbase.h:22
AudioOutputPulseAudio::m_sampleSpec
pa_sample_spec m_sampleSpec
Definition: audiooutputpulse.h:64
AudioOutputPulseAudio::ChooseHost
QString ChooseHost(void)
Definition: audiooutputpulse.cpp:471
AudioOutputPulseAudio::GetVolumeChannel
int GetVolumeChannel(int channel) const override
Definition: audiooutputpulse.cpp:315
AudioOutputPulseAudio
Definition: audiooutputpulse.h:28
AudioOutputBase::m_sampleRate
int m_sampleRate
Definition: audiooutputbase.h:177
AudioOutputPulseAudio::FlushStream
void FlushStream(const char *caller)
Definition: audiooutputpulse.cpp:591
AudioOutputPulseAudio::m_pcontext
pa_context * m_pcontext
Definition: audiooutputpulse.h:61
AudioOutputPulseAudio::GetOutputSettings
AudioOutputSettings * GetOutputSettings(bool digital) override
Definition: audiooutputpulse.cpp:55
AudioOutputPulseAudio::StreamStateCallback
static void StreamStateCallback(pa_stream *s, void *arg)
Definition: audiooutputpulse.cpp:621
AudioOutputPulseAudio::OpCompletionCallback
static void OpCompletionCallback(pa_context *c, int ok, void *arg)
Definition: audiooutputpulse.cpp:648
AudioSettings
Definition: audiosettings.h:29
AudioOutputBase::m_mainDevice
QString m_mainDevice
Definition: audiooutputbase.h:182
AudioOutputBase::Drain
void Drain(void) override
Block until all available frames have been written to the device.
Definition: audiooutputbase.cpp:1816
AudioOutputBase::m_soundcardBufferSize
long m_soundcardBufferSize
Definition: audiooutputbase.h:180
AudioOutputBase
Definition: audiooutputbase.h:50
AudioOutputPulseAudio::SetVolumeChannel
void SetVolumeChannel(int channel, int volume) override
Definition: audiooutputpulse.cpp:321
AudioSettings::m_init
bool m_init
If set to false, AudioOutput instance will not try to initially open the audio device.
Definition: audiosettings.h:85
AudioOutputPulseAudio::m_aoSettings
AudioOutputSettings * m_aoSettings
Definition: audiooutputpulse.h:68
AudioOutputPulseAudio::ConnectPlaybackStream
bool ConnectPlaybackStream(void)
Definition: audiooutputpulse.cpp:498
AudioOutputPulseAudio::ServerInfoCallback
static void ServerInfoCallback(pa_context *context, const pa_server_info *inf, void *arg)
Definition: audiooutputpulse.cpp:661
FORMAT_S24
@ FORMAT_S24
Definition: audiooutputsettings.h:30
FORMAT_FLT
@ FORMAT_FLT
Definition: audiooutputsettings.h:32
AudioOutputBase::Reconfigure
void Reconfigure(const AudioSettings &settings) override
(Re)Configure AudioOutputBase
Definition: audiooutputbase.cpp:465
AudioOutputPulseAudio::m_bufferSettings
pa_buffer_attr m_bufferSettings
Definition: audiooutputpulse.h:67
AudioOutputPulseAudio::OpenDevice
bool OpenDevice(void) override
Definition: audiooutputpulse.cpp:122
FORMAT_S32
@ FORMAT_S32
Definition: audiooutputsettings.h:31
AudioOutputPulseAudio::ContextConnect
bool ContextConnect(void)
Definition: audiooutputpulse.cpp:395
uint
unsigned int uint
Definition: compat.h:141
AudioOutputPulseAudio::GetBufferedOnSoundcard
int GetBufferedOnSoundcard(void) const override
Return the size in bytes of frames currently in the audio buffer adjusted with the audio playback lat...
Definition: audiooutputpulse.cpp:282
gCoreContext
MythCoreContext * gCoreContext
This global variable contains the MythCoreContext instance for the app.
Definition: mythcorecontext.cpp:60
MythCoreContext::GetNumSetting
int GetNumSetting(const QString &key, int defaultval=0)
Definition: mythcorecontext.cpp:933
AudioOutputBase::m_outputFormat
AudioFormat m_outputFormat
Definition: audiooutputbase.h:176
AudioOutputBase::m_channels
int m_channels
Definition: audiooutputbase.h:171
AudioOutputBase::m_setInitialVol
bool m_setInitialVol
Definition: audiooutputbase.h:200
AudioOutputPulseAudio::WriteAudio
void WriteAudio(unsigned char *aubuf, int size) override
Definition: audiooutputpulse.cpp:222
PULSE_MAX_CHANNELS
#define PULSE_MAX_CHANNELS
Definition: audiooutputpulse.cpp:31
audiooutputpulse.h
AudioOutputPulseAudio::CloseDevice
void CloseDevice(void) override
Definition: audiooutputpulse.cpp:193
AudioOutputBase::m_outputBytesPerFrame
int m_outputBytesPerFrame
Definition: audiooutputbase.h:174
AudioOutputSettings
Definition: audiooutputsettings.h:50
AudioOutputBase::InitSettings
void InitSettings(const AudioSettings &settings)
Definition: audiooutputbase.cpp:117
AudioOutputPulseAudio::ContextStateCallback
static void ContextStateCallback(pa_context *c, void *arg)
Definition: audiooutputpulse.cpp:603
AudioOutputPulseAudio::~AudioOutputPulseAudio
~AudioOutputPulseAudio() override
Definition: audiooutputpulse.cpp:45
AudioOutputPulseAudio::Drain
void Drain(void) override
Block until all available frames have been written to the device.
Definition: audiooutputpulse.cpp:382
AudioOutputBase::m_fragmentSize
int m_fragmentSize
Definition: audiooutputbase.h:179
AudioFormat
AudioFormat
Definition: audiooutputsettings.h:25
AudioOutputPulseAudio::m_mainloop
pa_threaded_mainloop * m_mainloop
Definition: audiooutputpulse.h:63
AudioOutputSettings::GetNextFormat
AudioFormat GetNextFormat()
Definition: audiooutputsettings.cpp:115
FORMAT_S24LSB
@ FORMAT_S24LSB
Definition: audiooutputsettings.h:29
MythCoreContext::GetSetting
QString GetSetting(const QString &key, const QString &defaultval="")
Definition: mythcorecontext.cpp:919
VBAUDIOTS
#define VBAUDIOTS(str)
Definition: audiooutputbase.h:20