vdr-plugin-softhddevice-drm-gles 1.6.4-d0291bb
audio.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
17#include <chrono>
18#include <cmath>
19#include <cstdint>
20#include <mutex>
21#include <string>
22#include <sstream>
23#include <vector>
24
25#include <alsa/asoundlib.h>
26
27extern "C" {
28#include <libavcodec/avcodec.h>
29#include <libavfilter/avfilter.h>
30#include <libavfilter/buffersink.h>
31#include <libavfilter/buffersrc.h>
32#include <libavutil/channel_layout.h>
33#include <libavutil/opt.h>
34}
35
36#include <vdr/thread.h>
37
38#include "audio.h"
39#include "codec_audio.h"
40#include "config.h"
41#include "event.h"
42#include "filllevel.h"
43#include "logger.h"
44#include "misc.h"
45#include "pidcontroller.h"
46#include "ringbuffer.h"
47#include "softhddevice.h"
48
53 : cThread("softhd audio"),
54 m_pDevice(device),
55 m_pConfig(m_pDevice->Config()),
56 m_pEventReceiver(device),
57 m_downmix(m_pConfig->ConfigAudioDownmix),
58 m_softVolume(m_pConfig->ConfigAudioSoftvol),
59 m_passthrough(m_pConfig->ConfigAudioPassthroughState ? m_pConfig->ConfigAudioPassthroughMask : 0),
60 m_pPCMDevice(m_pConfig->ConfigAudioPCMDevice),
61 m_pPassthroughDevice(m_pConfig->ConfigAudioPassthroughDevice),
62 m_appendAES(m_pConfig->ConfigAudioAutoAES),
63 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
64{
69}
70
71/******************************************************************************
72 * Audio filter and manipulation
73 *****************************************************************************/
74
81void cSoftHdAudio::Normalize(uint16_t *samples, int count)
82{
83 int i;
84 int l;
85 int n;
86 uint32_t avg;
87 int factor;
88 uint16_t *data;
89
90 // average samples
91 l = count / m_bytesPerSample;
92 data = samples;
93 do {
94 n = l;
97 }
99 for (i = 0; i < n; ++i) {
100 int t;
101
102 t = data[i];
103 avg += (t * t) / m_normalizeSamples;
104 }
110 } else {
111 avg = 0;
112 for (i = 0; i < NORMALIZE_MAX_INDEX; ++i) {
114 }
115
116 // calculate normalize factor
117 if (avg > 0) {
118 factor = ((INT16_MAX / 8) * 1000U) / (uint32_t) sqrt(avg);
119 // smooth normalize
120 m_normalizeFactor = (m_normalizeFactor * 500 + factor * 500) / 1000;
123 }
126 }
127 } else {
128 factor = 1000;
129 }
130 LOGDEBUG2(L_SOUND, "audio: %s: avg %8d, fac=%6.3f, norm=%6.3f", __FUNCTION__,
131 avg, factor / 1000.0, m_normalizeFactor / 1000.0);
132 }
133
137 }
138 data += n;
139 l -= n;
140 } while (l > 0);
141
142 // apply normalize factor
143 for (i = 0; i < count / m_bytesPerSample; ++i) {
144 int t;
145
146 t = (samples[i] * m_normalizeFactor) / 1000;
147 if (t < INT16_MIN) {
148 t = INT16_MIN;
149 } else if (t > INT16_MAX) {
150 t = INT16_MAX;
151 }
152 samples[i] = t;
153 }
154}
155
162void cSoftHdAudio::Compress(uint16_t *samples, int count)
163{
164 int maxSample;
165 int i;
166 int factor;
167
168 // find loudest sample
169 maxSample = 0;
170 for (i = 0; i < count / m_bytesPerSample; ++i) {
171 int t;
172
173 t = abs(samples[i]);
174 if (t > maxSample) {
175 maxSample = t;
176 }
177 }
178
179 // calculate compression factor
180 if (maxSample > 0) {
181 factor = (INT16_MAX * 1000) / maxSample;
182 // smooth compression (FIXME: make configurable?)
183 m_compressionFactor = (m_compressionFactor * 950 + factor * 50) / 1000;
184 if (m_compressionFactor > factor) {
185 m_compressionFactor = factor; // no clipping
186 }
189 }
190 } else {
191 return; // silent nothing todo
192 }
193
194 LOGDEBUG2(L_SOUND, "audio: %s: max %5d, fac=%6.3f, com=%6.3f", __FUNCTION__, maxSample,
195 factor / 1000.0, m_compressionFactor / 1000.0);
196
197 // apply compression factor
198 for (i = 0; i < count / m_bytesPerSample; ++i) {
199 int t;
200
201 t = (samples[i] * m_compressionFactor) / 1000;
202 if (t < INT16_MIN) {
203 t = INT16_MIN;
204 } else if (t > INT16_MAX) {
205 t = INT16_MAX;
206 }
207 samples[i] = t;
208 }
209}
210
219void cSoftHdAudio::SoftAmplify(int16_t *samples, int count)
220{
221 int i;
222
223 // silence
224 if (m_volume == 0 || !m_amplifier) {
225 memset(samples, 0, count);
226 return;
227 }
228
229 for (i = 0; i < count / m_bytesPerSample; ++i) {
230 int t;
231
232 t = (samples[i] * m_amplifier) / 1000;
233 if (t < INT16_MIN) {
234 t = INT16_MIN;
235 } else if (t > INT16_MAX) {
236 t = INT16_MAX;
237 }
238 samples[i] = t;
239 }
240}
241
248void cSoftHdAudio::SetEq(int band[18], int onoff)
249{
250 int i;
251/*
252 LOGDEBUG2(L_SOUND, "audio: %s: %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i %i onoff %d", __FUNCTION__,
253 band[0], band[1], band[2], band[3], band[4], band[5], band[6], band[7],
254 band[8], band[9], band[10], band[11], band[12], band[13], band[14],
255 band[15], band[16], band[17], onoff);
256*/
257 for (i = 0; i < 18; i++) {
258 switch (band[i]) {
259 case 1:
260 m_equalizerBand[i] = 1.5;
261 break;
262 case 0:
263 m_equalizerBand[i] = 1;
264 break;
265 case -1:
266 m_equalizerBand[i] = 0.95;
267 break;
268 case -2:
269 m_equalizerBand[i] = 0.9;
270 break;
271 case -3:
272 m_equalizerBand[i] = 0.85;
273 break;
274 case -4:
275 m_equalizerBand[i] = 0.8;
276 break;
277 case -5:
278 m_equalizerBand[i] = 0.75;
279 break;
280 case -6:
281 m_equalizerBand[i] = 0.7;
282 break;
283 case -7:
284 m_equalizerBand[i] = 0.65;
285 break;
286 case -8:
287 m_equalizerBand[i] = 0.6;
288 break;
289 case -9:
290 m_equalizerBand[i] = 0.55;
291 break;
292 case -10:
293 m_equalizerBand[i] = 0.5;
294 break;
295 case -11:
296 m_equalizerBand[i] = 0.45;
297 break;
298 case -12:
299 m_equalizerBand[i] = 0.4;
300 break;
301 case -13:
302 m_equalizerBand[i] = 0.35;
303 break;
304 case -14:
305 m_equalizerBand[i] = 0.3;
306 break;
307 case -15:
308 m_equalizerBand[i] = 0.25;
309 break;
310 }
311 }
312
313 m_filterChanged = 1;
314 m_useEqualizer = onoff;
315}
316
325static const char *alsaToFFmpegChannel(const char *alsaName)
326{
327 if (!strcmp(alsaName, "RL")) return "BL";
328 if (!strcmp(alsaName, "RR")) return "BR";
329
330 return alsaName;
331}
332
342static std::vector<std::string> GetAlsaChannelLayoutAsArray(snd_pcm_t *pcmHandle)
343{
344 std::vector<std::string> layout;
346 if (!map)
347 return layout;
348
349 for (unsigned int i = 0; i < map->channels; i++) {
350 const char *name = alsaToFFmpegChannel(snd_pcm_chmap_name(static_cast<snd_pcm_chmap_position>(map->pos[i])));
351 if (!name)
352 continue;
353 layout.push_back(std::string(name));
354 }
355 free(map);
356 return layout;
357}
358
368static std::vector<std::string> GetFFmpegChannelLayoutAsArray(const AVChannelLayout &layout)
369{
370 std::vector<std::string> names;
371 char buf[16];
372
373 for (int i = 0; i < layout.nb_channels; i++) {
375 int ret = av_channel_name(buf, sizeof(buf), ch);
376 if (ret < 0)
377 continue;
378 names.push_back(std::string(buf));
379 }
380 return names;
381}
382
393static bool LayoutsMatch(const std::vector<std::string> &ff, const std::vector<std::string> &alsa)
394{
395 if (ff.size() != alsa.size())
396 return false;
397
398 for (size_t i = 0; i < ff.size(); i++) {
399 if (ff[i] != alsa[i])
400 return false;
401 }
402
403 return true;
404}
405
417{
420
421 if (ff.size() != alsa.size()) {
422 LOGWARNING("audio: %s: FFmpeg and Alsa channel count differs: FFmpeg %zu ALSA %zu", __FUNCTION__, ff.size(), alsa.size());
423 return "";
424 }
425
426 std::string ffString;
427 for (size_t i = 0; i < ff.size(); i++) {
428 ffString += ff[i];
429 if (i < ff.size() - 1)
430 ffString += " ";
431 }
432
433 std::string alsaString;
434 for (size_t i = 0; i < alsa.size(); i++) {
435 alsaString += alsa[i];
436 if (i < alsa.size() - 1)
437 alsaString += " ";
438 }
439
440 if (LayoutsMatch(ff, alsa)) {
441 LOGDEBUG2(L_SOUND, "audio: %s: FFmpeg and Alsa channel layouts match: %s", __FUNCTION__, ffString.c_str());
442 return "";
443 }
444
445 std::stringstream ss;
446 for (size_t i = 0; i < ff.size(); i++) {
447 if (i != 0)
448 ss << "|";
449 ss << ff[i] << "-" << alsa[i];
450 }
451
452 LOGDEBUG2(L_SOUND, "audio: %s: FFmpeg Channel Layout: %s", __FUNCTION__, ffString.c_str());
453 LOGDEBUG2(L_SOUND, "audio: %s: Alsa Channel Layout : %s", __FUNCTION__, alsaString.c_str());
454 LOGDEBUG2(L_SOUND, "audio: %s: Layouts don't match, map FFmpeg to Alsa: %s", __FUNCTION__, ss.str().c_str());
455
456 return ss.str();
457}
458
474{
475 const AVFilter *abuffer;
477 const AVFilter *channelmap;
478 const AVFilter *eq;
479 const AVFilter *aformat;
480 const AVFilter *abuffersink;
481 char channelLayout[64];
482 char optionsStr[1024];
483 int err, i, numFilter = 0;
484
485 // Before filter init set HW parameter.
486 if (audioCtx->sample_rate != (int)m_hwSampleRate ||
487 (audioCtx->ch_layout.nb_channels != (int)m_hwNumChannels &&
488 !(m_downmix && m_hwNumChannels == 2))) {
489
490 err = AlsaSetup(audioCtx->ch_layout.nb_channels, audioCtx->sample_rate, 0);
491 if (err)
492 return err;
493 }
494
495 m_pTimebase = &audioCtx->pkt_timebase;
496
497#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
499#endif
500
502 LOGERROR("audio: %s: Unable to create filter graph.", __FUNCTION__);
503 return -1;
504 }
505
506 // input buffer
507 if (!(abuffer = avfilter_get_by_name("abuffer"))) {
508 LOGWARNING("audio: %s: Could not find the abuffer filter.", __FUNCTION__);
510 return -1;
511 }
513 LOGWARNING("audio: %s: Could not allocate the m_pBuffersrcCtx instance.", __FUNCTION__);
515 return -1;
516 }
517
519
520 LOGDEBUG2(L_SOUND, "audio: %s: IN channelLayout %s sample_fmt %s sample_rate %d channels %d", __FUNCTION__,
521 channelLayout, av_get_sample_fmt_name(audioCtx->sample_fmt), audioCtx->sample_rate, audioCtx->ch_layout.nb_channels);
522
525 av_opt_set_q (m_pBuffersrcCtx, "time_base", (AVRational){ 1, audioCtx->sample_rate }, AV_OPT_SEARCH_CHILDREN);
526 av_opt_set_int(m_pBuffersrcCtx, "sample_rate", audioCtx->sample_rate, AV_OPT_SEARCH_CHILDREN);
527// av_opt_set_int(m_pBuffersrcCtx, "channel_counts", audioCtx->channels, AV_OPT_SEARCH_CHILDREN);
528
529 // initialize the filter with NULL options, set all options above.
531 LOGWARNING("audio: %s: Could not initialize the abuffer filter.", __FUNCTION__);
533 return -1;
534 }
535
536 // channelmap
537 //
538 // Map FFmpeg channel layout to Alsa channel layout.
539 // Depending on the hardware, e.g. FC and LFE have to be swapped.
540 // This is the case for HDMI on RPI4, so we need to do the following:
541 // FL-FL|FR-FR|FC-LFE|LFE-FC|BL-BL|BR-BR
542 //
543 // The channel mapping is skipped, if
544 // - a stereo downmix is forced (downmix will be done later in aformat filter)
545 // - channel count differs, aformat will handle downmix later
546 if (!(m_downmix && m_hwNumChannels == 2)) {
547 std::string channelMapString;
549
550 if (!channelMapString.empty()) {
551 if (!(channelmap = avfilter_get_by_name("channelmap"))) {
552 LOGWARNING("audio: %s: Could not find the channelmap filter.", __FUNCTION__);
553 return -1;
554 }
556 LOGWARNING("audio: %s: Could not allocate the channelmap instance.", __FUNCTION__);
557 return -1;
558 }
559 snprintf(optionsStr, sizeof(optionsStr),"map=%s", channelMapString.c_str());
561 LOGWARNING("audio: %s: Could not initialize the channelmap filter \"%s\"", __FUNCTION__, optionsStr);
563 return -1;
564 }
565 numFilter++;
566 }
567 }
568
569 // superequalizer
570 if (m_useEqualizer) {
571 if (!(eq = avfilter_get_by_name("superequalizer"))) {
572 LOGWARNING("audio: %s: Could not find the superequalizer filter.", __FUNCTION__);
574 return -1;
575 }
576 if (!(pFilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, eq, "superequalizer"))) {
577 LOGWARNING("audio: %s: Could not allocate the superequalizer instance.", __FUNCTION__);
579 return -1;
580 }
581 snprintf(optionsStr, sizeof(optionsStr),"1b=%.2f:2b=%.2f:3b=%.2f:4b=%.2f:5b=%.2f"
582 ":6b=%.2f:7b=%.2f:8b=%.2f:9b=%.2f:10b=%.2f:11b=%.2f:12b=%.2f:13b=%.2f:14b=%.2f:"
583 "15b=%.2f:16b=%.2f:17b=%.2f:18b=%.2f ", m_equalizerBand[0], m_equalizerBand[1],
589 LOGWARNING("audio: %s: Could not initialize the superequalizer filter.", __FUNCTION__);
591 return -1;
592 }
593 numFilter++;
594 }
595
596 // aformat
598 if (m_downmix && m_hwNumChannels == 2) {
599 // explicit stereo downmix
601 } else {
602 if (av_channel_layout_copy(&channel_layout, &audioCtx->ch_layout) < 0) {
603 LOGWARNING("audio: %s: Could not copy channel layout", __FUNCTION__);
604 return -1;
605 }
606
607 // clamp channels if the hardware doesn't support them
608 if (channel_layout.nb_channels > (int)m_hwNumChannels) {
609 LOGDEBUG2(L_SOUND, "audio: %s: clamp channels from %d -> %d", __FUNCTION__, channel_layout.nb_channels, m_hwNumChannels);
612 }
613 }
616
617 LOGDEBUG2(L_SOUND, "audio: %s: OUT m_downmix %d m_hwNumChannels %d m_hwSampleRate %d channelLayout %s bytes_per_sample %d",
619
620 if (!(aformat = avfilter_get_by_name("aformat"))) {
621 LOGWARNING("audio: %s: Could not find the aformat filter.", __FUNCTION__);
623 return -1;
624 }
626 LOGWARNING("audio: %s: Could not allocate the aformat instance.", __FUNCTION__);
628 return -1;
629 }
631 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
634 LOGWARNING("audio: %s: Could not initialize the aformat filter.", __FUNCTION__);
636 return -1;
637 }
638 numFilter++;
639
640 // abuffersink
641 if (!(abuffersink = avfilter_get_by_name("abuffersink"))) {
642 LOGWARNING("audio: %s: Could not find the abuffersink filter.", __FUNCTION__);
644 return -1;
645 }
647 LOGWARNING("audio: %s: Could not allocate the abuffersink instance.", __FUNCTION__);
649 return -1;
650 }
652 LOGWARNING("audio: %s: Could not initialize the abuffersink instance.", __FUNCTION__);
654 return -1;
655 }
656 numFilter++;
657
658 // Connect the filters
659 for (i = 0; i < numFilter; i++) {
660 if (i == 0) {
662 } else {
663 err = avfilter_link(pFilterCtx[i - 1], 0, pFilterCtx[i], 0);
664 }
665 }
666 if (err < 0) {
667 LOGWARNING("audio: %s: Error connecting audio filters", __FUNCTION__);
669 return -1;
670 }
671
672 // Configure the graph.
674 LOGWARNING("audio: %s: Error configuring the audio filter graph", __FUNCTION__);
676 return -1;
677 }
678
680 m_filterChanged = 0;
681 m_filterReady = 1;
682
683 return 0;
684}
685
686/******************************************************************************
687 * Audio stream handling
688 *****************************************************************************/
689
699{
700 std::lock_guard<std::mutex> lock(m_mutex);
701
702 if (!HasInputPts())
703 return;
704
708
709 dropBytes = std::min(dropBytes, (int)m_pRingbuffer.UsedBytes());
710
711 if (dropBytes > 0) {
712 LOGDEBUG2(L_AV_SYNC, "audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
714 dropMs,
717
722 }
723}
724
731{
732 if (!frame)
733 return;
734
735 uint16_t *buffer;
736
737 int byteCount = frame->nb_samples * frame->ch_layout.nb_channels * m_bytesPerSample;
738 buffer = (uint16_t *)frame->data[0];
739
740 if (m_compression) // in place operation
741 Compress(buffer, byteCount);
742
743 if (m_normalize) // in place operation
744 Normalize(buffer, byteCount);
745
746 Enqueue((uint16_t *)buffer, byteCount, frame);
747
748 av_frame_free(&frame);
749}
750
755{
756 uint16_t *spdif = m_pauseBurst.data();
757
761 spdif[3] = 0;
762
763 memset(m_pauseBurst.data() + 4, 0, m_spdifBurstSize - 8);
764}
765
776{
777 std::lock_guard<std::mutex> lock(m_pauseMutex);
778
779 if (count != m_spdifBurstSize) {
780 LOGDEBUG2(L_SOUND, "audio: %s: spdif burst size changed %d -> %d, rebuild pause burst", __FUNCTION__, m_spdifBurstSize, count);
782 m_pauseBurst.resize(m_spdifBurstSize / 2);
783
785 }
786
787 Enqueue(buffer, count, frame);
788}
789
797void cSoftHdAudio::Enqueue(uint16_t *buffer, int count, AVFrame *frame)
798{
799 std::lock_guard<std::mutex> lock(m_mutex);
800
801 // pitch adjustment
802 if (m_pitchAdjustFrameCounter == 0 && std::abs(m_pitchPpm) > 1) { // only adjust if pitch has a significant value to prevent overly large values/division by zero
804
805 if (m_pitchPpm < 0 && m_pRingbuffer.Write((const uint16_t *)buffer, oneFrameBytes)) // insert additional frame
807 else if (m_pitchPpm > 0) // drop frame
808 count = std::max(0, count - oneFrameBytes);
809
810 m_pitchAdjustFrameCounter = std::round(1'000'000.0 / std::abs(m_pitchPpm));
811 }
812
814
815 // write to ringbuffer
816 int bytesWritten = m_pRingbuffer.Write((const uint16_t *)buffer, count);
817 if (bytesWritten != count)
818 LOGERROR("audio: %s: can't place %d samples in ring buffer", __FUNCTION__, count);
819
821
822 if (frame->pts != AV_NOPTS_VALUE) {
823 // discontinuity check, force a resync if the new pts differs more than AV_SYNC_BORDER_MS to the last
824 if (m_inputPts != AV_NOPTS_VALUE && std::abs(PtsToMs(m_inputPts) - PtsToMs(frame->pts)) > AV_SYNC_BORDER_MS) {
825 LOGDEBUG2(L_AV_SYNC, "audio: %s: discontinuity detected in audio PTS %s -> %s%s", __FUNCTION__,
827 PtsToMs(m_inputPts) > PtsToMs(frame->pts) ? " (PTS wrapped)" : "");
828 m_eventQueue.push_back(ScheduleResyncAtPtsMsEvent{PtsToMs(frame->pts)});
829 }
830
831 m_inputPts = frame->pts;
832 }
833}
834
850{
851 int err = 0;
852
853 m_pTimebase = &ctx->pkt_timebase;
854
855 // skip setup, nothing changed
856 if (samplerate == (int)m_hwSampleRate &&
857 (channels == (int)m_hwNumChannels || (m_downmix && m_hwNumChannels == 2)))
858 return 1;
859
861 if (err)
862 LOGERROR("audio: %s: failed!", __FUNCTION__);
863
864 return err;
865}
866
873{
874 AVFrame *outframe = nullptr;
876 if (!outframe) {
877 LOGERROR("audio: %s: Error allocating frame", __FUNCTION__);
878 return NULL;
879 }
880
882
883 if (err == AVERROR(EAGAIN)) {
884// LOGERROR("audio: %s: Error filtering AVERROR(EAGAIN)", __FUNCTION__);
886 } else if (err == AVERROR_EOF) {
887 LOGERROR("audio: %s: Error filtering AVERROR_EOF", __FUNCTION__);
889 } else if (err < 0) {
890 LOGERROR("audio: %s: Error filtering the data", __FUNCTION__);
892 }
893
894 return outframe;
895}
896
906{
908// LOGDEBUG2(L_SOUND, "audio: %s: m_filterReady %d sink_links_count %d channels %d nb_filters %d nb_outputs %d channels %d m_filterChanged %d",
909// __FUNCTION__, m_filterReady,
910// m_pFilterGraph->sink_links_count, m_pFilterGraph->sink_links[0]->channels,
911// m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->nb_outputs,
912// m_pFilterGraph->nb_filters, m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->outputs[m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->nb_outputs - 1]->channels,
913// m_filterChanged);
915 m_filterReady = 0;
916 LOGDEBUG2(L_SOUND, "audio: %s: Free the filter graph.", __FUNCTION__);
917 }
918
919 if (!m_filterReady) {
920 if (InitFilter(ctx)) {
921 LOGDEBUG2(L_SOUND, "audio: %s: AudioFilterReady failed!", __FUNCTION__);
922 return 1;
923 }
924 }
925
926 return 0;
927}
928
939{
941 int err = -1;
942 int err_count = 0;
943
944 if (inframe) {
945 while (err < 0) {
948 return;
949 }
950
952 if (err < 0) {
953 if (err_count) {
954 char errbuf[128];
955 av_strerror(err, errbuf, sizeof(errbuf));
956 LOGERROR("audio: %s: Error submitting the frame to the filter fmt %s channels %d %s", __FUNCTION__,
957 av_get_sample_fmt_name(ctx->sample_fmt), ctx->ch_layout.nb_channels, errbuf);
959 return;
960 } else {
961 m_filterChanged = 1;
962 err_count++;
963 LOGDEBUG2(L_SOUND, "audio: %s: m_filterChanged %d err_count %d", __FUNCTION__, m_filterChanged, err_count);
964 }
965 }
966 }
967 }
968
969// if (!inframe)
970// LOGDEBUG2(L_SOUND, "audio: %s: NO inframe!", __FUNCTION__);
971
974}
975
982{
983 std::lock_guard<std::mutex> lock(m_mutex);
984
985 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
986
987 if (!m_initialized)
988 return;
989
992
998 m_filterChanged = 1;
999}
1000
1005{
1006 // FIXME: not correct, if multiple buffer are in use
1007 return m_pRingbuffer.UsedBytes();
1008}
1009
1023{
1024 std::lock_guard<std::mutex> lock(m_mutex);
1025
1026 return GetOutputPtsMsInternal();
1027}
1028
1033
1044{
1045 std::lock_guard<std::mutex> lock(m_mutex);
1046
1048 return AV_NOPTS_VALUE;
1049
1052 delayFrames = 0L;
1053
1054 // subtract baseline to ignore pause bursts already in the buffer
1056
1058}
1059
1066{
1067 std::lock_guard<std::mutex> lock(m_mutex);
1068
1070 return AV_NOPTS_VALUE;
1071
1074 delayFrames = 0L;
1075
1076 return FramesToMs(delayFrames);
1077}
1078
1092
1099{
1100 m_volume = volume;
1101 // reduce loudness for stereo output
1104 if (volume < 0) {
1105 volume = 0;
1106 } else if (volume > 1000) {
1107 volume = 1000;
1108 }
1109 }
1111 if (!m_softVolume) {
1113 }
1114}
1115
1122{
1123 std::lock_guard<std::mutex> lock(m_pauseMutex);
1124 LOGDEBUG2(L_SOUND, "audio: %s: %d", __FUNCTION__, pause);
1125
1126 m_paused = pause;
1127}
1128
1140
1159
1166{
1168 SetVolume(m_volume); // update channel delta
1169}
1170
1177{
1179
1180 // Reset m_pitchPpm if we change the passthrough handling, otherwise
1181 // we may continue to adjust the pitch in Enqueue() when in passthrough
1182 if (m_passthrough)
1183 m_pitchPpm = 0;
1184}
1185
1196{
1197 if (!m_initialized) {
1198 AlsaInit();
1199 m_initialized = true;
1200 }
1201}
1202
1211{
1212 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
1213
1214 Stop();
1215
1216 if (!m_initialized)
1217 return;
1218
1220 AlsaExit();
1221 m_initialized = false;
1222}
1223
1224/******************************************************************************
1225 * A L S A
1226 *****************************************************************************/
1227
1232{
1233
1236
1238 LOGERROR("audio: %s: Cannot recover: %s", __FUNCTION__, snd_strerror(error));
1239
1241}
1242
1250
1258
1265{
1267 if (state == SND_PCM_STATE_OPEN)
1268 return;
1269
1270 LOGDEBUG2(L_SOUND, "audio: %s entered in pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1271
1272 int err;
1273 if (m_passthrough && !drop) {
1274 switch (state) {
1276 case SND_PCM_STATE_XRUN:
1279 if (err < 0)
1280 LOGERROR("audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1281 break;
1282 default:
1283 break;
1284 }
1285 } else {
1287 if (err < 0)
1288 LOGERROR("audio: %s: snd_pcm_drop(): %s", __FUNCTION__, snd_strerror(err));
1290 if (err < 0)
1291 LOGERROR("audio: %s: snd_pcm_prepare(): %s", __FUNCTION__, snd_strerror(err));
1292 }
1293
1294 // reset audio processing values
1295 m_compressionFactor = 2000;
1298
1300 m_normalizeReady = 0;
1301
1302 for (int i = 0; i < NORMALIZE_MAX_INDEX; ++i)
1304
1305 m_normalizeFactor = 1000;
1306
1308 LOGDEBUG2(L_SOUND, "audio: %s left in pcm state %s", __FUNCTION__, snd_pcm_state_name(state));
1309}
1310
1311/******************************************************************************
1312 * Thread playback
1313 *****************************************************************************/
1314
1320{
1321 LOGDEBUG("audio: thread started");
1322 while (Running()) {
1324 ProcessEvents();
1325
1327 usleep(1000);
1328 else
1329 usleep(10000);
1330 }
1331 LOGDEBUG("audio: thread stopped");
1332}
1333
1338{
1339 if (!Active())
1340 return;
1341
1342 LOGDEBUG("audio: stopping thread");
1343 Cancel(2);
1344}
1345
1358{
1359 std::lock_guard<std::mutex> lock1(m_pauseMutex);
1360
1361 // do nothing in paused PCM mode
1362 if (m_paused && !m_passthrough)
1363 return false;
1364
1365 // check, if the alsa device is ready for input
1366 int ret = snd_pcm_wait(m_pAlsaPCMHandle, 150);
1367 if (ret < 0) {
1368 LOGDEBUG2(L_SOUND, "audio: %s: Handle error in wait", __FUNCTION__);
1370 return false;
1371 } else if (ret == 0) {
1373 LOGERROR("audio: %s: snd_pcm_wait() timeout (state %s)", __FUNCTION__, snd_pcm_state_name(state));
1374 if (state == SND_PCM_STATE_PREPARED) {
1375 LOGDEBUG2(L_SOUND, "audio: %s: force start", __FUNCTION__);
1377 return true;
1378 }
1379 return false;
1380 }
1381
1382 std::lock_guard<std::mutex> lock2(m_mutex);
1383
1384 // query available space in alsa buffer
1386 if (freeAlsaBufferFrames < 0) {
1387 if (freeAlsaBufferFrames == -EAGAIN) {
1388 LOGDEBUG2(L_SOUND, "audio: %s: -EAGAIN", __FUNCTION__);
1389 return true;
1390 }
1391
1392 LOGDEBUG2(L_SOUND, "audio: %s: Handle error in avail", __FUNCTION__);
1394 return false;
1395 }
1396
1398 if (m_passthrough && m_paused) {
1399 // only write, if there is space for a full pause burst
1401 return false;
1402
1403 // send a pause burst to keep the audio stream locked
1404 return SendPause();
1405 }
1406
1408}
1409
1419{
1420 int bytesToWrite;
1422
1423 // query ringbuffer fill level
1424 const void *data;
1426
1428
1429 if (bytesToWrite == 0)
1430 return false;
1431
1432 // muting pass-through AC-3, can produce disturbance
1433 if (m_volume == 0 || (m_softVolume && !m_passthrough)) {
1434 // FIXME: quick&dirty cast
1435 SoftAmplify((int16_t *) data, bytesToWrite);
1436 // FIXME: if not all are written, we double amplify them
1437 }
1438
1440 int framesWritten;
1441 if (m_alsaUseMmap)
1443 else
1445
1447
1450
1451 if (framesWritten < 0) {
1452 if (framesWritten == -EAGAIN) {
1453 LOGDEBUG2(L_SOUND, "audio: %s: -EAGAIN", __FUNCTION__);
1454 return true;
1455 }
1456
1457 LOGWARNING("audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1458
1460 LOGERROR("audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1461
1462 return false;
1463 }
1464
1466 LOGWARNING("audio: %s: not all frames written", __FUNCTION__);
1467 return false;
1468 }
1469
1470 return true;
1471}
1472
1479{
1481
1482 int framesWritten;
1483 if (m_alsaUseMmap)
1485 else
1487
1489 if (framesWritten < 0) {
1490 if (framesWritten == -EAGAIN)
1491 return true;
1492
1493 LOGWARNING("audio: %s: writei failed: %s", __FUNCTION__, snd_strerror(framesWritten));
1494
1496 LOGERROR("audio: %s: failed to recover from writei: %s", __FUNCTION__, snd_strerror(framesWritten));
1497
1498 return false;
1499 } else {
1500 LOGWARNING("audio: %s: not all frames written", __FUNCTION__);
1501 return false;
1502 }
1503 }
1504
1505// LOGDEBUG2(L_SOUND, "audio: %s: %d frames (%dms) written", __FUNCTION__, framesWritten, FramesToMs(framesWritten));
1506 return true;
1507}
1508
1513{
1515 m_hwBaseline = 0;
1516
1517 if (!m_passthrough)
1518 return;
1519
1523
1524 LOGDEBUG2(L_SOUND, "audio: %s: first real audio was sent, hwBaseline %ld frames (%dms)", __FUNCTION__, m_hwBaseline, FramesToMs(m_hwBaseline));
1526 }
1527}
1528
1533{
1534 std::lock_guard<std::mutex> lock(m_mutex);
1535
1536 LOGDEBUG2(L_SOUND, "audio: %s: reset hw delay baseline to 0", __FUNCTION__);
1537 m_hwBaseline = 0;
1539}
1540
1545{
1546 for (Event event : m_eventQueue)
1548
1549 m_eventQueue.clear();
1550}
1551
1560char *cSoftHdAudio::OpenAlsaDevice(const char *device, int passthrough)
1561{
1562 int err;
1563 char tmp[80];
1564
1565 if (!device)
1566 return NULL;
1567
1568 LOGDEBUG2(L_SOUND, "audio: %s: try opening %sdevice '%s'", __FUNCTION__, passthrough ? "pass-through " : "", device);
1569
1570 if (passthrough && m_appendAES) {
1571 if (!(strchr(device, ':'))) {
1572 sprintf(tmp, "%s:AES0=%d,AES1=%d,AES2=0,AES3=%d",
1573 device,
1577 } else {
1578 sprintf(tmp, "%s,AES0=%d,AES1=%d,AES2=0,AES3=%d",
1579 device,
1583 }
1584 LOGDEBUG2(L_SOUND, "audio: %s: auto append AES: %s -> %s", __FUNCTION__, device, tmp);
1585 } else {
1586 sprintf(tmp, "%s", device);
1587 }
1588
1589 // open none blocking; if device is already used, we don't want wait
1591 SND_PCM_NONBLOCK)) < 0) {
1592
1593 LOGWARNING("audio: %s: could not open device '%s' error: %s", __FUNCTION__, device, snd_strerror(err));
1594 return NULL;
1595 }
1596
1597 LOGDEBUG2(L_SOUND, "audio: %s: opened %sdevice '%s'", __FUNCTION__, passthrough ? "pass-through " : "", device);
1598
1599 return (char *)device;
1600}
1601
1612char *cSoftHdAudio::FindAlsaDevice(const char *devname, const char *hint, int passthrough)
1613{
1614 char **hints;
1615 int err;
1616 char **n;
1617 char *name;
1618
1619 err = snd_device_name_hint(-1, devname, (void ***)&hints);
1620 if (err != 0) {
1621 LOGWARNING("audio: %s: Cannot get device names for %s!", __FUNCTION__, hint);
1622 return NULL;
1623 }
1624
1625 n = hints;
1626 while (*n != NULL) {
1627 name = snd_device_name_get_hint(*n, "NAME");
1628
1629 if (name && strstr(name, hint)) {
1630 if (OpenAlsaDevice(name, passthrough)) {
1632 return name;
1633 }
1634 }
1635
1636 if (name)
1637 free(name);
1638 n++;
1639 }
1640
1642 return NULL;
1643}
1644
1649{
1650 char *device = NULL;
1651 bool freeDevice = false; // track if device needs to be freed
1652 int err;
1653 LOGDEBUG2(L_SOUND, "audio: %s: passthrough %d", __FUNCTION__, m_passthrough);
1654
1655 // try user set device
1656 if (m_passthrough)
1658
1659 if (!device && m_passthrough)
1660 device = OpenAlsaDevice(getenv("ALSA_PASSTHROUGH_DEVICE"), m_passthrough);
1661
1662 if (!device)
1664
1665 if (!device)
1666 device = OpenAlsaDevice(getenv("ALSA_DEVICE"), m_passthrough);
1667
1668 // walkthrough hdmi: devices
1669 if (!device) {
1670 LOGDEBUG2(L_SOUND, "audio: %s: Try hdmi: devices...", __FUNCTION__);
1671 device = FindAlsaDevice("pcm", "hdmi:", m_passthrough);
1672 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1673 }
1674
1675 // Rockchip mainline kernel
1676 if (!device) {
1677 LOGDEBUG2(L_SOUND, "audio: %s: Try default:CARD=hdmisound devices...", __FUNCTION__);
1678 device = FindAlsaDevice("pcm", "default:CARD=hdmisound", m_passthrough);
1679 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1680 }
1681
1682 // walkthrough default: devices
1683 if (!device) {
1684 LOGDEBUG2(L_SOUND, "audio: %s: Try default: devices...", __FUNCTION__);
1685 device = FindAlsaDevice("pcm", "default:", m_passthrough);
1686 freeDevice = (device != NULL); // FindAlsaDevice allocates memory
1687 }
1688
1689 // try default device
1690 if (!device) {
1691 LOGDEBUG2(L_SOUND, "audio: %s: Try default device...", __FUNCTION__);
1692 device = OpenAlsaDevice("default", m_passthrough);
1693 }
1694
1695 // use null device
1696 if (!device) {
1697 LOGDEBUG2(L_SOUND, "audio: %s: Try null device...", __FUNCTION__);
1698 device = OpenAlsaDevice("null", m_passthrough);
1699 }
1700
1701 if (!device)
1702 LOGFATAL("audio: %s: could not open any device, abort!", __FUNCTION__);
1703
1704 if (!strcmp(device, "null"))
1705 LOGWARNING("audio: %s: using %sdevice '%s'", __FUNCTION__,
1706 m_passthrough ? "pass-through " : "", device);
1707 else
1708 LOGINFO("audio: using %sdevice '%s'",
1709 m_passthrough ? "pass-through " : "", device);
1710
1711 // Free device string if it was allocated by FindAlsaDevice
1712 if (freeDevice)
1713 free(device);
1714
1715 if ((err = snd_pcm_nonblock(m_pAlsaPCMHandle, 0)) < 0) {
1716 LOGERROR("audio: %s: can't set block mode: %s", __FUNCTION__, snd_strerror(err));
1717 }
1718}
1719
1720/******************************************************************************
1721 * Alsa Mixer
1722 *****************************************************************************/
1723
1728{
1729 const char *device;
1730 const char *channel;
1733 long alsaMixerElemMin;
1734 long alsaMixerElemMax;
1735
1736 if (!(device = m_pMixerDevice)) {
1737 if (!(device = getenv("ALSA_MIXER"))) {
1738 device = "default";
1739 }
1740 }
1741 if (!(channel = m_pMixerChannel)) {
1742 if (!(channel = getenv("ALSA_MIXER_CHANNEL"))) {
1743 channel = "PCM";
1744 }
1745 }
1746 LOGDEBUG2(L_SOUND, "audio: %s: mixer %s - %s open", __FUNCTION__, device, channel);
1748 if (alsaMixer && snd_mixer_attach(alsaMixer, device) >= 0
1750 && snd_mixer_load(alsaMixer) >= 0) {
1751
1752 const char *const alsaMixerElem_name = channel;
1753
1755 while (alsaMixerElem) {
1756 const char *name;
1757
1759 if (!strcasecmp(name, alsaMixerElem_name)) {
1762 LOGDEBUG2(L_SOUND, "audio: %s: %s mixer found %ld - %ld ratio %d", __FUNCTION__, channel, alsaMixerElemMin, alsaMixerElemMax, m_alsaRatio);
1763 break;
1764 }
1765
1767 }
1768
1771 } else {
1772 LOGERROR("audio: %s: can't open mixer '%s'", __FUNCTION__, device);
1773 }
1774}
1775
1790
1804{
1806 int err;
1807 unsigned bufferTimeUs = 100'000;
1808
1809 if (Active()) {
1810 Stop();
1812 }
1813
1814 m_downmix = 0;
1815
1818 LOGERROR("audio: %s: Read HW config failed! %s", __FUNCTION__, snd_strerror(err));
1819 return -1;
1820 }
1821
1823 m_alsaUseMmap = true;
1824 }
1825
1828 LOGERROR("audio: %s: SampleRate %d not supported! %s", __FUNCTION__, sample_rate, snd_strerror(err));
1829 return -1;
1830 }
1831 if ((int)m_hwSampleRate != sample_rate) {
1832 LOGDEBUG2(L_SOUND, "audio: %s: sample_rate %d m_hwSampleRate %d", __FUNCTION__, sample_rate, m_hwSampleRate);
1833 }
1834
1837 LOGWARNING("audio: %s: %d channels not supported! %s", __FUNCTION__, m_hwNumChannels, snd_strerror(err));
1838 }
1839 if ((int)m_hwNumChannels != channels && !passthrough) {
1840 m_downmix = 1;
1841 }
1843 LOGWARNING("audio: %s: bufferTime %d not supported! %s", __FUNCTION__, bufferTimeUs, snd_strerror(err));
1844 }
1845
1848 LOGWARNING("audio: %s: getting max periodSize not supported! %s", __FUNCTION__, snd_strerror(err));
1849 }
1850
1853 LOGWARNING("audio: %s: getting max bufferSize not supported! %s", __FUNCTION__, snd_strerror(err));
1854 }
1855
1857
1858/* err = snd_pcm_hw_params_test_format(m_pAlsaPCMHandle, hwparams, SND_PCM_FORMAT_S16);
1859 if (err < 0) // err == 0 if is supported
1860 LOGERROR("audio: %s: SND_PCM_FORMAT_S16 not supported! %s", __FUNCTION__,
1861 snd_strerror(err));
1862*/
1866
1868 LOGERROR("audio: %s: set params error: %s\n"
1869 " Channels %d SampleRate %d\n"
1870 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1871 " mmap: %s\n"
1872 " AlsaBufferTime %dms pcm state: %s\n"
1873 " periodSize %d frames, bufferSize %d frames",
1877 m_alsaUseMmap ? "yes" : "no",
1878 bufferTimeUs / 1000, snd_pcm_state_name(state),
1880 return -1;
1881 }
1882
1884 std::string channelMapString;
1885 for (size_t i = 0; i < alsaMap.size(); i++) {
1887 if (i < alsaMap.size() - 1)
1888 channelMapString += " ";
1889 }
1890
1892 LOGINFO("audio: %s:\n"
1893 " Channels %d (%s) SampleRate %d%s\n"
1894 " HWChannels %d HWSampleRate %d SampleFormat %s\n"
1895 " mmap: %s\n"
1896 " AlsaBufferTime %dms, pcm state: %s\n"
1897 " periodSize %d frames, bufferSize %d frames",
1899 channels, channelMapString.c_str(), sample_rate, passthrough ? " -> passthrough" : "",
1902 m_alsaUseMmap ? "yes" : "no",
1903 bufferTimeUs / 1000, snd_pcm_state_name(state),
1905
1906 Start();
1907
1908 return 0;
1909}
1910
1917 const char *file, __attribute__ ((unused))
1918 int line, __attribute__ ((unused))
1919 const char *function, __attribute__ ((unused))
1920 int err, __attribute__ ((unused))
1921 const char *fmt, ...)
1922{
1923}
1924
1929{
1930#ifdef ALSA_DEBUG
1932#else
1933 // disable display of alsa error messages
1935#endif
1936
1938 AlsaInitMixer();
1939}
1940
1945{
1946 if (m_pAlsaPCMHandle) {
1949 }
1950 if (m_pAlsaMixer) {
1954 }
1955}
1956
1967{
1968 if (m_passthrough)
1969 return;
1970
1972 if (m_fillLevel.IsSettled()) {
1973 auto now = std::chrono::steady_clock::now();
1974 std::chrono::duration<double> elapsedSec = now - m_lastPidInvocation;
1976
1978 } else
1980
1981 if (m_packetCounter++ % 1000 == 0) {
1982 LOGDEBUG2(L_SOUND, "audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
1986 m_pitchPpm.load(),
1990 }
1991
1992 // buffer fill level low pass filter
1994
1996 LOGWARNING("audio: %s: snd_pcm_avail() failes: %s", __FUNCTION__, snd_strerror(hardwareBufferFillLevelFrames));
1997 else
1999}
Audio and Alsa Interface Header File.
virtual void OnEventReceived(const Event &)=0
void ResetFramesCounters()
Resets the received and written frames counters.
Definition filllevel.cpp:28
void UpdateAvgBufferFillLevel(int)
Updates the buffer fill level average.
Definition filllevel.cpp:48
void ReceivedFrames(int count)
Definition filllevel.h:26
void WroteFrames(int count)
Definition filllevel.h:27
void Reset()
Resets the filter state.
Definition filllevel.cpp:18
void Reset()
Reset the internal state (integral sum and error history).
double GetTargetValue()
double GetPTerm()
double Update(double, double)
Calculate the new output value.
double GetDTerm()
void SetTargetValue(double value)
double GetITerm()
void LazyInit(void)
Initialize audio output module (alsa)
Definition audio.cpp:1195
cSoftHdAudio(cSoftHdDevice *)
Create a new audio context.
Definition audio.cpp:52
char * OpenAlsaDevice(const char *, int)
Open an alsa device.
Definition audio.cpp:1560
bool SendPause(void)
Write pause to passthrough device.
Definition audio.cpp:1478
int m_alsaBufferSizeFrames
alsa buffer size in frames
Definition audio.h:103
char * FindAlsaDevice(const char *, const char *, int)
Find alsa device giving some search hints.
Definition audio.cpp:1612
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
Definition audio.cpp:1532
virtual void Action(void)
Audio thread loop, started with Start().
Definition audio.cpp:1319
bool m_appendAES
flag ato utomatic append AES
Definition audio.h:127
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
Definition audio.cpp:938
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
Definition audio.h:184
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
Definition audio.h:116
double FramesToMsDouble(int frames)
Definition audio.h:213
int Setup(AVCodecContext *, int, int, int)
Alsa setup wrapper.
Definition audio.cpp:849
void Enqueue(uint16_t *, int, AVFrame *)
Send audio data to ringbuffer.
Definition audio.cpp:797
int m_compressionMaxFactor
max. compression factor
Definition audio.h:155
const char * m_pPCMDevice
PCM device name.
Definition audio.h:125
int m_volume
current volume (0 .. 1000)
Definition audio.h:161
void SetPassthroughMask(int)
Set audio passthrough mask.
Definition audio.cpp:1176
void SetHwDelayBaseline(void)
Set the hw delay baseline.
Definition audio.cpp:1512
void SetStereoDescent(int)
Set stereo loudness descent.
Definition audio.cpp:1165
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition audio.cpp:1043
std::mutex m_pauseMutex
mutex for a safe thread pausing
Definition audio.h:113
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
Definition audio.cpp:1084
void EnqueueSpdif(uint16_t *, int, AVFrame *)
Enqueue prepared spdif bursts in audio output queue.
Definition audio.cpp:775
AVFilterContext * m_pBuffersinkCtx
Definition audio.h:177
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
Definition audio.h:101
void SetVolume(int)
Set mixer volume (0-1000)
Definition audio.cpp:1098
AVFilterContext * m_pBuffersrcCtx
Definition audio.h:176
void HandleError(int)
Handle an alsa error.
Definition audio.cpp:1231
AVFilterGraph * m_pFilterGraph
Definition audio.h:175
int m_passthrough
passthrough mask
Definition audio.h:124
const int m_bytesPerSample
number of bytes per sample
Definition audio.h:108
const char * m_pMixerChannel
mixer channel name
Definition audio.h:170
void FlushAlsaBuffersInternal(bool)
Flush alsa buffers internally.
Definition audio.cpp:1264
unsigned int m_hwSampleRate
hardware sample rate in Hz
Definition audio.h:109
int64_t PtsToMs(int64_t pts)
Definition audio.h:209
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
Definition audio.h:100
IEventReceiver * m_pEventReceiver
pointer to event receiver
Definition audio.h:99
void ProcessEvents(void)
Process queued events and forward them to event receiver.
Definition audio.cpp:1544
int MsToFrames(int milliseconds)
Definition audio.h:211
std::vector< Event > m_eventQueue
event queue for incoming events
Definition audio.h:114
bool SendAudio(int)
Write regular audio data from the ringbuffer to the hardware.
Definition audio.cpp:1418
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:698
AVRational * m_pTimebase
pointer to AVCodecContext pkts_timebase
Definition audio.h:111
bool m_compression
flag to use compress volume
Definition audio.h:153
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
Definition audio.cpp:1022
bool m_normalize
flag to use volume normalize
Definition audio.h:141
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
Definition audio.cpp:872
void ClockDriftCompensation(void)
Calculate clock drift compensation.
Definition audio.cpp:1966
int m_filterChanged
filter has changed
Definition audio.h:173
void AlsaExit(void)
Cleanup the alsa audio output module.
Definition audio.cpp:1944
void SetCompression(bool, int)
Set volume compression parameters.
Definition audio.cpp:1147
void Stop(void)
Stop the thread.
Definition audio.cpp:1337
snd_mixer_elem_t * m_pAlsaMixerElem
alsa mixer element
Definition audio.h:189
void BuildPauseBurst(void)
Build a pause spdif burst with the size of the last recognized normal spdif audio.
Definition audio.cpp:754
void AlsaInit(void)
Initialize the alsa audio output module.
Definition audio.cpp:1928
void Compress(uint16_t *, int)
Compress audio samples.
Definition audio.cpp:162
int64_t m_inputPts
pts clock (last pts in ringbuffer)
Definition audio.h:120
int m_normalizeFactor
current normalize factor
Definition audio.h:147
void AlsaSetVolume(int)
Set alsa mixer volume (0-1000)
Definition audio.cpp:1781
int64_t GetOutputPtsMsInternal(void)
Definition audio.cpp:1029
cSoftHdConfig * m_pConfig
pointer to config
Definition audio.h:98
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
Definition audio.h:115
void Exit(void)
Cleanup audio output module (alsa)
Definition audio.cpp:1210
int m_amplifier
software volume amplify factor
Definition audio.h:159
int GetUsedBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:1004
void SetPaused(bool)
Set audio playback pause state.
Definition audio.cpp:1121
void AlsaInitMixer(void)
Initialize alsa mixer.
Definition audio.cpp:1727
int AlsaSetup(int, int, int)
Setup alsa audio for requested format.
Definition audio.cpp:1803
static constexpr int NORMALIZE_MAX_INDEX
number of normalize average samples
Definition audio.h:95
const char * m_pPassthroughDevice
passthrough device name
Definition audio.h:126
int m_normalizeMaxFactor
max. normalize factor
Definition audio.h:149
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
Definition audio.cpp:1065
bool m_alsaUseMmap
use mmap
Definition audio.h:191
void Normalize(uint16_t *, int)
Normalize audio samples.
Definition audio.cpp:81
int m_compressionFactor
current compression factor
Definition audio.h:154
int64_t MsToPts(int64_t ptsMs)
Definition audio.h:210
int m_spdifBurstSize
size of the current spdif burst
Definition audio.h:128
const int m_normalizeMinFactor
min. normalize factor
Definition audio.h:148
const int m_normalizeSamples
number of normalize samples
Definition audio.h:142
int m_filterReady
filter is ready
Definition audio.h:174
void SetEq(int[18], int)
Set equalizer bands.
Definition audio.cpp:248
int m_normalizeReady
index normalize counter
Definition audio.h:146
const char * m_pMixerDevice
mixer device name (not used)
Definition audio.h:169
void DropAlsaBuffers(void)
Drop alsa buffers.
Definition audio.cpp:1254
bool HasInputPts(void)
Definition audio.h:69
uint32_t m_normalizeAverage[NORMALIZE_MAX_INDEX]
average of n last normalize sample blocks
Definition audio.h:144
bool CyclicCall(void)
Cyclic audio playback call.
Definition audio.cpp:1357
void AlsaInitPCMDevice(void)
Search for an alsa pcm device and open it.
Definition audio.cpp:1648
unsigned int m_hwNumChannels
number of hardware channels
Definition audio.h:110
bool m_initialized
class initialized
Definition audio.h:107
int FramesToMs(int frames)
Definition audio.h:212
int m_stereoDescent
volume descent for stereo
Definition audio.h:160
std::mutex m_mutex
mutex for thread safety
Definition audio.h:112
static constexpr int AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
Definition audio.h:96
int m_alsaRatio
internal -> mixer ratio * 1000
Definition audio.h:190
int m_packetCounter
packet counter for logging
Definition audio.h:104
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
Definition audio.cpp:730
void SoftAmplify(int16_t *, int)
Amplify the samples in software.
Definition audio.cpp:219
void FlushAlsaBuffers(void)
Flush alsa buffers.
Definition audio.cpp:1246
void SetNormalize(bool, int)
Set normalize volume parameters.
Definition audio.cpp:1135
snd_pcm_t * m_pAlsaPCMHandle
alsa pcm handle
Definition audio.h:187
int m_downmix
set stereo downmix
Definition audio.h:118
int m_useEqualizer
flag to use equalizer
Definition audio.h:165
float m_equalizerBand[18]
equalizer band
Definition audio.h:166
snd_mixer_t * m_pAlsaMixer
alsa mixer handle
Definition audio.h:188
snd_pcm_sframes_t m_hwBaseline
saves the hw delay (pause bursts) once a real audio frame to correctly do the AV-Sync
Definition audio.h:130
int m_normalizeIndex
index into normalize average table
Definition audio.h:145
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
Definition audio.h:102
int m_normalizeCounter
normalize sample counter
Definition audio.h:143
std::atomic< bool > m_paused
audio is paused
Definition audio.h:121
bool m_firstRealAudioReceived
false, as long as no real audio was sent - used to trigger the baseline set
Definition audio.h:131
int CheckForFilterReady(AVCodecContext *)
Check if the filter has changed and is ready, init the filter if needed.
Definition audio.cpp:905
int InitFilter(AVCodecContext *)
Init audio filters.
Definition audio.cpp:473
std::vector< uint16_t > m_pauseBurst
holds the burst data itself
Definition audio.h:129
void FlushBuffers(void)
Flush audio buffers.
Definition audio.cpp:981
bool m_softVolume
flag to use soft volume
Definition audio.h:123
bool ConfigAudioNormalize
config use normalize volume
Definition config.h:48
int ConfigAudioStereoDescent
config reduce stereo loudness
Definition config.h:52
bool ConfigAudioCompression
config use volume compression
Definition config.h:50
int ConfigAudioEqBand[18]
config equalizer filter bands
Definition config.h:56
int ConfigAudioMaxCompression
config max volume compression
Definition config.h:51
int ConfigAudioEq
config equalizer filter
Definition config.h:55
int ConfigAudioMaxNormalize
config max normalize factor
Definition config.h:49
Output Device Implementation.
size_t UsedBytes(void)
Get used bytes in ring buffer.
size_t ReadAdvance(size_t)
Advance read pointer in ring buffer.
size_t GetReadPointer(const void **)
Get read pointer and used bytes at this position of ring buffer.
size_t Write(const void *, size_t)
Write to a ring buffer.
void Reset(void)
Reset ring buffer pointers.
Audio Decoder Header File.
Plugin Configuration Header File.
__attribute__((weak)) union gbm_bo_handle gbm_bo_get_handle_for_plane(struct gbm_bo *bo
State Machine and Event Header File.
Low-pass Filter for Audio Buffer Fill Level Measurement Header File.
static const char * alsaToFFmpegChannel(const char *alsaName)
FFmpeg does not have channels called "RL" or "RR" So "rename" Alsas RL (rear left) and RR (rear right...
Definition audio.cpp:325
static std::string BuildChannelMapFilter(snd_pcm_t *pcmHandle, const AVChannelLayout &layout)
Build the "|"-separated mappings list for the channelmap filter.
Definition audio.cpp:416
static std::vector< std::string > GetFFmpegChannelLayoutAsArray(const AVChannelLayout &layout)
Put FFmpeg channel layout in a dynamic array of strings.
Definition audio.cpp:368
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.
Definition audio.cpp:1916
static bool LayoutsMatch(const std::vector< std::string > &ff, const std::vector< std::string > &alsa)
Check, if FFmpeg and Alsa channel layout match.
Definition audio.cpp:393
static std::vector< std::string > GetAlsaChannelLayoutAsArray(snd_pcm_t *pcmHandle)
Put Alsa channel layout in a dynamic array of strings.
Definition audio.cpp:342
@ IEC61937_NULL
no data
Definition codec_audio.h:49
@ IEC61937_PREAMBLE1
Definition codec_audio.h:65
@ IEC61937_PREAMBLE2
Definition codec_audio.h:66
#define LOGDEBUG2
log to LOG_DEBUG and add a prefix
Definition logger.h:47
#define LOGDEBUG
log to LOG_DEBUG
Definition logger.h:45
#define LOGERROR
log to LOG_ERR
Definition logger.h:39
#define AV_NOPTS_VALUE
Definition misc.h:74
#define LOGWARNING
log to LOG_WARN
Definition logger.h:41
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, PipEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent > Event
Definition event.h:82
#define LOGINFO
log to LOG_INFO
Definition logger.h:43
#define LOGFATAL
log to LOG_ERR and abort
Definition logger.h:37
static const char * Timestamp2String(int64_t ts, uint8_t divisor)
Nice time-stamp string.
Definition misc.h:127
@ L_AV_SYNC
audio/video sync logs
Definition logger.h:57
@ L_SOUND
sound logs
Definition logger.h:58
@ AUDIO
Definition event.h:29
Logger Header File.
Misc Functions.
PID (proportional, integral, derivative) Controller Header File.
Audio Ringbuffer Header File.
Output Device Header File.