vdr-plugin-softhddevice-drm-gles 1.6.7
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
25extern "C" {
26#include <libavcodec/avcodec.h>
27#include <libavfilter/avfilter.h>
28#include <libavfilter/buffersink.h>
29#include <libavfilter/buffersrc.h>
30#include <libavutil/channel_layout.h>
31#include <libavutil/opt.h>
32}
33
34#include <vdr/thread.h>
35
36#include "alsadevice.h"
37#include "audio.h"
38#include "audioprocessor.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_alsa(m_pConfig),
57 m_pEventReceiver(device),
58 m_softVolume(m_pConfig->ConfigAudioSoftvol),
59 m_audioProcessor(BYTES_PER_SAMPLE),
60 m_pMixerChannel(m_pConfig->ConfigAudioMixerChannel)
61{
66}
67
68/******************************************************************************
69 * Audio Filter
70 *****************************************************************************/
71
81static std::vector<std::string> GetFFmpegChannelLayoutAsArray(const AVChannelLayout &layout)
82{
83 std::vector<std::string> names;
84 char buf[16];
85
86 for (int i = 0; i < layout.nb_channels; i++) {
88 int ret = av_channel_name(buf, sizeof(buf), ch);
89 if (ret < 0)
90 continue;
91 names.push_back(std::string(buf));
92 }
93 return names;
94}
95
106static bool LayoutsMatch(const std::vector<std::string> &ff, const std::vector<std::string> &alsa)
107{
108 if (ff.size() != alsa.size())
109 return false;
110
111 for (size_t i = 0; i < ff.size(); i++) {
112 if (ff[i] != alsa[i])
113 return false;
114 }
115
116 return true;
117}
118
128static bool LayoutIsValid(const std::vector<std::string> &channelLayout)
129{
130 return std::find(channelLayout.begin(), channelLayout.end(), "NA") == channelLayout.end();
131}
132
143{
146
147 if (ff.size() != alsa.size()) {
148 LOGWARNING("audio: %s: Skip channelmap filter, FFmpeg and Alsa channel count differs: FFmpeg %zu ALSA %zu", __FUNCTION__, ff.size(), alsa.size());
149 return "";
150 }
151
152 std::string ffString;
153 for (size_t i = 0; i < ff.size(); i++) {
154 ffString += ff[i];
155 if (i < ff.size() - 1)
156 ffString += " ";
157 }
158
159 std::string alsaString;
160 for (size_t i = 0; i < alsa.size(); i++) {
161 alsaString += alsa[i];
162 if (i < alsa.size() - 1)
163 alsaString += " ";
164 }
165
166 if (!LayoutIsValid(alsa)) {
167 LOGDEBUG2(L_SOUND, "audio: %s: Skip channelmap filter, alsa channel layout isn't valid: %s", __FUNCTION__, alsaString.c_str());
168 return "";
169 }
170
171 if (LayoutsMatch(ff, alsa)) {
172 LOGDEBUG2(L_SOUND, "audio: %s: Skip channelmap filter, FFmpeg and Alsa channel layouts match: %s", __FUNCTION__, ffString.c_str());
173 return "";
174 }
175
176 std::stringstream ss;
177 for (size_t i = 0; i < ff.size(); i++) {
178 if (i != 0)
179 ss << "|";
180 ss << ff[i] << "-" << alsa[i];
181 }
182
183 LOGDEBUG2(L_SOUND, "audio: %s: FFmpeg Channel Layout: %s", __FUNCTION__, ffString.c_str());
184 LOGDEBUG2(L_SOUND, "audio: %s: Alsa Channel Layout : %s", __FUNCTION__, alsaString.c_str());
185 LOGDEBUG2(L_SOUND, "audio: %s: Layouts don't match, map FFmpeg to Alsa: %s", __FUNCTION__, ss.str().c_str());
186
187 return ss.str();
188}
189
205{
206 const AVFilter *abuffer;
208 const AVFilter *channelmap;
209 const AVFilter *eq;
210 const AVFilter *aformat;
211 const AVFilter *abuffersink;
212 char channelLayout[64];
213 char optionsStr[1024];
214 int err, i, numFilter = 0;
215
216 // Before filter init setup HW parameter
217 err = Setup(audioCtx->pkt_timebase, audioCtx->sample_rate, audioCtx->ch_layout.nb_channels, false);
218 if (err < 0) {
219 LOGERROR("audio: %s: failed!", __FUNCTION__);
220 return err;
221 }
222
223#if LIBAVFILTER_VERSION_INT < AV_VERSION_INT(7,16,100)
225#endif
226
228 LOGERROR("audio: %s: Unable to create filter graph.", __FUNCTION__);
229 return -1;
230 }
231
232 // input buffer
233 if (!(abuffer = avfilter_get_by_name("abuffer"))) {
234 LOGWARNING("audio: %s: Could not find the abuffer filter.", __FUNCTION__);
236 return -1;
237 }
239 LOGWARNING("audio: %s: Could not allocate the m_pBuffersrcCtx instance.", __FUNCTION__);
241 return -1;
242 }
243
245
246 LOGDEBUG2(L_SOUND, "audio: %s: IN channelLayout %s sample_fmt %s sample_rate %d channels %d", __FUNCTION__,
247 channelLayout, av_get_sample_fmt_name(audioCtx->sample_fmt), audioCtx->sample_rate, audioCtx->ch_layout.nb_channels);
248
251 av_opt_set_q (m_pBuffersrcCtx, "time_base", (AVRational){ 1, audioCtx->sample_rate }, AV_OPT_SEARCH_CHILDREN);
252 av_opt_set_int(m_pBuffersrcCtx, "sample_rate", audioCtx->sample_rate, AV_OPT_SEARCH_CHILDREN);
253// av_opt_set_int(m_pBuffersrcCtx, "channel_counts", audioCtx->channels, AV_OPT_SEARCH_CHILDREN);
254
255 // initialize the filter with NULL options, set all options above.
257 LOGWARNING("audio: %s: Could not initialize the abuffer filter.", __FUNCTION__);
259 return -1;
260 }
261
262 // channelmap
263 //
264 // Map FFmpeg channel layout to Alsa channel layout.
265 // Depending on the hardware, e.g. FC and LFE have to be swapped.
266 // This is the case for HDMI on RPI4, so we need to do the following:
267 // FL-FL|FR-FR|FC-LFE|LFE-FC|BL-BL|BR-BR
268 //
269 // The channel mapping is skipped, if
270 // - a stereo downmix is forced (downmix will be done later in aformat filter)
271 // - channel count differs, aformat will handle downmix later
272 if (!(m_alsa.GetDownmix() && m_alsa.GetHwNumChannels() == 2)) {
273 std::string channelMapString;
275
276 if (!channelMapString.empty()) {
277 if (!(channelmap = avfilter_get_by_name("channelmap"))) {
278 LOGWARNING("audio: %s: Could not find the channelmap filter.", __FUNCTION__);
279 return -1;
280 }
282 LOGWARNING("audio: %s: Could not allocate the channelmap instance.", __FUNCTION__);
283 return -1;
284 }
285 snprintf(optionsStr, sizeof(optionsStr),"map=%s", channelMapString.c_str());
287 LOGWARNING("audio: %s: Could not initialize the channelmap filter \"%s\"", __FUNCTION__, optionsStr);
289 return -1;
290 }
291 numFilter++;
292 }
293 }
294
295 // superequalizer
296 if (m_useEqualizer) {
297 if (!(eq = avfilter_get_by_name("superequalizer"))) {
298 LOGWARNING("audio: %s: Could not find the superequalizer filter.", __FUNCTION__);
300 return -1;
301 }
302 if (!(pFilterCtx[numFilter] = avfilter_graph_alloc_filter(m_pFilterGraph, eq, "superequalizer"))) {
303 LOGWARNING("audio: %s: Could not allocate the superequalizer instance.", __FUNCTION__);
305 return -1;
306 }
307
309 snprintf(optionsStr, sizeof(optionsStr), "%s", equalizerOptions.c_str());
310
312 LOGWARNING("audio: %s: Could not initialize the superequalizer filter.", __FUNCTION__);
314 return -1;
315 }
316 numFilter++;
317 }
318
319 // aformat
321 if (m_alsa.GetDownmix() && m_alsa.GetHwNumChannels() == 2) {
322 // explicit stereo downmix
324 } else {
325 if (av_channel_layout_copy(&channel_layout, &audioCtx->ch_layout) < 0) {
326 LOGWARNING("audio: %s: Could not copy channel layout", __FUNCTION__);
327 return -1;
328 }
329
330 // clamp channels if the hardware doesn't support them
331 if (channel_layout.nb_channels > (int)m_alsa.GetHwNumChannels()) {
332 LOGDEBUG2(L_SOUND, "audio: %s: clamp channels from %d -> %d", __FUNCTION__, channel_layout.nb_channels, m_alsa.GetHwNumChannels());
335 }
336 }
339
340 LOGDEBUG2(L_SOUND, "audio: %s: OUT downmix %d hwNumChannels %d hwSampleRate %d channelLayout %s bytes_per_sample %d",
342
343 if (!(aformat = avfilter_get_by_name("aformat"))) {
344 LOGWARNING("audio: %s: Could not find the aformat filter.", __FUNCTION__);
346 return -1;
347 }
349 LOGWARNING("audio: %s: Could not allocate the aformat instance.", __FUNCTION__);
351 return -1;
352 }
354 "sample_fmts=%s:sample_rates=%d:channel_layouts=%s",
357 LOGWARNING("audio: %s: Could not initialize the aformat filter.", __FUNCTION__);
359 return -1;
360 }
361 numFilter++;
362
363 // abuffersink
364 if (!(abuffersink = avfilter_get_by_name("abuffersink"))) {
365 LOGWARNING("audio: %s: Could not find the abuffersink filter.", __FUNCTION__);
367 return -1;
368 }
370 LOGWARNING("audio: %s: Could not allocate the abuffersink instance.", __FUNCTION__);
372 return -1;
373 }
375 LOGWARNING("audio: %s: Could not initialize the abuffersink instance.", __FUNCTION__);
377 return -1;
378 }
379 numFilter++;
380
381 // Connect the filters
382 for (i = 0; i < numFilter; i++) {
383 if (i == 0) {
385 } else {
386 err = avfilter_link(pFilterCtx[i - 1], 0, pFilterCtx[i], 0);
387 }
388 }
389 if (err < 0) {
390 LOGWARNING("audio: %s: Error connecting audio filters", __FUNCTION__);
392 return -1;
393 }
394
395 // Configure the graph.
397 LOGWARNING("audio: %s: Error configuring the audio filter graph", __FUNCTION__);
399 return -1;
400 }
401
403 m_filterChanged = 0;
404 m_filterReady = 1;
405
406 return 0;
407}
408
409/******************************************************************************
410 * Audio stream handling
411 *****************************************************************************/
412
422{
423 std::lock_guard<std::mutex> lock(m_mutex);
424
425 if (!HasInputPts())
426 return;
427
431
432 dropBytes = std::min(dropBytes, (int)m_pRingbuffer.UsedBytes());
433
434 if (dropBytes > 0) {
435 LOGDEBUG2(L_AV_SYNC, "audio: %s: dropping %dms audio samples to start in sync with the video (output PTS %s -> %s)",
437 dropMs,
440
445 }
446}
447
454{
455 if (!frame)
456 return;
457
458 uint16_t *buffer;
459
460 int byteCount = frame->nb_samples * frame->ch_layout.nb_channels * BYTES_PER_SAMPLE;
461 buffer = (uint16_t *)frame->data[0];
462
463 if (m_useCompressor) // in place operation
465
466 if (m_useNormalizer) // in place operation
468
469 Enqueue((uint16_t *)buffer, byteCount, frame->pts);
470
471 av_frame_free(&frame);
472}
473
480{
481 if (size == m_spdifBurstSize)
482 return;
483
484 LOGDEBUG2(L_SOUND, "audio: %s: spdif burst size changed %d -> %d, rebuild pause burst", __FUNCTION__, m_spdifBurstSize, size);
485
486 m_spdifBurstSize = size;
487 m_pauseBurst.resize(size / 2);
488 uint16_t *spdif = m_pauseBurst.data();
489
490 constexpr int IEC61937_PREAMBLE1 = 0xF872;
491 constexpr int IEC61937_PREAMBLE2 = 0x4E1F;
492 constexpr int IEC61937_NULL = 0x00;
493
497 spdif[3] = 0;
498
499 memset(m_pauseBurst.data() + 4, 0, m_spdifBurstSize - 8);
500}
501
512{
513 std::lock_guard<std::mutex> lock(m_pauseMutex);
514
516
517 Enqueue(buffer, count, pts);
518}
519
527void cSoftHdAudio::Enqueue(const uint16_t *buffer, int count, int64_t pts)
528{
529 std::lock_guard<std::mutex> lock(m_mutex);
530
531 // pitch adjustment
532 if (!m_alsa.IsPassthroughActive() && m_pitchAdjustFrameCounter == 0 && std::abs(m_pitchPpm) > 1) { // only adjust if pitch has a significant value to prevent overly large values/division by zero
534
535 if (m_pitchPpm < 0 && m_pRingbuffer.Write((const uint16_t *)buffer, oneFrameBytes)) // insert additional frame
537 else if (m_pitchPpm > 0) // drop frame
538 count = std::max(0, count - oneFrameBytes);
539
540 m_pitchAdjustFrameCounter = std::round(1'000'000.0 / std::abs(m_pitchPpm));
541 }
542
544
545 // write to ringbuffer
546 int bytesWritten = m_pRingbuffer.Write((const uint16_t *)buffer, count);
547 if (bytesWritten != count)
548 LOGERROR("audio: %s: can't place %d samples in ring buffer", __FUNCTION__, count);
549
551
552 if (pts != AV_NOPTS_VALUE) {
553 // discontinuity check, force a resync if the new pts differs more than AV_SYNC_BORDER_MS to the last
555 LOGDEBUG2(L_AV_SYNC, "audio: %s: discontinuity detected in audio PTS %s -> %s%s", __FUNCTION__,
557 m_alsa.PtsToMs(m_inputPts, av_q2d(m_timebase)) > m_alsa.PtsToMs(pts, av_q2d(m_timebase)) ? " (PTS wrapped)" : "");
558 std::lock_guard<std::mutex> lock(m_queueMutex);
560 }
561
562 m_inputPts = pts;
563 } else if (m_inputPts != AV_NOPTS_VALUE) {
565 }
566}
567
583{
584 int err = 0;
585
587
588 // skip setup, nothing changed
589 if (samplerate == (int)m_alsa.GetHwSampleRate() &&
592 return 1;
593
594 if (Active()) {
595 Stop();
597 }
598
600 if (err)
601 LOGERROR("audio: %s: failed!", __FUNCTION__);
602 else
603 Start();
604
605 return err;
606}
607
614{
615 AVFrame *outframe = nullptr;
617 if (!outframe) {
618 LOGERROR("audio: %s: Error allocating frame", __FUNCTION__);
619 return NULL;
620 }
621
623
624 if (err == AVERROR(EAGAIN)) {
625// LOGERROR("audio: %s: Error filtering AVERROR(EAGAIN)", __FUNCTION__);
627 } else if (err == AVERROR_EOF) {
628 LOGERROR("audio: %s: Error filtering AVERROR_EOF", __FUNCTION__);
630 } else if (err < 0) {
631 LOGERROR("audio: %s: Error filtering the data", __FUNCTION__);
633 }
634
635 return outframe;
636}
637
647{
649// 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",
650// __FUNCTION__, m_filterReady,
651// m_pFilterGraph->sink_links_count, m_pFilterGraph->sink_links[0]->channels,
652// m_pFilterGraph->filters[m_pFilterGraph->nb_filters - 1]->nb_outputs,
653// 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,
654// m_filterChanged);
656 m_filterReady = 0;
657 LOGDEBUG2(L_SOUND, "audio: %s: Free the filter graph.", __FUNCTION__);
658 }
659
660 if (!m_filterReady) {
661 if (InitFilter(ctx)) {
662 LOGDEBUG2(L_SOUND, "audio: %s: AudioFilterReady failed!", __FUNCTION__);
663 return 1;
664 }
665 }
666
667 return 0;
668}
669
680{
682 int err = -1;
683 int err_count = 0;
684
685 if (inframe) {
686 while (err < 0) {
689 return;
690 }
691
693 if (err < 0) {
694 if (err_count) {
695 char errbuf[128];
696 av_strerror(err, errbuf, sizeof(errbuf));
697 LOGERROR("audio: %s: Error submitting the frame to the filter fmt %s channels %d %s", __FUNCTION__,
698 av_get_sample_fmt_name(ctx->sample_fmt), ctx->ch_layout.nb_channels, errbuf);
700 return;
701 } else {
702 m_filterChanged = 1;
703 err_count++;
704 LOGDEBUG2(L_SOUND, "audio: %s: m_filterChanged %d err_count %d", __FUNCTION__, m_filterChanged, err_count);
705 }
706 }
707 }
708 }
709
710// if (!inframe)
711// LOGDEBUG2(L_SOUND, "audio: %s: NO inframe!", __FUNCTION__);
712
715}
716
723{
724 std::lock_guard<std::mutex> lock(m_mutex);
725
726 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
727
728 if (!m_initialized)
729 return;
730
733
739 m_filterChanged = 1;
740}
741
746{
747 std::lock_guard<std::mutex> lock(m_mutex);
748
749 return m_pRingbuffer.UsedBytes();
750}
751
756{
757 std::lock_guard<std::mutex> lock(m_mutex);
758
760}
761
775{
776 std::lock_guard<std::mutex> lock(m_mutex);
777
778 return GetOutputPtsMsInternal();
779}
780
785
796{
797 std::lock_guard<std::mutex> lock(m_mutex);
798
800 return AV_NOPTS_VALUE;
801
803
804 // subtract baseline to ignore pause bursts already in the buffer
806
808}
809
816{
817 std::lock_guard<std::mutex> lock(m_mutex);
818
820 return AV_NOPTS_VALUE;
821
823
825}
826
840
847{
849 // reduce loudness for stereo output
852 if (volume < 0)
853 volume = 0;
854 else if (volume > 1000)
855 volume = 1000;
856 }
857
859 if (!m_softVolume)
861}
862
869{
870 std::lock_guard<std::mutex> lock(m_pauseMutex);
871 LOGDEBUG2(L_SOUND, "audio: %s: %d", __FUNCTION__, pause);
872
873 m_paused = pause;
874}
875
887
899
912
919{
921 SetVolume(m_volume); // update channel delta
922}
923
934{
935 if (!m_initialized) {
936 if (!m_alsa.Init())
937 LOGFATAL("audio: could not initialize alsa, abort!");
938 m_initialized = true;
939 }
940}
941
950{
951 LOGDEBUG2(L_SOUND, "audio: %s", __FUNCTION__);
952
953 Stop();
954
955 if (!m_initialized)
956 return;
957
959 m_alsa.Exit();
960 m_initialized = false;
961}
962
973
984
985/******************************************************************************
986 * Thread playback
987 *****************************************************************************/
988
994{
995 LOGDEBUG("audio: thread started");
996 while (Running()) {
999
1001 usleep(1000);
1002 else
1003 usleep(10000);
1004 }
1005 LOGDEBUG("audio: thread stopped");
1006}
1007
1012{
1013 if (!Active())
1014 return;
1015
1016 LOGDEBUG("audio: stopping thread");
1017 Cancel(2);
1018}
1019
1032{
1033 std::lock_guard<std::mutex> lock1(m_pauseMutex);
1034
1035 // do nothing in paused PCM mode
1037 return false;
1038
1039 int err = m_alsa.WaitUntilReady();
1040 if (err < 0) {
1041 if (m_alsa.HandleError(err)) {
1042 std::lock_guard<std::mutex> lock(m_queueMutex);
1044 }
1045 return false;
1046 } else if (err == 0) {
1047 return true;
1048 }
1049
1050 std::lock_guard<std::mutex> lock2(m_mutex);
1051
1054 return true; // ?? is this correct?
1055 else if (freeAlsaBufferFrames < 0) {
1057 std::lock_guard<std::mutex> lock(m_queueMutex);
1059 }
1060 return false;
1061 }
1062
1064 // only write, if there is space for a full pause burst
1067 return false;
1068
1069 // send a pause burst to keep the audio stream locked
1070 return SendPause();
1071 }
1072
1074}
1075
1076
1086{
1087 int bytesToWrite;
1089
1090 // query ringbuffer fill level
1091 const void *data;
1093
1095
1096 if (bytesToWrite == 0)
1097 return false;
1098
1099 // muting pass-through AC-3, can produce disturbance
1100 if (m_volume == 0 || (m_softVolume && !m_alsa.IsPassthroughActive())) {
1101 // FIXME: quick&dirty cast
1103 // FIXME: if not all are written, we double amplify them
1104 }
1105
1110
1112}
1113
1126
1127
1132{
1134 m_hwBaseline = 0;
1135
1137 return;
1138
1140
1141 LOGDEBUG2(L_SOUND, "audio: %s: first real audio was sent, hwBaseline %ld frames (%dms)", __FUNCTION__, m_hwBaseline, m_alsa.FramesToMs(m_hwBaseline));
1143 }
1144}
1145
1150{
1151 std::lock_guard<std::mutex> lock(m_mutex);
1152
1153 LOGDEBUG2(L_SOUND, "audio: %s: reset hw delay baseline to 0", __FUNCTION__);
1154 m_hwBaseline = 0;
1156}
1157
1162{
1163 std::lock_guard<std::mutex> lock(m_queueMutex);
1164 for (Event event : m_eventQueue)
1166
1167 m_eventQueue.clear();
1168}
1169
1180{
1182 return;
1183
1185 if (m_fillLevel.IsSettled()) {
1186 auto now = std::chrono::steady_clock::now();
1187 std::chrono::duration<double> elapsedSec = now - m_lastPidInvocation;
1189
1191 } else
1193
1194 if (m_packetCounter++ % 1000 == 0) {
1195 LOGDEBUG2(L_SOUND, "audio: %s: buffer fill level: %.1fms (target: %.1fms), clock drift compensating pitch: %.1fppm, PID controller: P=%.2fppm I=%.2fppm D=%.2fppm",
1199 m_pitchPpm.load(),
1203 }
1204
1205 // buffer fill level low pass filter
1207
1208 if (availableFrames >= 0)
1210}
ALSA Output Device Header File.
Audio Interface Header File.
Audio Manipulation Interface Header File.
virtual void OnEventReceived(const Event &)=0
double FramesToMsDouble(int frames)
Definition alsadevice.h:74
int GetBufferSizeFrames(void)
Definition alsadevice.h:55
size_t FramesToBytes(int frames)
Definition alsadevice.h:67
int GetHwSampleRate(void)
Definition alsadevice.h:58
int GetHwNumChannels(void)
Definition alsadevice.h:57
bool HandleError(int)
Handle an alsa error.
int64_t FramesToPts(int frames, double timebase)
Definition alsadevice.h:73
int Write(const void *, int)
Write data to the output device.
bool IsPassthroughActive(void)
Definition alsadevice.h:60
void Exit(void)
Cleanup the ALSA audio output module.
int GetAvailableBufferFrames(bool)
Get the number of frames that could be written to the device.
int GetHwDelayFrames(void)
Return the current hardware audio delay in frames.
int WaitUntilReady(void)
Wait until data can be written or read to/from the device (Timeout is 150ms currently)
int GetDownmix(void)
Definition alsadevice.h:56
int Setup(int, int, bool, int)
Setup ALSA audio for requested format.
int BytesToFrames(size_t bytes)
Definition alsadevice.h:68
void SetVolume(int)
Set alsa mixer volume (0-1000)
int64_t MsToPts(int64_t ptsMs, double timebase)
Definition alsadevice.h:70
bool IsRunning(void)
Definition alsadevice.h:59
void FlushBuffers(bool)
Flush ALSA buffers internally.
int64_t PtsToMs(int64_t pts, double timebase)
Definition alsadevice.h:69
int MsToFrames(int milliseconds)
Definition alsadevice.h:71
bool Init(void)
Initialize the ALSA audio output module.
bool CheckWrittenFrames(int, int)
Check, if all frames have been written.
int FramesToMs(int frames)
Definition alsadevice.h:72
void SetNormalizer(int)
Set normalize volume parameters.
void ResetNormalizer(void)
void Normalize(uint16_t *, int)
Normalize audio samples.
void SetEqualizer(int[18])
Set equalizer bands.
std::string GetEqualizerOptions(void) const
Get equalizer filter options.
void SetAmplifier(int volume)
void Amplify(int16_t *, int, int)
Amplify the samples in software.
void SetCompressor(int)
Set volume compression parameters.
void Compress(uint16_t *, int)
Compress audio samples.
void ResetCompressor(void)
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:933
AVRational m_timebase
AVCodecContext pkts_timebase.
Definition audio.h:122
cSoftHdAudio(cSoftHdDevice *)
Create a new audio context.
Definition audio.cpp:52
bool SendPause(void)
Write pause to passthrough device.
Definition audio.cpp:1119
void ResetHwDelayBaseline(void)
Reset the hw delay baseline.
Definition audio.cpp:1149
virtual void Action(void)
Audio thread loop, started with Start().
Definition audio.cpp:993
void Filter(AVFrame *, AVCodecContext *)
Send audio frame to filter and enqueue it.
Definition audio.cpp:679
cSoftHdRingbuffer m_pRingbuffer
sample ring buffer
Definition audio.h:167
int m_pitchAdjustFrameCounter
counter for pitch adjustment frames
Definition audio.h:118
int m_volume
current volume (0 .. 1000)
Definition audio.h:120
void SetHwDelayBaseline(void)
Set the hw delay baseline.
Definition audio.cpp:1131
void SetStereoDescent(int)
Set stereo loudness descent.
Definition audio.cpp:918
int GetUsedRingbufferMs(void)
Get used ms in audio ringbuffer.
Definition audio.cpp:755
int64_t GetHardwareOutputPtsMs(void)
Get the hardware output PTS in milliseconds.
Definition audio.cpp:795
int Setup(AVRational, int, int, bool)
Alsa setup wrapper.
Definition audio.cpp:582
std::mutex m_pauseMutex
mutex for a safe thread pausing
Definition audio.h:114
int64_t GetHardwareOutputPtsTimebaseUnits(void)
Get the hardware output PTS in timebase units.
Definition audio.cpp:832
AVFilterContext * m_pBuffersinkCtx
Definition audio.h:159
cPidController m_pidController
PID controller for clock drift compensation with tuning values coming from educated guesses.
Definition audio.h:107
void SetVolume(int)
Set mixer volume (0-1000)
Definition audio.cpp:846
void SetEqualizer(bool, int[18])
Set equalizer bands.
Definition audio.cpp:906
AVFilterContext * m_pBuffersrcCtx
Definition audio.h:158
AVFilterGraph * m_pFilterGraph
Definition audio.h:157
cBufferFillLevelLowPassFilter m_fillLevel
low pass filter for the buffer fill level
Definition audio.h:106
IEventReceiver * m_pEventReceiver
pointer to event receiver
Definition audio.h:105
void ProcessEvents(void)
Process queued events and forward them to event receiver.
Definition audio.cpp:1161
std::vector< Event > m_eventQueue
event queue for incoming events
Definition audio.h:116
bool SendAudio(int)
Write regular audio data from the ringbuffer to the hardware.
Definition audio.cpp:1085
void DropSamplesOlderThanPtsMs(int64_t)
Drop samples older than the given PTS.
Definition audio.cpp:421
void Enqueue(const uint16_t *, int, int64_t)
Send audio data to ringbuffer.
Definition audio.cpp:527
int64_t GetOutputPtsMs(void)
Get the output PTS of the ringbuffer.
Definition audio.cpp:774
AVFrame * FilterGetFrame(void)
Get frame from filter sink.
Definition audio.cpp:613
void ClockDriftCompensation(void)
Calculate clock drift compensation.
Definition audio.cpp:1179
int m_filterChanged
filter has changed
Definition audio.h:155
void SetCompression(bool, int)
Set volume compression parameters.
Definition audio.cpp:894
void Stop(void)
Stop the thread.
Definition audio.cpp:1011
int64_t m_inputPts
pts clock (last pts in ringbuffer)
Definition audio.h:124
int64_t GetOutputPtsMsInternal(void)
Definition audio.cpp:781
cSoftHdConfig * m_pConfig
pointer to config
Definition audio.h:103
std::atomic< double > m_pitchPpm
pitch adjustment in ppm. Positive values are faster
Definition audio.h:117
void Exit(void)
Cleanup audio output module (alsa)
Definition audio.cpp:949
void SetPaused(bool)
Set audio playback pause state.
Definition audio.cpp:868
int64_t GetHardwareOutputDelayMs(void)
Get the hardware delay in milliseconds.
Definition audio.cpp:815
bool m_useEqualizer
flag to use equalizer
Definition audio.h:148
int m_spdifBurstSize
size of the current spdif burst
Definition audio.h:128
int m_filterReady
filter is ready
Definition audio.h:156
void DropAlsaBuffers(void)
Drop alsa buffers.
Definition audio.cpp:977
bool HasInputPts(void)
Definition audio.h:70
cAlsaDevice m_alsa
alsa device
Definition audio.h:104
bool CyclicCall(void)
Cyclic audio playback call.
Definition audio.cpp:1031
int m_hwBaseline
saves the hw delay (pause bursts) once a real audio frame to correctly do the AV-Sync
Definition audio.h:130
bool m_initialized
class initialized
Definition audio.h:112
int m_stereoDescent
volume descent for stereo
Definition audio.h:121
std::mutex m_mutex
mutex for thread safety
Definition audio.h:113
static constexpr int AV_SYNC_BORDER_MS
absolute max a/v difference in ms which should trigger a resync
Definition audio.h:99
int m_packetCounter
packet counter for logging
Definition audio.h:109
int GetUsedRingbufferBytes(void)
Get used bytes in audio ringbuffer.
Definition audio.cpp:745
void EnqueueFrame(AVFrame *)
Place samples in audio output queue.
Definition audio.cpp:453
void FlushAlsaBuffers(void)
Flush alsa buffers.
Definition audio.cpp:966
void SetNormalize(bool, int)
Set normalize volume parameters.
Definition audio.cpp:882
bool m_useNormalizer
flag to use volume normalize
Definition audio.h:146
static constexpr int BYTES_PER_SAMPLE
number of bytes per sample
Definition audio.h:100
std::chrono::steady_clock::time_point m_lastPidInvocation
last time the PID controller was invoked
Definition audio.h:108
std::atomic< bool > m_paused
audio is paused
Definition audio.h:125
std::mutex m_queueMutex
mutex for queue safety
Definition audio.h:115
cAudioProcessor m_audioProcessor
Definition audio.h:145
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:646
int InitFilter(AVCodecContext *)
Init audio filters.
Definition audio.cpp:204
void RebuildPauseBurst(int)
Rebuild the pause spdif burst with the size of the last recognized normal spdif audio if size changed...
Definition audio.cpp:479
std::vector< uint16_t > m_pauseBurst
holds the burst data itself
Definition audio.h:129
void EnqueueSpdif(const uint16_t *, int, int64_t pts)
Enqueue prepared spdif bursts in audio output queue.
Definition audio.cpp:511
void FlushBuffers(void)
Flush audio buffers.
Definition audio.cpp:722
bool m_useCompressor
flag to use compress volume
Definition audio.h:147
bool m_softVolume
flag to use soft volume
Definition audio.h:127
bool ConfigAudioNormalize
config use normalize volume
Definition config.h:69
int ConfigAudioStereoDescent
config reduce stereo loudness
Definition config.h:73
bool ConfigAudioCompression
config use volume compression
Definition config.h:71
int ConfigAudioEqBand[18]
config equalizer filter bands
Definition config.h:77
int ConfigAudioMaxCompression
config max volume compression
Definition config.h:72
int ConfigAudioEq
config equalizer filter
Definition config.h:76
bool ConfigAudioDownmix
config ffmpeg audio downmix
Definition config.h:64
int ConfigAudioMaxNormalize
config max normalize factor
Definition config.h:70
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.
State Machine and Event Header File.
Low-pass Filter for Audio Buffer Fill Level Measurement Header File.
std::string BuildChannelMapFilter(const AVChannelLayout &)
Build the "|"-separated mappings list for the channelmap filter.
Definition audio.cpp:142
static std::vector< std::string > GetFFmpegChannelLayoutAsArray(const AVChannelLayout &layout)
Put FFmpeg channel layout in a dynamic array of strings.
Definition audio.cpp:81
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:106
std::vector< std::string > GetChannelLayoutAsArray(void)
Put ALSA channel layout in a dynamic array of strings.
static bool LayoutIsValid(const std::vector< std::string > &channelLayout)
Check, if the channel layout has channels named "NA" (N/A, silent)
Definition audio.cpp:128
#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
std::variant< PlayEvent, PauseEvent, StopEvent, TrickSpeedEvent, StillPictureEvent, DetachEvent, AttachEvent, BufferUnderrunEvent, BufferingThresholdReachedEvent, ScheduleResyncAtPtsMsEvent, ResyncEvent, DisplayChangeEvent > Event
Definition event.h:73
#define LOGWARNING
log to LOG_WARN
Definition logger.h:41
#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:31
Logger Header File.
Misc Functions.
PID (proportional, integral, derivative) Controller Header File.
Audio Ringbuffer Header File.
Output Device Header File.