34#include <alsa/asoundlib.h>
37#include <libavcodec/avcodec.h>
38#include <libavfilter/avfilter.h>
39#include <libavfilter/buffersink.h>
40#include <libavfilter/buffersrc.h>
41#include <libavutil/channel_layout.h>
42#include <libavutil/opt.h>
45#include <vdr/thread.h>
62 : cThread(
"softhd audio"),
64 m_pConfig(m_pDevice->Config()),
65 m_pEventReceiver(device),
66 m_downmix(m_pConfig->ConfigAudioDownmix),
67 m_softVolume(m_pConfig->ConfigAudioSoftvol),
68 m_passthrough(m_pConfig->ConfigAudioPassthroughState ? m_pConfig->ConfigAudioPassthroughMask : 0),
69 m_pPCMDevice(m_pConfig->ConfigAudioPCMDevice),
70 m_pPassthroughDevice(m_pConfig->ConfigAudioPassthroughDevice),
71 m_appendAES(m_pConfig->ConfigAudioAutoAES),
72 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
106 for (i = 0; i < size; i += 5) {
117 for (i = 0; i < size; i += 6) {
132 for (i = 0; i < size; i += 8) {
170 for (i = 0; i < n; ++i) {
189 factor = ((INT16_MAX / 8) * 1000U) / (uint32_t) sqrt(avg);
201 LOGDEBUG2(
L_SOUND,
"audio: %s: avg %8d, fac=%6.3f, norm=%6.3f", __FUNCTION__,
220 }
else if (t > INT16_MAX) {
252 factor = (INT16_MAX * 1000) / maxSample;
265 LOGDEBUG2(
L_SOUND,
"audio: %s: max %5d, fac=%6.3f, com=%6.3f", __FUNCTION__, maxSample,
275 }
else if (t > INT16_MAX) {
296 memset(samples, 0, count);
306 }
else if (t > INT16_MAX) {
328 for (i = 0; i < 18; i++) {
403 const AVFilter *abuffer;
404 AVFilterContext *pfilterCtx[3];
406 const AVFilter *aformat;
407 const AVFilter *abuffersink;
408 char channelLayout[64];
409 char optionsStr[1024];
410 int err, i, numFilter = 0;
417 err =
AlsaSetup(audioCtx->ch_layout.nb_channels, audioCtx->sample_rate, 0);
424#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
425 avfilter_register_all();
429 LOGERROR(
"audio: %s: Unable to create filter graph.", __FUNCTION__);
434 if (!(abuffer = avfilter_get_by_name(
"abuffer"))) {
435 LOGWARNING(
"audio: %s: Could not find the abuffer filter.", __FUNCTION__);
440 LOGWARNING(
"audio: %s: Could not allocate the m_pBuffersrcCtx instance.", __FUNCTION__);
445 av_channel_layout_describe(&audioCtx->ch_layout, channelLayout,
sizeof(channelLayout));
447 LOGDEBUG2(
L_SOUND,
"audio: %s: IN channelLayout %s sample_fmt %s sample_rate %d channels %d", __FUNCTION__,
448 channelLayout, av_get_sample_fmt_name(audioCtx->sample_fmt), audioCtx->sample_rate, audioCtx->ch_layout.nb_channels);
450 av_opt_set (
m_pBuffersrcCtx,
"channel_layout", channelLayout, AV_OPT_SEARCH_CHILDREN);
451 av_opt_set (
m_pBuffersrcCtx,
"sample_fmt", av_get_sample_fmt_name(audioCtx->sample_fmt), AV_OPT_SEARCH_CHILDREN);
452 av_opt_set_q (
m_pBuffersrcCtx,
"time_base", (AVRational){ 1, audioCtx->sample_rate }, AV_OPT_SEARCH_CHILDREN);
453 av_opt_set_int(
m_pBuffersrcCtx,
"sample_rate", audioCtx->sample_rate, AV_OPT_SEARCH_CHILDREN);
458 LOGWARNING(
"audio: %s: Could not initialize the abuffer filter.", __FUNCTION__);
465 if (!(eq = avfilter_get_by_name(
"superequalizer"))) {
466 LOGWARNING(
"audio: %s: Could not find the superequalizer filter.", __FUNCTION__);
470 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(
m_pFilterGraph, eq,
"superequalizer"))) {
471 LOGWARNING(
"audio: %s: Could not allocate the superequalizer instance.", __FUNCTION__);
475 snprintf(optionsStr,
sizeof(optionsStr),
"1b=%.2f:2b=%.2f:3b=%.2f:4b=%.2f:5b=%.2f"
476 ":6b=%.2f:7b=%.2f:8b=%.2f:9b=%.2f:10b=%.2f:11b=%.2f:12b=%.2f:13b=%.2f:14b=%.2f:"
482 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
483 LOGWARNING(
"audio: %s: Could not initialize the superequalizer filter.", __FUNCTION__);
491 AVChannelLayout channel_layout;
493 av_channel_layout_describe(&channel_layout, channelLayout,
sizeof(channelLayout));
494 av_channel_layout_uninit(&channel_layout);
496 LOGDEBUG2(
L_SOUND,
"audio: %s: OUT m_downmix %d m_hwNumChannels %d m_hwSampleRate %d channelLayout %s bytes_per_sample %d",
498 if (!(aformat = avfilter_get_by_name(
"aformat"))) {
499 LOGWARNING(
"audio: %s: Could not find the aformat filter.", __FUNCTION__);
503 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(
m_pFilterGraph, aformat,
"aformat"))) {
504 LOGWARNING(
"audio: %s: Could not allocate the aformat instance.", __FUNCTION__);
508 snprintf(optionsStr,
sizeof(optionsStr),
509 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
510 av_get_sample_fmt_name(AV_SAMPLE_FMT_S16),
m_hwSampleRate, channelLayout);
511 if (avfilter_init_str(pfilterCtx[numFilter], optionsStr) < 0) {
512 LOGWARNING(
"audio: %s: Could not initialize the aformat filter.", __FUNCTION__);
519 if (!(abuffersink = avfilter_get_by_name(
"abuffersink"))) {
520 LOGWARNING(
"audio: %s: Could not find the abuffersink filter.", __FUNCTION__);
524 if (!(pfilterCtx[numFilter] = avfilter_graph_alloc_filter(
m_pFilterGraph, abuffersink,
"sink"))) {
525 LOGWARNING(
"audio: %s: Could not allocate the abuffersink instance.", __FUNCTION__);
529 if (avfilter_init_str(pfilterCtx[numFilter], NULL) < 0) {
530 LOGWARNING(
"audio: %s: Could not initialize the abuffersink instance.", __FUNCTION__);
537 for (i = 0; i < numFilter; i++) {
541 err = avfilter_link(pfilterCtx[i - 1], 0, pfilterCtx[i], 0);
545 LOGWARNING(
"audio: %s: Error connecting audio filters", __FUNCTION__);
552 LOGWARNING(
"audio: %s: Error configuring the audio filter graph", __FUNCTION__);
578 std::lock_guard<std::mutex> lock(
m_mutex);
590 LOGDEBUG2(
L_AV_SYNC,
"audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
615 int byteCount = frame->nb_samples * frame->ch_layout.nb_channels *
m_bytesPerSample;
616 buffer = (uint16_t *)frame->data[0];
626 Enqueue((uint16_t *)buffer, byteCount, frame);
628 av_frame_free(&frame);
679 std::lock_guard<std::mutex> lock(
m_mutex);
688 count = std::max(0, count - oneFrameBytes);
697 if (bytesWritten != count)
698 LOGERROR(
"audio: %s: can't place %d samples in ring buffer", __FUNCTION__, count);
705 LOGDEBUG2(
L_AV_SYNC,
"audio: %s: discontinuity detected in audio PTS %s -> %s%s", __FUNCTION__,
740 err =
AlsaSetup(channels, samplerate, passthrough);
742 LOGERROR(
"audio: %s: failed!", __FUNCTION__);
754 AVFrame *outframe =
nullptr;
755 outframe = av_frame_alloc();
757 LOGERROR(
"audio: %s: Error allocating frame", __FUNCTION__);
763 if (err == AVERROR(EAGAIN)) {
765 av_frame_free(&outframe);
766 }
else if (err == AVERROR_EOF) {
767 LOGERROR(
"audio: %s: Error filtering AVERROR_EOF", __FUNCTION__);
768 av_frame_free(&outframe);
769 }
else if (err < 0) {
770 LOGERROR(
"audio: %s: Error filtering the data", __FUNCTION__);
771 av_frame_free(&outframe);
820 AVFrame *outframe = NULL;
827 av_frame_unref(inframe);
835 av_strerror(err, errbuf,
sizeof(errbuf));
836 LOGERROR(
"audio: %s: Error submitting the frame to the filter fmt %s channels %d %s", __FUNCTION__,
837 av_get_sample_fmt_name(ctx->sample_fmt), ctx->ch_layout.nb_channels, errbuf);
838 av_frame_unref(inframe);
863 std::lock_guard<std::mutex> lock(
m_mutex);
904 std::lock_guard<std::mutex> lock(
m_mutex);
925 std::lock_guard<std::mutex> lock(
m_mutex);
930 snd_pcm_sframes_t delayFrames;
947 std::lock_guard<std::mutex> lock(
m_mutex);
952 snd_pcm_sframes_t delayFrames;
986 }
else if (volume > 1000) {
1118 LOGERROR(
"audio: %s: Cannot recover: %s", __FUNCTION__, snd_strerror(error));
1147 if (state == SND_PCM_STATE_OPEN)
1150 LOGDEBUG2(
L_SOUND,
"audio: %s entered in pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1155 case SND_PCM_STATE_SETUP:
1156 case SND_PCM_STATE_XRUN:
1157 case SND_PCM_STATE_DRAINING:
1160 LOGERROR(
"audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1168 LOGERROR(
"audio: %s: snd_pcm_drop(): %s", __FUNCTION__, snd_strerror(err));
1171 LOGERROR(
"audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1188 LOGDEBUG2(
L_SOUND,
"audio: %s left in pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1206 if (scheduleImmediately)
1222 LOGDEBUG(
"audio: stopping thread");
1251 }
else if (ret == 0) {
1253 LOGERROR(
"audio: %s: snd_pcm_wait() timeout (state %s)", __FUNCTION__, snd_pcm_state_name(state));
1254 if (state == SND_PCM_STATE_PREPARED) {
1262 std::lock_guard<std::mutex> lock2(
m_mutex);
1266 if (freeAlsaBufferFrames < 0) {
1267 if (freeAlsaBufferFrames == -EAGAIN) {
1277 size_t freeAlsaBufferBytes = snd_pcm_frames_to_bytes(
m_pAlsaPCMHandle, freeAlsaBufferFrames);
1301 int freeAlsaBufferBytes = snd_pcm_frames_to_bytes(
m_pAlsaPCMHandle, freeAlsaBufferFrames);
1307 bytesToWrite = std::min(freeAlsaBufferBytes, (
int)ringBufferFillLevelBytes);
1309 if (bytesToWrite == 0)
1319 int framesToWrite = snd_pcm_bytes_to_frames(
m_pAlsaPCMHandle, bytesToWrite);
1322 framesWritten = snd_pcm_mmap_writei(
m_pAlsaPCMHandle, data, framesToWrite);
1328 int bytesWritten = snd_pcm_frames_to_bytes(
m_pAlsaPCMHandle, framesWritten);
1331 if (framesWritten < 0) {
1332 if (framesWritten == -EAGAIN) {
1337 LOGWARNING(
"audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1340 LOGERROR(
"audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1345 if (framesWritten != framesToWrite) {
1346 LOGWARNING(
"audio: %s: not all frames written", __FUNCTION__);
1368 if (framesWritten != framesToWrite) {
1369 if (framesWritten < 0) {
1370 if (framesWritten == -EAGAIN)
1373 LOGWARNING(
"audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1376 LOGERROR(
"audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1380 LOGWARNING(
"audio: %s: not all frames written", __FUNCTION__);
1400 snd_pcm_sframes_t delayFrames = 0;
1414 std::lock_guard<std::mutex> lock(
m_mutex);
1416 LOGDEBUG2(
L_SOUND,
"audio: %s: reset hw delay baseline to 0", __FUNCTION__);
1448 LOGDEBUG2(
L_SOUND,
"audio: %s: try opening %sdevice '%s'", __FUNCTION__, passthrough ?
"pass-through " :
"", device);
1451 if (!(strchr(device,
':'))) {
1452 sprintf(tmp,
"%s:AES0=%d,AES1=%d,AES2=0,AES3=%d",
1454 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1455 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1456 IEC958_AES3_CON_FS_48000);
1458 sprintf(tmp,
"%s,AES0=%d,AES1=%d,AES2=0,AES3=%d",
1460 IEC958_AES0_NONAUDIO | IEC958_AES0_PRO_EMPHASIS_NONE,
1461 IEC958_AES1_CON_ORIGINAL | IEC958_AES1_CON_PCM_CODER,
1462 IEC958_AES3_CON_FS_48000);
1464 LOGDEBUG2(
L_SOUND,
"audio: %s: auto append AES: %s -> %s", __FUNCTION__, device, tmp);
1466 sprintf(tmp,
"%s", device);
1471 SND_PCM_NONBLOCK)) < 0) {
1473 LOGWARNING(
"audio: %s: could not open device '%s' error: %s", __FUNCTION__, device, snd_strerror(err));
1477 LOGDEBUG2(
L_SOUND,
"audio: %s: opened %sdevice '%s'", __FUNCTION__, passthrough ?
"pass-through " :
"", device);
1479 return (
char *)device;
1499 err = snd_device_name_hint(-1, devname, (
void ***)&hints);
1501 LOGWARNING(
"audio: %s: Cannot get device names for %s!", __FUNCTION__, hint);
1506 while (*n != NULL) {
1507 name = snd_device_name_get_hint(*n,
"NAME");
1509 if (name && strstr(name, hint)) {
1511 snd_device_name_free_hint((
void **)hints);
1521 snd_device_name_free_hint((
void **)hints);
1530 char *device = NULL;
1531 bool freeDevice =
false;
1552 freeDevice = (device != NULL);
1557 LOGDEBUG2(
L_SOUND,
"audio: %s: Try default:CARD=hdmisound devices...", __FUNCTION__);
1559 freeDevice = (device != NULL);
1566 freeDevice = (device != NULL);
1582 LOGFATAL(
"audio: %s: could not open any device, abort!", __FUNCTION__);
1584 if (!strcmp(device,
"null"))
1585 LOGWARNING(
"audio: %s: using %sdevice '%s'", __FUNCTION__,
1588 LOGINFO(
"audio: using %sdevice '%s'",
1596 LOGERROR(
"audio: %s: can't set block mode: %s", __FUNCTION__, snd_strerror(err));
1610 const char *channel;
1611 snd_mixer_t *alsaMixer;
1612 snd_mixer_elem_t *alsaMixerElem;
1613 long alsaMixerElemMin;
1614 long alsaMixerElemMax;
1617 if (!(device = getenv(
"ALSA_MIXER"))) {
1622 if (!(channel = getenv(
"ALSA_MIXER_CHANNEL"))) {
1626 LOGDEBUG2(
L_SOUND,
"audio: %s: mixer %s - %s open", __FUNCTION__, device, channel);
1627 snd_mixer_open(&alsaMixer, 0);
1628 if (alsaMixer && snd_mixer_attach(alsaMixer, device) >= 0
1629 && snd_mixer_selem_register(alsaMixer, NULL, NULL) >= 0
1630 && snd_mixer_load(alsaMixer) >= 0) {
1632 const char *
const alsaMixerElem_name = channel;
1634 alsaMixerElem = snd_mixer_first_elem(alsaMixer);
1635 while (alsaMixerElem) {
1638 name = snd_mixer_selem_get_name(alsaMixerElem);
1639 if (!strcasecmp(name, alsaMixerElem_name)) {
1640 snd_mixer_selem_get_playback_volume_range(alsaMixerElem, &alsaMixerElemMin, &alsaMixerElemMax);
1641 m_alsaRatio = 1000 * (alsaMixerElemMax - alsaMixerElemMin);
1642 LOGDEBUG2(
L_SOUND,
"audio: %s: %s mixer found %ld - %ld ratio %d", __FUNCTION__, channel, alsaMixerElemMin, alsaMixerElemMax,
m_alsaRatio);
1646 alsaMixerElem = snd_mixer_elem_next(alsaMixerElem);
1652 LOGERROR(
"audio: %s: can't open mixer '%s'", __FUNCTION__, device);
1666 snd_mixer_selem_set_playback_volume(
m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_LEFT, v);
1667 snd_mixer_selem_set_playback_volume(
m_pAlsaMixerElem, SND_MIXER_SCHN_FRONT_RIGHT, v);
1685 snd_pcm_hw_params_t *hwparams;
1687 unsigned bufferTimeUs = 100'000;
1696 snd_pcm_hw_params_alloca(&hwparams);
1698 LOGERROR(
"audio: %s: Read HW config failed! %s", __FUNCTION__, snd_strerror(err));
1702 if (!snd_pcm_hw_params_test_access(
m_pAlsaPCMHandle, hwparams, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
1708 LOGERROR(
"audio: %s: SampleRate %d not supported! %s", __FUNCTION__, sample_rate, snd_strerror(err));
1723 if ((err = snd_pcm_hw_params_set_buffer_time_near(
m_pAlsaPCMHandle, hwparams, &bufferTimeUs, NULL)) < 0) {
1724 LOGWARNING(
"audio: %s: bufferTime %d not supported! %s", __FUNCTION__, bufferTimeUs, snd_strerror(err));
1727 snd_pcm_uframes_t periodSize = 0;
1728 if ((err = snd_pcm_hw_params_get_period_size_max(hwparams, &periodSize, NULL)) < 0) {
1729 LOGWARNING(
"audio: %s: getting max periodSize not supported! %s", __FUNCTION__, snd_strerror(err));
1732 snd_pcm_uframes_t bufferSize = 0;
1733 if ((err = snd_pcm_hw_params_get_buffer_size_max(hwparams, &bufferSize)) < 0) {
1734 LOGWARNING(
"audio: %s: getting max bufferSize not supported! %s", __FUNCTION__, snd_strerror(err));
1749 LOGERROR(
"audio: %s: set params error: %s\n"
1750 " Channels %d SampleRate %d\n"
1751 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1753 " AlsaBufferTime %dms pcm state: %s\n"
1754 " periodSize %d frames, bufferSize %d frames",
1759 bufferTimeUs / 1000, snd_pcm_state_name(state),
1760 periodSize, bufferSize);
1766 " Channels %d SampleRate %d%s\n"
1767 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1769 " AlsaBufferTime %dms, pcm state: %s\n"
1770 " periodSize %d frames, bufferSize %d frames",
1772 channels, sample_rate, passthrough ?
" -> passthrough" :
"",
1774 snd_pcm_format_name(SND_PCM_FORMAT_S16),
1776 bufferTimeUs / 1000, snd_pcm_state_name(state),
1777 periodSize, bufferSize);
1792 const char *fmt, ...)
1844 auto now = std::chrono::steady_clock::now();
1853 LOGDEBUG2(
L_SOUND,
"audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
1866 if (hardwareBufferFillLevelFrames < 0)
1867 LOGWARNING(
"audio: %s: snd_pcm_avail() failes: %s", __FUNCTION__, snd_strerror(hardwareBufferFillLevelFrames));
Audio and Alsa Interface Header File.
virtual void OnEventReceived(const Event &)=0
void ReceivedFrames(int count)
void WroteFrames(int count)
double GetBufferFillLevelFramesAvg()
void SetTargetValue(double value)
int m_alsaBufferSizeFrames
alsa buffer size in frames
bool m_appendAES
flag ato utomatic append AES
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
double FramesToMsDouble(int frames)
int m_compressionMaxFactor
max. compression factor
const char * m_pPCMDevice
PCM device name.
int m_volume
current volume (0 .. 1000)
std::mutex m_pauseMutex
mutex for a safe thread pausing
AVFilterContext * m_pBuffersinkCtx
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
AVFilterContext * m_pBuffersrcCtx
AVFilterGraph * m_pFilterGraph
int m_passthrough
passthrough mask
const int m_bytesPerSample
number of bytes per sample
const char * m_pMixerChannel
mixer channel name
unsigned int m_hwSampleRate
hardware sample rate in Hz
int64_t PtsToMs(int64_t pts)
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
IEventReceiver * m_pEventReceiver
pointer to event receiver
int MsToFrames(int milliseconds)
std::vector< Event > m_eventQueue
event queue for incoming events
AVRational * m_pTimebase
pointer to AVCodecContext pkts_timebase
bool m_compression
flag to use compress volume
bool m_normalize
flag to use volume normalize
int m_filterChanged
filter has changed
snd_mixer_elem_t * m_pAlsaMixerElem
alsa mixer element
int64_t m_inputPts
pts clock (last pts in ringbuffer)
int m_normalizeFactor
current normalize factor
cSoftHdConfig * m_pConfig
pointer to config
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
int m_amplifier
software volume amplify factor
static constexpr int NORMALIZE_MAX_INDEX
number of normalize average samples
const char * m_pPassthroughDevice
passthrough device name
int m_normalizeMaxFactor
max. normalize factor
bool m_alsaUseMmap
use mmap
int m_compressionFactor
current compression factor
int64_t MsToPts(int64_t ptsMs)
int m_spdifBurstSize
size of the current spdif burst
const int m_normalizeMinFactor
min. normalize factor
const int m_normalizeSamples
number of normalize samples
int m_filterReady
filter is ready
int m_normalizeReady
index normalize counter
const char * m_pMixerDevice
mixer device name (not used)
uint32_t m_normalizeAverage[NORMALIZE_MAX_INDEX]
average of n last normalize sample blocks
unsigned int m_hwNumChannels
number of hardware channels
bool m_initialized
class initialized
int FramesToMs(int frames)
int m_stereoDescent
volume descent for stereo
std::mutex m_mutex
mutex for thread safety
static constexpr int AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
int m_alsaRatio
internal -> mixer ratio * 1000
int m_packetCounter
packet counter for logging
snd_pcm_t * m_pAlsaPCMHandle
alsa pcm handle
int m_downmix
set stereo downmix
int m_useEqualizer
flag to use equalizer
float m_equalizerBand[18]
equalizer band
snd_mixer_t * m_pAlsaMixer
alsa mixer handle
snd_pcm_sframes_t m_hwBaseline
saves the hw delay (pause bursts) once a real audio frame to correctly do the AV-Sync
int m_normalizeIndex
index into normalize average table
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
int m_normalizeCounter
normalize sample counter
std::atomic< bool > m_paused
audio is paused
bool m_firstRealAudioReceived
false, as long as no real audio was sent - used to trigger the baseline set
std::vector< uint16_t > m_pauseBurst
holds the burst data itself
bool m_softVolume
flag to use soft volume
bool ConfigAudioNormalize
config use normalize volume
int ConfigAudioStereoDescent
config reduce stereo loudness
bool ConfigAudioCompression
config use volume compression
int ConfigAudioEqBand[18]
config equalizer filter bands
int ConfigAudioMaxCompression
config max volume compression
int ConfigAudioEq
config equalizer filter
int ConfigAudioMaxNormalize
config max normalize factor
Output Device Implementation.
Audio Decoder Header File.
Plugin Configuration Header File.
State Machine and Event Header File.
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent > Event
Low-pass Filter for Audio Buffer Fill Level Measurement Header File.
void LazyInit(void)
Initialize audio output module (alsa)
cSoftHdAudio(cSoftHdDevice *)
Create a new audio context.
char * OpenAlsaDevice(const char *, int)
Opens an alsa device.
bool SendPause(void)
Write pause to passthrough device.
char * FindAlsaDevice(const char *, const char *, int)
Find alsa device giving some search hints.
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
virtual void Action(void)
Audio thread loop, started with Start().
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
int AlsaSetup(int channels, int sample_rate, int passthrough)
Setup alsa audio for requested format.
void Reset()
Reset the internal state (integral sum and error history).
int Setup(AVCodecContext *, int, int, int)
Alsa setup wrapper.
void Enqueue(uint16_t *, int, AVFrame *)
Send audio data to ringbuffer.
void SetPassthroughMask(int)
Set audio passthrough mask.
void SetHwDelayBaseline(void)
Set the hw delay baseline.
void SetStereoDescent(int)
Set stereo loudness descent.
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
static void ReorderAudioFrame(uint16_t *buf, int size, int channels)
Reorder audio frame.
void EnqueueSpdif(uint16_t *, int, AVFrame *)
Enqueue prepared spdif bursts in audio output queue.
void SetVolume(int)
Set mixer volume (0-1000)
void HandleError(int)
Handle an alsa error.
void FlushAlsaBuffersInternal(bool)
Flush alsa buffers internally.
void ProcessEvents(void)
Process queued events and forward them to event receiver.
bool SendAudio(int)
Write regular audio data from the ringbuffer to the hardware.
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
size_t UsedBytes(void)
Get used bytes in ring buffer.
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
void ClockDriftCompensation(void)
Calculate clock drift compensation.
void AlsaExit(void)
Cleanup the alsa audio output module.
void SetCompression(bool, int)
Set volume compression parameters.
void Stop(void)
Stop the thread.
void BuildPauseBurst(void)
Build a pause spdif burst with the size of the last recognized normal spdif audio.
void AlsaInit(void)
Initialize the alsa audio output module.
void Compress(uint16_t *, int)
Compress audio samples.
double Update(double, double)
Calculate the new output value.
void AlsaSetVolume(int)
Set alsa mixer volume (0-1000)
int64_t GetOutputPtsMsInternal(void)
void Exit(void)
Cleanup audio output module (alsa)
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
void SetPaused(bool)
Set audio playback pause state.
void AlsaInitMixer(void)
Initialize alsa mixer.
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
static void AlsaNoopCallback(__attribute__((unused)) const char *file, __attribute__((unused)) int line, __attribute__((unused)) const char *function, __attribute__((unused)) int err, __attribute__((unused)) const char *fmt,...)
Empty log callback.
void Normalize(uint16_t *, int)
Normalize audio samples.
size_t ReadAdvance(size_t)
Advance read pointer in ring buffer.
void SetEq(int[18], int)
Set equalizer bands.
void DropAlsaBuffers(void)
Drop alsa buffers.
bool CyclicCall(void)
Cyclic audio playback call.
size_t GetReadPointer(const void **)
Get read pointer and used bytes at this position of ring buffer.
void AlsaInitPCMDevice(void)
Search for an alsa pcm device and open it.
size_t Write(const void *, size_t)
Write to a ring buffer.
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
void SoftAmplify(int16_t *, int)
Amplify the samples in software.
void FlushAlsaBuffers(void)
Flush alsa buffers.
void SetNormalize(bool, int)
Set normalize volume parameters.
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
int InitFilter(AVCodecContext *)
Init audio filters.
void Reset(void)
Reset ring buffer pointers.
void FlushBuffers(void)
Flush audio buffers.
__attribute__((weak)) union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
void ResetFramesCounters()
Resets the received and written frames counters.
void UpdateAvgBufferFillLevel(int)
Updates the buffer fill level average.
#define LOGDEBUG
log to LOG_DEBUG
#define LOGERROR
log to LOG_ERR
#define LOGWARNING
log to LOG_WARN
#define LOGINFO
log to LOG_INFO
#define LOGFATAL
log to LOG_ERR and abort
void Reset()
Resets the filter state.
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Nice time-stamp string.
@ L_AV_SYNC
audio/video sync logs
PID (proportional, integral, derivative) Controller Header File.
Audio Ringbuffer Header File.
Output Device Header File.